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 + 31 files changed, 99837 insertions(+) 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 (limited to 'recipes/linux/linux-omap-pm-2.6.29') 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 0; /* success */ ++ ++ if (au_br_writable(br->br_perm)) { ++ /* remove whiteout base */ ++ err = au_br_init_wh(sb, br, mod->perm, mod->h_root); ++ if (unlikely(err)) ++ goto out; ++ ++ if (!au_br_writable(mod->perm)) { ++ /* rw --> ro, file might be mmapped */ ++ DiMustNoWaiters(root); ++ IiMustNoWaiters(root->d_inode); ++ di_write_unlock(root); ++ err = au_br_mod_files_ro(sb, bindex); ++ /* aufs_write_lock() calls ..._child() */ ++ di_write_lock_child(root); ++ ++ if (unlikely(err)) { ++ rerr = -ENOMEM; ++ br->br_wbr = kmalloc(sizeof(*br->br_wbr), ++ GFP_NOFS); ++ if (br->br_wbr) ++ rerr = au_br_init_wh ++ (sb, br, br->br_perm, ++ mod->h_root); ++ if (unlikely(rerr)) { ++ AuIOErr("nested error %d (%d)\n", ++ rerr, err); ++ br->br_perm = mod->perm; ++ } ++ } ++ } ++ } else if (au_br_writable(mod->perm)) { ++ /* ro --> rw */ ++ err = -ENOMEM; ++ br->br_wbr = kmalloc(sizeof(*br->br_wbr), GFP_NOFS); ++ if (br->br_wbr) { ++ struct path path = { ++ .mnt = br->br_mnt, ++ .dentry = mod->h_root ++ }; ++ ++ err = au_wbr_init(br, sb, mod->perm, &path); ++ if (unlikely(err)) { ++ kfree(br->br_wbr); ++ br->br_wbr = NULL; ++ } ++ } ++ } ++ ++ if (!err) { ++ *do_update |= need_sigen_inc(br->br_perm, mod->perm); ++ br->br_perm = mod->perm; ++ } ++ ++ out: ++ return err; ++} +diff -Naur a/fs/aufs/branch.h b/fs/aufs/branch.h +--- a/fs/aufs/branch.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/branch.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,215 @@ ++/* ++ * 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 filesystems and xino for them ++ */ ++ ++#ifndef __AUFS_BRANCH_H__ ++#define __AUFS_BRANCH_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include "rwsem.h" ++#include "super.h" ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* a xino file */ ++struct au_xino_file { ++ struct file *xi_file; ++ struct mutex xi_nondir_mtx; ++ ++ /* todo: make xino files an array to support huge inode number */ ++ ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *xi_dbgaufs; ++#endif ++}; ++ ++/* members for writable branch only */ ++enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last}; ++struct au_wbr { ++ struct rw_semaphore wbr_wh_rwsem; ++ struct dentry *wbr_wh[AuBrWh_Last]; ++ atomic_t wbr_wh_running; ++#define wbr_whbase wbr_wh[AuBrWh_BASE] /* whiteout base */ ++#define wbr_plink wbr_wh[AuBrWh_PLINK] /* pseudo-link dir */ ++#define wbr_orph wbr_wh[AuBrWh_ORPH] /* dir for orphans */ ++ ++ /* mfs mode */ ++ unsigned long long wbr_bytes; ++}; ++ ++/* protected by superblock rwsem */ ++struct au_branch { ++ struct au_xino_file br_xino; ++ ++ aufs_bindex_t br_id; ++ ++ int br_perm; ++ struct vfsmount *br_mnt; ++ atomic_t br_count; ++ ++ struct au_wbr *br_wbr; ++ ++ /* xino truncation */ ++ blkcnt_t br_xino_upper; /* watermark in blocks */ ++ atomic_t br_xino_running; ++ ++#ifdef CONFIG_SYSFS ++ /* an entry under sysfs per mount-point */ ++ char br_name[8]; ++ struct attribute br_attr; ++#endif ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* branch permission and attribute */ ++enum { ++ AuBrPerm_RW, /* writable, linkable wh */ ++ AuBrPerm_RO, /* readonly, no wh */ ++ AuBrPerm_RR, /* natively readonly, no wh */ ++ ++ AuBrPerm_RWNoLinkWH, /* un-linkable whiteouts */ ++ ++ AuBrPerm_ROWH, /* whiteout-able */ ++ AuBrPerm_RRWH, /* whiteout-able */ ++ ++ AuBrPerm_Last ++}; ++ ++static inline int au_br_writable(int brperm) ++{ ++ return brperm == AuBrPerm_RW || brperm == AuBrPerm_RWNoLinkWH; ++} ++ ++static inline int au_br_whable(int brperm) ++{ ++ return brperm == AuBrPerm_RW ++ || brperm == AuBrPerm_ROWH ++ || brperm == AuBrPerm_RRWH; ++} ++ ++static inline int au_br_rdonly(struct au_branch *br) ++{ ++ return ((br->br_mnt->mnt_sb->s_flags & MS_RDONLY) ++ || !au_br_writable(br->br_perm)) ++ ? -EROFS : 0; ++} ++ ++static inline int au_br_hinotifyable(int brperm __maybe_unused) ++{ ++#ifdef CONFIG_AUFS_HINOTIFY ++ return brperm != AuBrPerm_RR && brperm != AuBrPerm_RRWH; ++#else ++ return 0; ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* branch.c */ ++struct au_sbinfo; ++void au_br_free(struct au_sbinfo *sinfo); ++int au_br_index(struct super_block *sb, aufs_bindex_t br_id); ++struct au_opt_add; ++int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount); ++struct au_opt_del; ++int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount); ++struct au_opt_mod; ++int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, ++ int *do_update); ++ ++/* xino.c */ ++static const loff_t au_loff_max = LLONG_MAX; ++ ++int au_xib_trunc(struct super_block *sb); ++ssize_t xino_fread(au_readf_t func, struct file *file, void *buf, size_t size, ++ loff_t *pos); ++ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size, ++ loff_t *pos); ++struct file *au_xino_create2(struct file *base_file, struct file *copy_src); ++struct file *au_xino_create(struct super_block *sb, char *fname, int silent); ++ino_t au_xino_new_ino(struct super_block *sb); ++int au_xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t ino); ++int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t ino); ++int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t *ino); ++int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t hino, ++ struct file *base_file, int do_test); ++int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex); ++ ++struct au_opt_xino; ++int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount); ++void au_xino_clr(struct super_block *sb); ++struct file *au_xino_def(struct super_block *sb); ++int au_xino_path(struct seq_file *seq, struct file *file); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* Superblock to branch */ ++static inline ++aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_sbr(sb, bindex)->br_id; ++} ++ ++static inline ++struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_sbr(sb, bindex)->br_mnt; ++} ++ ++static inline ++struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_sbr_mnt(sb, bindex)->mnt_sb; ++} ++ ++static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ atomic_dec(&au_sbr(sb, bindex)->br_count); ++} ++ ++static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_sbr(sb, bindex)->br_perm; ++} ++ ++static inline int au_sbr_whable(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_br_whable(au_sbr_perm(sb, bindex)); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * wbr_wh_read_lock, wbr_wh_write_lock ++ * wbr_wh_read_unlock, wbr_wh_write_unlock, wbr_wh_downgrade_lock ++ */ ++AuSimpleRwsemFuncs(wbr_wh, struct au_wbr *wbr, &wbr->wbr_wh_rwsem); ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_BRANCH_H__ */ +diff -Naur a/fs/aufs/cpup.c b/fs/aufs/cpup.c +--- a/fs/aufs/cpup.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/cpup.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,1039 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * copy-up functions, see wbr_policy.c for copy-down ++ */ ++ ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++void au_cpup_attr_flags(struct inode *dst, struct inode *src) ++{ ++ const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE ++ | S_NOATIME | S_NOCMTIME; ++ ++ dst->i_flags |= src->i_flags & ~mask; ++ if (au_test_fs_notime(dst->i_sb)) ++ dst->i_flags |= S_NOATIME | S_NOCMTIME; ++} ++ ++void au_cpup_attr_timesizes(struct inode *inode) ++{ ++ struct inode *h_inode; ++ ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ fsstack_copy_attr_times(inode, h_inode); ++ vfsub_copy_inode_size(inode, h_inode); ++} ++ ++void au_cpup_attr_nlink(struct inode *inode, int force) ++{ ++ struct inode *h_inode; ++ struct super_block *sb; ++ aufs_bindex_t bindex, bend; ++ ++ sb = inode->i_sb; ++ bindex = au_ibstart(inode); ++ h_inode = au_h_iptr(inode, bindex); ++ if (!force ++ && !S_ISDIR(h_inode->i_mode) ++ && au_opt_test(au_mntflags(sb), PLINK) ++ && au_plink_test(inode)) ++ return; ++ ++ inode->i_nlink = h_inode->i_nlink; ++ ++ /* ++ * fewer nlink makes find(1) noisy, but larger nlink doesn't. ++ * it may includes whplink directory. ++ */ ++ if (S_ISDIR(h_inode->i_mode)) { ++ bend = au_ibend(inode); ++ for (bindex++; bindex <= bend; bindex++) { ++ h_inode = au_h_iptr(inode, bindex); ++ if (h_inode) ++ au_add_nlink(inode, h_inode); ++ } ++ } ++} ++ ++void au_cpup_attr_changeable(struct inode *inode) ++{ ++ struct inode *h_inode; ++ ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ inode->i_mode = h_inode->i_mode; ++ inode->i_uid = h_inode->i_uid; ++ inode->i_gid = h_inode->i_gid; ++ au_cpup_attr_timesizes(inode); ++ au_cpup_attr_flags(inode, h_inode); ++} ++ ++void au_cpup_igen(struct inode *inode, struct inode *h_inode) ++{ ++ struct au_iinfo *iinfo = au_ii(inode); ++ ++ iinfo->ii_higen = h_inode->i_generation; ++ iinfo->ii_hsb1 = h_inode->i_sb; ++} ++ ++void au_cpup_attr_all(struct inode *inode, int force) ++{ ++ struct inode *h_inode; ++ ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ au_cpup_attr_changeable(inode); ++ if (inode->i_nlink > 0) ++ au_cpup_attr_nlink(inode, force); ++ inode->i_rdev = h_inode->i_rdev; ++ inode->i_blkbits = h_inode->i_blkbits; ++ au_cpup_igen(inode, h_inode); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* Note: dt_dentry and dt_h_dentry are not dget/dput-ed */ ++ ++/* keep the timestamps of the parent dir when cpup */ ++void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, ++ struct path *h_path) ++{ ++ struct inode *h_inode; ++ ++ dt->dt_dentry = dentry; ++ dt->dt_h_path = *h_path; ++ h_inode = h_path->dentry->d_inode; ++ dt->dt_atime = h_inode->i_atime; ++ dt->dt_mtime = h_inode->i_mtime; ++ /* smp_mb(); */ ++} ++ ++void au_dtime_revert(struct au_dtime *dt) ++{ ++ struct iattr attr; ++ int err; ++ ++ attr.ia_atime = dt->dt_atime; ++ attr.ia_mtime = dt->dt_mtime; ++ attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET ++ | ATTR_ATIME | ATTR_ATIME_SET; ++ ++ err = vfsub_notify_change(&dt->dt_h_path, &attr); ++ if (unlikely(err)) ++ AuWarn("restoring timestamps failed(%d). ignored\n", err); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static noinline_for_stack ++int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src) ++{ ++ int err, sbits; ++ struct iattr ia; ++ struct path h_path; ++ struct inode *h_isrc; ++ ++ h_path.dentry = au_h_dptr(dst, bindex); ++ h_path.mnt = au_sbr_mnt(dst->d_sb, bindex); ++ h_isrc = h_src->d_inode; ++ ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID ++ | ATTR_ATIME | ATTR_MTIME ++ | ATTR_ATIME_SET | ATTR_MTIME_SET; ++ ia.ia_mode = h_isrc->i_mode; ++ ia.ia_uid = h_isrc->i_uid; ++ ia.ia_gid = h_isrc->i_gid; ++ ia.ia_atime = h_isrc->i_atime; ++ ia.ia_mtime = h_isrc->i_mtime; ++ sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); ++ au_cpup_attr_flags(h_path.dentry->d_inode, h_isrc); ++ err = vfsub_notify_change(&h_path, &ia); ++ ++ /* is this nfs only? */ ++ if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) { ++ ia.ia_valid = ATTR_FORCE | ATTR_MODE; ++ ia.ia_mode = h_isrc->i_mode; ++ err = vfsub_notify_change(&h_path, &ia); ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_copy_file(struct file *dst, struct file *src, loff_t len, ++ char *buf, unsigned long blksize) ++{ ++ int err; ++ size_t sz, rbytes, wbytes; ++ unsigned char all_zero; ++ char *p, *zp; ++ struct mutex *h_mtx; ++ /* reduce stack usage */ ++ struct iattr *ia; ++ ++ zp = page_address(ZERO_PAGE(0)); ++ if (unlikely(!zp)) ++ return -ENOMEM; /* possible? */ ++ ++ err = 0; ++ all_zero = 0; ++ while (len) { ++ AuDbg("len %lld\n", len); ++ sz = blksize; ++ if (len < blksize) ++ sz = len; ++ ++ rbytes = 0; ++ /* todo: signal_pending? */ ++ while (!rbytes || err == -EAGAIN || err == -EINTR) { ++ rbytes = vfsub_read_k(src, buf, sz, &src->f_pos); ++ err = rbytes; ++ } ++ if (unlikely(err < 0)) ++ break; ++ ++ all_zero = 0; ++ if (len >= rbytes && rbytes == blksize) ++ all_zero = !memcmp(buf, zp, rbytes); ++ if (!all_zero) { ++ wbytes = rbytes; ++ p = buf; ++ while (wbytes) { ++ size_t b; ++ ++ b = vfsub_write_k(dst, p, wbytes, &dst->f_pos); ++ err = b; ++ /* todo: signal_pending? */ ++ if (unlikely(err == -EAGAIN || err == -EINTR)) ++ continue; ++ if (unlikely(err < 0)) ++ break; ++ wbytes -= b; ++ p += b; ++ } ++ } else { ++ loff_t res; ++ ++ AuLabel(hole); ++ res = vfsub_llseek(dst, rbytes, SEEK_CUR); ++ err = res; ++ if (unlikely(res < 0)) ++ break; ++ } ++ len -= rbytes; ++ err = 0; ++ } ++ ++ /* the last block may be a hole */ ++ if (!err && all_zero) { ++ AuLabel(last hole); ++ ++ err = 1; ++ if (au_test_nfs(dst->f_dentry->d_sb)) { ++ /* nfs requires this step to make last hole */ ++ /* is this only nfs? */ ++ do { ++ /* todo: signal_pending? */ ++ err = vfsub_write_k(dst, "\0", 1, &dst->f_pos); ++ } while (err == -EAGAIN || err == -EINTR); ++ if (err == 1) ++ dst->f_pos--; ++ } ++ ++ if (err == 1) { ++ ia = (void *)buf; ++ ia->ia_size = dst->f_pos; ++ ia->ia_valid = ATTR_SIZE | ATTR_FILE; ++ ia->ia_file = dst; ++ h_mtx = &dst->f_dentry->d_inode->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); ++ err = vfsub_notify_change(&dst->f_path, ia); ++ mutex_unlock(h_mtx); ++ } ++ } ++ ++ return err; ++} ++ ++int au_copy_file(struct file *dst, struct file *src, loff_t len) ++{ ++ int err; ++ unsigned long blksize; ++ unsigned char do_kfree; ++ char *buf; ++ ++ err = -ENOMEM; ++ blksize = dst->f_dentry->d_sb->s_blocksize; ++ if (!blksize || PAGE_SIZE < blksize) ++ blksize = PAGE_SIZE; ++ AuDbg("blksize %lu\n", blksize); ++ do_kfree = (blksize != PAGE_SIZE && blksize >= sizeof(struct iattr *)); ++ if (do_kfree) ++ buf = kmalloc(blksize, GFP_NOFS); ++ else ++ buf = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!buf)) ++ goto out; ++ ++ if (len > (1 << 22)) ++ AuDbg("copying a large file %lld\n", (long long)len); ++ ++ src->f_pos = 0; ++ dst->f_pos = 0; ++ err = au_do_copy_file(dst, src, len, buf, blksize); ++ if (do_kfree) ++ kfree(buf); ++ else ++ free_page((unsigned long)buf); ++ ++ out: ++ return err; ++} ++ ++/* ++ * to support a sparse file which is opened with O_APPEND, ++ * we need to close the file. ++ */ ++static int au_cp_regular(struct dentry *dentry, aufs_bindex_t bdst, ++ aufs_bindex_t bsrc, loff_t len) ++{ ++ int err, i; ++ enum { SRC, DST }; ++ struct { ++ aufs_bindex_t bindex; ++ unsigned int flags; ++ struct dentry *dentry; ++ struct file *file; ++ void *label, *label_file; ++ } *f, file[] = { ++ { ++ .bindex = bsrc, ++ .flags = O_RDONLY | O_NOATIME | O_LARGEFILE, ++ .file = NULL, ++ .label = &&out, ++ .label_file = &&out_src ++ }, ++ { ++ .bindex = bdst, ++ .flags = O_WRONLY | O_NOATIME | O_LARGEFILE, ++ .file = NULL, ++ .label = &&out_src, ++ .label_file = &&out_dst ++ } ++ }; ++ struct super_block *sb; ++ ++ /* bsrc branch can be ro/rw. */ ++ sb = dentry->d_sb; ++ f = file; ++ for (i = 0; i < 2; i++, f++) { ++ f->dentry = au_h_dptr(dentry, f->bindex); ++ f->file = au_h_open(dentry, f->bindex, f->flags, /*file*/NULL); ++ err = PTR_ERR(f->file); ++ if (IS_ERR(f->file)) ++ goto *f->label; ++ err = -EINVAL; ++ if (unlikely(!f->file->f_op)) ++ goto *f->label_file; ++ } ++ ++ /* try stopping to update while we copyup */ ++ IMustLock(file[SRC].dentry->d_inode); ++ err = au_copy_file(file[DST].file, file[SRC].file, len); ++ ++ out_dst: ++ fput(file[DST].file); ++ au_sbr_put(sb, file[DST].bindex); ++ out_src: ++ fput(file[SRC].file); ++ au_sbr_put(sb, file[SRC].bindex); ++ out: ++ return err; ++} ++ ++static int au_do_cpup_regular(struct dentry *dentry, aufs_bindex_t bdst, ++ aufs_bindex_t bsrc, loff_t len, ++ struct inode *h_dir, struct path *h_path) ++{ ++ int err, rerr; ++ loff_t l; ++ ++ err = 0; ++ l = i_size_read(au_h_iptr(dentry->d_inode, bsrc)); ++ if (len == -1 || l < len) ++ len = l; ++ if (len) ++ err = au_cp_regular(dentry, bdst, bsrc, len); ++ if (!err) ++ goto out; /* success */ ++ ++ rerr = vfsub_unlink(h_dir, h_path, /*force*/0); ++ if (rerr) { ++ AuIOErr("failed unlinking cpup-ed %.*s(%d, %d)\n", ++ AuDLNPair(h_path->dentry), err, rerr); ++ err = -EIO; ++ } ++ ++ out: ++ return err; ++} ++ ++static int au_do_cpup_symlink(struct path *h_path, struct dentry *h_src, ++ struct inode *h_dir) ++{ ++ int err, symlen; ++ mm_segment_t old_fs; ++ char *sym; ++ ++ err = -ENOSYS; ++ if (unlikely(!h_src->d_inode->i_op->readlink)) ++ goto out; ++ ++ err = -ENOMEM; ++ sym = __getname(); ++ if (unlikely(!sym)) ++ goto out; ++ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ symlen = h_src->d_inode->i_op->readlink(h_src, (char __user *)sym, ++ PATH_MAX); ++ err = symlen; ++ set_fs(old_fs); ++ ++ if (symlen > 0) { ++ sym[symlen] = 0; ++ err = vfsub_symlink(h_dir, h_path, sym); ++ } ++ __putname(sym); ++ ++ out: ++ return err; ++} ++ ++/* return with the lower dst inode is locked */ ++static noinline_for_stack ++int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst, ++ aufs_bindex_t bsrc, loff_t len, unsigned int flags, ++ struct dentry *dst_parent) ++{ ++ int err; ++ umode_t mode; ++ unsigned int mnt_flags; ++ unsigned char isdir; ++ const unsigned char do_dt = !!au_ftest_cpup(flags, DTIME); ++ struct au_dtime dt; ++ struct path h_path; ++ struct dentry *h_src, *h_dst, *h_parent; ++ struct inode *h_inode, *h_dir; ++ struct super_block *sb; ++ ++ /* bsrc branch can be ro/rw. */ ++ h_src = au_h_dptr(dentry, bsrc); ++ h_inode = h_src->d_inode; ++ AuDebugOn(h_inode != au_h_iptr(dentry->d_inode, bsrc)); ++ ++ /* try stopping to be referenced while we are creating */ ++ h_dst = au_h_dptr(dentry, bdst); ++ h_parent = h_dst->d_parent; /* dir inode is locked */ ++ h_dir = h_parent->d_inode; ++ IMustLock(h_dir); ++ AuDebugOn(h_parent != h_dst->d_parent); ++ ++ sb = dentry->d_sb; ++ h_path.mnt = au_sbr_mnt(sb, bdst); ++ if (do_dt) { ++ h_path.dentry = h_parent; ++ au_dtime_store(&dt, dst_parent, &h_path); ++ } ++ h_path.dentry = h_dst; ++ ++ isdir = 0; ++ mode = h_inode->i_mode; ++ switch (mode & S_IFMT) { ++ case S_IFREG: ++ /* try stopping to update while we are referencing */ ++ IMustLock(h_inode); ++ err = vfsub_create(h_dir, &h_path, mode | S_IWUSR); ++ if (!err) ++ err = au_do_cpup_regular ++ (dentry, bdst, bsrc, len, ++ au_h_iptr(dst_parent->d_inode, bdst), &h_path); ++ break; ++ case S_IFDIR: ++ isdir = 1; ++ err = vfsub_mkdir(h_dir, &h_path, mode); ++ if (!err) { ++ /* ++ * strange behaviour from the users view, ++ * particularry setattr case ++ */ ++ if (au_ibstart(dst_parent->d_inode) == bdst) ++ au_cpup_attr_nlink(dst_parent->d_inode, ++ /*force*/1); ++ au_cpup_attr_nlink(dentry->d_inode, /*force*/1); ++ } ++ break; ++ case S_IFLNK: ++ err = au_do_cpup_symlink(&h_path, h_src, h_dir); ++ break; ++ case S_IFCHR: ++ case S_IFBLK: ++ AuDebugOn(!capable(CAP_MKNOD)); ++ /*FALLTHROUGH*/ ++ case S_IFIFO: ++ case S_IFSOCK: ++ err = vfsub_mknod(h_dir, &h_path, mode, h_inode->i_rdev); ++ break; ++ default: ++ AuIOErr("Unknown inode type 0%o\n", mode); ++ err = -EIO; ++ } ++ ++ mnt_flags = au_mntflags(sb); ++ if (!au_opt_test(mnt_flags, UDBA_NONE) ++ && !isdir ++ && au_opt_test(mnt_flags, XINO) ++ && h_inode->i_nlink == 1 ++ /* todo: unnecessary? */ ++ /* && dentry->d_inode->i_nlink == 1 */ ++ && bdst < bsrc ++ && !au_ftest_cpup(flags, KEEPLINO)) ++ au_xino_write0(sb, bsrc, h_inode->i_ino, /*ino*/0); ++ /* ignore this error */ ++ ++ if (do_dt) ++ au_dtime_revert(&dt); ++ return err; ++} ++ ++/* ++ * copyup the @dentry from @bsrc to @bdst. ++ * the caller must set the both of lower dentries. ++ * @len is for truncating when it is -1 copyup the entire file. ++ * in link/rename cases, @dst_parent may be different from the real one. ++ */ ++static int au_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, ++ aufs_bindex_t bsrc, loff_t len, unsigned int flags, ++ struct dentry *dst_parent) ++{ ++ int err, rerr; ++ aufs_bindex_t old_ibstart; ++ unsigned char isdir, plink; ++ struct au_dtime dt; ++ struct path h_path; ++ struct dentry *h_src, *h_dst, *h_parent; ++ struct inode *dst_inode, *h_dir, *inode; ++ struct super_block *sb; ++ ++ AuDebugOn(bsrc <= bdst); ++ ++ sb = dentry->d_sb; ++ h_path.mnt = au_sbr_mnt(sb, bdst); ++ h_dst = au_h_dptr(dentry, bdst); ++ h_parent = h_dst->d_parent; /* dir inode is locked */ ++ h_dir = h_parent->d_inode; ++ IMustLock(h_dir); ++ ++ h_src = au_h_dptr(dentry, bsrc); ++ inode = dentry->d_inode; ++ ++ if (!dst_parent) ++ dst_parent = dget_parent(dentry); ++ else ++ dget(dst_parent); ++ ++ plink = !!au_opt_test(au_mntflags(sb), PLINK); ++ dst_inode = au_h_iptr(inode, bdst); ++ if (dst_inode) { ++ if (unlikely(!plink)) { ++ err = -EIO; ++ AuIOErr("i%lu exists on a upper branch " ++ "but plink is disabled\n", inode->i_ino); ++ goto out; ++ } ++ ++ if (dst_inode->i_nlink) { ++ const int do_dt = au_ftest_cpup(flags, DTIME); ++ ++ h_src = au_plink_lkup(inode, bdst); ++ err = PTR_ERR(h_src); ++ if (IS_ERR(h_src)) ++ goto out; ++ if (unlikely(!h_src->d_inode)) { ++ err = -EIO; ++ AuIOErr("i%lu exists on a upper branch " ++ "but plink is broken\n", inode->i_ino); ++ dput(h_src); ++ goto out; ++ } ++ ++ if (do_dt) { ++ h_path.dentry = h_parent; ++ au_dtime_store(&dt, dst_parent, &h_path); ++ } ++ h_path.dentry = h_dst; ++ err = vfsub_link(h_src, h_dir, &h_path); ++ if (do_dt) ++ au_dtime_revert(&dt); ++ dput(h_src); ++ goto out; ++ } else ++ /* todo: cpup_wh_file? */ ++ /* udba work */ ++ au_update_brange(inode, 1); ++ } ++ ++ old_ibstart = au_ibstart(inode); ++ err = cpup_entry(dentry, bdst, bsrc, len, flags, dst_parent); ++ if (unlikely(err)) ++ goto out; ++ dst_inode = h_dst->d_inode; ++ mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2); ++ ++ err = cpup_iattr(dentry, bdst, h_src); ++ isdir = S_ISDIR(dst_inode->i_mode); ++ if (!err) { ++ if (bdst < old_ibstart) ++ au_set_ibstart(inode, bdst); ++ au_set_h_iptr(inode, bdst, au_igrab(dst_inode), ++ au_hi_flags(inode, isdir)); ++ mutex_unlock(&dst_inode->i_mutex); ++ if (!isdir ++ && h_src->d_inode->i_nlink > 1 ++ && plink) ++ au_plink_append(inode, bdst, h_dst); ++ goto out; /* success */ ++ } ++ ++ /* revert */ ++ h_path.dentry = h_parent; ++ mutex_unlock(&dst_inode->i_mutex); ++ au_dtime_store(&dt, dst_parent, &h_path); ++ h_path.dentry = h_dst; ++ if (!isdir) ++ rerr = vfsub_unlink(h_dir, &h_path, /*force*/0); ++ else ++ rerr = vfsub_rmdir(h_dir, &h_path); ++ au_dtime_revert(&dt); ++ if (rerr) { ++ AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr); ++ err = -EIO; ++ } ++ ++ out: ++ dput(dst_parent); ++ return err; ++} ++ ++struct au_cpup_single_args { ++ int *errp; ++ struct dentry *dentry; ++ aufs_bindex_t bdst, bsrc; ++ loff_t len; ++ unsigned int flags; ++ struct dentry *dst_parent; ++}; ++ ++static void au_call_cpup_single(void *args) ++{ ++ struct au_cpup_single_args *a = args; ++ *a->errp = au_cpup_single(a->dentry, a->bdst, a->bsrc, a->len, ++ a->flags, a->dst_parent); ++} ++ ++int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, ++ aufs_bindex_t bsrc, loff_t len, unsigned int flags, ++ struct dentry *dst_parent) ++{ ++ int err, wkq_err; ++ umode_t mode; ++ struct dentry *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bsrc); ++ mode = h_dentry->d_inode->i_mode & S_IFMT; ++ if ((mode != S_IFCHR && mode != S_IFBLK) ++ || capable(CAP_MKNOD)) ++ err = au_cpup_single(dentry, bdst, bsrc, len, flags, ++ dst_parent); ++ else { ++ struct au_cpup_single_args args = { ++ .errp = &err, ++ .dentry = dentry, ++ .bdst = bdst, ++ .bsrc = bsrc, ++ .len = len, ++ .flags = flags, ++ .dst_parent = dst_parent ++ }; ++ wkq_err = au_wkq_wait(au_call_cpup_single, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} ++ ++/* ++ * copyup the @dentry from the first active lower branch to @bdst, ++ * using au_cpup_single(). ++ */ ++static int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, ++ unsigned int flags) ++{ ++ int err; ++ aufs_bindex_t bsrc, bend; ++ ++ bend = au_dbend(dentry); ++ for (bsrc = bdst + 1; bsrc <= bend; bsrc++) ++ if (au_h_dptr(dentry, bsrc)) ++ break; ++ ++ err = au_lkup_neg(dentry, bdst); ++ if (!err) { ++ err = au_cpup_single(dentry, bdst, bsrc, len, flags, NULL); ++ if (!err) ++ return 0; /* success */ ++ ++ /* revert */ ++ au_set_h_dptr(dentry, bdst, NULL); ++ au_set_dbstart(dentry, bsrc); ++ } ++ ++ return err; ++} ++ ++struct au_cpup_simple_args { ++ int *errp; ++ struct dentry *dentry; ++ aufs_bindex_t bdst; ++ loff_t len; ++ unsigned int flags; ++}; ++ ++static void au_call_cpup_simple(void *args) ++{ ++ struct au_cpup_simple_args *a = args; ++ *a->errp = au_cpup_simple(a->dentry, a->bdst, a->len, a->flags); ++} ++ ++int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, ++ unsigned int flags) ++{ ++ int err, wkq_err; ++ unsigned char do_sio; ++ struct dentry *parent; ++ struct inode *h_dir; ++ ++ parent = dget_parent(dentry); ++ h_dir = au_h_iptr(parent->d_inode, bdst); ++ do_sio = !!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE); ++ if (!do_sio) { ++ /* ++ * testing CAP_MKNOD is for generic fs, ++ * but CAP_FSETID is for xfs only, currently. ++ */ ++ umode_t mode = dentry->d_inode->i_mode; ++ do_sio = (((mode & (S_IFCHR | S_IFBLK)) ++ && !capable(CAP_MKNOD)) ++ || ((mode & (S_ISUID | S_ISGID)) ++ && !capable(CAP_FSETID))); ++ } ++ if (!do_sio) ++ err = au_cpup_simple(dentry, bdst, len, flags); ++ else { ++ struct au_cpup_simple_args args = { ++ .errp = &err, ++ .dentry = dentry, ++ .bdst = bdst, ++ .len = len, ++ .flags = flags ++ }; ++ wkq_err = au_wkq_wait(au_call_cpup_simple, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ dput(parent); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * copyup the deleted file for writing. ++ */ ++static int au_do_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, ++ struct dentry *wh_dentry, struct file *file, ++ loff_t len) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ struct au_dinfo *dinfo; ++ struct dentry *h_d_dst, *h_d_start; ++ ++ dinfo = au_di(dentry); ++ bstart = dinfo->di_bstart; ++ h_d_dst = dinfo->di_hdentry[0 + bdst].hd_dentry; ++ dinfo->di_bstart = bdst; ++ dinfo->di_hdentry[0 + bdst].hd_dentry = wh_dentry; ++ h_d_start = dinfo->di_hdentry[0 + bstart].hd_dentry; ++ if (file) ++ dinfo->di_hdentry[0 + bstart].hd_dentry ++ = au_h_fptr(file, au_fbstart(file))->f_dentry; ++ err = au_cpup_single(dentry, bdst, bstart, len, !AuCpup_DTIME, ++ /*h_parent*/NULL); ++ if (!err && file) { ++ err = au_reopen_nondir(file); ++ dinfo->di_hdentry[0 + bstart].hd_dentry = h_d_start; ++ } ++ dinfo->di_hdentry[0 + bdst].hd_dentry = h_d_dst; ++ dinfo->di_bstart = bstart; ++ ++ return err; ++} ++ ++static int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, ++ struct file *file) ++{ ++ int err; ++ struct au_dtime dt; ++ struct dentry *parent, *h_parent, *wh_dentry; ++ struct au_branch *br; ++ struct path h_path; ++ ++ br = au_sbr(dentry->d_sb, bdst); ++ parent = dget_parent(dentry); ++ h_parent = au_h_dptr(parent, bdst); ++ wh_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out; ++ ++ h_path.dentry = h_parent; ++ h_path.mnt = br->br_mnt; ++ au_dtime_store(&dt, parent, &h_path); ++ err = au_do_cpup_wh(dentry, bdst, wh_dentry, file, len); ++ if (unlikely(err)) ++ goto out_wh; ++ ++ dget(wh_dentry); ++ h_path.dentry = wh_dentry; ++ err = vfsub_unlink(h_parent->d_inode, &h_path, /*force*/0); ++ if (unlikely(err)) { ++ AuIOErr("failed remove copied-up tmp file %.*s(%d)\n", ++ AuDLNPair(wh_dentry), err); ++ err = -EIO; ++ } ++ au_dtime_revert(&dt); ++ au_set_hi_wh(dentry->d_inode, bdst, wh_dentry); ++ ++ out_wh: ++ dput(wh_dentry); ++ out: ++ dput(parent); ++ return err; ++} ++ ++struct au_cpup_wh_args { ++ int *errp; ++ struct dentry *dentry; ++ aufs_bindex_t bdst; ++ loff_t len; ++ struct file *file; ++}; ++ ++static void au_call_cpup_wh(void *args) ++{ ++ struct au_cpup_wh_args *a = args; ++ *a->errp = au_cpup_wh(a->dentry, a->bdst, a->len, a->file); ++} ++ ++int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, ++ struct file *file) ++{ ++ int err, wkq_err; ++ struct dentry *parent, *h_orph, *h_parent, *h_dentry; ++ struct inode *dir, *h_dir, *h_tmpdir, *h_inode; ++ struct au_wbr *wbr; ++ ++ parent = dget_parent(dentry); ++ dir = parent->d_inode; ++ h_orph = NULL; ++ h_parent = NULL; ++ h_dir = au_igrab(au_h_iptr(dir, bdst)); ++ h_tmpdir = h_dir; ++ if (!h_dir->i_nlink) { ++ wbr = au_sbr(dentry->d_sb, bdst)->br_wbr; ++ h_orph = wbr->wbr_orph; ++ ++ h_parent = dget(au_h_dptr(parent, bdst)); ++ au_set_h_dptr(parent, bdst, NULL); ++ au_set_h_dptr(parent, bdst, dget(h_orph)); ++ h_tmpdir = h_orph->d_inode; ++ au_set_h_iptr(dir, bdst, NULL, 0); ++ au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0); ++ ++ /* this temporary unlock is safe */ ++ if (file) ++ h_dentry = au_h_fptr(file, au_fbstart(file))->f_dentry; ++ else ++ h_dentry = au_h_dptr(dentry, au_dbstart(dentry)); ++ h_inode = h_dentry->d_inode; ++ IMustLock(h_inode); ++ mutex_unlock(&h_inode->i_mutex); ++ mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT2); ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ } ++ ++ if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE)) ++ err = au_cpup_wh(dentry, bdst, len, file); ++ else { ++ struct au_cpup_wh_args args = { ++ .errp = &err, ++ .dentry = dentry, ++ .bdst = bdst, ++ .len = len, ++ .file = file ++ }; ++ wkq_err = au_wkq_wait(au_call_cpup_wh, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ if (h_orph) { ++ mutex_unlock(&h_tmpdir->i_mutex); ++ au_set_h_iptr(dir, bdst, NULL, 0); ++ au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0); ++ au_set_h_dptr(parent, bdst, NULL); ++ au_set_h_dptr(parent, bdst, h_parent); ++ } ++ iput(h_dir); ++ dput(parent); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * generic routine for both of copy-up and copy-down. ++ */ ++/* cf. revalidate function in file.c */ ++int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, ++ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, ++ struct dentry *h_parent, void *arg), ++ void *arg) ++{ ++ int err; ++ struct au_pin pin; ++ struct dentry *d, *parent, *h_parent, *real_parent; ++ ++ err = 0; ++ parent = dget_parent(dentry); ++ if (IS_ROOT(parent)) ++ goto out; ++ ++ au_pin_init(&pin, dentry, bdst, AuLsc_DI_PARENT2, AuLsc_I_PARENT2, ++ au_opt_udba(dentry->d_sb), AuPin_MNT_WRITE); ++ ++ /* do not use au_dpage */ ++ real_parent = parent; ++ while (1) { ++ dput(parent); ++ parent = dget_parent(dentry); ++ h_parent = au_h_dptr(parent, bdst); ++ if (h_parent) ++ goto out; /* success */ ++ ++ /* find top dir which is necessary to cpup */ ++ do { ++ d = parent; ++ dput(parent); ++ parent = dget_parent(d); ++ di_read_lock_parent3(parent, !AuLock_IR); ++ h_parent = au_h_dptr(parent, bdst); ++ di_read_unlock(parent, !AuLock_IR); ++ } while (!h_parent); ++ ++ if (d != real_parent) ++ di_write_lock_child3(d); ++ ++ /* somebody else might create while we were sleeping */ ++ if (!au_h_dptr(d, bdst) || !au_h_dptr(d, bdst)->d_inode) { ++ if (au_h_dptr(d, bdst)) ++ au_update_dbstart(d); ++ ++ au_pin_set_dentry(&pin, d); ++ err = au_do_pin(&pin); ++ if (!err) { ++ err = cp(d, bdst, h_parent, arg); ++ au_unpin(&pin); ++ } ++ } ++ ++ if (d != real_parent) ++ di_write_unlock(d); ++ if (unlikely(err)) ++ break; ++ } ++ ++ out: ++ dput(parent); ++ return err; ++} ++ ++static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst, ++ struct dentry *h_parent __maybe_unused , ++ void *arg __maybe_unused) ++{ ++ return au_sio_cpup_simple(dentry, bdst, -1, AuCpup_DTIME); ++} ++ ++int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) ++{ ++ return au_cp_dirs(dentry, bdst, au_cpup_dir, NULL); ++} ++ ++int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) ++{ ++ int err; ++ struct dentry *parent; ++ struct inode *dir; ++ ++ parent = dget_parent(dentry); ++ dir = parent->d_inode; ++ err = 0; ++ if (au_h_iptr(dir, bdst)) ++ goto out; ++ ++ di_read_unlock(parent, AuLock_IR); ++ di_write_lock_parent(parent); ++ /* someone else might change our inode while we were sleeping */ ++ if (!au_h_iptr(dir, bdst)) ++ err = au_cpup_dirs(dentry, bdst); ++ di_downgrade_lock(parent, AuLock_IR); ++ ++ out: ++ dput(parent); ++ return err; ++} +diff -Naur a/fs/aufs/cpup.h b/fs/aufs/cpup.h +--- a/fs/aufs/cpup.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/cpup.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,81 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * copy-up/down functions ++ */ ++ ++#ifndef __AUFS_CPUP_H__ ++#define __AUFS_CPUP_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++ ++struct inode; ++struct file; ++ ++void au_cpup_attr_flags(struct inode *dst, struct inode *src); ++void au_cpup_attr_timesizes(struct inode *inode); ++void au_cpup_attr_nlink(struct inode *inode, int force); ++void au_cpup_attr_changeable(struct inode *inode); ++void au_cpup_igen(struct inode *inode, struct inode *h_inode); ++void au_cpup_attr_all(struct inode *inode, int force); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* cpup flags */ ++#define AuCpup_DTIME 1 /* do dtime_store/revert */ ++#define AuCpup_KEEPLINO (1 << 1) /* do not clear the lower xino, ++ for link(2) */ ++#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name) ++#define au_fset_cpup(flags, name) { (flags) |= AuCpup_##name; } ++#define au_fclr_cpup(flags, name) { (flags) &= ~AuCpup_##name; } ++ ++int au_copy_file(struct file *dst, struct file *src, loff_t len); ++int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, ++ aufs_bindex_t bsrc, loff_t len, unsigned int flags, ++ struct dentry *dst_parent); ++int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, ++ unsigned int flags); ++int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, ++ struct file *file); ++ ++int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, ++ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, ++ struct dentry *h_parent, void *arg), ++ void *arg); ++int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); ++int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* keep timestamps when copyup */ ++struct au_dtime { ++ struct dentry *dt_dentry; ++ struct path dt_h_path; ++ struct timespec dt_atime, dt_mtime; ++}; ++void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, ++ struct path *h_path); ++void au_dtime_revert(struct au_dtime *dt); ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_CPUP_H__ */ +diff -Naur a/fs/aufs/dbgaufs.c b/fs/aufs/dbgaufs.c +--- a/fs/aufs/dbgaufs.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/dbgaufs.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,313 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * debugfs interface ++ */ ++ ++#include ++#include "aufs.h" ++ ++#ifndef CONFIG_SYSFS ++#error DEBUG_FS depends upon SYSFS ++#endif ++ ++static struct dentry *dbgaufs; ++static const mode_t dbgaufs_mode = S_IRUSR | S_IRGRP | S_IROTH; ++ ++/* 20 is max digits length of ulong 64 */ ++struct dbgaufs_arg { ++ int n; ++ char a[20 * 4]; ++}; ++ ++/* ++ * common function for all XINO files ++ */ ++static int dbgaufs_xi_release(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ kfree(file->private_data); ++ return 0; ++} ++ ++static int dbgaufs_xi_open(struct file *xf, struct file *file, int do_fcnt) ++{ ++ int err; ++ struct kstat st; ++ struct dbgaufs_arg *p; ++ ++ err = -ENOMEM; ++ p = kmalloc(sizeof(*p), GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ ++ err = 0; ++ p->n = 0; ++ file->private_data = p; ++ if (!xf) ++ goto out; ++ ++ err = vfs_getattr(xf->f_vfsmnt, xf->f_dentry, &st); ++ if (!err) { ++ if (do_fcnt) ++ p->n = snprintf ++ (p->a, sizeof(p->a), "%ld, %llux%lu %lld\n", ++ (long)file_count(xf), st.blocks, st.blksize, ++ (long long)st.size); ++ else ++ p->n = snprintf(p->a, sizeof(p->a), "%llux%lu %lld\n", ++ st.blocks, st.blksize, ++ (long long)st.size); ++ AuDebugOn(p->n >= sizeof(p->a)); ++ } else { ++ p->n = snprintf(p->a, sizeof(p->a), "err %d\n", err); ++ err = 0; ++ } ++ ++ out: ++ return err; ++ ++} ++ ++static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dbgaufs_arg *p; ++ ++ p = file->private_data; ++ return simple_read_from_buffer(buf, count, ppos, p->a, p->n); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int dbgaufs_xib_open(struct inode *inode, struct file *file) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ ++ sbinfo = inode->i_private; ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0); ++ si_read_unlock(sb); ++ return err; ++} ++ ++static const struct file_operations dbgaufs_xib_fop = { ++ .open = dbgaufs_xib_open, ++ .release = dbgaufs_xi_release, ++ .read = dbgaufs_xi_read ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define DbgaufsXi_PREFIX "xi" ++ ++static int dbgaufs_xino_open(struct inode *inode, struct file *file) ++{ ++ int err; ++ long l; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ struct file *xf; ++ struct qstr *name; ++ ++ err = -ENOENT; ++ xf = NULL; ++ name = &file->f_dentry->d_name; ++ if (unlikely(name->len < sizeof(DbgaufsXi_PREFIX) ++ || memcmp(name->name, DbgaufsXi_PREFIX, ++ sizeof(DbgaufsXi_PREFIX) - 1))) ++ goto out; ++ err = strict_strtol(name->name + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l); ++ if (unlikely(err)) ++ goto out; ++ ++ sbinfo = inode->i_private; ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ if (l <= au_sbend(sb)) { ++ xf = au_sbr(sb, (aufs_bindex_t)l)->br_xino.xi_file; ++ err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1); ++ } else ++ err = -ENOENT; ++ si_read_unlock(sb); ++ ++ out: ++ return err; ++} ++ ++static const struct file_operations dbgaufs_xino_fop = { ++ .open = dbgaufs_xino_open, ++ .release = dbgaufs_xi_release, ++ .read = dbgaufs_xi_read ++}; ++ ++void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ aufs_bindex_t bend; ++ struct au_branch *br; ++ struct au_xino_file *xi; ++ ++ if (!au_sbi(sb)->si_dbgaufs) ++ return; ++ ++ bend = au_sbend(sb); ++ for (; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ xi = &br->br_xino; ++ if (xi->xi_dbgaufs) { ++ debugfs_remove(xi->xi_dbgaufs); ++ xi->xi_dbgaufs = NULL; ++ } ++ } ++} ++ ++void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ struct au_sbinfo *sbinfo; ++ struct dentry *parent; ++ struct au_branch *br; ++ struct au_xino_file *xi; ++ aufs_bindex_t bend; ++ char name[sizeof(DbgaufsXi_PREFIX) + 5]; /* "xi" bindex NULL */ ++ ++ sbinfo = au_sbi(sb); ++ parent = sbinfo->si_dbgaufs; ++ if (!parent) ++ return; ++ ++ bend = au_sbend(sb); ++ for (; bindex <= bend; bindex++) { ++ snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex); ++ br = au_sbr(sb, bindex); ++ xi = &br->br_xino; ++ AuDebugOn(xi->xi_dbgaufs); ++ xi->xi_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent, ++ sbinfo, &dbgaufs_xino_fop); ++ /* ignore an error */ ++ if (unlikely(!xi->xi_dbgaufs)) ++ AuWarn1("failed %s under debugfs\n", name); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_EXPORT ++static int dbgaufs_xigen_open(struct inode *inode, struct file *file) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ ++ sbinfo = inode->i_private; ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0); ++ si_read_unlock(sb); ++ return err; ++} ++ ++static const struct file_operations dbgaufs_xigen_fop = { ++ .open = dbgaufs_xigen_open, ++ .release = dbgaufs_xi_release, ++ .read = dbgaufs_xi_read ++}; ++ ++static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) ++{ ++ int err; ++ ++ err = -EIO; ++ sbinfo->si_dbgaufs_xigen = debugfs_create_file ++ ("xigen", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, ++ &dbgaufs_xigen_fop); ++ if (sbinfo->si_dbgaufs_xigen) ++ err = 0; ++ ++ return err; ++} ++#else ++static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) ++{ ++ return 0; ++} ++#endif /* CONFIG_AUFS_EXPORT */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++void dbgaufs_si_fin(struct au_sbinfo *sbinfo) ++{ ++ debugfs_remove_recursive(sbinfo->si_dbgaufs); ++ sbinfo->si_dbgaufs = NULL; ++ kobject_put(&sbinfo->si_kobj); ++} ++ ++int dbgaufs_si_init(struct au_sbinfo *sbinfo) ++{ ++ int err; ++ char name[SysaufsSiNameLen]; ++ ++ err = -ENOENT; ++ if (!dbgaufs) { ++ AuErr1("/debug/aufs is uninitialized\n"); ++ goto out; ++ } ++ ++ err = -EIO; ++ sysaufs_name(sbinfo, name); ++ sbinfo->si_dbgaufs = debugfs_create_dir(name, dbgaufs); ++ if (unlikely(!sbinfo->si_dbgaufs)) ++ goto out; ++ kobject_get(&sbinfo->si_kobj); ++ ++ sbinfo->si_dbgaufs_xib = debugfs_create_file ++ ("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, ++ &dbgaufs_xib_fop); ++ if (unlikely(!sbinfo->si_dbgaufs_xib)) ++ goto out_dir; ++ ++ err = dbgaufs_xigen_init(sbinfo); ++ if (!err) ++ goto out; /* success */ ++ ++ out_dir: ++ dbgaufs_si_fin(sbinfo); ++ out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void dbgaufs_fin(void) ++{ ++ debugfs_remove(dbgaufs); ++} ++ ++int __init dbgaufs_init(void) ++{ ++ int err; ++ ++ err = -EIO; ++ dbgaufs = debugfs_create_dir(AUFS_NAME, NULL); ++ if (dbgaufs) ++ err = 0; ++ return err; ++} +diff -Naur a/fs/aufs/dbgaufs.h b/fs/aufs/dbgaufs.h +--- a/fs/aufs/dbgaufs.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/dbgaufs.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,79 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * debugfs interface ++ */ ++ ++#ifndef __DBGAUFS_H__ ++#define __DBGAUFS_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++ ++struct super_block; ++struct au_sbinfo; ++ ++#ifdef CONFIG_DEBUG_FS ++/* dbgaufs.c */ ++void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); ++void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); ++void dbgaufs_si_fin(struct au_sbinfo *sbinfo); ++int dbgaufs_si_init(struct au_sbinfo *sbinfo); ++void dbgaufs_fin(void); ++int __init dbgaufs_init(void); ++ ++#else ++ ++static inline ++void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ /* empty */ ++} ++ ++static inline ++void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ /* empty */ ++} ++ ++static inline ++void dbgaufs_si_fin(struct au_sbinfo *sbinfo) ++{ ++ /* empty */ ++} ++ ++static inline ++int dbgaufs_si_init(struct au_sbinfo *sbinfo) ++{ ++ return 0; ++} ++ ++#define dbgaufs_fin() do {} while (0) ++ ++static inline ++int __init dbgaufs_init(void) ++{ ++ return 0; ++} ++#endif /* CONFIG_DEBUG_FS */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __DBGAUFS_H__ */ +diff -Naur a/fs/aufs/dcsub.c b/fs/aufs/dcsub.c +--- a/fs/aufs/dcsub.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/dcsub.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,223 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * sub-routines for dentry cache ++ */ ++ ++#include "aufs.h" ++ ++static void au_dpage_free(struct au_dpage *dpage) ++{ ++ int i; ++ struct dentry **p; ++ ++ p = dpage->dentries; ++ for (i = 0; i < dpage->ndentry; i++) ++ dput(*p++); ++ free_page((unsigned long)dpage->dentries); ++} ++ ++int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp) ++{ ++ int err; ++ void *p; ++ ++ err = -ENOMEM; ++ dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp); ++ if (unlikely(!dpages->dpages)) ++ goto out; ++ ++ p = (void *)__get_free_page(gfp); ++ if (unlikely(!p)) ++ goto out_dpages; ++ ++ dpages->dpages[0].ndentry = 0; ++ dpages->dpages[0].dentries = p; ++ dpages->ndpage = 1; ++ return 0; /* success */ ++ ++ out_dpages: ++ kfree(dpages->dpages); ++ out: ++ return err; ++} ++ ++void au_dpages_free(struct au_dcsub_pages *dpages) ++{ ++ int i; ++ struct au_dpage *p; ++ ++ p = dpages->dpages; ++ for (i = 0; i < dpages->ndpage; i++) ++ au_dpage_free(p++); ++ kfree(dpages->dpages); ++} ++ ++static int au_dpages_append(struct au_dcsub_pages *dpages, ++ struct dentry *dentry, gfp_t gfp) ++{ ++ int err, sz; ++ struct au_dpage *dpage; ++ void *p; ++ ++ dpage = dpages->dpages + dpages->ndpage - 1; ++ sz = PAGE_SIZE / sizeof(dentry); ++ if (unlikely(dpage->ndentry >= sz)) { ++ AuLabel(new dpage); ++ err = -ENOMEM; ++ sz = dpages->ndpage * sizeof(*dpages->dpages); ++ p = au_kzrealloc(dpages->dpages, sz, ++ sz + sizeof(*dpages->dpages), gfp); ++ if (unlikely(!p)) ++ goto out; ++ ++ dpages->dpages = p; ++ dpage = dpages->dpages + dpages->ndpage; ++ p = (void *)__get_free_page(gfp); ++ if (unlikely(!p)) ++ goto out; ++ ++ dpage->ndentry = 0; ++ dpage->dentries = p; ++ dpages->ndpage++; ++ } ++ ++ dpage->dentries[dpage->ndentry++] = dget(dentry); ++ return 0; /* success */ ++ ++ out: ++ return err; ++} ++ ++int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, ++ au_dpages_test test, void *arg) ++{ ++ int err; ++ struct dentry *this_parent = root; ++ struct list_head *next; ++ struct super_block *sb = root->d_sb; ++ ++ err = 0; ++ spin_lock(&dcache_lock); ++ repeat: ++ next = this_parent->d_subdirs.next; ++ resume: ++ if (this_parent->d_sb == sb ++ && !IS_ROOT(this_parent) ++ && atomic_read(&this_parent->d_count) ++ && this_parent->d_inode ++ && (!test || test(this_parent, arg))) { ++ err = au_dpages_append(dpages, this_parent, GFP_ATOMIC); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ while (next != &this_parent->d_subdirs) { ++ struct list_head *tmp = next; ++ struct dentry *dentry = list_entry(tmp, struct dentry, ++ d_u.d_child); ++ next = tmp->next; ++ if (/*d_unhashed(dentry) || */!dentry->d_inode) ++ continue; ++ if (!list_empty(&dentry->d_subdirs)) { ++ this_parent = dentry; ++ goto repeat; ++ } ++ if (dentry->d_sb == sb ++ && atomic_read(&dentry->d_count) ++ && (!test || test(dentry, arg))) { ++ err = au_dpages_append(dpages, dentry, GFP_ATOMIC); ++ if (unlikely(err)) ++ goto out; ++ } ++ } ++ ++ if (this_parent != root) { ++ next = this_parent->d_u.d_child.next; ++ this_parent = this_parent->d_parent; /* dcache_lock is locked */ ++ goto resume; ++ } ++ out: ++ spin_unlock(&dcache_lock); ++ return err; ++} ++ ++int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, ++ int do_include, au_dpages_test test, void *arg) ++{ ++ int err; ++ ++ err = 0; ++ spin_lock(&dcache_lock); ++ if (do_include && (!test || test(dentry, arg))) { ++ err = au_dpages_append(dpages, dentry, GFP_ATOMIC); ++ if (unlikely(err)) ++ goto out; ++ } ++ while (!IS_ROOT(dentry)) { ++ dentry = dentry->d_parent; /* dcache_lock is locked */ ++ if (!test || test(dentry, arg)) { ++ err = au_dpages_append(dpages, dentry, GFP_ATOMIC); ++ if (unlikely(err)) ++ break; ++ } ++ } ++ ++ out: ++ spin_unlock(&dcache_lock); ++ ++ return err; ++} ++ ++struct dentry *au_test_subdir(struct dentry *d1, struct dentry *d2) ++{ ++ struct dentry *trap, **dentries; ++ int err, i, j; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ ++ trap = ERR_PTR(-ENOMEM); ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_dcsub_pages_rev(&dpages, d1, /*do_include*/1, NULL, NULL); ++ if (unlikely(err)) ++ goto out_dpages; ++ ++ trap = d1; ++ for (i = 0; !err && i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ dentries = dpage->dentries; ++ for (j = 0; !err && j < dpage->ndentry; j++) { ++ struct dentry *d; ++ ++ d = dentries[j]; ++ err = (d == d2); ++ if (!err) ++ trap = d; ++ } ++ } ++ if (!err) ++ trap = NULL; ++ ++ out_dpages: ++ au_dpages_free(&dpages); ++ out: ++ return trap; ++} +diff -Naur a/fs/aufs/dcsub.h b/fs/aufs/dcsub.h +--- a/fs/aufs/dcsub.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/dcsub.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,54 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * sub-routines for dentry cache ++ */ ++ ++#ifndef __AUFS_DCSUB_H__ ++#define __AUFS_DCSUB_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++ ++struct dentry; ++ ++struct au_dpage { ++ int ndentry; ++ struct dentry **dentries; ++}; ++ ++struct au_dcsub_pages { ++ int ndpage; ++ struct au_dpage *dpages; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp); ++void au_dpages_free(struct au_dcsub_pages *dpages); ++typedef int (*au_dpages_test)(struct dentry *dentry, void *arg); ++int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, ++ au_dpages_test test, void *arg); ++int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, ++ int do_include, au_dpages_test test, void *arg); ++struct dentry *au_test_subdir(struct dentry *d1, struct dentry *d2); ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DCSUB_H__ */ +diff -Naur a/fs/aufs/debug.c b/fs/aufs/debug.c +--- a/fs/aufs/debug.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/debug.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,427 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * debug print functions ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++int aufs_debug; ++MODULE_PARM_DESC(debug, "debug print"); ++module_param_named(debug, aufs_debug, int, S_IRUGO | S_IWUSR | S_IWGRP); ++ ++char *au_plevel = KERN_DEBUG; ++#define dpri(fmt, arg...) do { \ ++ if (au_debug_test()) \ ++ printk("%s" fmt, au_plevel, ##arg); \ ++} while (0) ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_dpri_whlist(struct au_nhash *whlist) ++{ ++ unsigned long ul, n; ++ struct hlist_head *head; ++ struct au_vdir_wh *tpos; ++ struct hlist_node *pos; ++ ++ n = whlist->nh_num; ++ head = whlist->nh_head; ++ for (ul = 0; ul < n; ul++) { ++ hlist_for_each_entry(tpos, pos, head, wh_hash) ++ dpri("b%d, %.*s, %d\n", ++ tpos->wh_bindex, ++ tpos->wh_str.len, tpos->wh_str.name, ++ tpos->wh_str.len); ++ head++; ++ } ++} ++ ++void au_dpri_vdir(struct au_vdir *vdir) ++{ ++ unsigned long ul; ++ union au_vdir_deblk_p p; ++ unsigned char *o; ++ ++ if (!vdir || IS_ERR(vdir)) { ++ dpri("err %ld\n", PTR_ERR(vdir)); ++ return; ++ } ++ ++ dpri("deblk %u, nblk %lu, deblk %p, last{%lu, %p}, ver %lu\n", ++ vdir->vd_deblk_sz, vdir->vd_nblk, vdir->vd_deblk, ++ vdir->vd_last.ul, vdir->vd_last.p.deblk, vdir->vd_version); ++ for (ul = 0; ul < vdir->vd_nblk; ul++) { ++ p.deblk = vdir->vd_deblk[ul]; ++ o = p.deblk; ++ dpri("[%lu]: %p\n", ul, o); ++ } ++} ++ ++static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode, ++ struct dentry *wh) ++{ ++ char *n = NULL; ++ int l = 0; ++ ++ if (!inode || IS_ERR(inode)) { ++ dpri("i%d: err %ld\n", bindex, PTR_ERR(inode)); ++ return -1; ++ } ++ ++ /* the type of i_blocks depends upon CONFIG_LSF */ ++ BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long) ++ && sizeof(inode->i_blocks) != sizeof(u64)); ++ if (wh) { ++ n = (void *)wh->d_name.name; ++ l = wh->d_name.len; ++ } ++ ++ dpri("i%d: i%lu, %s, cnt %d, nl %u, 0%o, sz %llu, blk %llu," ++ " ct %lld, np %lu, st 0x%lx, f 0x%x, g %x%s%.*s\n", ++ bindex, ++ inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??", ++ atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode, ++ i_size_read(inode), (unsigned long long)inode->i_blocks, ++ (long long)timespec_to_ns(&inode->i_ctime) & 0x0ffff, ++ inode->i_mapping ? inode->i_mapping->nrpages : 0, ++ inode->i_state, inode->i_flags, inode->i_generation, ++ l ? ", wh " : "", l, n); ++ return 0; ++} ++ ++void au_dpri_inode(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ aufs_bindex_t bindex; ++ int err; ++ ++ err = do_pri_inode(-1, inode, NULL); ++ if (err || !au_test_aufs(inode->i_sb)) ++ return; ++ ++ iinfo = au_ii(inode); ++ if (!iinfo) ++ return; ++ dpri("i-1: bstart %d, bend %d, gen %d\n", ++ iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode)); ++ if (iinfo->ii_bstart < 0) ++ return; ++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++) ++ do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode, ++ iinfo->ii_hinode[0 + bindex].hi_whdentry); ++} ++ ++static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry) ++{ ++ struct dentry *wh = NULL; ++ ++ if (!dentry || IS_ERR(dentry)) { ++ dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry)); ++ return -1; ++ } ++ /* do not call dget_parent() here */ ++ dpri("d%d: %.*s?/%.*s, %s, cnt %d, flags 0x%x\n", ++ bindex, ++ AuDLNPair(dentry->d_parent), AuDLNPair(dentry), ++ dentry->d_sb ? au_sbtype(dentry->d_sb) : "??", ++ atomic_read(&dentry->d_count), dentry->d_flags); ++ if (bindex >= 0 && dentry->d_inode && au_test_aufs(dentry->d_sb)) { ++ struct au_iinfo *iinfo = au_ii(dentry->d_inode); ++ if (iinfo) ++ wh = iinfo->ii_hinode[0 + bindex].hi_whdentry; ++ } ++ do_pri_inode(bindex, dentry->d_inode, wh); ++ return 0; ++} ++ ++void au_dpri_dentry(struct dentry *dentry) ++{ ++ struct au_dinfo *dinfo; ++ aufs_bindex_t bindex; ++ int err; ++ ++ err = do_pri_dentry(-1, dentry); ++ if (err || !au_test_aufs(dentry->d_sb)) ++ return; ++ ++ dinfo = au_di(dentry); ++ if (!dinfo) ++ return; ++ dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d\n", ++ dinfo->di_bstart, dinfo->di_bend, ++ dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry)); ++ if (dinfo->di_bstart < 0) ++ return; ++ for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++) ++ do_pri_dentry(bindex, dinfo->di_hdentry[0 + bindex].hd_dentry); ++} ++ ++static int do_pri_file(aufs_bindex_t bindex, struct file *file) ++{ ++ char a[32]; ++ ++ if (!file || IS_ERR(file)) { ++ dpri("f%d: err %ld\n", bindex, PTR_ERR(file)); ++ return -1; ++ } ++ a[0] = 0; ++ if (bindex < 0 ++ && file->f_dentry ++ && au_test_aufs(file->f_dentry->d_sb) ++ && au_fi(file)) ++ snprintf(a, sizeof(a), ", mmapped %d", au_test_mmapped(file)); ++ dpri("f%d: mode 0x%x, flags 0%o, cnt %ld, pos %llu%s\n", ++ bindex, file->f_mode, file->f_flags, (long)file_count(file), ++ file->f_pos, a); ++ if (file->f_dentry) ++ do_pri_dentry(bindex, file->f_dentry); ++ return 0; ++} ++ ++void au_dpri_file(struct file *file) ++{ ++ struct au_finfo *finfo; ++ aufs_bindex_t bindex; ++ int err; ++ ++ err = do_pri_file(-1, file); ++ if (err || !file->f_dentry || !au_test_aufs(file->f_dentry->d_sb)) ++ return; ++ ++ finfo = au_fi(file); ++ if (!finfo) ++ return; ++ if (finfo->fi_bstart < 0) ++ return; ++ for (bindex = finfo->fi_bstart; bindex <= finfo->fi_bend; bindex++) { ++ struct au_hfile *hf; ++ ++ hf = finfo->fi_hfile + bindex; ++ do_pri_file(bindex, hf ? hf->hf_file : NULL); ++ } ++} ++ ++static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br) ++{ ++ struct vfsmount *mnt; ++ struct super_block *sb; ++ ++ if (!br || IS_ERR(br)) ++ goto out; ++ mnt = br->br_mnt; ++ if (!mnt || IS_ERR(mnt)) ++ goto out; ++ sb = mnt->mnt_sb; ++ if (!sb || IS_ERR(sb)) ++ goto out; ++ ++ dpri("s%d: {perm 0x%x, cnt %d, wbr %p}, " ++ "%s, dev 0x%02x%02x, flags 0x%lx, cnt(BIAS) %d, active %d, " ++ "xino %d\n", ++ bindex, br->br_perm, atomic_read(&br->br_count), br->br_wbr, ++ au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev), ++ sb->s_flags, sb->s_count - S_BIAS, ++ atomic_read(&sb->s_active), !!br->br_xino.xi_file); ++ return 0; ++ ++ out: ++ dpri("s%d: err %ld\n", bindex, PTR_ERR(br)); ++ return -1; ++} ++ ++void au_dpri_sb(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ aufs_bindex_t bindex; ++ int err; ++ /* to reuduce stack size */ ++ struct { ++ struct vfsmount mnt; ++ struct au_branch fake; ++ } *a; ++ ++ /* this function can be called from magic sysrq */ ++ a = kzalloc(sizeof(*a), GFP_ATOMIC); ++ if (unlikely(!a)) { ++ dpri("no memory\n"); ++ return; ++ } ++ ++ a->mnt.mnt_sb = sb; ++ a->fake.br_perm = 0; ++ a->fake.br_mnt = &a->mnt; ++ a->fake.br_xino.xi_file = NULL; ++ atomic_set(&a->fake.br_count, 0); ++ smp_mb(); /* atomic_set */ ++ err = do_pri_br(-1, &a->fake); ++ kfree(a); ++ dpri("dev 0x%x\n", sb->s_dev); ++ if (err || !au_test_aufs(sb)) ++ return; ++ ++ sbinfo = au_sbi(sb); ++ if (!sbinfo) ++ return; ++ dpri("nw %d, gen %u, kobj %d\n", ++ atomic_read(&sbinfo->si_nowait.nw_len), sbinfo->si_generation, ++ atomic_read(&sbinfo->si_kobj.kref.refcount)); ++ for (bindex = 0; bindex <= sbinfo->si_bend; bindex++) ++ do_pri_br(bindex, sbinfo->si_branch[0 + bindex]); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_dbg_sleep_jiffy(int jiffy) ++{ ++ while (jiffy) ++ jiffy = schedule_timeout_uninterruptible(jiffy); ++} ++ ++void au_dbg_iattr(struct iattr *ia) ++{ ++#define AuBit(name) if (ia->ia_valid & ATTR_ ## name) \ ++ dpri(#name "\n") ++ AuBit(MODE); ++ AuBit(UID); ++ AuBit(GID); ++ AuBit(SIZE); ++ AuBit(ATIME); ++ AuBit(MTIME); ++ AuBit(CTIME); ++ AuBit(ATIME_SET); ++ AuBit(MTIME_SET); ++ AuBit(FORCE); ++ AuBit(ATTR_FLAG); ++ AuBit(KILL_SUID); ++ AuBit(KILL_SGID); ++ AuBit(FILE); ++ AuBit(KILL_PRIV); ++ AuBit(OPEN); ++ AuBit(TIMES_SET); ++#undef AuBit ++ dpri("ia_file %p\n", ia->ia_file); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_dbg_verify_dir_parent(struct dentry *dentry, unsigned int sigen) ++{ ++ struct dentry *parent; ++ ++ parent = dget_parent(dentry); ++ AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode) ++ || IS_ROOT(dentry) ++ || au_digen(parent) != sigen); ++ dput(parent); ++} ++ ++void au_dbg_verify_nondir_parent(struct dentry *dentry, unsigned int sigen) ++{ ++ struct dentry *parent; ++ ++ parent = dget_parent(dentry); ++ AuDebugOn(S_ISDIR(dentry->d_inode->i_mode) ++ || au_digen(parent) != sigen); ++ dput(parent); ++} ++ ++void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen) ++{ ++ int err, i, j; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry **dentries; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ AuDebugOn(err); ++ err = au_dcsub_pages_rev(&dpages, parent, /*do_include*/1, NULL, NULL); ++ AuDebugOn(err); ++ for (i = dpages.ndpage - 1; !err && i >= 0; i--) { ++ dpage = dpages.dpages + i; ++ dentries = dpage->dentries; ++ for (j = dpage->ndentry - 1; !err && j >= 0; j--) ++ AuDebugOn(au_digen(dentries[j]) != sigen); ++ } ++ au_dpages_free(&dpages); ++} ++ ++void au_dbg_verify_hf(struct au_finfo *finfo) ++{ ++ struct au_hfile *hf; ++ aufs_bindex_t bend, bindex; ++ ++ if (finfo->fi_bstart >= 0) { ++ bend = finfo->fi_bend; ++ for (bindex = finfo->fi_bstart; bindex <= bend; bindex++) { ++ hf = finfo->fi_hfile + bindex; ++ AuDebugOn(hf->hf_file || hf->hf_br); ++ } ++ } ++} ++ ++void au_dbg_verify_kthread(void) ++{ ++ if (au_test_wkq(current)) { ++ au_dbg_blocked(); ++ BUG(); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_debug_sbinfo_init(struct au_sbinfo *sbinfo __maybe_unused) ++{ ++#ifdef AuForceNoPlink ++ au_opt_clr(sbinfo->si_mntflags, PLINK); ++#endif ++#ifdef AuForceNoXino ++ au_opt_clr(sbinfo->si_mntflags, XINO); ++#endif ++#ifdef AuForceNoRefrof ++ au_opt_clr(sbinfo->si_mntflags, REFROF); ++#endif ++#ifdef AuForceHinotify ++ au_opt_set_udba(sbinfo->si_mntflags, UDBA_HINOTIFY); ++#endif ++} ++ ++int __init au_debug_init(void) ++{ ++ aufs_bindex_t bindex; ++ struct au_vdir_destr destr; ++ ++ bindex = -1; ++ AuDebugOn(bindex >= 0); ++ ++ destr.len = -1; ++ AuDebugOn(destr.len < NAME_MAX); ++ ++#ifdef CONFIG_4KSTACKS ++ AuWarn("CONFIG_4KSTACKS is defined.\n"); ++#endif ++ ++#ifdef AuForceNoBrs ++ sysaufs_brs = 0; ++#endif ++ ++ return 0; ++} +diff -Naur a/fs/aufs/debug.h b/fs/aufs/debug.h +--- a/fs/aufs/debug.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/debug.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,260 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * debug print functions ++ */ ++ ++#ifndef __AUFS_DEBUG_H__ ++#define __AUFS_DEBUG_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++/* #include */ ++/* #include */ ++/* #include */ ++#include ++/* #include */ ++/* #include */ ++#include ++#include ++ ++#ifdef CONFIG_AUFS_DEBUG ++#define AuDebugOn(a) BUG_ON(a) ++ ++/* module parameter */ ++extern int aufs_debug; ++static inline void au_debug(int n) ++{ ++ aufs_debug = n; ++ smp_mb(); ++} ++ ++static inline int au_debug_test(void) ++{ ++ return aufs_debug; ++} ++#else ++#define AuDebugOn(a) do {} while (0) ++#define au_debug() do {} while (0) ++static inline int au_debug_test(void) ++{ ++ return 0; ++} ++#endif /* CONFIG_AUFS_DEBUG */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* debug print */ ++ ++#define AuDpri(lvl, fmt, arg...) \ ++ printk(lvl AUFS_NAME " %s:%d:%s[%d]: " fmt, \ ++ __func__, __LINE__, current->comm, current->pid, ##arg) ++#define AuDbg(fmt, arg...) do { \ ++ if (au_debug_test()) \ ++ AuDpri(KERN_DEBUG, fmt, ##arg); \ ++} while (0) ++#define AuLabel(l) AuDbg(#l "\n") ++#define AuInfo(fmt, arg...) AuDpri(KERN_INFO, fmt, ##arg) ++#define AuWarn(fmt, arg...) AuDpri(KERN_WARNING, fmt, ##arg) ++#define AuErr(fmt, arg...) AuDpri(KERN_ERR, fmt, ##arg) ++#define AuIOErr(fmt, arg...) AuErr("I/O Error, " fmt, ##arg) ++#define AuWarn1(fmt, arg...) do { \ ++ static unsigned char _c; \ ++ if (!_c++) \ ++ AuWarn(fmt, ##arg); \ ++} while (0) ++ ++#define AuErr1(fmt, arg...) do { \ ++ static unsigned char _c; \ ++ if (!_c++) \ ++ AuErr(fmt, ##arg); \ ++} while (0) ++ ++#define AuIOErr1(fmt, arg...) do { \ ++ static unsigned char _c; \ ++ if (!_c++) \ ++ AuIOErr(fmt, ##arg); \ ++} while (0) ++ ++#define AuUnsupportMsg "This operation is not supported." \ ++ " Please report this application to aufs-users ML." ++#define AuUnsupport(fmt, args...) do { \ ++ AuErr(AuUnsupportMsg "\n" fmt, ##args); \ ++ dump_stack(); \ ++} while (0) ++ ++#define AuTraceErr(e) do { \ ++ if (unlikely((e) < 0)) \ ++ AuDbg("err %d\n", (int)(e)); \ ++} while (0) ++ ++#define AuTraceErrPtr(p) do { \ ++ if (IS_ERR(p)) \ ++ AuDbg("err %ld\n", PTR_ERR(p)); \ ++} while (0) ++ ++/* dirty macros for debug print, use with "%.*s" and caution */ ++#define AuLNPair(qstr) (qstr)->len, (qstr)->name ++#define AuDLNPair(d) AuLNPair(&(d)->d_name) ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_sbinfo; ++struct au_finfo; ++struct dentry; ++#ifdef CONFIG_AUFS_DEBUG ++extern char *au_plevel; ++struct au_nhash; ++void au_dpri_whlist(struct au_nhash *whlist); ++struct au_vdir; ++void au_dpri_vdir(struct au_vdir *vdir); ++struct inode; ++void au_dpri_inode(struct inode *inode); ++void au_dpri_dentry(struct dentry *dentry); ++struct file; ++void au_dpri_file(struct file *filp); ++struct super_block; ++void au_dpri_sb(struct super_block *sb); ++ ++void au_dbg_sleep_jiffy(int jiffy); ++struct iattr; ++void au_dbg_iattr(struct iattr *ia); ++ ++void au_dbg_verify_dir_parent(struct dentry *dentry, unsigned int sigen); ++void au_dbg_verify_nondir_parent(struct dentry *dentry, unsigned int sigen); ++void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen); ++void au_dbg_verify_hf(struct au_finfo *finfo); ++void au_dbg_verify_kthread(void); ++ ++int __init au_debug_init(void); ++void au_debug_sbinfo_init(struct au_sbinfo *sbinfo); ++#define AuDbgWhlist(w) do { \ ++ AuDbg(#w "\n"); \ ++ au_dpri_whlist(w); \ ++} while (0) ++ ++#define AuDbgVdir(v) do { \ ++ AuDbg(#v "\n"); \ ++ au_dpri_vdir(v); \ ++} while (0) ++ ++#define AuDbgInode(i) do { \ ++ AuDbg(#i "\n"); \ ++ au_dpri_inode(i); \ ++} while (0) ++ ++#define AuDbgDentry(d) do { \ ++ AuDbg(#d "\n"); \ ++ au_dpri_dentry(d); \ ++} while (0) ++ ++#define AuDbgFile(f) do { \ ++ AuDbg(#f "\n"); \ ++ au_dpri_file(f); \ ++} while (0) ++ ++#define AuDbgSb(sb) do { \ ++ AuDbg(#sb "\n"); \ ++ au_dpri_sb(sb); \ ++} while (0) ++ ++#define AuDbgSleep(sec) do { \ ++ AuDbg("sleep %d sec\n", sec); \ ++ ssleep(sec); \ ++} while (0) ++ ++#define AuDbgSleepJiffy(jiffy) do { \ ++ AuDbg("sleep %d jiffies\n", jiffy); \ ++ au_dbg_sleep_jiffy(jiffy); \ ++} while (0) ++ ++#define AuDbgIAttr(ia) do { \ ++ AuDbg("ia_valid 0x%x\n", (ia)->ia_valid); \ ++ au_dbg_iattr(ia); \ ++} while (0) ++#else ++static inline void au_dbg_verify_dir_parent(struct dentry *dentry, ++ unsigned int sigen) ++{ ++ /* empty */ ++} ++static inline void au_dbg_verify_nondir_parent(struct dentry *dentry, ++ unsigned int sigen) ++{ ++ /* empty */ ++} ++static inline void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen) ++{ ++ /* empty */ ++} ++static inline void au_dbg_verify_hf(struct au_finfo *finfo) ++{ ++ /* empty */ ++} ++static inline void au_dbg_verify_kthread(void) ++{ ++ /* empty */ ++} ++ ++static inline int au_debug_init(void) ++{ ++ return 0; ++} ++static inline void au_debug_sbinfo_init(struct au_sbinfo *sbinfo) ++{ ++ /* empty */ ++} ++#define AuDbgWhlist(w) do {} while (0) ++#define AuDbgVdir(v) do {} while (0) ++#define AuDbgInode(i) do {} while (0) ++#define AuDbgDentry(d) do {} while (0) ++#define AuDbgFile(f) do {} while (0) ++#define AuDbgSb(sb) do {} while (0) ++#define AuDbgSleep(sec) do {} while (0) ++#define AuDbgSleepJiffy(jiffy) do {} while (0) ++#define AuDbgIAttr(ia) do {} while (0) ++#endif /* CONFIG_AUFS_DEBUG */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_MAGIC_SYSRQ ++int __init au_sysrq_init(void); ++void au_sysrq_fin(void); ++ ++#ifdef CONFIG_HW_CONSOLE ++#define au_dbg_blocked() do { \ ++ WARN_ON(1); \ ++ handle_sysrq('w', vc_cons[fg_console].d->vc_tty); \ ++} while (0) ++#else ++#define au_dbg_blocked() do {} while (0) ++#endif ++ ++#else ++static inline int au_sysrq_init(void) ++{ ++ return 0; ++} ++#define au_sysrq_fin() do {} while (0) ++#define au_dbg_blocked() do {} while (0) ++#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DEBUG_H__ */ +diff -Naur a/fs/aufs/dentry.c b/fs/aufs/dentry.c +--- a/fs/aufs/dentry.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/dentry.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,876 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * lookup and dentry operations ++ */ ++ ++#include ++#include "aufs.h" ++ ++static void au_h_nd(struct nameidata *h_nd, struct nameidata *nd) ++{ ++ if (nd) { ++ *h_nd = *nd; ++ ++ /* ++ * gave up supporting LOOKUP_CREATE/OPEN for lower fs, ++ * due to whiteout and branch permission. ++ */ ++ h_nd->flags &= ~(/*LOOKUP_PARENT |*/ LOOKUP_OPEN | LOOKUP_CREATE ++ | LOOKUP_FOLLOW); ++ /* unnecessary? */ ++ h_nd->intent.open.file = NULL; ++ } else ++ memset(h_nd, 0, sizeof(*h_nd)); ++} ++ ++struct au_lkup_one_args { ++ struct dentry **errp; ++ struct qstr *name; ++ struct dentry *h_parent; ++ struct au_branch *br; ++ struct nameidata *nd; ++}; ++ ++struct dentry *au_lkup_one(struct qstr *name, struct dentry *h_parent, ++ struct au_branch *br, struct nameidata *nd) ++{ ++ struct dentry *h_dentry; ++ int err; ++ struct nameidata h_nd; ++ ++ if (au_test_fs_null_nd(h_parent->d_sb)) ++ return vfsub_lookup_one_len(name->name, h_parent, name->len); ++ ++ au_h_nd(&h_nd, nd); ++ h_nd.path.dentry = h_parent; ++ h_nd.path.mnt = br->br_mnt; ++ ++ err = __lookup_one_len(name->name, &h_nd.last, NULL, name->len); ++ h_dentry = ERR_PTR(err); ++ if (!err) { ++ path_get(&h_nd.path); ++ h_dentry = vfsub_lookup_hash(&h_nd); ++ path_put(&h_nd.path); ++ } ++ ++ return h_dentry; ++} ++ ++static void au_call_lkup_one(void *args) ++{ ++ struct au_lkup_one_args *a = args; ++ *a->errp = au_lkup_one(a->name, a->h_parent, a->br, a->nd); ++} ++ ++#define AuLkup_ALLOW_NEG 1 ++#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name) ++#define au_fset_lkup(flags, name) { (flags) |= AuLkup_##name; } ++#define au_fclr_lkup(flags, name) { (flags) &= ~AuLkup_##name; } ++ ++struct au_do_lookup_args { ++ unsigned int flags; ++ mode_t type; ++ struct nameidata *nd; ++}; ++ ++/* ++ * returns positive/negative dentry, NULL or an error. ++ * NULL means whiteout-ed or not-found. ++ */ ++static struct dentry* ++au_do_lookup(struct dentry *h_parent, struct dentry *dentry, ++ aufs_bindex_t bindex, struct qstr *wh_name, ++ struct au_do_lookup_args *args) ++{ ++ struct dentry *h_dentry; ++ struct inode *h_inode, *inode; ++ struct qstr *name; ++ struct au_branch *br; ++ int wh_found, opq; ++ unsigned char wh_able; ++ const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG); ++ ++ name = &dentry->d_name; ++ wh_found = 0; ++ br = au_sbr(dentry->d_sb, bindex); ++ wh_able = !!au_br_whable(br->br_perm); ++ if (wh_able) ++ wh_found = au_wh_test(h_parent, wh_name, br, /*try_sio*/0); ++ h_dentry = ERR_PTR(wh_found); ++ if (!wh_found) ++ goto real_lookup; ++ if (unlikely(wh_found < 0)) ++ goto out; ++ ++ /* We found a whiteout */ ++ /* au_set_dbend(dentry, bindex); */ ++ au_set_dbwh(dentry, bindex); ++ if (!allow_neg) ++ return NULL; /* success */ ++ ++ real_lookup: ++ h_dentry = au_lkup_one(name, h_parent, br, args->nd); ++ if (IS_ERR(h_dentry)) ++ goto out; ++ ++ h_inode = h_dentry->d_inode; ++ if (!h_inode) { ++ if (!allow_neg) ++ goto out_neg; ++ } else if (wh_found ++ || (args->type && args->type != (h_inode->i_mode & S_IFMT))) ++ goto out_neg; ++ ++ if (au_dbend(dentry) <= bindex) ++ au_set_dbend(dentry, bindex); ++ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) ++ au_set_dbstart(dentry, bindex); ++ au_set_h_dptr(dentry, bindex, h_dentry); ++ ++ inode = dentry->d_inode; ++ if (!h_inode || !S_ISDIR(h_inode->i_mode) || !wh_able ++ || (inode && !S_ISDIR(inode->i_mode))) ++ goto out; /* success */ ++ ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ opq = au_diropq_test(h_dentry, br); ++ mutex_unlock(&h_inode->i_mutex); ++ if (opq > 0) ++ au_set_dbdiropq(dentry, bindex); ++ else if (unlikely(opq < 0)) { ++ au_set_h_dptr(dentry, bindex, NULL); ++ h_dentry = ERR_PTR(opq); ++ } ++ goto out; ++ ++ out_neg: ++ dput(h_dentry); ++ h_dentry = NULL; ++ out: ++ return h_dentry; ++} ++ ++static int au_test_shwh(struct super_block *sb, const struct qstr *name) ++{ ++ if (unlikely(!au_opt_test(au_mntflags(sb), SHWH) ++ && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))) ++ return -EPERM; ++ return 0; ++} ++ ++/* ++ * returns the number of lower positive dentries, ++ * otherwise an error. ++ * can be called at unlinking with @type is zero. ++ */ ++int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type, ++ struct nameidata *nd) ++{ ++ int npositive, err; ++ aufs_bindex_t bindex, btail, bdiropq; ++ unsigned char isdir; ++ struct qstr whname; ++ struct au_do_lookup_args args = { ++ .flags = 0, ++ .type = type, ++ .nd = nd ++ }; ++ const struct qstr *name = &dentry->d_name; ++ struct dentry *parent; ++ struct inode *inode; ++ ++ parent = dget_parent(dentry); ++ err = au_test_shwh(dentry->d_sb, name); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_wh_name_alloc(&whname, name); ++ if (unlikely(err)) ++ goto out; ++ ++ inode = dentry->d_inode; ++ isdir = !!(inode && S_ISDIR(inode->i_mode)); ++ if (!type) ++ au_fset_lkup(args.flags, ALLOW_NEG); ++ ++ npositive = 0; ++ btail = au_dbtaildir(parent); ++ for (bindex = bstart; bindex <= btail; bindex++) { ++ struct dentry *h_parent, *h_dentry; ++ struct inode *h_inode, *h_dir; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry) { ++ if (h_dentry->d_inode) ++ npositive++; ++ if (type != S_IFDIR) ++ break; ++ continue; ++ } ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent) ++ continue; ++ h_dir = h_parent->d_inode; ++ if (!h_dir || !S_ISDIR(h_dir->i_mode)) ++ continue; ++ ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); ++ h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname, ++ &args); ++ mutex_unlock(&h_dir->i_mutex); ++ err = PTR_ERR(h_dentry); ++ if (IS_ERR(h_dentry)) ++ goto out_wh; ++ au_fclr_lkup(args.flags, ALLOW_NEG); ++ ++ if (au_dbwh(dentry) >= 0) ++ break; ++ if (!h_dentry) ++ continue; ++ h_inode = h_dentry->d_inode; ++ if (!h_inode) ++ continue; ++ npositive++; ++ if (!args.type) ++ args.type = h_inode->i_mode & S_IFMT; ++ if (args.type != S_IFDIR) ++ break; ++ else if (isdir) { ++ /* the type of lower may be different */ ++ bdiropq = au_dbdiropq(dentry); ++ if (bdiropq >= 0 && bdiropq <= bindex) ++ break; ++ } ++ } ++ ++ if (npositive) { ++ AuLabel(positive); ++ au_update_dbstart(dentry); ++ } ++ err = npositive; ++ if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE) ++ && au_dbstart(dentry) < 0)) ++ /* both of real entry and whiteout found */ ++ err = -EIO; ++ ++ out_wh: ++ kfree(whname.name); ++ out: ++ dput(parent); ++ return err; ++} ++ ++struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent, ++ struct au_branch *br) ++{ ++ struct dentry *dentry; ++ int wkq_err; ++ ++ if (!au_test_h_perm_sio(parent->d_inode, MAY_EXEC)) ++ dentry = au_lkup_one(name, parent, br, /*nd*/NULL); ++ else { ++ struct au_lkup_one_args args = { ++ .errp = &dentry, ++ .name = name, ++ .h_parent = parent, ++ .br = br, ++ .nd = NULL ++ }; ++ ++ wkq_err = au_wkq_wait(au_call_lkup_one, &args); ++ if (unlikely(wkq_err)) ++ dentry = ERR_PTR(wkq_err); ++ } ++ ++ return dentry; ++} ++ ++/* ++ * lookup @dentry on @bindex which should be negative. ++ */ ++int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ int err; ++ struct dentry *parent, *h_parent, *h_dentry; ++ struct qstr *name; ++ ++ name = &dentry->d_name; ++ parent = dget_parent(dentry); ++ h_parent = au_h_dptr(parent, bindex); ++ h_dentry = au_sio_lkup_one(name, h_parent, ++ au_sbr(dentry->d_sb, bindex)); ++ err = PTR_ERR(h_dentry); ++ if (IS_ERR(h_dentry)) ++ goto out; ++ if (unlikely(h_dentry->d_inode)) { ++ err = -EIO; ++ AuIOErr("b%d %.*s should be negative.\n", ++ bindex, AuDLNPair(h_dentry)); ++ dput(h_dentry); ++ goto out; ++ } ++ ++ if (bindex < au_dbstart(dentry)) ++ au_set_dbstart(dentry, bindex); ++ if (au_dbend(dentry) < bindex) ++ au_set_dbend(dentry, bindex); ++ au_set_h_dptr(dentry, bindex, h_dentry); ++ err = 0; ++ ++ out: ++ dput(parent); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* subset of struct inode */ ++struct au_iattr { ++ unsigned long i_ino; ++ /* unsigned int i_nlink; */ ++ uid_t i_uid; ++ gid_t i_gid; ++ u64 i_version; ++/* ++ loff_t i_size; ++ blkcnt_t i_blocks; ++*/ ++ umode_t i_mode; ++}; ++ ++static void au_iattr_save(struct au_iattr *ia, struct inode *h_inode) ++{ ++ ia->i_ino = h_inode->i_ino; ++ /* ia->i_nlink = h_inode->i_nlink; */ ++ ia->i_uid = h_inode->i_uid; ++ ia->i_gid = h_inode->i_gid; ++ ia->i_version = h_inode->i_version; ++/* ++ ia->i_size = h_inode->i_size; ++ ia->i_blocks = h_inode->i_blocks; ++*/ ++ ia->i_mode = (h_inode->i_mode & S_IFMT); ++} ++ ++static int au_iattr_test(struct au_iattr *ia, struct inode *h_inode) ++{ ++ return ia->i_ino != h_inode->i_ino ++ /* || ia->i_nlink != h_inode->i_nlink */ ++ || ia->i_uid != h_inode->i_uid ++ || ia->i_gid != h_inode->i_gid ++ || ia->i_version != h_inode->i_version ++/* ++ || ia->i_size != h_inode->i_size ++ || ia->i_blocks != h_inode->i_blocks ++*/ ++ || ia->i_mode != (h_inode->i_mode & S_IFMT); ++} ++ ++static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent, ++ struct au_branch *br) ++{ ++ int err; ++ struct au_iattr ia; ++ struct inode *h_inode; ++ struct dentry *h_d; ++ struct super_block *h_sb; ++ ++ err = 0; ++ memset(&ia, -1, sizeof(ia)); ++ h_sb = h_dentry->d_sb; ++ h_inode = h_dentry->d_inode; ++ if (h_inode) ++ au_iattr_save(&ia, h_inode); ++ else if (au_test_nfs(h_sb) || au_test_fuse(h_sb)) ++ /* nfs d_revalidate may return 0 for negative dentry */ ++ /* fuse d_revalidate always return 0 for negative dentry */ ++ goto out; ++ ++ /* main purpose is namei.c:cached_lookup() and d_revalidate */ ++ h_d = au_lkup_one(&h_dentry->d_name, h_parent, br, /*nd*/NULL); ++ err = PTR_ERR(h_d); ++ if (IS_ERR(h_d)) ++ goto out; ++ ++ err = 0; ++ if (unlikely(h_d != h_dentry ++ || h_d->d_inode != h_inode ++ || (h_inode && au_iattr_test(&ia, h_inode)))) ++ err = au_busy_or_stale(); ++ dput(h_d); ++ ++ out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, ++ struct dentry *h_parent, struct au_branch *br) ++{ ++ int err; ++ ++ err = 0; ++ if (udba == AuOpt_UDBA_REVAL) { ++ IMustLock(h_dir); ++ err = (h_dentry->d_parent->d_inode != h_dir); ++ } else if (udba == AuOpt_UDBA_HINOTIFY) ++ err = au_h_verify_dentry(h_dentry, h_parent, br); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_do_refresh_hdentry(struct au_hdentry *p, struct au_dinfo *dinfo, ++ struct dentry *parent) ++{ ++ struct dentry *h_d, *h_dp; ++ struct au_hdentry tmp, *q; ++ struct super_block *sb; ++ aufs_bindex_t new_bindex, bindex, bend, bwh, bdiropq; ++ ++ bend = dinfo->di_bend; ++ bwh = dinfo->di_bwh; ++ bdiropq = dinfo->di_bdiropq; ++ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) { ++ h_d = p->hd_dentry; ++ if (!h_d) ++ continue; ++ ++ h_dp = dget_parent(h_d); ++ if (h_dp == au_h_dptr(parent, bindex)) { ++ dput(h_dp); ++ continue; ++ } ++ ++ new_bindex = au_find_dbindex(parent, h_dp); ++ dput(h_dp); ++ if (dinfo->di_bwh == bindex) ++ bwh = new_bindex; ++ if (dinfo->di_bdiropq == bindex) ++ bdiropq = new_bindex; ++ if (new_bindex < 0) { ++ au_hdput(p); ++ p->hd_dentry = NULL; ++ continue; ++ } ++ ++ /* swap two lower dentries, and loop again */ ++ q = dinfo->di_hdentry + new_bindex; ++ tmp = *q; ++ *q = *p; ++ *p = tmp; ++ if (tmp.hd_dentry) { ++ bindex--; ++ p--; ++ } ++ } ++ ++ sb = parent->d_sb; ++ dinfo->di_bwh = -1; ++ if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh)) ++ dinfo->di_bwh = bwh; ++ ++ dinfo->di_bdiropq = -1; ++ if (bdiropq >= 0 ++ && bdiropq <= au_sbend(sb) ++ && au_sbr_whable(sb, bdiropq)) ++ dinfo->di_bdiropq = bdiropq; ++ ++ bend = au_dbend(parent); ++ p = dinfo->di_hdentry; ++ for (bindex = 0; bindex <= bend; bindex++, p++) ++ if (p->hd_dentry) { ++ dinfo->di_bstart = bindex; ++ break; ++ } ++ ++ p = dinfo->di_hdentry + bend; ++ for (bindex = bend; bindex >= 0; bindex--, p--) ++ if (p->hd_dentry) { ++ dinfo->di_bend = bindex; ++ break; ++ } ++} ++ ++/* ++ * returns the number of found lower positive dentries, ++ * otherwise an error. ++ */ ++int au_refresh_hdentry(struct dentry *dentry, mode_t type) ++{ ++ int npositive, err; ++ unsigned int sigen; ++ aufs_bindex_t bstart; ++ struct au_dinfo *dinfo; ++ struct super_block *sb; ++ struct dentry *parent; ++ ++ sb = dentry->d_sb; ++ AuDebugOn(IS_ROOT(dentry)); ++ sigen = au_sigen(sb); ++ parent = dget_parent(dentry); ++ AuDebugOn(au_digen(parent) != sigen ++ || au_iigen(parent->d_inode) != sigen); ++ ++ dinfo = au_di(dentry); ++ err = au_di_realloc(dinfo, au_sbend(sb) + 1); ++ npositive = err; ++ if (unlikely(err)) ++ goto out; ++ au_do_refresh_hdentry(dinfo->di_hdentry + dinfo->di_bstart, dinfo, ++ parent); ++ ++ npositive = 0; ++ bstart = au_dbstart(parent); ++ if (type != S_IFDIR && dinfo->di_bstart == bstart) ++ goto out_dgen; /* success */ ++ ++ npositive = au_lkup_dentry(dentry, bstart, type, /*nd*/NULL); ++ if (npositive < 0) ++ goto out; ++ if (dinfo->di_bwh >= 0 && dinfo->di_bwh <= dinfo->di_bstart) ++ d_drop(dentry); ++ ++ out_dgen: ++ au_update_digen(dentry); ++ out: ++ dput(parent); ++ AuTraceErr(npositive); ++ return npositive; ++} ++ ++static noinline_for_stack ++int au_do_h_d_reval(struct dentry *h_dentry, struct nameidata *nd, ++ struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ int err, valid; ++ int (*reval)(struct dentry *, struct nameidata *); ++ ++ err = 0; ++ reval = NULL; ++ if (h_dentry->d_op) ++ reval = h_dentry->d_op->d_revalidate; ++ if (!reval) ++ goto out; ++ ++ AuDbg("b%d\n", bindex); ++ if (au_test_fs_null_nd(h_dentry->d_sb)) ++ /* it may return tri-state */ ++ valid = reval(h_dentry, NULL); ++ else { ++ struct nameidata h_nd; ++ int locked; ++ struct dentry *parent; ++ ++ au_h_nd(&h_nd, nd); ++ parent = nd->path.dentry; ++ locked = (nd && nd->path.dentry != dentry); ++ if (locked) ++ di_read_lock_parent(parent, AuLock_IR); ++ BUG_ON(bindex > au_dbend(parent)); ++ h_nd.path.dentry = au_h_dptr(parent, bindex); ++ BUG_ON(!h_nd.path.dentry); ++ h_nd.path.mnt = au_sbr(parent->d_sb, bindex)->br_mnt; ++ path_get(&h_nd.path); ++ valid = reval(h_dentry, &h_nd); ++ path_put(&h_nd.path); ++ if (locked) ++ di_read_unlock(parent, AuLock_IR); ++ } ++ ++ if (unlikely(valid < 0)) ++ err = valid; ++ else if (!valid) ++ err = -EINVAL; ++ ++ out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* todo: remove this */ ++static int h_d_revalidate(struct dentry *dentry, struct inode *inode, ++ struct nameidata *nd, int do_udba) ++{ ++ int err; ++ umode_t mode, h_mode; ++ aufs_bindex_t bindex, btail, bstart, ibs, ibe; ++ unsigned char plus, unhashed, is_root, h_plus; ++ struct inode *first, *h_inode, *h_cached_inode; ++ struct dentry *h_dentry; ++ struct qstr *name, *h_name; ++ ++ err = 0; ++ plus = 0; ++ mode = 0; ++ first = NULL; ++ ibs = -1; ++ ibe = -1; ++ unhashed = !!d_unhashed(dentry); ++ is_root = !!IS_ROOT(dentry); ++ name = &dentry->d_name; ++ ++ /* ++ * Theoretically, REVAL test should be unnecessary in case of INOTIFY. ++ * But inotify doesn't fire some necessary events, ++ * IN_ATTRIB for atime/nlink/pageio ++ * IN_DELETE for NFS dentry ++ * Let's do REVAL test too. ++ */ ++ if (do_udba && inode) { ++ mode = (inode->i_mode & S_IFMT); ++ plus = (inode->i_nlink > 0); ++ first = au_h_iptr(inode, au_ibstart(inode)); ++ ibs = au_ibstart(inode); ++ ibe = au_ibend(inode); ++ } ++ ++ bstart = au_dbstart(dentry); ++ btail = bstart; ++ if (inode && S_ISDIR(inode->i_mode)) ++ btail = au_dbtaildir(dentry); ++ for (bindex = bstart; bindex <= btail; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ ++ AuDbg("b%d, %.*s\n", bindex, AuDLNPair(h_dentry)); ++ h_name = &h_dentry->d_name; ++ if (unlikely(do_udba ++ && !is_root ++ && (unhashed != !!d_unhashed(h_dentry) ++ || name->len != h_name->len ++ || memcmp(name->name, h_name->name, name->len)) ++ )) { ++ AuDbg("unhash 0x%x 0x%x, %.*s %.*s\n", ++ unhashed, d_unhashed(h_dentry), ++ AuDLNPair(dentry), AuDLNPair(h_dentry)); ++ goto err; ++ } ++ ++ err = au_do_h_d_reval(h_dentry, nd, dentry, bindex); ++ if (unlikely(err)) ++ /* do not goto err, to keep the errno */ ++ break; ++ ++ /* todo: plink too? */ ++ if (!do_udba) ++ continue; ++ ++ /* UDBA tests */ ++ h_inode = h_dentry->d_inode; ++ if (unlikely(!!inode != !!h_inode)) ++ goto err; ++ ++ h_plus = plus; ++ h_mode = mode; ++ h_cached_inode = h_inode; ++ if (h_inode) { ++ h_mode = (h_inode->i_mode & S_IFMT); ++ h_plus = (h_inode->i_nlink > 0); ++ } ++ if (inode && ibs <= bindex && bindex <= ibe) ++ h_cached_inode = au_h_iptr(inode, bindex); ++ ++ if (unlikely(plus != h_plus ++ || mode != h_mode ++ || h_cached_inode != h_inode)) ++ goto err; ++ continue; ++ ++ err: ++ err = -EINVAL; ++ break; ++ } ++ ++ return err; ++} ++ ++static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen) ++{ ++ int err; ++ struct dentry *parent; ++ struct inode *inode; ++ ++ inode = dentry->d_inode; ++ if (au_digen(dentry) == sigen && au_iigen(inode) == sigen) ++ return 0; ++ ++ parent = dget_parent(dentry); ++ di_read_lock_parent(parent, AuLock_IR); ++ AuDebugOn(au_digen(parent) != sigen ++ || au_iigen(parent->d_inode) != sigen); ++ au_dbg_verify_gen(parent, sigen); ++ ++ /* returns a number of positive dentries */ ++ err = au_refresh_hdentry(dentry, inode->i_mode & S_IFMT); ++ if (err >= 0) ++ err = au_refresh_hinode(inode, dentry); ++ ++ di_read_unlock(parent, AuLock_IR); ++ dput(parent); ++ return err; ++} ++ ++int au_reval_dpath(struct dentry *dentry, unsigned int sigen) ++{ ++ int err; ++ struct dentry *d, *parent; ++ struct inode *inode; ++ ++ if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIRS)) ++ return simple_reval_dpath(dentry, sigen); ++ ++ /* slow loop, keep it simple and stupid */ ++ /* cf: au_cpup_dirs() */ ++ err = 0; ++ parent = NULL; ++ while (au_digen(dentry) != sigen ++ || au_iigen(dentry->d_inode) != sigen) { ++ d = dentry; ++ while (1) { ++ dput(parent); ++ parent = dget_parent(d); ++ if (au_digen(parent) == sigen ++ && au_iigen(parent->d_inode) == sigen) ++ break; ++ d = parent; ++ } ++ ++ inode = d->d_inode; ++ if (d != dentry) ++ di_write_lock_child(d); ++ ++ /* someone might update our dentry while we were sleeping */ ++ if (au_digen(d) != sigen || au_iigen(d->d_inode) != sigen) { ++ di_read_lock_parent(parent, AuLock_IR); ++ /* returns a number of positive dentries */ ++ err = au_refresh_hdentry(d, inode->i_mode & S_IFMT); ++ if (err >= 0) ++ err = au_refresh_hinode(inode, d); ++ di_read_unlock(parent, AuLock_IR); ++ } ++ ++ if (d != dentry) ++ di_write_unlock(d); ++ dput(parent); ++ if (unlikely(err)) ++ break; ++ } ++ ++ return err; ++} ++ ++/* ++ * if valid returns 1, otherwise 0. ++ */ ++static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd) ++{ ++ int valid, err; ++ unsigned int sigen; ++ unsigned char do_udba; ++ struct super_block *sb; ++ struct inode *inode; ++ ++ err = -EINVAL; ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW); ++ sigen = au_sigen(sb); ++ if (au_digen(dentry) != sigen) { ++ AuDebugOn(IS_ROOT(dentry)); ++ if (inode) ++ err = au_reval_dpath(dentry, sigen); ++ if (unlikely(err)) ++ goto out_dgrade; ++ AuDebugOn(au_digen(dentry) != sigen); ++ } ++ if (inode && au_iigen(inode) != sigen) { ++ AuDebugOn(IS_ROOT(dentry)); ++ err = au_refresh_hinode(inode, dentry); ++ if (unlikely(err)) ++ goto out_dgrade; ++ AuDebugOn(au_iigen(inode) != sigen); ++ } ++ di_downgrade_lock(dentry, AuLock_IR); ++ ++ AuDebugOn(au_digen(dentry) != sigen); ++ AuDebugOn(inode && au_iigen(inode) != sigen); ++ err = -EINVAL; ++ do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); ++ if (do_udba && inode) { ++ aufs_bindex_t bstart = au_ibstart(inode); ++ ++ if (bstart >= 0 ++ && au_test_higen(inode, au_h_iptr(inode, bstart))) ++ goto out; ++ } ++ ++ err = h_d_revalidate(dentry, inode, nd, do_udba); ++ if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) ++ /* both of real entry and whiteout found */ ++ err = -EIO; ++ goto out; ++ ++ out_dgrade: ++ di_downgrade_lock(dentry, AuLock_IR); ++ out: ++ au_store_oflag(nd, inode); ++ aufs_read_unlock(dentry, AuLock_IR); ++ AuTraceErr(err); ++ valid = !err; ++ if (!valid) ++ AuDbg("%.*s invalid\n", AuDLNPair(dentry)); ++ return valid; ++} ++ ++static void aufs_d_release(struct dentry *dentry) ++{ ++ struct au_dinfo *dinfo; ++ aufs_bindex_t bend, bindex; ++ ++ dinfo = dentry->d_fsdata; ++ if (!dinfo) ++ return; ++ ++ /* dentry may not be revalidated */ ++ bindex = dinfo->di_bstart; ++ if (bindex >= 0) { ++ struct au_hdentry *p; ++ ++ bend = dinfo->di_bend; ++ p = dinfo->di_hdentry + bindex; ++ while (bindex++ <= bend) { ++ if (p->hd_dentry) ++ au_hdput(p); ++ p++; ++ } ++ } ++ kfree(dinfo->di_hdentry); ++ au_rwsem_destroy(&dinfo->di_rwsem); ++ au_cache_free_dinfo(dinfo); ++ au_hin_di_reinit(dentry); ++} ++ ++struct dentry_operations aufs_dop = { ++ .d_revalidate = aufs_d_revalidate, ++ .d_release = aufs_d_release ++}; +diff -Naur a/fs/aufs/dentry.h b/fs/aufs/dentry.h +--- a/fs/aufs/dentry.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/dentry.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,221 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * lookup and dentry operations ++ */ ++ ++#ifndef __AUFS_DENTRY_H__ ++#define __AUFS_DENTRY_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include "rwsem.h" ++ ++/* make a single member structure for future use */ ++/* todo: remove this structure */ ++struct au_hdentry { ++ struct dentry *hd_dentry; ++}; ++ ++struct au_dinfo { ++ atomic_t di_generation; ++ ++ struct rw_semaphore di_rwsem; ++ aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq; ++ struct au_hdentry *di_hdentry; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dentry.c */ ++extern struct dentry_operations aufs_dop; ++struct au_branch; ++struct dentry *au_lkup_one(struct qstr *name, struct dentry *h_parent, ++ struct au_branch *br, struct nameidata *nd); ++struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent, ++ struct au_branch *br); ++int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, ++ struct dentry *h_parent, struct au_branch *br); ++ ++int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type, ++ struct nameidata *nd); ++int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex); ++int au_refresh_hdentry(struct dentry *dentry, mode_t type); ++int au_reval_dpath(struct dentry *dentry, unsigned int sigen); ++ ++/* dinfo.c */ ++int au_alloc_dinfo(struct dentry *dentry); ++int au_di_realloc(struct au_dinfo *dinfo, int nbr); ++ ++void di_read_lock(struct dentry *d, int flags, unsigned int lsc); ++void di_read_unlock(struct dentry *d, int flags); ++void di_downgrade_lock(struct dentry *d, int flags); ++void di_write_lock(struct dentry *d, unsigned int lsc); ++void di_write_unlock(struct dentry *d); ++void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir); ++void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir); ++void di_write_unlock2(struct dentry *d1, struct dentry *d2); ++ ++struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex); ++aufs_bindex_t au_dbtail(struct dentry *dentry); ++aufs_bindex_t au_dbtaildir(struct dentry *dentry); ++ ++void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_dentry); ++void au_update_digen(struct dentry *dentry); ++void au_update_dbrange(struct dentry *dentry, int do_put_zero); ++void au_update_dbstart(struct dentry *dentry); ++void au_update_dbend(struct dentry *dentry); ++int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct au_dinfo *au_di(struct dentry *dentry) ++{ ++ return dentry->d_fsdata; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* lock subclass for dinfo */ ++enum { ++ AuLsc_DI_CHILD, /* child first */ ++ AuLsc_DI_CHILD2, /* rename(2), link(2), and cpup at hinotify */ ++ AuLsc_DI_CHILD3, /* copyup dirs */ ++ AuLsc_DI_PARENT, ++ AuLsc_DI_PARENT2, ++ AuLsc_DI_PARENT3 ++}; ++ ++/* ++ * di_read_lock_child, di_write_lock_child, ++ * di_read_lock_child2, di_write_lock_child2, ++ * di_read_lock_child3, di_write_lock_child3, ++ * di_read_lock_parent, di_write_lock_parent, ++ * di_read_lock_parent2, di_write_lock_parent2, ++ * di_read_lock_parent3, di_write_lock_parent3, ++ */ ++#define AuReadLockFunc(name, lsc) \ ++static inline void di_read_lock_##name(struct dentry *d, int flags) \ ++{ di_read_lock(d, flags, AuLsc_DI_##lsc); } ++ ++#define AuWriteLockFunc(name, lsc) \ ++static inline void di_write_lock_##name(struct dentry *d) \ ++{ di_write_lock(d, AuLsc_DI_##lsc); } ++ ++#define AuRWLockFuncs(name, lsc) \ ++ AuReadLockFunc(name, lsc) \ ++ AuWriteLockFunc(name, lsc) ++ ++AuRWLockFuncs(child, CHILD); ++AuRWLockFuncs(child2, CHILD2); ++AuRWLockFuncs(child3, CHILD3); ++AuRWLockFuncs(parent, PARENT); ++AuRWLockFuncs(parent2, PARENT2); ++AuRWLockFuncs(parent3, PARENT3); ++ ++#undef AuReadLockFunc ++#undef AuWriteLockFunc ++#undef AuRWLockFuncs ++ ++#define DiMustNoWaiters(d) AuRwMustNoWaiters(&au_di(d)->di_rwsem) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* todo: memory barrier? */ ++static inline unsigned int au_digen(struct dentry *d) ++{ ++ return atomic_read(&au_di(d)->di_generation); ++} ++ ++static inline void au_h_dentry_init(struct au_hdentry *hdentry) ++{ ++ hdentry->hd_dentry = NULL; ++} ++ ++static inline void au_hdput(struct au_hdentry *hd) ++{ ++ dput(hd->hd_dentry); ++} ++ ++static inline aufs_bindex_t au_dbstart(struct dentry *dentry) ++{ ++ return au_di(dentry)->di_bstart; ++} ++ ++static inline aufs_bindex_t au_dbend(struct dentry *dentry) ++{ ++ return au_di(dentry)->di_bend; ++} ++ ++static inline aufs_bindex_t au_dbwh(struct dentry *dentry) ++{ ++ return au_di(dentry)->di_bwh; ++} ++ ++static inline aufs_bindex_t au_dbdiropq(struct dentry *dentry) ++{ ++ return au_di(dentry)->di_bdiropq; ++} ++ ++/* todo: hard/soft set? */ ++static inline void au_set_dbstart(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ au_di(dentry)->di_bstart = bindex; ++} ++ ++static inline void au_set_dbend(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ au_di(dentry)->di_bend = bindex; ++} ++ ++static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ /* dbwh can be outside of bstart - bend range */ ++ au_di(dentry)->di_bwh = bindex; ++} ++ ++static inline void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ au_di(dentry)->di_bdiropq = bindex; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_HINOTIFY ++static inline void au_digen_dec(struct dentry *d) ++{ ++ atomic_dec(&au_di(d)->di_generation); ++} ++ ++static inline void au_hin_di_reinit(struct dentry *dentry) ++{ ++ dentry->d_fsdata = NULL; ++} ++#else ++static inline void au_hin_di_reinit(struct dentry *dentry __maybe_unused) ++{ ++ /* empty */ ++} ++#endif /* CONFIG_AUFS_HINOTIFY */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DENTRY_H__ */ +diff -Naur a/fs/aufs/dinfo.c b/fs/aufs/dinfo.c +--- a/fs/aufs/dinfo.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/dinfo.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,360 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * dentry private data ++ */ ++ ++#include "aufs.h" ++ ++int au_alloc_dinfo(struct dentry *dentry) ++{ ++ struct au_dinfo *dinfo; ++ struct super_block *sb; ++ int nbr; ++ ++ dinfo = au_cache_alloc_dinfo(); ++ if (unlikely(!dinfo)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ nbr = au_sbend(sb) + 1; ++ if (nbr <= 0) ++ nbr = 1; ++ dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); ++ if (unlikely(!dinfo->di_hdentry)) ++ goto out_dinfo; ++ ++ atomic_set(&dinfo->di_generation, au_sigen(sb)); ++ /* smp_mb(); */ /* atomic_set */ ++ init_rwsem(&dinfo->di_rwsem); ++ down_write_nested(&dinfo->di_rwsem, AuLsc_DI_CHILD); ++ dinfo->di_bstart = -1; ++ dinfo->di_bend = -1; ++ dinfo->di_bwh = -1; ++ dinfo->di_bdiropq = -1; ++ ++ dentry->d_fsdata = dinfo; ++ dentry->d_op = &aufs_dop; ++ return 0; /* success */ ++ ++ out_dinfo: ++ au_cache_free_dinfo(dinfo); ++ out: ++ return -ENOMEM; ++} ++ ++int au_di_realloc(struct au_dinfo *dinfo, int nbr) ++{ ++ int err, sz; ++ struct au_hdentry *hdp; ++ ++ err = -ENOMEM; ++ sz = sizeof(*hdp) * (dinfo->di_bend + 1); ++ if (!sz) ++ sz = sizeof(*hdp); ++ hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS); ++ if (hdp) { ++ dinfo->di_hdentry = hdp; ++ err = 0; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void do_ii_write_lock(struct inode *inode, unsigned int lsc) ++{ ++ switch (lsc) { ++ case AuLsc_DI_CHILD: ++ ii_write_lock_child(inode); ++ break; ++ case AuLsc_DI_CHILD2: ++ ii_write_lock_child2(inode); ++ break; ++ case AuLsc_DI_CHILD3: ++ ii_write_lock_child3(inode); ++ break; ++ case AuLsc_DI_PARENT: ++ ii_write_lock_parent(inode); ++ break; ++ case AuLsc_DI_PARENT2: ++ ii_write_lock_parent2(inode); ++ break; ++ case AuLsc_DI_PARENT3: ++ ii_write_lock_parent3(inode); ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++static void do_ii_read_lock(struct inode *inode, unsigned int lsc) ++{ ++ switch (lsc) { ++ case AuLsc_DI_CHILD: ++ ii_read_lock_child(inode); ++ break; ++ case AuLsc_DI_CHILD2: ++ ii_read_lock_child2(inode); ++ break; ++ case AuLsc_DI_CHILD3: ++ ii_read_lock_child3(inode); ++ break; ++ case AuLsc_DI_PARENT: ++ ii_read_lock_parent(inode); ++ break; ++ case AuLsc_DI_PARENT2: ++ ii_read_lock_parent2(inode); ++ break; ++ case AuLsc_DI_PARENT3: ++ ii_read_lock_parent3(inode); ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++void di_read_lock(struct dentry *d, int flags, unsigned int lsc) ++{ ++ down_read_nested(&au_di(d)->di_rwsem, lsc); ++ if (d->d_inode) { ++ if (au_ftest_lock(flags, IW)) ++ do_ii_write_lock(d->d_inode, lsc); ++ else if (au_ftest_lock(flags, IR)) ++ do_ii_read_lock(d->d_inode, lsc); ++ } ++} ++ ++void di_read_unlock(struct dentry *d, int flags) ++{ ++ if (d->d_inode) { ++ if (au_ftest_lock(flags, IW)) ++ ii_write_unlock(d->d_inode); ++ else if (au_ftest_lock(flags, IR)) ++ ii_read_unlock(d->d_inode); ++ } ++ up_read(&au_di(d)->di_rwsem); ++} ++ ++void di_downgrade_lock(struct dentry *d, int flags) ++{ ++ downgrade_write(&au_di(d)->di_rwsem); ++ if (d->d_inode && au_ftest_lock(flags, IR)) ++ ii_downgrade_lock(d->d_inode); ++} ++ ++void di_write_lock(struct dentry *d, unsigned int lsc) ++{ ++ down_write_nested(&au_di(d)->di_rwsem, lsc); ++ if (d->d_inode) ++ do_ii_write_lock(d->d_inode, lsc); ++} ++ ++void di_write_unlock(struct dentry *d) ++{ ++ if (d->d_inode) ++ ii_write_unlock(d->d_inode); ++ up_write(&au_di(d)->di_rwsem); ++} ++ ++void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir) ++{ ++ AuDebugOn(d1 == d2 ++ || d1->d_inode == d2->d_inode ++ || d1->d_sb != d2->d_sb); ++ ++ if (isdir && au_test_subdir(d1, d2)) { ++ di_write_lock_child(d1); ++ di_write_lock_child2(d2); ++ } else { ++ /* there should be no races */ ++ di_write_lock_child(d2); ++ di_write_lock_child2(d1); ++ } ++} ++ ++void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir) ++{ ++ AuDebugOn(d1 == d2 ++ || d1->d_inode == d2->d_inode ++ || d1->d_sb != d2->d_sb); ++ ++ if (isdir && au_test_subdir(d1, d2)) { ++ di_write_lock_parent(d1); ++ di_write_lock_parent2(d2); ++ } else { ++ /* there should be no races */ ++ di_write_lock_parent(d2); ++ di_write_lock_parent2(d1); ++ } ++} ++ ++void di_write_unlock2(struct dentry *d1, struct dentry *d2) ++{ ++ di_write_unlock(d1); ++ if (d1->d_inode == d2->d_inode) ++ up_write(&au_di(d2)->di_rwsem); ++ else ++ di_write_unlock(d2); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ struct dentry *d; ++ ++ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) ++ return NULL; ++ AuDebugOn(bindex < 0); ++ d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry; ++ AuDebugOn(d && (atomic_read(&d->d_count) <= 0)); ++ return d; ++} ++ ++aufs_bindex_t au_dbtail(struct dentry *dentry) ++{ ++ aufs_bindex_t bend, bwh; ++ ++ bend = au_dbend(dentry); ++ if (0 <= bend) { ++ bwh = au_dbwh(dentry); ++ if (!bwh) ++ return bwh; ++ if (0 < bwh && bwh < bend) ++ return bwh - 1; ++ } ++ return bend; ++} ++ ++aufs_bindex_t au_dbtaildir(struct dentry *dentry) ++{ ++ aufs_bindex_t bend, bopq; ++ ++ bend = au_dbtail(dentry); ++ if (0 <= bend) { ++ bopq = au_dbdiropq(dentry); ++ if (0 <= bopq && bopq < bend) ++ bend = bopq; ++ } ++ return bend; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_dentry) ++{ ++ struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; ++ ++ if (hd->hd_dentry) ++ au_hdput(hd); ++ hd->hd_dentry = h_dentry; ++} ++ ++void au_update_digen(struct dentry *dentry) ++{ ++ atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb)); ++ /* smp_mb(); */ /* atomic_set */ ++} ++ ++void au_update_dbrange(struct dentry *dentry, int do_put_zero) ++{ ++ struct au_dinfo *dinfo; ++ struct dentry *h_d; ++ ++ dinfo = au_di(dentry); ++ if (!dinfo || dinfo->di_bstart < 0) ++ return; ++ ++ if (do_put_zero) { ++ aufs_bindex_t bindex, bend; ++ ++ bend = dinfo->di_bend; ++ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++) { ++ h_d = dinfo->di_hdentry[0 + bindex].hd_dentry; ++ if (h_d && !h_d->d_inode) ++ au_set_h_dptr(dentry, bindex, NULL); ++ } ++ } ++ ++ dinfo->di_bstart = -1; ++ while (++dinfo->di_bstart <= dinfo->di_bend) ++ if (dinfo->di_hdentry[0 + dinfo->di_bstart].hd_dentry) ++ break; ++ if (dinfo->di_bstart > dinfo->di_bend) { ++ dinfo->di_bstart = -1; ++ dinfo->di_bend = -1; ++ return; ++ } ++ ++ dinfo->di_bend++; ++ while (0 <= --dinfo->di_bend) ++ if (dinfo->di_hdentry[0 + dinfo->di_bend].hd_dentry) ++ break; ++ AuDebugOn(dinfo->di_bstart > dinfo->di_bend || dinfo->di_bend < 0); ++} ++ ++void au_update_dbstart(struct dentry *dentry) ++{ ++ aufs_bindex_t bindex, bend; ++ struct dentry *h_dentry; ++ ++ bend = au_dbend(dentry); ++ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ if (h_dentry->d_inode) { ++ au_set_dbstart(dentry, bindex); ++ return; ++ } ++ au_set_h_dptr(dentry, bindex, NULL); ++ } ++} ++ ++void au_update_dbend(struct dentry *dentry) ++{ ++ aufs_bindex_t bindex, bstart; ++ struct dentry *h_dentry; ++ ++ bstart = au_dbstart(dentry); ++ for (bindex = au_dbend(dentry); bindex <= bstart; bindex--) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ if (h_dentry->d_inode) { ++ au_set_dbend(dentry, bindex); ++ return; ++ } ++ au_set_h_dptr(dentry, bindex, NULL); ++ } ++} ++ ++int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry) ++{ ++ aufs_bindex_t bindex, bend; ++ ++ bend = au_dbend(dentry); ++ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) ++ if (au_h_dptr(dentry, bindex) == h_dentry) ++ return bindex; ++ return -1; ++} +diff -Naur a/fs/aufs/dir.c b/fs/aufs/dir.c +--- a/fs/aufs/dir.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/dir.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,532 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * directory operations ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++void au_add_nlink(struct inode *dir, struct inode *h_dir) ++{ ++ AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); ++ ++ dir->i_nlink += h_dir->i_nlink - 2; ++ if (h_dir->i_nlink < 2) ++ dir->i_nlink += 2; ++} ++ ++void au_sub_nlink(struct inode *dir, struct inode *h_dir) ++{ ++ AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); ++ ++ dir->i_nlink -= h_dir->i_nlink - 2; ++ if (h_dir->i_nlink < 2) ++ dir->i_nlink -= 2; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int reopen_dir(struct file *file) ++{ ++ int err; ++ unsigned int flags; ++ aufs_bindex_t bindex, btail, bstart; ++ struct dentry *dentry, *h_dentry; ++ struct file *h_file; ++ ++ /* open all lower dirs */ ++ dentry = file->f_dentry; ++ bstart = au_dbstart(dentry); ++ for (bindex = au_fbstart(file); bindex < bstart; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ au_set_fbstart(file, bstart); ++ ++ btail = au_dbtaildir(dentry); ++ for (bindex = au_fbend(file); btail < bindex; bindex--) ++ au_set_h_fptr(file, bindex, NULL); ++ au_set_fbend(file, btail); ++ ++ flags = file->f_flags; ++ for (bindex = bstart; bindex <= btail; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ h_file = au_h_fptr(file, bindex); ++ if (h_file) ++ continue; ++ ++ h_file = au_h_open(dentry, bindex, flags, file); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; /* close all? */ ++ au_set_h_fptr(file, bindex, h_file); ++ } ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ err = 0; ++ ++ out: ++ return err; ++} ++ ++static int do_open_dir(struct file *file, int flags) ++{ ++ int err; ++ aufs_bindex_t bindex, btail; ++ struct dentry *dentry, *h_dentry; ++ struct file *h_file; ++ ++ err = 0; ++ dentry = file->f_dentry; ++ au_set_fvdir_cache(file, NULL); ++ au_fi(file)->fi_maintain_plink = 0; ++ file->f_version = dentry->d_inode->i_version; ++ bindex = au_dbstart(dentry); ++ au_set_fbstart(file, bindex); ++ btail = au_dbtaildir(dentry); ++ au_set_fbend(file, btail); ++ for (; !err && bindex <= btail; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ ++ h_file = au_h_open(dentry, bindex, flags, file); ++ if (IS_ERR(h_file)) { ++ err = PTR_ERR(h_file); ++ break; ++ } ++ au_set_h_fptr(file, bindex, h_file); ++ } ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ if (!err) ++ return 0; /* success */ ++ ++ /* close all */ ++ for (bindex = au_fbstart(file); bindex <= btail; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ au_set_fbstart(file, -1); ++ au_set_fbend(file, -1); ++ return err; ++} ++ ++static int aufs_open_dir(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ return au_do_open(file, do_open_dir); ++} ++ ++static int aufs_release_dir(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ struct au_vdir *vdir_cache; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++ sb = file->f_dentry->d_sb; ++ si_noflush_read_lock(sb); ++ fi_write_lock(file); ++ vdir_cache = au_fvdir_cache(file); ++ if (vdir_cache) ++ au_vdir_free(vdir_cache); ++ if (au_fi(file)->fi_maintain_plink) { ++ sbinfo = au_sbi(sb); ++ au_fclr_si(sbinfo, MAINTAIN_PLINK); ++ wake_up_all(&sbinfo->si_plink_wq); ++ } ++ fi_write_unlock(file); ++ au_finfo_fin(file); ++ si_read_unlock(sb); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct inode *inode; ++ struct super_block *sb; ++ ++ err = 0; ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ IMustLock(inode); ++ bend = au_dbend(dentry); ++ for (bindex = au_dbstart(dentry); !err && bindex <= bend; bindex++) { ++ struct path h_path; ++ struct inode *h_inode; ++ ++ if (au_test_ro(sb, bindex, inode)) ++ continue; ++ h_path.dentry = au_h_dptr(dentry, bindex); ++ if (!h_path.dentry) ++ continue; ++ h_inode = h_path.dentry->d_inode; ++ if (!h_inode) ++ continue; ++ ++ /* no mnt_want_write() */ ++ /* cf. fs/nsfd/vfs.c and fs/nfsd/nfs4recover.c */ ++ /* todo: inotiry fired? */ ++ h_path.mnt = au_sbr_mnt(sb, bindex); ++ mutex_lock(&h_inode->i_mutex); ++ err = filemap_fdatawrite(h_inode->i_mapping); ++ AuDebugOn(!h_inode->i_fop); ++ if (!err && h_inode->i_fop->fsync) ++ err = h_inode->i_fop->fsync(NULL, h_path.dentry, ++ datasync); ++ if (!err) ++ err = filemap_fdatawrite(h_inode->i_mapping); ++ if (!err) ++ vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/ ++ mutex_unlock(&h_inode->i_mutex); ++ } ++ ++ return err; ++} ++ ++static int au_do_fsync_dir(struct file *file, int datasync) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct file *h_file; ++ struct super_block *sb; ++ struct inode *inode; ++ struct mutex *h_mtx; ++ ++ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ sb = file->f_dentry->d_sb; ++ inode = file->f_dentry->d_inode; ++ bend = au_fbend(file); ++ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { ++ h_file = au_h_fptr(file, bindex); ++ if (!h_file || au_test_ro(sb, bindex, inode)) ++ continue; ++ ++ err = vfs_fsync(h_file, h_file->f_dentry, datasync); ++ if (!err) { ++ h_mtx = &h_file->f_dentry->d_inode->i_mutex; ++ mutex_lock(h_mtx); ++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); ++ /*ignore*/ ++ mutex_unlock(h_mtx); ++ } ++ } ++ ++ out: ++ return err; ++} ++ ++/* ++ * @file may be NULL ++ */ ++static int aufs_fsync_dir(struct file *file, struct dentry *dentry, ++ int datasync) ++{ ++ int err; ++ struct super_block *sb; ++ ++ IMustLock(dentry->d_inode); ++ ++ err = 0; ++ sb = dentry->d_sb; ++ si_noflush_read_lock(sb); ++ if (file) ++ err = au_do_fsync_dir(file, datasync); ++ else { ++ di_write_lock_child(dentry); ++ err = au_do_fsync_dir_no_file(dentry, datasync); ++ } ++ au_cpup_attr_timesizes(dentry->d_inode); ++ di_write_unlock(dentry); ++ if (file) ++ fi_write_unlock(file); ++ ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_readdir(struct file *file, void *dirent, filldir_t filldir) ++{ ++ int err; ++ struct dentry *dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ ++ dentry = file->f_dentry; ++ inode = dentry->d_inode; ++ IMustLock(inode); ++ ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ err = au_vdir_init(file); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ if (!au_test_nfsd(current)) { ++ err = au_vdir_fill_de(file, dirent, filldir); ++ fsstack_copy_attr_atime(inode, ++ au_h_iptr(inode, au_ibstart(inode))); ++ } else { ++ /* ++ * nfsd filldir may call lookup_one_len(), vfs_getattr(), ++ * encode_fh() and others. ++ */ ++ struct inode *h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ ++ di_read_unlock(dentry, AuLock_IR); ++ si_read_unlock(sb); ++ lockdep_off(); ++ err = au_vdir_fill_de(file, dirent, filldir); ++ lockdep_on(); ++ fsstack_copy_attr_atime(inode, h_inode); ++ fi_write_unlock(file); ++ ++ AuTraceErr(err); ++ return err; ++ } ++ ++ out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define AuTestEmpty_WHONLY 1 ++#define AuTestEmpty_CALLED (1 << 1) ++#define AuTestEmpty_SHWH (1 << 2) ++#define au_ftest_testempty(flags, name) ((flags) & AuTestEmpty_##name) ++#define au_fset_testempty(flags, name) { (flags) |= AuTestEmpty_##name; } ++#define au_fclr_testempty(flags, name) { (flags) &= ~AuTestEmpty_##name; } ++ ++#ifndef CONFIG_AUFS_SHWH ++#undef AuTestEmpty_SHWH ++#define AuTestEmpty_SHWH 0 ++#endif ++ ++struct test_empty_arg { ++ struct au_nhash whlist; ++ unsigned int flags; ++ int err; ++ aufs_bindex_t bindex; ++}; ++ ++static int test_empty_cb(void *__arg, const char *__name, int namelen, ++ loff_t offset __maybe_unused, u64 ino, ++ unsigned int d_type) ++{ ++ struct test_empty_arg *arg = __arg; ++ char *name = (void *)__name; ++ ++ arg->err = 0; ++ au_fset_testempty(arg->flags, CALLED); ++ /* smp_mb(); */ ++ if (name[0] == '.' ++ && (namelen == 1 || (name[1] == '.' && namelen == 2))) ++ goto out; /* success */ ++ ++ if (namelen <= AUFS_WH_PFX_LEN ++ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { ++ if (au_ftest_testempty(arg->flags, WHONLY) ++ && !au_nhash_test_known_wh(&arg->whlist, name, namelen)) ++ arg->err = -ENOTEMPTY; ++ goto out; ++ } ++ ++ name += AUFS_WH_PFX_LEN; ++ namelen -= AUFS_WH_PFX_LEN; ++ if (!au_nhash_test_known_wh(&arg->whlist, name, namelen)) ++ arg->err = au_nhash_append_wh ++ (&arg->whlist, name, namelen, ino, d_type, arg->bindex, ++ au_ftest_testempty(arg->flags, SHWH)); ++ ++ out: ++ /* smp_mb(); */ ++ AuTraceErr(arg->err); ++ return arg->err; ++} ++ ++static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg) ++{ ++ int err; ++ struct file *h_file; ++ ++ h_file = au_h_open(dentry, arg->bindex, ++ O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE, ++ /*file*/NULL); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ err = 0; ++ if (!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE) ++ && !h_file->f_dentry->d_inode->i_nlink) ++ goto out_put; ++ ++ do { ++ arg->err = 0; ++ au_fclr_testempty(arg->flags, CALLED); ++ /* smp_mb(); */ ++ err = vfsub_readdir(h_file, test_empty_cb, arg); ++ if (err >= 0) ++ err = arg->err; ++ } while (!err && au_ftest_testempty(arg->flags, CALLED)); ++ ++ out_put: ++ fput(h_file); ++ au_sbr_put(dentry->d_sb, arg->bindex); ++ out: ++ return err; ++} ++ ++struct do_test_empty_args { ++ int *errp; ++ struct dentry *dentry; ++ struct test_empty_arg *arg; ++}; ++ ++static void call_do_test_empty(void *args) ++{ ++ struct do_test_empty_args *a = args; ++ *a->errp = do_test_empty(a->dentry, a->arg); ++} ++ ++static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg) ++{ ++ int err, wkq_err; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ ++ h_dentry = au_h_dptr(dentry, arg->bindex); ++ h_inode = h_dentry->d_inode; ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ); ++ mutex_unlock(&h_inode->i_mutex); ++ if (!err) ++ err = do_test_empty(dentry, arg); ++ else { ++ struct do_test_empty_args args = { ++ .errp = &err, ++ .dentry = dentry, ++ .arg = arg ++ }; ++ unsigned int flags = arg->flags; ++ ++ wkq_err = au_wkq_wait(call_do_test_empty, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ arg->flags = flags; ++ } ++ ++ return err; ++} ++ ++int au_test_empty_lower(struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bindex, bstart, btail; ++ struct test_empty_arg arg; ++ ++ err = au_nhash_alloc(&arg.whlist, au_sbi(dentry->d_sb)->si_rdhash, ++ GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ ++ bstart = au_dbstart(dentry); ++ arg.flags = 0; ++ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) ++ au_fset_testempty(arg.flags, SHWH); ++ arg.bindex = bstart; ++ err = do_test_empty(dentry, &arg); ++ if (unlikely(err)) ++ goto out_whlist; ++ ++ au_fset_testempty(arg.flags, WHONLY); ++ btail = au_dbtaildir(dentry); ++ for (bindex = bstart + 1; !err && bindex <= btail; bindex++) { ++ struct dentry *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry && h_dentry->d_inode) { ++ arg.bindex = bindex; ++ err = do_test_empty(dentry, &arg); ++ } ++ } ++ ++ out_whlist: ++ au_nhash_wh_free(&arg.whlist); ++ out: ++ return err; ++} ++ ++int au_test_empty(struct dentry *dentry, struct au_nhash *whlist) ++{ ++ int err; ++ struct test_empty_arg arg; ++ aufs_bindex_t bindex, btail; ++ ++ err = 0; ++ arg.whlist = *whlist; ++ arg.flags = AuTestEmpty_WHONLY; ++ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) ++ au_fset_testempty(arg.flags, SHWH); ++ btail = au_dbtaildir(dentry); ++ for (bindex = au_dbstart(dentry); !err && bindex <= btail; bindex++) { ++ struct dentry *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry && h_dentry->d_inode) { ++ arg.bindex = bindex; ++ err = sio_test_empty(dentry, &arg); ++ } ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++const struct file_operations aufs_dir_fop = { ++ .read = generic_read_dir, ++ .readdir = aufs_readdir, ++ .unlocked_ioctl = aufs_ioctl_dir, ++ .open = aufs_open_dir, ++ .release = aufs_release_dir, ++ .flush = aufs_flush, ++ .fsync = aufs_fsync_dir ++}; +diff -Naur a/fs/aufs/dir.h b/fs/aufs/dir.h +--- a/fs/aufs/dir.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/dir.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,114 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * directory operations ++ */ ++ ++#ifndef __AUFS_DIR_H__ ++#define __AUFS_DIR_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* need to be faster and smaller */ ++ ++struct au_nhash { ++ unsigned int nh_num; ++ struct hlist_head *nh_head; ++}; ++ ++struct au_vdir_destr { ++ unsigned char len; ++ unsigned char name[0]; ++} __packed; ++ ++struct au_vdir_dehstr { ++ struct hlist_node hash; ++ struct au_vdir_destr *str; ++}; ++ ++struct au_vdir_de { ++ ino_t de_ino; ++ unsigned char de_type; ++ /* caution: packed */ ++ struct au_vdir_destr de_str; ++} __packed; ++ ++struct au_vdir_wh { ++ struct hlist_node wh_hash; ++#ifdef CONFIG_AUFS_SHWH ++ ino_t wh_ino; ++ aufs_bindex_t wh_bindex; ++ unsigned char wh_type; ++#else ++ aufs_bindex_t wh_bindex; ++#endif ++ /* caution: packed */ ++ struct au_vdir_destr wh_str; ++} __packed; ++ ++union au_vdir_deblk_p { ++ unsigned char *deblk; ++ struct au_vdir_de *de; ++}; ++ ++struct au_vdir { ++ unsigned char **vd_deblk; ++ unsigned long vd_nblk; ++ struct { ++ unsigned long ul; ++ union au_vdir_deblk_p p; ++ } vd_last; ++ ++ unsigned long vd_version; ++ unsigned int vd_deblk_sz; ++ unsigned long vd_jiffy; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dir.c */ ++extern const struct file_operations aufs_dir_fop; ++void au_add_nlink(struct inode *dir, struct inode *h_dir); ++void au_sub_nlink(struct inode *dir, struct inode *h_dir); ++int au_test_empty_lower(struct dentry *dentry); ++int au_test_empty(struct dentry *dentry, struct au_nhash *whlist); ++ ++/* vdir.c */ ++int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp); ++void au_nhash_wh_free(struct au_nhash *whlist); ++int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, ++ int limit); ++int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen); ++int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, ++ unsigned int d_type, aufs_bindex_t bindex, ++ unsigned char shwh); ++void au_vdir_free(struct au_vdir *vdir); ++int au_vdir_init(struct file *file); ++int au_vdir_fill_de(struct file *file, void *dirent, filldir_t filldir); ++ ++/* ioctl.c */ ++long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg); ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DIR_H__ */ +diff -Naur a/fs/aufs/export.c b/fs/aufs/export.c +--- a/fs/aufs/export.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/export.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,736 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * export via nfs ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++union conv { ++#ifdef CONFIG_AUFS_INO_T_64 ++ __u32 a[2]; ++#else ++ __u32 a[1]; ++#endif ++ ino_t ino; ++}; ++ ++static ino_t decode_ino(__u32 *a) ++{ ++ union conv u; ++ ++ BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a)); ++ u.a[0] = a[0]; ++#ifdef CONFIG_AUFS_INO_T_64 ++ u.a[1] = a[1]; ++#endif ++ return u.ino; ++} ++ ++static void encode_ino(__u32 *a, ino_t ino) ++{ ++ union conv u; ++ ++ u.ino = ino; ++ a[0] = u.a[0]; ++#ifdef CONFIG_AUFS_INO_T_64 ++ a[1] = u.a[1]; ++#endif ++} ++ ++/* NFS file handle */ ++enum { ++ Fh_br_id, ++ Fh_sigen, ++#ifdef CONFIG_AUFS_INO_T_64 ++ /* support 64bit inode number */ ++ Fh_ino1, ++ Fh_ino2, ++ Fh_dir_ino1, ++ Fh_dir_ino2, ++#else ++ Fh_ino1, ++ Fh_dir_ino1, ++#endif ++ Fh_igen, ++ Fh_h_type, ++ Fh_tail, ++ ++ Fh_ino = Fh_ino1, ++ Fh_dir_ino = Fh_dir_ino1 ++}; ++ ++static int au_test_anon(struct dentry *dentry) ++{ ++ return !!(dentry->d_flags & DCACHE_DISCONNECTED); ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* inode generation external table */ ++ ++int au_xigen_inc(struct inode *inode) ++{ ++ int err; ++ loff_t pos; ++ ssize_t sz; ++ __u32 igen; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++ err = 0; ++ sb = inode->i_sb; ++ if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) ++ goto out; ++ ++ pos = inode->i_ino; ++ pos *= sizeof(igen); ++ igen = inode->i_generation + 1; ++ sbinfo = au_sbi(sb); ++ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen, ++ sizeof(igen), &pos); ++ if (sz == sizeof(igen)) ++ goto out; /* success */ ++ ++ err = sz; ++ if (unlikely(sz >= 0)) { ++ err = -EIO; ++ AuIOErr("xigen error (%zd)\n", sz); ++ } ++ ++ out: ++ return err; ++} ++ ++int au_xigen_new(struct inode *inode) ++{ ++ int err; ++ loff_t pos; ++ ssize_t sz; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ ++ err = 0; ++ /* todo: dirty, at mount time */ ++ if (inode->i_ino == AUFS_ROOT_INO) ++ goto out; ++ sb = inode->i_sb; ++ if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) ++ goto out; ++ ++ err = -EFBIG; ++ pos = inode->i_ino; ++ if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) { ++ AuIOErr1("too large i%lld\n", pos); ++ goto out; ++ } ++ pos *= sizeof(inode->i_generation); ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ file = sbinfo->si_xigen; ++ BUG_ON(!file); ++ ++ if (i_size_read(file->f_dentry->d_inode) ++ < pos + sizeof(inode->i_generation)) { ++ inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next); ++ sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation, ++ sizeof(inode->i_generation), &pos); ++ } else ++ sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation, ++ sizeof(inode->i_generation), &pos); ++ if (sz == sizeof(inode->i_generation)) ++ goto out; /* success */ ++ ++ err = sz; ++ if (unlikely(sz >= 0)) { ++ err = -EIO; ++ AuIOErr("xigen error (%zd)\n", sz); ++ } ++ ++ out: ++ return err; ++} ++ ++int au_xigen_set(struct super_block *sb, struct file *base) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ ++ sbinfo = au_sbi(sb); ++ file = au_xino_create2(base, sbinfo->si_xigen); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ err = 0; ++ if (sbinfo->si_xigen) ++ fput(sbinfo->si_xigen); ++ sbinfo->si_xigen = file; ++ ++ out: ++ return err; ++} ++ ++void au_xigen_clr(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = au_sbi(sb); ++ if (sbinfo->si_xigen) { ++ fput(sbinfo->si_xigen); ++ sbinfo->si_xigen = NULL; ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, ++ ino_t dir_ino) ++{ ++ struct dentry *dentry, *d; ++ struct inode *inode; ++ unsigned int sigen; ++ ++ dentry = NULL; ++ inode = ilookup(sb, ino); ++ if (!inode) ++ goto out; ++ ++ dentry = ERR_PTR(-ESTALE); ++ sigen = au_sigen(sb); ++ if (unlikely(is_bad_inode(inode) ++ || IS_DEADDIR(inode) ++ || sigen != au_iigen(inode))) ++ goto out_iput; ++ ++ dentry = NULL; ++ if (!dir_ino || S_ISDIR(inode->i_mode)) ++ dentry = d_find_alias(inode); ++ else { ++ spin_lock(&dcache_lock); ++ list_for_each_entry(d, &inode->i_dentry, d_alias) ++ if (!au_test_anon(d) ++ && d->d_parent->d_inode->i_ino == dir_ino) { ++ dentry = dget_locked(d); ++ break; ++ } ++ spin_unlock(&dcache_lock); ++ } ++ if (unlikely(dentry && sigen != au_digen(dentry))) { ++ dput(dentry); ++ dentry = ERR_PTR(-ESTALE); ++ } ++ ++ out_iput: ++ iput(inode); ++ out: ++ return dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* todo: dirty? */ ++/* if exportfs_decode_fh() passed vfsmount*, we could be happy */ ++static struct vfsmount *au_mnt_get(struct super_block *sb) ++{ ++ struct mnt_namespace *ns; ++ struct vfsmount *pos, *mnt; ++ ++ spin_lock(&vfsmount_lock); ++ /* no get/put ?? */ ++ AuDebugOn(!current->nsproxy); ++ ns = current->nsproxy->mnt_ns; ++ AuDebugOn(!ns); ++ mnt = NULL; ++ /* the order (reverse) will not be a problem */ ++ list_for_each_entry(pos, &ns->list, mnt_list) ++ if (pos->mnt_sb == sb) { ++ mnt = mntget(pos); ++ break; ++ } ++ spin_unlock(&vfsmount_lock); ++ AuDebugOn(!mnt); ++ ++ return mnt; ++} ++ ++struct au_nfsd_si_lock { ++ const unsigned int sigen; ++ const aufs_bindex_t br_id; ++ unsigned char force_lock; ++}; ++ ++static aufs_bindex_t si_nfsd_read_lock(struct super_block *sb, ++ struct au_nfsd_si_lock *nsi_lock) ++{ ++ aufs_bindex_t bindex; ++ ++ si_read_lock(sb, AuLock_FLUSH); ++ ++ /* branch id may be wrapped around */ ++ bindex = au_br_index(sb, nsi_lock->br_id); ++ if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb)) ++ goto out; /* success */ ++ ++ if (!nsi_lock->force_lock) ++ si_read_unlock(sb); ++ bindex = -1; ++ ++ out: ++ return bindex; ++} ++ ++struct find_name_by_ino { ++ int called, found; ++ ino_t ino; ++ char *name; ++ int namelen; ++}; ++ ++static int ++find_name_by_ino(void *arg, const char *name, int namelen, loff_t offset, ++ u64 ino, unsigned int d_type) ++{ ++ struct find_name_by_ino *a = arg; ++ ++ a->called++; ++ if (a->ino != ino) ++ return 0; ++ ++ memcpy(a->name, name, namelen); ++ a->namelen = namelen; ++ a->found = 1; ++ return 1; ++} ++ ++static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino, ++ struct au_nfsd_si_lock *nsi_lock) ++{ ++ struct dentry *dentry, *parent; ++ struct file *file; ++ struct inode *dir; ++ struct find_name_by_ino arg; ++ int err; ++ ++ parent = path->dentry; ++ if (nsi_lock) ++ si_read_unlock(parent->d_sb); ++ path_get(path); ++ file = dentry_open(parent, path->mnt, au_dir_roflags, current_cred()); ++ dentry = (void *)file; ++ if (IS_ERR(file)) ++ goto out; ++ ++ dentry = ERR_PTR(-ENOMEM); ++ arg.name = __getname(); ++ if (unlikely(!arg.name)) ++ goto out_file; ++ arg.ino = ino; ++ arg.found = 0; ++ do { ++ arg.called = 0; ++ /* smp_mb(); */ ++ err = vfsub_readdir(file, find_name_by_ino, &arg); ++ } while (!err && !arg.found && arg.called); ++ dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_name; ++ dentry = ERR_PTR(-ENOENT); ++ if (!arg.found) ++ goto out_name; ++ ++ /* do not call au_lkup_one() */ ++ dir = parent->d_inode; ++ mutex_lock(&dir->i_mutex); ++ dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen); ++ mutex_unlock(&dir->i_mutex); ++ AuTraceErrPtr(dentry); ++ if (IS_ERR(dentry)) ++ goto out_name; ++ AuDebugOn(au_test_anon(dentry)); ++ if (unlikely(!dentry->d_inode)) { ++ dput(dentry); ++ dentry = ERR_PTR(-ENOENT); ++ } ++ ++ out_name: ++ __putname(arg.name); ++ out_file: ++ fput(file); ++ out: ++ if (unlikely(nsi_lock ++ && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0)) ++ if (!IS_ERR(dentry)) { ++ dput(dentry); ++ dentry = ERR_PTR(-ESTALE); ++ } ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino, ++ ino_t dir_ino, ++ struct au_nfsd_si_lock *nsi_lock) ++{ ++ struct dentry *dentry; ++ struct path path; ++ ++ if (dir_ino != AUFS_ROOT_INO) { ++ path.dentry = decode_by_ino(sb, dir_ino, 0); ++ dentry = path.dentry; ++ if (!path.dentry || IS_ERR(path.dentry)) ++ goto out; ++ AuDebugOn(au_test_anon(path.dentry)); ++ } else ++ path.dentry = dget(sb->s_root); ++ ++ path.mnt = au_mnt_get(sb); ++ dentry = au_lkup_by_ino(&path, ino, nsi_lock); ++ path_put(&path); ++ ++ out: ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int h_acceptable(void *expv, struct dentry *dentry) ++{ ++ return 1; ++} ++ ++static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath, ++ char *buf, int len, struct super_block *sb) ++{ ++ char *p; ++ int n; ++ struct path path; ++ ++ p = d_path(h_rootpath, buf, len); ++ if (IS_ERR(p)) ++ goto out; ++ n = strlen(p); ++ ++ path.mnt = h_rootpath->mnt; ++ path.dentry = h_parent; ++ p = d_path(&path, buf, len); ++ if (IS_ERR(p)) ++ goto out; ++ if (n != 1) ++ p += n; ++ ++ path.mnt = au_mnt_get(sb); ++ path.dentry = sb->s_root; ++ p = d_path(&path, buf, len - strlen(p)); ++ mntput(path.mnt); ++ if (IS_ERR(p)) ++ goto out; ++ if (n != 1) ++ p[strlen(p)] = '/'; ++ ++ out: ++ AuTraceErrPtr(p); ++ return p; ++} ++ ++static ++struct dentry *decode_by_path(struct super_block *sb, aufs_bindex_t bindex, ++ ino_t ino, __u32 *fh, int fh_len, ++ struct au_nfsd_si_lock *nsi_lock) ++{ ++ struct dentry *dentry, *h_parent, *root; ++ struct super_block *h_sb; ++ char *pathname, *p; ++ struct vfsmount *h_mnt; ++ struct au_branch *br; ++ int err; ++ struct path path; ++ ++ br = au_sbr(sb, bindex); ++ /* au_br_get(br); */ ++ h_mnt = br->br_mnt; ++ h_sb = h_mnt->mnt_sb; ++ /* todo: call lower fh_to_dentry()? fh_to_parent()? */ ++ h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), ++ fh_len - Fh_tail, fh[Fh_h_type], ++ h_acceptable, /*context*/NULL); ++ dentry = h_parent; ++ if (unlikely(!h_parent || IS_ERR(h_parent))) { ++ AuWarn1("%s decode_fh failed, %ld\n", ++ au_sbtype(h_sb), PTR_ERR(h_parent)); ++ goto out; ++ } ++ dentry = NULL; ++ if (unlikely(au_test_anon(h_parent))) { ++ AuWarn1("%s decode_fh returned a disconnected dentry\n", ++ au_sbtype(h_sb)); ++ goto out_h_parent; ++ } ++ ++ dentry = ERR_PTR(-ENOMEM); ++ pathname = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!pathname)) ++ goto out_h_parent; ++ ++ root = sb->s_root; ++ path.mnt = h_mnt; ++ di_read_lock_parent(root, !AuLock_IR); ++ path.dentry = au_h_dptr(root, bindex); ++ di_read_unlock(root, !AuLock_IR); ++ p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb); ++ dentry = (void *)p; ++ if (IS_ERR(p)) ++ goto out_pathname; ++ ++ si_read_unlock(sb); ++ err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); ++ dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_relock; ++ ++ dentry = ERR_PTR(-ENOENT); ++ AuDebugOn(au_test_anon(path.dentry)); ++ if (unlikely(!path.dentry->d_inode)) ++ goto out_path; ++ ++ if (ino != path.dentry->d_inode->i_ino) ++ dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL); ++ else ++ dentry = dget(path.dentry); ++ ++ out_path: ++ path_put(&path); ++ out_relock: ++ if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0)) ++ if (!IS_ERR(dentry)) { ++ dput(dentry); ++ dentry = ERR_PTR(-ESTALE); ++ } ++ out_pathname: ++ free_page((unsigned long)pathname); ++ out_h_parent: ++ dput(h_parent); ++ out: ++ /* au_br_put(br); */ ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry * ++aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ struct dentry *dentry; ++ __u32 *fh = fid->raw; ++ ino_t ino, dir_ino; ++ aufs_bindex_t bindex; ++ struct au_nfsd_si_lock nsi_lock = { ++ .sigen = fh[Fh_sigen], ++ .br_id = fh[Fh_br_id], ++ .force_lock = 0 ++ }; ++ ++ AuDebugOn(fh_len < Fh_tail); ++ ++ dentry = ERR_PTR(-ESTALE); ++ /* branch id may be wrapped around */ ++ bindex = si_nfsd_read_lock(sb, &nsi_lock); ++ if (unlikely(bindex < 0)) ++ goto out; ++ nsi_lock.force_lock = 1; ++ ++ /* is this inode still cached? */ ++ ino = decode_ino(fh + Fh_ino); ++ AuDebugOn(ino == AUFS_ROOT_INO); ++ dir_ino = decode_ino(fh + Fh_dir_ino); ++ dentry = decode_by_ino(sb, ino, dir_ino); ++ if (IS_ERR(dentry)) ++ goto out_unlock; ++ if (dentry) ++ goto accept; ++ ++ /* is the parent dir cached? */ ++ dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); ++ if (IS_ERR(dentry)) ++ goto out_unlock; ++ if (dentry) ++ goto accept; ++ ++ /* lookup path */ ++ dentry = decode_by_path(sb, bindex, ino, fh, fh_len, &nsi_lock); ++ if (IS_ERR(dentry)) ++ goto out_unlock; ++ if (unlikely(!dentry)) ++ /* todo?: make it ESTALE */ ++ goto out_unlock; ++ ++ accept: ++ if (dentry->d_inode->i_generation == fh[Fh_igen]) ++ goto out_unlock; /* success */ ++ ++ dput(dentry); ++ dentry = ERR_PTR(-ESTALE); ++ out_unlock: ++ si_read_unlock(sb); ++ out: ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++#if 0 /* reserved for future use */ ++/* support subtreecheck option */ ++static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid, ++ int fh_len, int fh_type) ++{ ++ struct dentry *parent; ++ __u32 *fh = fid->raw; ++ ino_t dir_ino; ++ ++ dir_ino = decode_ino(fh + Fh_dir_ino); ++ parent = decode_by_ino(sb, dir_ino, 0); ++ if (IS_ERR(parent)) ++ goto out; ++ if (!parent) ++ parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]), ++ dir_ino, fh, fh_len); ++ ++ out: ++ AuTraceErrPtr(parent); ++ return parent; ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, ++ int connectable) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ struct super_block *sb, *h_sb; ++ struct inode *inode; ++ struct dentry *parent, *h_parent; ++ struct au_branch *br; ++ ++ AuDebugOn(au_test_anon(dentry)); ++ ++ parent = NULL; ++ err = -ENOSPC; ++ if (unlikely(*max_len <= Fh_tail)) { ++ AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); ++ goto out; ++ } ++ ++ err = FILEID_ROOT; ++ if (IS_ROOT(dentry)) { ++ AuDebugOn(dentry->d_inode->i_ino != AUFS_ROOT_INO); ++ goto out; ++ } ++ ++ err = -EIO; ++ h_parent = NULL; ++ sb = dentry->d_sb; ++ aufs_read_lock(dentry, AuLock_FLUSH | AuLock_IR); ++ parent = dget_parent(dentry); ++ di_read_lock_parent(parent, !AuLock_IR); ++ inode = dentry->d_inode; ++ AuDebugOn(!inode); ++#ifdef CONFIG_AUFS_DEBUG ++ if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) ++ AuWarn1("NFS-exporting requires xino\n"); ++#endif ++ ++ bend = au_dbtaildir(parent); ++ for (bindex = au_dbstart(parent); bindex <= bend; bindex++) { ++ h_parent = au_h_dptr(parent, bindex); ++ if (h_parent) { ++ dget(h_parent); ++ break; ++ } ++ } ++ if (unlikely(!h_parent)) ++ goto out_unlock; ++ ++ err = -EPERM; ++ br = au_sbr(sb, bindex); ++ h_sb = br->br_mnt->mnt_sb; ++ if (unlikely(!h_sb->s_export_op)) { ++ AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); ++ goto out_dput; ++ } ++ ++ fh[Fh_br_id] = br->br_id; ++ fh[Fh_sigen] = au_sigen(sb); ++ encode_ino(fh + Fh_ino, inode->i_ino); ++ encode_ino(fh + Fh_dir_ino, parent->d_inode->i_ino); ++ fh[Fh_igen] = inode->i_generation; ++ ++ *max_len -= Fh_tail; ++ fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail), ++ max_len, ++ /*connectable or subtreecheck*/0); ++ err = fh[Fh_h_type]; ++ *max_len += Fh_tail; ++ /* todo: macros? */ ++ if (err != 255) ++ err = 99; ++ else ++ AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); ++ ++ out_dput: ++ dput(h_parent); ++ out_unlock: ++ di_read_unlock(parent, !AuLock_IR); ++ dput(parent); ++ aufs_read_unlock(dentry, AuLock_IR); ++ out: ++ if (unlikely(err < 0)) ++ err = 255; ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct export_operations aufs_export_op = { ++ .fh_to_dentry = aufs_fh_to_dentry, ++ /* .fh_to_parent = aufs_fh_to_parent, */ ++ .encode_fh = aufs_encode_fh ++}; ++ ++void au_export_init(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ __u32 u; ++ ++ sb->s_export_op = &aufs_export_op; ++ sbinfo = au_sbi(sb); ++ sbinfo->si_xigen = NULL; ++ get_random_bytes(&u, sizeof(u)); ++ BUILD_BUG_ON(sizeof(u) != sizeof(int)); ++ atomic_set(&sbinfo->si_xigen_next, u); ++} +diff -Naur a/fs/aufs/file.c b/fs/aufs/file.c +--- a/fs/aufs/file.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/file.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,568 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * handling file/dir, and address_space operation ++ */ ++ ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++/* ++ * a dirty trick for handling deny_write_access(). ++ * because FMODE_EXEC flag is not passed to f_op->open(), ++ * set it to file->private_data temporary. ++ */ ++void au_store_oflag(struct nameidata *nd, struct inode *inode) ++{ ++ if (nd ++ /* && !(nd->flags & LOOKUP_CONTINUE) */ ++ && (nd->flags & LOOKUP_OPEN) ++ && (nd->intent.open.flags & vfsub_fmode_to_uint(FMODE_EXEC)) ++ && inode ++ && S_ISREG(inode->i_mode)) { ++ /* suppress a warning in lp64 */ ++ unsigned long flags = nd->intent.open.flags; ++ nd->intent.open.file->private_data = (void *)flags; ++ /* smp_mb(); */ ++ } ++} ++ ++/* drop flags for writing */ ++unsigned int au_file_roflags(unsigned int flags) ++{ ++ flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC); ++ flags |= O_RDONLY | O_NOATIME; ++ return flags; ++} ++ ++/* common functions to regular file and dir */ ++struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, ++ struct file *file) ++{ ++ struct file *h_file; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ struct super_block *sb; ++ struct au_branch *br; ++ int err; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ h_inode = h_dentry->d_inode; ++ /* a race condition can happen between open and unlink/rmdir */ ++ h_file = ERR_PTR(-ENOENT); ++ if (unlikely((!d_unhashed(dentry) && d_unhashed(h_dentry)) ++ || !h_inode)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ br = au_sbr(sb, bindex); ++ h_file = ERR_PTR(-EACCES); ++ if (file && (file->f_mode & FMODE_EXEC) ++ && (br->br_mnt->mnt_flags & MNT_NOEXEC)) ++ goto out; ++ ++ /* drop flags for writing */ ++ if (au_test_ro(sb, bindex, dentry->d_inode)) ++ flags = au_file_roflags(flags); ++ flags &= ~O_CREAT; ++ atomic_inc(&br->br_count); ++ h_file = dentry_open(dget(h_dentry), mntget(br->br_mnt), flags, ++ current_cred()); ++ if (IS_ERR(h_file)) ++ goto out_br; ++ ++ if (file && (file->f_mode & FMODE_EXEC)) { ++ h_file->f_mode |= FMODE_EXEC; ++ err = deny_write_access(h_file); ++ if (unlikely(err)) { ++ fput(h_file); ++ h_file = ERR_PTR(err); ++ goto out_br; ++ } ++ } ++ fsnotify_open(h_dentry); ++ goto out; /* success */ ++ ++ out_br: ++ atomic_dec(&br->br_count); ++ out: ++ return h_file; ++} ++ ++int au_do_open(struct file *file, int (*open)(struct file *file, int flags)) ++{ ++ int err; ++ struct dentry *dentry; ++ struct super_block *sb; ++ ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ err = au_finfo_init(file); ++ if (unlikely(err)) ++ goto out; ++ ++ di_read_lock_child(dentry, AuLock_IR); ++ err = open(file, file->f_flags); ++ di_read_unlock(dentry, AuLock_IR); ++ ++ fi_write_unlock(file); ++ if (unlikely(err)) ++ au_finfo_fin(file); ++ out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++int au_reopen_nondir(struct file *file) ++{ ++ int err; ++ aufs_bindex_t bstart, bindex, bend; ++ struct dentry *dentry; ++ struct file *h_file, *h_file_tmp; ++ ++ dentry = file->f_dentry; ++ bstart = au_dbstart(dentry); ++ h_file_tmp = NULL; ++ if (au_fbstart(file) == bstart) { ++ h_file = au_h_fptr(file, bstart); ++ if (file->f_mode == h_file->f_mode) ++ return 0; /* success */ ++ h_file_tmp = h_file; ++ get_file(h_file_tmp); ++ au_set_h_fptr(file, bstart, NULL); ++ } ++ AuDebugOn(au_fbstart(file) < bstart ++ || au_fi(file)->fi_hfile[0 + bstart].hf_file); ++ ++ h_file = au_h_open(dentry, bstart, file->f_flags & ~O_TRUNC, file); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; /* todo: close all? */ ++ ++ err = 0; ++ au_set_fbstart(file, bstart); ++ au_set_h_fptr(file, bstart, h_file); ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ ++ /* close lower files */ ++ bend = au_fbend(file); ++ for (bindex = bstart + 1; bindex <= bend; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ au_set_fbend(file, bstart); ++ ++ out: ++ if (h_file_tmp) ++ fput(h_file_tmp); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_reopen_wh(struct file *file, aufs_bindex_t btgt, ++ struct dentry *hi_wh) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ struct au_dinfo *dinfo; ++ struct dentry *h_dentry; ++ ++ dinfo = au_di(file->f_dentry); ++ bstart = dinfo->di_bstart; ++ dinfo->di_bstart = btgt; ++ h_dentry = dinfo->di_hdentry[0 + btgt].hd_dentry; ++ dinfo->di_hdentry[0 + btgt].hd_dentry = hi_wh; ++ err = au_reopen_nondir(file); ++ dinfo->di_hdentry[0 + btgt].hd_dentry = h_dentry; ++ dinfo->di_bstart = bstart; ++ ++ return err; ++} ++ ++static int au_ready_to_write_wh(struct file *file, loff_t len, ++ aufs_bindex_t bcpup) ++{ ++ int err; ++ struct inode *inode; ++ struct dentry *dentry, *hi_wh; ++ struct super_block *sb; ++ ++ dentry = file->f_dentry; ++ inode = dentry->d_inode; ++ hi_wh = au_hi_wh(inode, bcpup); ++ if (!hi_wh) ++ err = au_sio_cpup_wh(dentry, bcpup, len, file); ++ else ++ /* already copied-up after unlink */ ++ err = au_reopen_wh(file, bcpup, hi_wh); ++ ++ sb = dentry->d_sb; ++ if (!err && inode->i_nlink > 1 && au_opt_test(au_mntflags(sb), PLINK)) ++ au_plink_append(inode, bcpup, au_h_dptr(dentry, bcpup)); ++ ++ return err; ++} ++ ++/* ++ * prepare the @file for writing. ++ */ ++int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) ++{ ++ int err; ++ aufs_bindex_t bstart, bcpup; ++ struct dentry *dentry, *parent, *h_dentry; ++ struct inode *h_inode, *inode; ++ struct super_block *sb; ++ ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ bstart = au_fbstart(file); ++ inode = dentry->d_inode; ++ err = au_test_ro(sb, bstart, inode); ++ if (!err && (au_h_fptr(file, bstart)->f_mode & FMODE_WRITE)) { ++ err = au_pin(pin, dentry, bstart, AuOpt_UDBA_NONE, /*flags*/0); ++ goto out; ++ } ++ ++ /* need to cpup */ ++ parent = dget_parent(dentry); ++ di_write_lock_parent(parent); ++ err = AuWbrCopyup(au_sbi(sb), dentry); ++ bcpup = err; ++ if (unlikely(err < 0)) ++ goto out_dgrade; ++ err = 0; ++ ++ if (!au_h_dptr(parent, bcpup)) { ++ err = au_cpup_dirs(dentry, bcpup); ++ if (unlikely(err)) ++ goto out_dgrade; ++ } ++ ++ err = au_pin(pin, dentry, bcpup, AuOpt_UDBA_NONE, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out_dgrade; ++ ++ h_dentry = au_h_fptr(file, bstart)->f_dentry; ++ h_inode = h_dentry->d_inode; ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ if (d_unhashed(dentry) /* || d_unhashed(h_dentry) */ ++ /* || !h_inode->i_nlink */) { ++ err = au_ready_to_write_wh(file, len, bcpup); ++ di_downgrade_lock(parent, AuLock_IR); ++ } else { ++ di_downgrade_lock(parent, AuLock_IR); ++ if (!au_h_dptr(dentry, bcpup)) ++ err = au_sio_cpup_simple(dentry, bcpup, len, ++ AuCpup_DTIME); ++ if (!err) ++ err = au_reopen_nondir(file); ++ } ++ mutex_unlock(&h_inode->i_mutex); ++ ++ if (!err) { ++ au_pin_set_parent_lflag(pin, /*lflag*/0); ++ goto out_dput; /* success */ ++ } ++ au_unpin(pin); ++ goto out_unlock; ++ ++ out_dgrade: ++ di_downgrade_lock(parent, AuLock_IR); ++ out_unlock: ++ di_read_unlock(parent, AuLock_IR); ++ out_dput: ++ dput(parent); ++ out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_file_refresh_by_inode(struct file *file, int *need_reopen) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ struct au_pin pin; ++ struct au_finfo *finfo; ++ struct dentry *dentry, *parent, *hi_wh; ++ struct inode *inode; ++ struct super_block *sb; ++ ++ err = 0; ++ finfo = au_fi(file); ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ bstart = au_ibstart(inode); ++ if (bstart == finfo->fi_bstart) ++ goto out; ++ ++ parent = dget_parent(dentry); ++ if (au_test_ro(sb, bstart, inode)) { ++ di_read_lock_parent(parent, !AuLock_IR); ++ err = AuWbrCopyup(au_sbi(sb), dentry); ++ bstart = err; ++ di_read_unlock(parent, !AuLock_IR); ++ if (unlikely(err < 0)) ++ goto out_parent; ++ err = 0; ++ } ++ ++ di_read_lock_parent(parent, AuLock_IR); ++ hi_wh = au_hi_wh(inode, bstart); ++ if (au_opt_test(au_mntflags(sb), PLINK) ++ && au_plink_test(inode) ++ && !d_unhashed(dentry)) { ++ err = au_test_and_cpup_dirs(dentry, bstart); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ /* always superio. */ ++ err = au_pin(&pin, dentry, bstart, AuOpt_UDBA_NONE, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (!err) ++ err = au_sio_cpup_simple(dentry, bstart, -1, ++ AuCpup_DTIME); ++ au_unpin(&pin); ++ } else if (hi_wh) { ++ /* already copied-up after unlink */ ++ err = au_reopen_wh(file, bstart, hi_wh); ++ *need_reopen = 0; ++ } ++ ++ out_unlock: ++ di_read_unlock(parent, AuLock_IR); ++ out_parent: ++ dput(parent); ++ out: ++ return err; ++} ++ ++static void au_do_refresh_file(struct file *file) ++{ ++ aufs_bindex_t bindex, bend, new_bindex, brid; ++ struct au_hfile *p, tmp, *q; ++ struct au_finfo *finfo; ++ struct super_block *sb; ++ ++ sb = file->f_dentry->d_sb; ++ finfo = au_fi(file); ++ p = finfo->fi_hfile + finfo->fi_bstart; ++ brid = p->hf_br->br_id; ++ bend = finfo->fi_bend; ++ for (bindex = finfo->fi_bstart; bindex <= bend; bindex++, p++) { ++ if (!p->hf_file) ++ continue; ++ ++ new_bindex = au_br_index(sb, p->hf_br->br_id); ++ if (new_bindex == bindex) ++ continue; ++ if (new_bindex < 0) { ++ au_set_h_fptr(file, bindex, NULL); ++ continue; ++ } ++ ++ /* swap two lower inode, and loop again */ ++ q = finfo->fi_hfile + new_bindex; ++ tmp = *q; ++ *q = *p; ++ *p = tmp; ++ if (tmp.hf_file) { ++ bindex--; ++ p--; ++ } ++ } ++ ++ p = finfo->fi_hfile; ++ if (!au_test_mmapped(file) && !d_unhashed(file->f_dentry)) { ++ bend = au_sbend(sb); ++ for (finfo->fi_bstart = 0; finfo->fi_bstart <= bend; ++ finfo->fi_bstart++, p++) ++ if (p->hf_file) { ++ if (p->hf_file->f_dentry ++ && p->hf_file->f_dentry->d_inode) ++ break; ++ else ++ au_hfput(p, file); ++ } ++ } else { ++ bend = au_br_index(sb, brid); ++ for (finfo->fi_bstart = 0; finfo->fi_bstart < bend; ++ finfo->fi_bstart++, p++) ++ if (p->hf_file) ++ au_hfput(p, file); ++ bend = au_sbend(sb); ++ } ++ ++ p = finfo->fi_hfile + bend; ++ for (finfo->fi_bend = bend; finfo->fi_bend >= finfo->fi_bstart; ++ finfo->fi_bend--, p--) ++ if (p->hf_file) { ++ if (p->hf_file->f_dentry ++ && p->hf_file->f_dentry->d_inode) ++ break; ++ else ++ au_hfput(p, file); ++ } ++ AuDebugOn(finfo->fi_bend < finfo->fi_bstart); ++} ++ ++/* ++ * after branch manipulating, refresh the file. ++ */ ++static int refresh_file(struct file *file, int (*reopen)(struct file *file)) ++{ ++ int err, need_reopen; ++ struct dentry *dentry; ++ aufs_bindex_t bend, bindex; ++ ++ dentry = file->f_dentry; ++ err = au_fi_realloc(au_fi(file), au_sbend(dentry->d_sb) + 1); ++ if (unlikely(err)) ++ goto out; ++ au_do_refresh_file(file); ++ ++ err = 0; ++ need_reopen = 1; ++ if (!au_test_mmapped(file)) ++ err = au_file_refresh_by_inode(file, &need_reopen); ++ if (!err && need_reopen && !d_unhashed(dentry)) ++ err = reopen(file); ++ if (!err) { ++ au_update_figen(file); ++ return 0; /* success */ ++ } ++ ++ /* error, close all lower files */ ++ bend = au_fbend(file); ++ for (bindex = au_fbstart(file); bindex <= bend; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ ++ out: ++ return err; ++} ++ ++/* common function to regular file and dir */ ++int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), ++ int wlock) ++{ ++ int err; ++ unsigned int sigen, figen; ++ aufs_bindex_t bstart; ++ unsigned char pseudo_link; ++ struct dentry *dentry; ++ ++ err = 0; ++ dentry = file->f_dentry; ++ sigen = au_sigen(dentry->d_sb); ++ fi_write_lock(file); ++ figen = au_figen(file); ++ di_write_lock_child(dentry); ++ bstart = au_dbstart(dentry); ++ pseudo_link = (bstart != au_ibstart(dentry->d_inode)); ++ if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) { ++ if (!wlock) { ++ di_downgrade_lock(dentry, AuLock_IR); ++ fi_downgrade_lock(file); ++ } ++ goto out; /* success */ ++ } ++ ++ AuDbg("sigen %d, figen %d\n", sigen, figen); ++ if (sigen != au_digen(dentry) ++ || sigen != au_iigen(dentry->d_inode)) { ++ err = au_reval_dpath(dentry, sigen); ++ if (unlikely(err < 0)) ++ goto out; ++ AuDebugOn(au_digen(dentry) != sigen ++ || au_iigen(dentry->d_inode) != sigen); ++ } ++ ++ err = refresh_file(file, reopen); ++ if (!err) { ++ if (!wlock) { ++ di_downgrade_lock(dentry, AuLock_IR); ++ fi_downgrade_lock(file); ++ } ++ } else { ++ di_write_unlock(dentry); ++ fi_write_unlock(file); ++ } ++ ++ out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* cf. aufs_nopage() */ ++/* for madvise(2) */ ++static int aufs_readpage(struct file *file __maybe_unused, struct page *page) ++{ ++ unlock_page(page); ++ return 0; ++} ++ ++/* they will never be called. */ ++#ifdef CONFIG_AUFS_DEBUG ++static int aufs_write_begin(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned flags, ++ struct page **pagep, void **fsdata) ++{ AuUnsupport(); return 0; } ++static int aufs_write_end(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned copied, ++ struct page *page, void *fsdata) ++{ AuUnsupport(); return 0; } ++static int aufs_writepage(struct page *page, struct writeback_control *wbc) ++{ AuUnsupport(); return 0; } ++static void aufs_sync_page(struct page *page) ++{ AuUnsupport(); } ++ ++static int aufs_set_page_dirty(struct page *page) ++{ AuUnsupport(); return 0; } ++static void aufs_invalidatepage(struct page *page, unsigned long offset) ++{ AuUnsupport(); } ++static int aufs_releasepage(struct page *page, gfp_t gfp) ++{ AuUnsupport(); return 0; } ++static ssize_t aufs_direct_IO(int rw, struct kiocb *iocb, ++ const struct iovec *iov, loff_t offset, ++ unsigned long nr_segs) ++{ AuUnsupport(); return 0; } ++#endif /* CONFIG_AUFS_DEBUG */ ++ ++struct address_space_operations aufs_aop = { ++ .readpage = aufs_readpage, ++#ifdef CONFIG_AUFS_DEBUG ++ .writepage = aufs_writepage, ++ .sync_page = aufs_sync_page, ++ .set_page_dirty = aufs_set_page_dirty, ++ .write_begin = aufs_write_begin, ++ .write_end = aufs_write_end, ++ .invalidatepage = aufs_invalidatepage, ++ .releasepage = aufs_releasepage, ++ .direct_IO = aufs_direct_IO, ++#endif /* CONFIG_AUFS_DEBUG */ ++}; +diff -Naur a/fs/aufs/file.h b/fs/aufs/file.h +--- a/fs/aufs/file.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/file.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,159 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * file operations ++ */ ++ ++#ifndef __AUFS_FILE_H__ ++#define __AUFS_FILE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include "rwsem.h" ++ ++struct au_branch; ++struct au_hfile { ++ struct file *hf_file; ++ struct au_branch *hf_br; ++}; ++ ++struct au_vdir; ++struct au_finfo { ++ atomic_t fi_generation; ++ ++ struct rw_semaphore fi_rwsem; ++ struct au_hfile *fi_hfile; ++ aufs_bindex_t fi_bstart, fi_bend; ++ ++ union { ++ /* non-dir only */ ++ struct { ++ struct vm_operations_struct *fi_h_vm_ops; ++ struct vm_operations_struct *fi_vm_ops; ++ }; ++ ++ /* dir only */ ++ struct { ++ struct au_vdir *fi_vdir_cache; ++ int fi_maintain_plink; ++ }; ++ }; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* file.c */ ++extern struct address_space_operations aufs_aop; ++void au_store_oflag(struct nameidata *nd, struct inode *inode); ++unsigned int au_file_roflags(unsigned int flags); ++struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, ++ struct file *file); ++int au_do_open(struct file *file, int (*open)(struct file *file, int flags)); ++int au_reopen_nondir(struct file *file); ++struct au_pin; ++int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin); ++int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), ++ int wlock); ++ ++/* f_op.c */ ++extern const struct file_operations aufs_file_fop; ++int aufs_flush(struct file *file, fl_owner_t id); ++ ++/* finfo.c */ ++void au_hfput(struct au_hfile *hf, struct file *file); ++void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, ++ struct file *h_file); ++ ++void au_update_figen(struct file *file); ++ ++void au_finfo_fin(struct file *file); ++int au_finfo_init(struct file *file); ++int au_fi_realloc(struct au_finfo *finfo, int nbr); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct au_finfo *au_fi(struct file *file) ++{ ++ return file->private_data; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * fi_read_lock, fi_write_lock, ++ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock ++ */ ++AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem); ++ ++#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* todo: hard/soft set? */ ++static inline aufs_bindex_t au_fbstart(struct file *file) ++{ ++ return au_fi(file)->fi_bstart; ++} ++ ++static inline aufs_bindex_t au_fbend(struct file *file) ++{ ++ return au_fi(file)->fi_bend; ++} ++ ++static inline struct au_vdir *au_fvdir_cache(struct file *file) ++{ ++ return au_fi(file)->fi_vdir_cache; ++} ++ ++static inline void au_set_fbstart(struct file *file, aufs_bindex_t bindex) ++{ ++ au_fi(file)->fi_bstart = bindex; ++} ++ ++static inline void au_set_fbend(struct file *file, aufs_bindex_t bindex) ++{ ++ au_fi(file)->fi_bend = bindex; ++} ++ ++static inline void au_set_fvdir_cache(struct file *file, ++ struct au_vdir *vdir_cache) ++{ ++ au_fi(file)->fi_vdir_cache = vdir_cache; ++} ++ ++static inline struct file *au_h_fptr(struct file *file, aufs_bindex_t bindex) ++{ ++ return au_fi(file)->fi_hfile[0 + bindex].hf_file; ++} ++ ++/* todo: memory barrier? */ ++static inline unsigned int au_figen(struct file *f) ++{ ++ return atomic_read(&au_fi(f)->fi_generation); ++} ++ ++static inline int au_test_mmapped(struct file *f) ++{ ++ return !!(au_fi(f)->fi_h_vm_ops); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_FILE_H__ */ +diff -Naur a/fs/aufs/finfo.c b/fs/aufs/finfo.c +--- a/fs/aufs/finfo.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/finfo.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,134 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * file private data ++ */ ++ ++#include ++#include "aufs.h" ++ ++void au_hfput(struct au_hfile *hf, struct file *file) ++{ ++ if (file->f_mode & FMODE_EXEC) ++ allow_write_access(hf->hf_file); ++ fput(hf->hf_file); ++ hf->hf_file = NULL; ++ atomic_dec(&hf->hf_br->br_count); ++ hf->hf_br = NULL; ++} ++ ++void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val) ++{ ++ struct au_finfo *finfo = au_fi(file); ++ struct au_hfile *hf; ++ ++ hf = finfo->fi_hfile + bindex; ++ if (hf->hf_file) ++ au_hfput(hf, file); ++ if (val) { ++ hf->hf_file = val; ++ hf->hf_br = au_sbr(file->f_dentry->d_sb, bindex); ++ } ++} ++ ++void au_update_figen(struct file *file) ++{ ++ atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_dentry)); ++ /* smp_mb(); */ /* atomic_set */ ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_finfo_fin(struct file *file) ++{ ++ struct au_finfo *finfo; ++ aufs_bindex_t bindex, bend; ++ ++ fi_write_lock(file); ++ bend = au_fbend(file); ++ bindex = au_fbstart(file); ++ if (bindex >= 0) ++ /* ++ * calls fput() instead of filp_close(), ++ * since no dnotify or lock for the lower file. ++ */ ++ for (; bindex <= bend; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ ++ finfo = au_fi(file); ++ au_dbg_verify_hf(finfo); ++ kfree(finfo->fi_hfile); ++ fi_write_unlock(file); ++ au_rwsem_destroy(&finfo->fi_rwsem); ++ au_cache_free_finfo(finfo); ++} ++ ++int au_finfo_init(struct file *file) ++{ ++ struct au_finfo *finfo; ++ struct dentry *dentry; ++ unsigned long ul; ++ ++ dentry = file->f_dentry; ++ finfo = au_cache_alloc_finfo(); ++ if (unlikely(!finfo)) ++ goto out; ++ ++ finfo->fi_hfile = kcalloc(au_sbend(dentry->d_sb) + 1, ++ sizeof(*finfo->fi_hfile), GFP_NOFS); ++ if (unlikely(!finfo->fi_hfile)) ++ goto out_finfo; ++ ++ init_rwsem(&finfo->fi_rwsem); ++ down_write(&finfo->fi_rwsem); ++ finfo->fi_bstart = -1; ++ finfo->fi_bend = -1; ++ atomic_set(&finfo->fi_generation, au_digen(dentry)); ++ /* smp_mb(); */ /* atomic_set */ ++ ++ /* cf. au_store_oflag() */ ++ /* suppress a warning in lp64 */ ++ ul = (unsigned long)file->private_data; ++ file->f_mode |= (vfsub_uint_to_fmode(ul) & FMODE_EXEC); ++ file->private_data = finfo; ++ return 0; /* success */ ++ ++ out_finfo: ++ au_cache_free_finfo(finfo); ++ out: ++ return -ENOMEM; ++} ++ ++int au_fi_realloc(struct au_finfo *finfo, int nbr) ++{ ++ int err, sz; ++ struct au_hfile *hfp; ++ ++ err = -ENOMEM; ++ sz = sizeof(*hfp) * (finfo->fi_bend + 1); ++ if (!sz) ++ sz = sizeof(*hfp); ++ hfp = au_kzrealloc(finfo->fi_hfile, sz, sizeof(*hfp) * nbr, GFP_NOFS); ++ if (hfp) { ++ finfo->fi_hfile = hfp; ++ err = 0; ++ } ++ ++ return err; ++} +diff -Naur a/fs/aufs/f_op.c b/fs/aufs/f_op.c +--- a/fs/aufs/f_op.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/f_op.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,648 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * file and vm operations ++ */ ++ ++#include ++#include ++#include ++#include "aufs.h" ++ ++/* common function to regular file and dir */ ++int aufs_flush(struct file *file, fl_owner_t id) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ struct dentry *dentry; ++ struct file *h_file; ++ ++ dentry = file->f_dentry; ++ si_noflush_read_lock(dentry->d_sb); ++ fi_read_lock(file); ++ di_read_lock_child(dentry, AuLock_IW); ++ ++ err = 0; ++ bend = au_fbend(file); ++ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { ++ h_file = au_h_fptr(file, bindex); ++ if (!h_file || !h_file->f_op || !h_file->f_op->flush) ++ continue; ++ ++ err = h_file->f_op->flush(h_file, id); ++ if (!err) ++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); ++ /*ignore*/ ++ } ++ au_cpup_attr_timesizes(dentry->d_inode); ++ ++ di_read_unlock(dentry, AuLock_IW); ++ fi_read_unlock(file); ++ si_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int do_open_nondir(struct file *file, int flags) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct file *h_file; ++ struct dentry *dentry; ++ struct au_finfo *finfo; ++ ++ err = 0; ++ dentry = file->f_dentry; ++ finfo = au_fi(file); ++ finfo->fi_h_vm_ops = NULL; ++ finfo->fi_vm_ops = NULL; ++ bindex = au_dbstart(dentry); ++ /* O_TRUNC is processed already */ ++ BUG_ON(au_test_ro(dentry->d_sb, bindex, dentry->d_inode) ++ && (flags & O_TRUNC)); ++ ++ h_file = au_h_open(dentry, bindex, flags, file); ++ if (IS_ERR(h_file)) ++ err = PTR_ERR(h_file); ++ else { ++ au_set_fbstart(file, bindex); ++ au_set_fbend(file, bindex); ++ au_set_h_fptr(file, bindex, h_file); ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ } ++ return err; ++} ++ ++static int aufs_open_nondir(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ return au_do_open(file, do_open_nondir); ++} ++ ++static int aufs_release_nondir(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ struct super_block *sb = file->f_dentry->d_sb; ++ ++ si_noflush_read_lock(sb); ++ kfree(au_fi(file)->fi_vm_ops); ++ au_finfo_fin(file); ++ si_read_unlock(sb); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static ssize_t aufs_read(struct file *file, char __user *buf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ struct dentry *dentry; ++ struct file *h_file; ++ struct super_block *sb; ++ ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ if (unlikely(err)) ++ goto out; ++ ++ h_file = au_h_fptr(file, au_fbstart(file)); ++ err = vfsub_read_u(h_file, buf, count, ppos); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode); ++ ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++ out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++static ssize_t aufs_write(struct file *file, const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ ssize_t err; ++ aufs_bindex_t bstart; ++ struct au_pin pin; ++ struct dentry *dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ struct file *h_file; ++ char __user *buf = (char __user *)ubuf; ++ ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ mutex_lock(&inode->i_mutex); ++ si_read_lock(sb, AuLock_FLUSH); ++ ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_ready_to_write(file, -1, &pin); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ bstart = au_fbstart(file); ++ h_file = au_h_fptr(file, bstart); ++ au_unpin(&pin); ++ err = vfsub_write_u(h_file, buf, count, ppos); ++ au_cpup_attr_timesizes(inode); ++ inode->i_mode = h_file->f_dentry->d_inode->i_mode; ++ ++ out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ out: ++ si_read_unlock(sb); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++ ++static ssize_t aufs_splice_read(struct file *file, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) ++{ ++ ssize_t err; ++ struct file *h_file; ++ struct dentry *dentry; ++ struct super_block *sb; ++ ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ if (unlikely(err)) ++ goto out; ++ ++ err = -EINVAL; ++ h_file = au_h_fptr(file, au_fbstart(file)); ++ if (au_test_loopback_kthread()) { ++ file->f_mapping = h_file->f_mapping; ++ smp_mb(); /* unnecessary? */ ++ } ++ err = vfsub_splice_to(h_file, ppos, pipe, len, flags); ++ /* todo: necessasry? */ ++ /* file->f_ra = h_file->f_ra; */ ++ fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode); ++ ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++ ++ out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++static ssize_t ++aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos, ++ size_t len, unsigned int flags) ++{ ++ ssize_t err; ++ struct au_pin pin; ++ struct dentry *dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ struct file *h_file; ++ ++ dentry = file->f_dentry; ++ inode = dentry->d_inode; ++ mutex_lock(&inode->i_mutex); ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_ready_to_write(file, -1, &pin); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ h_file = au_h_fptr(file, au_fbstart(file)); ++ au_unpin(&pin); ++ err = vfsub_splice_from(pipe, h_file, ppos, len, flags); ++ au_cpup_attr_timesizes(inode); ++ inode->i_mode = h_file->f_dentry->d_inode->i_mode; ++ ++ out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ out: ++ si_read_unlock(sb); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct file *au_safe_file(struct vm_area_struct *vma) ++{ ++ struct file *file; ++ ++ file = vma->vm_file; ++ if (file->private_data && au_test_aufs(file->f_dentry->d_sb)) ++ return file; ++ return NULL; ++} ++ ++static void au_reset_file(struct vm_area_struct *vma, struct file *file) ++{ ++ vma->vm_file = file; ++ /* smp_mb(); */ /* flush vm_file */ ++} ++ ++static int aufs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) ++{ ++ int err; ++ static DECLARE_WAIT_QUEUE_HEAD(wq); ++ struct file *file, *h_file; ++ struct au_finfo *finfo; ++ ++ /* todo: non-robr mode, user vm_file as it is? */ ++ wait_event(wq, (file = au_safe_file(vma))); ++ ++ /* do not revalidate, no si lock */ ++ finfo = au_fi(file); ++ h_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file; ++ AuDebugOn(!h_file || !au_test_mmapped(file)); ++ ++ fi_write_lock(file); ++ vma->vm_file = h_file; ++ err = finfo->fi_h_vm_ops->fault(vma, vmf); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ au_reset_file(vma, file); ++ fi_write_unlock(file); ++#if 0 /* def CONFIG_SMP */ ++ /* wake_up_nr(&wq, online_cpu - 1); */ ++ wake_up_all(&wq); ++#else ++ wake_up(&wq); ++#endif ++ ++ return err; ++} ++ ++static int aufs_page_mkwrite(struct vm_area_struct *vma, struct page *page) ++{ ++ int err; ++ static DECLARE_WAIT_QUEUE_HEAD(wq); ++ struct file *file, *h_file; ++ struct au_finfo *finfo; ++ ++ wait_event(wq, (file = au_safe_file(vma))); ++ ++ finfo = au_fi(file); ++ h_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file; ++ AuDebugOn(!h_file || !au_test_mmapped(file)); ++ ++ fi_write_lock(file); ++ vma->vm_file = h_file; ++ err = finfo->fi_h_vm_ops->page_mkwrite(vma, page); ++ au_reset_file(vma, file); ++ fi_write_unlock(file); ++ wake_up(&wq); ++ ++ return err; ++} ++ ++static void aufs_vm_close(struct vm_area_struct *vma) ++{ ++ static DECLARE_WAIT_QUEUE_HEAD(wq); ++ struct file *file, *h_file; ++ struct au_finfo *finfo; ++ ++ wait_event(wq, (file = au_safe_file(vma))); ++ ++ finfo = au_fi(file); ++ h_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file; ++ AuDebugOn(!h_file || !au_test_mmapped(file)); ++ ++ fi_write_lock(file); ++ vma->vm_file = h_file; ++ finfo->fi_h_vm_ops->close(vma); ++ au_reset_file(vma, file); ++ fi_write_unlock(file); ++ wake_up(&wq); ++} ++ ++static struct vm_operations_struct aufs_vm_ops = { ++ /* .close and .page_mkwrite are not set by default */ ++ .fault = aufs_fault, ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct vm_operations_struct *au_vm_ops(struct file *h_file, ++ struct vm_area_struct *vma) ++{ ++ struct vm_operations_struct *vm_ops; ++ int err; ++ ++ vm_ops = ERR_PTR(-ENODEV); ++ if (!h_file->f_op || !h_file->f_op->mmap) ++ goto out; ++ ++ err = h_file->f_op->mmap(h_file, vma); ++ vm_ops = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ vm_ops = vma->vm_ops; ++ err = do_munmap(current->mm, vma->vm_start, ++ vma->vm_end - vma->vm_start); ++ if (unlikely(err)) { ++ AuIOErr("failed internal unmapping %.*s, %d\n", ++ AuDLNPair(h_file->f_dentry), err); ++ vm_ops = ERR_PTR(-EIO); ++ } ++ ++ out: ++ return vm_ops; ++} ++ ++static int au_custom_vm_ops(struct au_finfo *finfo, struct vm_area_struct *vma) ++{ ++ int err; ++ struct vm_operations_struct *h_ops; ++ ++ err = 0; ++ h_ops = finfo->fi_h_vm_ops; ++ AuDebugOn(!h_ops); ++ if ((!h_ops->page_mkwrite && !h_ops->close) ++ || finfo->fi_vm_ops) ++ goto out; ++ ++ err = -ENOMEM; ++ finfo->fi_vm_ops = kmemdup(&aufs_vm_ops, sizeof(aufs_vm_ops), GFP_NOFS); ++ if (unlikely(!finfo->fi_vm_ops)) ++ goto out; ++ ++ err = 0; ++ if (h_ops->page_mkwrite) ++ finfo->fi_vm_ops->page_mkwrite = aufs_page_mkwrite; ++ if (h_ops->close) ++ finfo->fi_vm_ops->close = aufs_vm_close; ++ ++ vma->vm_ops = finfo->fi_vm_ops; ++ ++ out: ++ return err; ++} ++ ++static int aufs_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int err; ++ unsigned char wlock, mmapped; ++ struct dentry *dentry; ++ struct super_block *sb; ++ struct file *h_file; ++ struct vm_operations_struct *vm_ops; ++ ++ dentry = file->f_dentry; ++ mmapped = !!au_test_mmapped(file); /* can be harmless race condition */ ++ wlock = !!(file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED); ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, wlock | !mmapped); ++ if (unlikely(err)) ++ goto out; ++ ++ if (wlock) { ++ struct au_pin pin; ++ ++ err = au_ready_to_write(file, -1, &pin); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) ++ goto out_unlock; ++ au_unpin(&pin); ++ } else if (!mmapped) ++ di_downgrade_lock(dentry, AuLock_IR); ++ ++ h_file = au_h_fptr(file, au_fbstart(file)); ++ if (au_test_fs_bad_mapping(h_file->f_dentry->d_sb)) { ++ /* ++ * by this assignment, f_mapping will differs from aufs inode ++ * i_mapping. ++ * if someone else mixes the use of f_dentry->d_inode and ++ * f_mapping->host, then a problem may arise. ++ */ ++ file->f_mapping = h_file->f_mapping; ++ } ++ ++ vm_ops = NULL; ++ if (!mmapped) { ++ vm_ops = au_vm_ops(h_file, vma); ++ err = PTR_ERR(vm_ops); ++ if (IS_ERR(vm_ops)) ++ goto out_unlock; ++ } ++ ++ /* ++ * unnecessary to handle MAP_DENYWRITE and deny_write_access()? ++ * currently MAP_DENYWRITE from userspace is ignored, but elf loader ++ * sets it. when FMODE_EXEC is set (by open_exec() or sys_uselib()), ++ * both of the aufs file and the lower file is deny_write_access()-ed. ++ * finally I hope we can skip handlling MAP_DENYWRITE here. ++ */ ++ err = generic_file_mmap(file, vma); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ vma->vm_ops = &aufs_vm_ops; ++ /* test again */ ++ if (!au_test_mmapped(file)) ++ au_fi(file)->fi_h_vm_ops = vm_ops; ++ ++ err = au_custom_vm_ops(au_fi(file), vma); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ vfsub_file_accessed(h_file); ++ fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode); ++ ++ out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ if (!wlock && mmapped) ++ fi_read_unlock(file); ++ else ++ fi_write_unlock(file); ++ out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static unsigned int aufs_poll(struct file *file, poll_table *wait) ++{ ++ unsigned int mask; ++ int err; ++ struct file *h_file; ++ struct dentry *dentry; ++ struct super_block *sb; ++ ++ /* We should pretend an error happened. */ ++ mask = POLLERR /* | POLLIN | POLLOUT */; ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ if (unlikely(err)) ++ goto out; ++ ++ /* it is not an error if h_file has no operation */ ++ mask = DEFAULT_POLLMASK; ++ h_file = au_h_fptr(file, au_fbstart(file)); ++ if (h_file->f_op && h_file->f_op->poll) ++ mask = h_file->f_op->poll(h_file, wait); ++ ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++ ++ out: ++ si_read_unlock(sb); ++ AuTraceErr((int)mask); ++ return mask; ++} ++ ++static int aufs_fsync_nondir(struct file *file, struct dentry *dentry, ++ int datasync) ++{ ++ int err; ++ struct au_pin pin; ++ struct inode *inode; ++ struct file *h_file; ++ struct super_block *sb; ++ ++ inode = dentry->d_inode; ++ IMustLock(file->f_mapping->host); ++ if (inode != file->f_mapping->host) { ++ mutex_unlock(&file->f_mapping->host->i_mutex); ++ mutex_lock(&inode->i_mutex); ++ } ++ IMustLock(inode); ++ ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ ++ err = 0; /* -EBADF; */ /* posix? */ ++ if (unlikely(!(file->f_mode & FMODE_WRITE))) ++ goto out; ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_ready_to_write(file, -1, &pin); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) ++ goto out_unlock; ++ au_unpin(&pin); ++ ++ err = -EINVAL; ++ h_file = au_h_fptr(file, au_fbstart(file)); ++ if (h_file->f_op && h_file->f_op->fsync) { ++ struct dentry *h_d; ++ struct mutex *h_mtx; ++ ++ /* ++ * no filemap_fdatawrite() since aufs file has no its own ++ * mapping, but dir. ++ */ ++ h_d = h_file->f_dentry; ++ h_mtx = &h_d->d_inode->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ err = h_file->f_op->fsync(h_file, h_d, datasync); ++ if (!err) ++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); ++ /*ignore*/ ++ au_cpup_attr_timesizes(inode); ++ mutex_unlock(h_mtx); ++ } ++ ++ out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ out: ++ si_read_unlock(sb); ++ if (inode != file->f_mapping->host) { ++ mutex_unlock(&inode->i_mutex); ++ mutex_lock(&file->f_mapping->host->i_mutex); ++ } ++ return err; ++} ++ ++static int aufs_fasync(int fd, struct file *file, int flag) ++{ ++ int err; ++ struct file *h_file; ++ struct dentry *dentry; ++ struct super_block *sb; ++ ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ if (unlikely(err)) ++ goto out; ++ ++ h_file = au_h_fptr(file, au_fbstart(file)); ++ if (h_file->f_op && h_file->f_op->fasync) ++ err = h_file->f_op->fasync(fd, h_file, flag); ++ ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++ ++ out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++const struct file_operations aufs_file_fop = { ++ /* ++ * while generic_file_llseek/_unlocked() don't use BKL, ++ * don't use it since it operates file->f_mapping->host. ++ * in aufs, it may be a real file and may confuse users by UDBA. ++ */ ++ /* .llseek = generic_file_llseek, */ ++ ++ .read = aufs_read, ++ .write = aufs_write, ++ .poll = aufs_poll, ++ .mmap = aufs_mmap, ++ .open = aufs_open_nondir, ++ .flush = aufs_flush, ++ .release = aufs_release_nondir, ++ .fsync = aufs_fsync_nondir, ++ .fasync = aufs_fasync, ++ .splice_write = aufs_splice_write, ++ .splice_read = aufs_splice_read ++}; +diff -Naur a/fs/aufs/fstype.h b/fs/aufs/fstype.h +--- a/fs/aufs/fstype.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/fstype.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,474 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * judging filesystem type ++ */ ++ ++#ifndef __AUFS_FSTYPE_H__ ++#define __AUFS_FSTYPE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static inline int au_test_aufs(struct super_block *sb) ++{ ++ return sb->s_magic == AUFS_SUPER_MAGIC; ++} ++ ++static inline const char *au_sbtype(struct super_block *sb) ++{ ++ return sb->s_type->name; ++} ++ ++static inline int au_test_iso9660(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_ROMFS_FS) || defined(CONFIG_ROMFS_FS_MODULE) ++ return sb->s_magic == ROMFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_romfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_ISO9660_FS) || defined(CONFIG_ISO9660_FS_MODULE) ++ return sb->s_magic == ISOFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_cramfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_CRAMFS) || defined(CONFIG_CRAMFS_MODULE) ++ return sb->s_magic == CRAMFS_MAGIC; ++#endif ++ return 0; ++} ++ ++static inline int au_test_nfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE) ++ return sb->s_magic == NFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_fuse(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_FUSE_FS) || defined(CONFIG_FUSE_FS_MODULE) ++ return sb->s_magic == FUSE_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_xfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_XFS_FS) || defined(CONFIG_XFS_FS_MODULE) ++ return sb->s_magic == XFS_SB_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_tmpfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_TMPFS ++ return sb->s_magic == TMPFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_ECRYPT_FS) || defined(CONFIG_ECRYPT_FS_MODULE) ++ return !strcmp(au_sbtype(sb), "ecryptfs"); ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_smbfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_SMB_FS) || defined(CONFIG_SMB_FS_MODULE) ++ return sb->s_magic == SMB_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_ocfs2(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_OCFS2_FS) || defined(CONFIG_OCFS2_FS_MODULE) ++ return sb->s_magic == OCFS2_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_ocfs2_dlmfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_OCFS2_FS_O2CB) || defined(CONFIG_OCFS2_FS_O2CB_MODULE) ++ return sb->s_magic == DLMFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_coda(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_CODA_FS) || defined(CONFIG_CODA_FS_MODULE) ++ return sb->s_magic == CODA_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_v9fs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_9P_FS) || defined(CONFIG_9P_FS_MODULE) ++ return sb->s_magic == V9FS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_ext4(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_EXT4DEV_FS) || defined(CONFIG_EXT4DEV_FS_MODULE) ++ return sb->s_magic == EXT4_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_sysv(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_SYSV_FS) || defined(CONFIG_SYSV_FS_MODULE) ++ return !strcmp(au_sbtype(sb), "sysv"); ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_ramfs(struct super_block *sb) ++{ ++ return sb->s_magic == RAMFS_MAGIC; ++} ++ ++static inline int au_test_ubifs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_UBIFS_FS) || defined(CONFIG_UBIFS_FS_MODULE) ++ return sb->s_magic == UBIFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_procfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_PROC_FS ++ return sb->s_magic == PROC_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_sysfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_SYSFS ++ return sb->s_magic == SYSFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_configfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_CONFIGFS_FS) || defined(CONFIG_CONFIGFS_FS_MODULE) ++ return sb->s_magic == CONFIGFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_minix(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_MINIX_FS) || defined(CONFIG_MINIX_FS_MODULE) ++ return sb->s_magic == MINIX3_SUPER_MAGIC ++ || sb->s_magic == MINIX2_SUPER_MAGIC ++ || sb->s_magic == MINIX2_SUPER_MAGIC2 ++ || sb->s_magic == MINIX_SUPER_MAGIC ++ || sb->s_magic == MINIX_SUPER_MAGIC2; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_cifs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_CIFS_FS) || defined(CONFIGCIFS_FS_MODULE) ++ return sb->s_magic == CIFS_MAGIC_NUMBER; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_fat(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_FAT_FS) || defined(CONFIG_FAT_FS_MODULE) ++ return sb->s_magic == MSDOS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_msdos(struct super_block *sb) ++{ ++ return au_test_fat(sb); ++} ++ ++static inline int au_test_vfat(struct super_block *sb) ++{ ++ return au_test_fat(sb); ++} ++ ++static inline int au_test_securityfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_SECURITYFS ++ return sb->s_magic == SECURITYFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_squashfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_SQUASHFS) || defined(CONFIG_SQUASHFS_MODULE) ++ return sb->s_magic == SQUASHFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_btrfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_BTRFS_FS) || defined(CONFIG_BTRFS_FS_MODULE) ++ return sb->s_magic == BTRFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_xenfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_XENFS) || defined(CONFIG_XENFS_MODULE) ++ return sb->s_magic == XENFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_debugfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_DEBUG_FS ++ return sb->s_magic == DEBUGFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * they can't be an aufs branch. ++ */ ++static inline int au_test_fs_unsuppoted(struct super_block *sb) ++{ ++ return ++#ifndef CONFIG_AUFS_BR_RAMFS ++ au_test_ramfs(sb) || ++#endif ++ au_test_procfs(sb) ++ || au_test_sysfs(sb) ++ || au_test_configfs(sb) ++ || au_test_debugfs(sb) ++ || au_test_securityfs(sb) ++ || au_test_xenfs(sb) ++ /* || !strcmp(au_sbtype(sb), "unionfs") */ ++ || au_test_aufs(sb); /* will be supported in next version */ ++} ++ ++/* ++ * If the filesystem supports NFS-export, then it has to support NULL as ++ * a nameidata parameter for ->create(), ->lookup() and ->d_revalidate(). ++ * We can apply this principle when we handle a lower filesystem. ++ */ ++static inline int au_test_fs_null_nd(struct super_block *sb) ++{ ++ return !!sb->s_export_op; ++} ++ ++static inline int au_test_fs_remote(struct super_block *sb) ++{ ++ return !au_test_tmpfs(sb) ++#ifdef CONFIG_AUFS_BR_RAMFS ++ && !au_test_ramfs(sb) ++#endif ++ && !(sb->s_type->fs_flags & FS_REQUIRES_DEV); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * Note: these functions (below) are created after reading ->getattr() in all ++ * filesystems under linux/fs. it means we have to do so in every update... ++ */ ++ ++/* ++ * some filesystems require getattr to refresh the inode attributes before ++ * referencing. ++ * in most cases, we can rely on the inode attribute in NFS (or every remote fs) ++ * and leave the work for d_revalidate() ++ */ ++static inline int au_test_fs_refresh_iattr(struct super_block *sb) ++{ ++ return au_test_nfs(sb) ++ || au_test_fuse(sb) ++ /* || au_test_smbfs(sb) */ /* untested */ ++ /* || au_test_ocfs2(sb) */ /* untested */ ++ /* || au_test_btrfs(sb) */ /* untested */ ++ /* || au_test_coda(sb) */ /* untested */ ++ /* || au_test_v9fs(sb) */ /* untested */ ++ ; ++} ++ ++/* ++ * filesystems which don't maintain i_size or i_blocks. ++ */ ++static inline int au_test_fs_bad_iattr_size(struct super_block *sb) ++{ ++ return au_test_xfs(sb) ++ /* || au_test_ext4(sb) */ /* untested */ ++ /* || au_test_ocfs2(sb) */ /* untested */ ++ /* || au_test_ocfs2_dlmfs(sb) */ /* untested */ ++ /* || au_test_sysv(sb) */ /* untested */ ++ /* || au_test_ubifs(sb) */ /* untested */ ++ /* || au_test_minix(sb) */ /* untested */ ++ ; ++} ++ ++/* ++ * filesystems which don't store the correct value in some of their inode ++ * attributes. ++ */ ++static inline int au_test_fs_bad_iattr(struct super_block *sb) ++{ ++ return au_test_fs_bad_iattr_size(sb) ++ /* || au_test_cifs(sb) */ /* untested */ ++ || au_test_fat(sb) ++ || au_test_msdos(sb) ++ || au_test_vfat(sb); ++} ++ ++/* they don't check i_nlink in link(2) */ ++static inline int au_test_fs_no_limit_nlink(struct super_block *sb) ++{ ++ return au_test_tmpfs(sb) ++#ifdef CONFIG_AUFS_BR_RAMFS ++ || au_test_ramfs(sb) ++#endif ++ || au_test_ubifs(sb); ++} ++ ++/* ++ * filesystems which sets S_NOATIME and S_NOCMTIME. ++ */ ++static inline int au_test_fs_notime(struct super_block *sb) ++{ ++ return au_test_nfs(sb) ++ || au_test_fuse(sb) ++ || au_test_ubifs(sb) ++ /* || au_test_cifs(sb) */ /* untested */ ++ ; ++} ++ ++/* ++ * filesystems which requires replacing i_mapping. ++ */ ++static inline int au_test_fs_bad_mapping(struct super_block *sb) ++{ ++ return au_test_fuse(sb) ++ || au_test_ubifs(sb); ++} ++ ++/* temporary support for i#1 in cramfs */ ++static inline int au_test_fs_unique_ino(struct inode *inode) ++{ ++ if (au_test_cramfs(inode->i_sb)) ++ return inode->i_ino != 1; ++ return 1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * the filesystem where the xino files placed must support i/o after unlink and ++ * maintain i_size and i_blocks. ++ */ ++static inline int au_test_fs_bad_xino(struct super_block *sb) ++{ ++ return au_test_fs_remote(sb) ++ || au_test_fs_bad_iattr_size(sb) ++#ifdef CONFIG_AUFS_BR_RAMFS ++ || !(au_test_ramfs(sb) || au_test_fs_null_nd(sb)) ++#else ++ || !au_test_fs_null_nd(sb) /* to keep xino code simple */ ++#endif ++ /* don't want unnecessary work for xino */ ++ || au_test_aufs(sb) ++ || au_test_ecryptfs(sb); ++} ++ ++static inline int au_test_fs_trunc_xino(struct super_block *sb) ++{ ++ return au_test_tmpfs(sb) ++ || au_test_ramfs(sb); ++} ++ ++/* ++ * test if the @sb is real-readonly. ++ */ ++static inline int au_test_fs_rr(struct super_block *sb) ++{ ++ return au_test_squashfs(sb) ++ || au_test_iso9660(sb) ++ || au_test_cramfs(sb) ++ || au_test_romfs(sb); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_FSTYPE_H__ */ +diff -Naur a/fs/aufs/hinotify.c b/fs/aufs/hinotify.c +--- a/fs/aufs/hinotify.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/hinotify.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,755 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * inotify for the lower directories ++ */ ++ ++#include "aufs.h" ++ ++static const __u32 AuHinMask = (IN_MOVE | IN_DELETE | IN_CREATE); ++static struct inotify_handle *au_hin_handle; ++ ++AuCacheFuncs(hinotify, HINOTIFY); ++ ++int au_hin_alloc(struct au_hinode *hinode, struct inode *inode, ++ struct inode *h_inode) ++{ ++ int err; ++ struct au_hinotify *hin; ++ s32 wd; ++ ++ err = -ENOMEM; ++ hin = au_cache_alloc_hinotify(); ++ if (hin) { ++ AuDebugOn(hinode->hi_notify); ++ hinode->hi_notify = hin; ++ hin->hin_aufs_inode = inode; ++ ++ inotify_init_watch(&hin->hin_watch); ++ wd = inotify_add_watch(au_hin_handle, &hin->hin_watch, h_inode, ++ AuHinMask); ++ if (wd >= 0) ++ return 0; /* success */ ++ ++ err = wd; ++ put_inotify_watch(&hin->hin_watch); ++ au_cache_free_hinotify(hin); ++ hinode->hi_notify = NULL; ++ } ++ ++ return err; ++} ++ ++void au_hin_free(struct au_hinode *hinode) ++{ ++ int err; ++ struct au_hinotify *hin; ++ ++ hin = hinode->hi_notify; ++ if (hin) { ++ err = 0; ++ if (atomic_read(&hin->hin_watch.count)) ++ err = inotify_rm_watch(au_hin_handle, &hin->hin_watch); ++ if (unlikely(err)) ++ /* it means the watch is already removed */ ++ AuWarn("failed inotify_rm_watch() %d\n", err); ++ au_cache_free_hinotify(hin); ++ hinode->hi_notify = NULL; ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_hin_ctl(struct au_hinode *hinode, int do_set) ++{ ++ struct inode *h_inode; ++ struct inotify_watch *watch; ++ ++ if (!hinode->hi_notify) ++ return; ++ ++ h_inode = hinode->hi_inode; ++ IMustLock(h_inode); ++ ++ /* todo: try inotify_find_update_watch()? */ ++ watch = &hinode->hi_notify->hin_watch; ++ mutex_lock(&h_inode->inotify_mutex); ++ /* mutex_lock(&watch->ih->mutex); */ ++ if (do_set) { ++ AuDebugOn(watch->mask & AuHinMask); ++ watch->mask |= AuHinMask; ++ } else { ++ AuDebugOn(!(watch->mask & AuHinMask)); ++ watch->mask &= ~AuHinMask; ++ } ++ /* mutex_unlock(&watch->ih->mutex); */ ++ mutex_unlock(&h_inode->inotify_mutex); ++} ++ ++void au_reset_hinotify(struct inode *inode, unsigned int flags) ++{ ++ aufs_bindex_t bindex, bend; ++ struct inode *hi; ++ struct dentry *iwhdentry; ++ ++ bend = au_ibend(inode); ++ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { ++ hi = au_h_iptr(inode, bindex); ++ if (!hi) ++ continue; ++ ++ /* mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD); */ ++ iwhdentry = au_hi_wh(inode, bindex); ++ if (iwhdentry) ++ dget(iwhdentry); ++ au_igrab(hi); ++ au_set_h_iptr(inode, bindex, NULL, 0); ++ au_set_h_iptr(inode, bindex, au_igrab(hi), ++ flags & ~AuHi_XINO); ++ iput(hi); ++ dput(iwhdentry); ++ /* mutex_unlock(&hi->i_mutex); */ ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int hin_xino(struct inode *inode, struct inode *h_inode) ++{ ++ int err; ++ aufs_bindex_t bindex, bend, bfound, bstart; ++ struct inode *h_i; ++ ++ err = 0; ++ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { ++ AuWarn("branch root dir was changed\n"); ++ goto out; ++ } ++ ++ bfound = -1; ++ bend = au_ibend(inode); ++ bstart = au_ibstart(inode); ++#if 0 /* reserved for future use */ ++ if (bindex == bend) { ++ /* keep this ino in rename case */ ++ goto out; ++ } ++#endif ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ if (au_h_iptr(inode, bindex) == h_inode) { ++ bfound = bindex; ++ break; ++ } ++ } ++ if (bfound < 0) ++ goto out; ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ h_i = au_h_iptr(inode, bindex); ++ if (!h_i) ++ continue; ++ ++ err = au_xino_write0(inode->i_sb, bindex, h_i->i_ino, 0); ++ /* ignore this error */ ++ /* bad action? */ ++ } ++ ++ /* children inode number will be broken */ ++ ++ out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int hin_gen_tree(struct dentry *dentry) ++{ ++ int err, i, j, ndentry; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry **dentries; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_dcsub_pages(&dpages, dentry, NULL, NULL); ++ if (unlikely(err)) ++ goto out_dpages; ++ ++ for (i = 0; i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ dentries = dpage->dentries; ++ ndentry = dpage->ndentry; ++ for (j = 0; j < ndentry; j++) { ++ struct dentry *d; ++ ++ d = dentries[j]; ++ if (IS_ROOT(d)) ++ continue; ++ ++ d_drop(d); ++ au_digen_dec(d); ++ if (d->d_inode) ++ /* todo: reset children xino? ++ cached children only? */ ++ au_iigen_dec(d->d_inode); ++ } ++ } ++ ++ out_dpages: ++ au_dpages_free(&dpages); ++ ++ /* discard children */ ++ dentry_unhash(dentry); ++ dput(dentry); ++ out: ++ return err; ++} ++ ++/* ++ * return 0 if processed. ++ */ ++static int hin_gen_by_inode(char *name, unsigned int nlen, struct inode *inode, ++ const unsigned int isdir) ++{ ++ int err; ++ struct dentry *d; ++ struct qstr *dname; ++ ++ err = 1; ++ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { ++ AuWarn("branch root dir was changed\n"); ++ err = 0; ++ goto out; ++ } ++ ++ if (!isdir) { ++ AuDebugOn(!name); ++ au_iigen_dec(inode); ++ spin_lock(&dcache_lock); ++ list_for_each_entry(d, &inode->i_dentry, d_alias) { ++ dname = &d->d_name; ++ if (dname->len != nlen ++ && memcmp(dname->name, name, nlen)) ++ continue; ++ err = 0; ++ spin_lock(&d->d_lock); ++ __d_drop(d); ++ au_digen_dec(d); ++ spin_unlock(&d->d_lock); ++ break; ++ } ++ spin_unlock(&dcache_lock); ++ } else { ++ au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIRS); ++ d = d_find_alias(inode); ++ if (!d) { ++ au_iigen_dec(inode); ++ goto out; ++ } ++ ++ dname = &d->d_name; ++ if (dname->len == nlen && !memcmp(dname->name, name, nlen)) ++ err = hin_gen_tree(d); ++ dput(d); ++ } ++ ++ out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int hin_gen_by_name(struct dentry *dentry, const unsigned int isdir) ++{ ++ int err; ++ struct inode *inode; ++ ++ inode = dentry->d_inode; ++ if (IS_ROOT(dentry) ++ /* || (inode && inode->i_ino == AUFS_ROOT_INO) */ ++ ) { ++ AuWarn("branch root dir was changed\n"); ++ return 0; ++ } ++ ++ err = 0; ++ if (!isdir) { ++ d_drop(dentry); ++ au_digen_dec(dentry); ++ if (inode) ++ au_iigen_dec(inode); ++ } else { ++ au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIRS); ++ if (inode) ++ err = hin_gen_tree(dentry); ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* hinotify job flags */ ++#define AuHinJob_XINO0 1 ++#define AuHinJob_GEN (1 << 1) ++#define AuHinJob_DIRENT (1 << 2) ++#define AuHinJob_ISDIR (1 << 3) ++#define AuHinJob_TRYXINO0 (1 << 4) ++#define AuHinJob_MNTPNT (1 << 5) ++#define au_ftest_hinjob(flags, name) ((flags) & AuHinJob_##name) ++#define au_fset_hinjob(flags, name) { (flags) |= AuHinJob_##name; } ++#define au_fclr_hinjob(flags, name) { (flags) &= ~AuHinJob_##name; } ++ ++struct hin_job_args { ++ unsigned int flags; ++ struct inode *inode, *h_inode, *dir, *h_dir; ++ struct dentry *dentry; ++ char *h_name; ++ int h_nlen; ++}; ++ ++static int hin_job(struct hin_job_args *a) ++{ ++ const unsigned int isdir = au_ftest_hinjob(a->flags, ISDIR); ++ ++ /* reset xino */ ++ if (au_ftest_hinjob(a->flags, XINO0) && a->inode) ++ hin_xino(a->inode, a->h_inode); /* ignore this error */ ++ ++ if (au_ftest_hinjob(a->flags, TRYXINO0) ++ && a->inode ++ && a->h_inode) { ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ if (!a->h_inode->i_nlink) ++ hin_xino(a->inode, a->h_inode); /* ignore this error */ ++ mutex_unlock(&a->h_inode->i_mutex); ++ } ++ ++ /* make the generation obsolete */ ++ if (au_ftest_hinjob(a->flags, GEN)) { ++ int err = -1; ++ if (a->inode) ++ err = hin_gen_by_inode(a->h_name, a->h_nlen, a->inode, ++ isdir); ++ if (err && a->dentry) ++ hin_gen_by_name(a->dentry, isdir); ++ /* ignore this error */ ++ } ++ ++ /* make dir entries obsolete */ ++ if (au_ftest_hinjob(a->flags, DIRENT) && a->inode) { ++ struct au_vdir *vdir; ++ ++ vdir = au_ivdir(a->inode); ++ if (vdir) ++ vdir->vd_jiffy = 0; ++ /* IMustLock(a->inode); */ ++ /* a->inode->i_version++; */ ++ } ++ ++ /* can do nothing but warn */ ++ if (au_ftest_hinjob(a->flags, MNTPNT) ++ && a->dentry ++ && d_mountpoint(a->dentry)) ++ AuWarn("mount-point %.*s is removed or renamed\n", ++ AuDLNPair(a->dentry)); ++ ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static char *in_name(u32 mask) ++{ ++#ifdef CONFIG_AUFS_DEBUG ++#define test_ret(flag) if (mask & flag) \ ++ return #flag; ++ test_ret(IN_ACCESS); ++ test_ret(IN_MODIFY); ++ test_ret(IN_ATTRIB); ++ test_ret(IN_CLOSE_WRITE); ++ test_ret(IN_CLOSE_NOWRITE); ++ test_ret(IN_OPEN); ++ test_ret(IN_MOVED_FROM); ++ test_ret(IN_MOVED_TO); ++ test_ret(IN_CREATE); ++ test_ret(IN_DELETE); ++ test_ret(IN_DELETE_SELF); ++ test_ret(IN_MOVE_SELF); ++ test_ret(IN_UNMOUNT); ++ test_ret(IN_Q_OVERFLOW); ++ test_ret(IN_IGNORED); ++ return ""; ++#undef test_ret ++#else ++ return "??"; ++#endif ++} ++ ++static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen, ++ struct inode *dir) ++{ ++ struct dentry *dentry, *d, *parent; ++ struct qstr *dname; ++ ++ parent = d_find_alias(dir); ++ if (!parent) ++ return NULL; ++ ++ dentry = NULL; ++ spin_lock(&dcache_lock); ++ list_for_each_entry(d, &parent->d_subdirs, d_u.d_child) { ++ /* AuDbg("%.*s\n", AuDLNPair(d)); */ ++ dname = &d->d_name; ++ if (dname->len != nlen || memcmp(dname->name, name, nlen)) ++ continue; ++ if (!atomic_read(&d->d_count) || !d->d_fsdata) { ++ spin_lock(&d->d_lock); ++ __d_drop(d); ++ spin_unlock(&d->d_lock); ++ continue; ++ } ++ ++ dentry = dget(d); ++ break; ++ } ++ spin_unlock(&dcache_lock); ++ dput(parent); ++ ++ if (dentry) ++ di_write_lock_child(dentry); ++ ++ return dentry; ++} ++ ++static struct inode *lookup_wlock_by_ino(struct super_block *sb, ++ aufs_bindex_t bindex, ino_t h_ino) ++{ ++ struct inode *inode; ++ ino_t ino; ++ int err; ++ ++ inode = NULL; ++ err = au_xino_read(sb, bindex, h_ino, &ino); ++ if (!err && ino) ++ inode = ilookup(sb, ino); ++ if (!inode) ++ goto out; ++ ++ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { ++ AuWarn("wrong root branch\n"); ++ iput(inode); ++ inode = NULL; ++ goto out; ++ } ++ ++ ii_write_lock_child(inode); ++ ++ out: ++ return inode; ++} ++ ++enum { CHILD, PARENT }; ++struct postproc_args { ++ struct inode *h_dir, *dir, *h_child_inode; ++ u32 mask; ++ unsigned int flags[2]; ++ unsigned int h_child_nlen; ++ char h_child_name[]; ++}; ++ ++static void postproc(void *_args) ++{ ++ struct postproc_args *a = _args; ++ struct super_block *sb; ++ aufs_bindex_t bindex, bend, bfound; ++ unsigned char xino, try_iput; ++ int err; ++ struct inode *inode; ++ ino_t h_ino; ++ struct hin_job_args args; ++ struct dentry *dentry; ++ struct au_sbinfo *sbinfo; ++ ++ AuDebugOn(!_args); ++ AuDebugOn(!a->h_dir); ++ AuDebugOn(!a->dir); ++ AuDebugOn(!a->mask); ++ AuDbg("mask 0x%x %s, i%lu, hi%lu, hci%lu\n", ++ a->mask, in_name(a->mask), a->dir->i_ino, a->h_dir->i_ino, ++ a->h_child_inode ? a->h_child_inode->i_ino : 0); ++ ++ inode = NULL; ++ dentry = NULL; ++ /* ++ * do not lock a->dir->i_mutex here ++ * because of d_revalidate() may cause a deadlock. ++ */ ++ sb = a->dir->i_sb; ++ AuDebugOn(!sb); ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!sbinfo); ++ /* big aufs lock */ ++ si_noflush_write_lock(sb); ++ ++ ii_read_lock_parent(a->dir); ++ bfound = -1; ++ bend = au_ibend(a->dir); ++ for (bindex = au_ibstart(a->dir); bindex <= bend; bindex++) ++ if (au_h_iptr(a->dir, bindex) == a->h_dir) { ++ bfound = bindex; ++ break; ++ } ++ ii_read_unlock(a->dir); ++ if (unlikely(bfound < 0)) ++ goto out; ++ ++ xino = !!au_opt_test(au_mntflags(sb), XINO); ++ h_ino = 0; ++ if (a->h_child_inode) ++ h_ino = a->h_child_inode->i_ino; ++ ++ if (a->h_child_nlen ++ && (au_ftest_hinjob(a->flags[CHILD], GEN) ++ || au_ftest_hinjob(a->flags[CHILD], MNTPNT))) ++ dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen, ++ a->dir); ++ try_iput = 0; ++ if (dentry) ++ inode = dentry->d_inode; ++ if (xino && !inode && h_ino ++ && (au_ftest_hinjob(a->flags[CHILD], XINO0) ++ || au_ftest_hinjob(a->flags[CHILD], TRYXINO0) ++ || au_ftest_hinjob(a->flags[CHILD], GEN))) { ++ inode = lookup_wlock_by_ino(sb, bfound, h_ino); ++ try_iput = 1; ++ } ++ ++ args.flags = a->flags[CHILD]; ++ args.dentry = dentry; ++ args.inode = inode; ++ args.h_inode = a->h_child_inode; ++ args.dir = a->dir; ++ args.h_dir = a->h_dir; ++ args.h_name = a->h_child_name; ++ args.h_nlen = a->h_child_nlen; ++ err = hin_job(&args); ++ if (dentry) { ++ if (dentry->d_fsdata) ++ di_write_unlock(dentry); ++ dput(dentry); ++ } ++ if (inode && try_iput) { ++ ii_write_unlock(inode); ++ iput(inode); ++ } ++ ++ ii_write_lock_parent(a->dir); ++ args.flags = a->flags[PARENT]; ++ args.dentry = NULL; ++ args.inode = a->dir; ++ args.h_inode = a->h_dir; ++ args.dir = NULL; ++ args.h_dir = NULL; ++ args.h_name = NULL; ++ args.h_nlen = 0; ++ err = hin_job(&args); ++ ii_write_unlock(a->dir); ++ ++ out: ++ au_nwt_done(&sbinfo->si_nowait); ++ si_write_unlock(sb); ++ ++ iput(a->h_child_inode); ++ iput(a->h_dir); ++ iput(a->dir); ++ kfree(a); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void aufs_inotify(struct inotify_watch *watch, u32 wd __maybe_unused, ++ u32 mask, u32 cookie __maybe_unused, ++ const char *h_child_name, struct inode *h_child_inode) ++{ ++ struct au_hinotify *hinotify; ++ struct postproc_args *args; ++ int len, wkq_err; ++ unsigned char isdir, isroot, wh; ++ char *p; ++ struct inode *dir; ++ unsigned int flags[2]; ++ ++ /* if IN_UNMOUNT happens, there must be another bug */ ++ AuDebugOn(mask & IN_UNMOUNT); ++ if (mask & (IN_IGNORED | IN_UNMOUNT)) { ++ put_inotify_watch(watch); ++ return; ++ } ++#ifdef AuDbgHinotify ++ au_debug(1); ++ if (1 || !h_child_name || strcmp(h_child_name, AUFS_XINO_FNAME)) { ++ AuDbg("i%lu, wd %d, mask 0x%x %s, cookie 0x%x, hcname %s," ++ " hi%lu\n", ++ watch->inode->i_ino, wd, mask, in_name(mask), cookie, ++ h_child_name ? h_child_name : "", ++ h_child_inode ? h_child_inode->i_ino : 0); ++ WARN_ON(1); ++ } ++ au_debug(0); ++#endif ++ ++ hinotify = container_of(watch, struct au_hinotify, hin_watch); ++ AuDebugOn(!hinotify || !hinotify->hin_aufs_inode); ++ dir = igrab(hinotify->hin_aufs_inode); ++ if (!dir) ++ return; ++ ++ isroot = (dir->i_ino == AUFS_ROOT_INO); ++ len = 0; ++ wh = 0; ++ if (h_child_name) { ++ len = strlen(h_child_name); ++ if (!memcmp(h_child_name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { ++ h_child_name += AUFS_WH_PFX_LEN; ++ len -= AUFS_WH_PFX_LEN; ++ wh = 1; ++ } ++ } ++ ++ isdir = 0; ++ if (h_child_inode) ++ isdir = !!S_ISDIR(h_child_inode->i_mode); ++ flags[PARENT] = AuHinJob_ISDIR; ++ flags[CHILD] = 0; ++ if (isdir) ++ flags[CHILD] = AuHinJob_ISDIR; ++ switch (mask & IN_ALL_EVENTS) { ++ case IN_MOVED_FROM: ++ case IN_MOVED_TO: ++ AuDebugOn(!h_child_name || !h_child_inode); ++ au_fset_hinjob(flags[CHILD], GEN); ++ au_fset_hinjob(flags[CHILD], XINO0); ++ au_fset_hinjob(flags[CHILD], MNTPNT); ++ au_fset_hinjob(flags[PARENT], DIRENT); ++ break; ++ ++ case IN_CREATE: ++ AuDebugOn(!h_child_name || !h_child_inode); ++ au_fset_hinjob(flags[PARENT], DIRENT); ++ au_fset_hinjob(flags[CHILD], GEN); ++ break; ++ ++ case IN_DELETE: ++ /* ++ * aufs never be able to get this child inode. ++ * revalidation should be in d_revalidate() ++ * by checking i_nlink, i_generation or d_unhashed(). ++ */ ++ AuDebugOn(!h_child_name); ++ au_fset_hinjob(flags[PARENT], DIRENT); ++ au_fset_hinjob(flags[CHILD], GEN); ++ au_fset_hinjob(flags[CHILD], TRYXINO0); ++ au_fset_hinjob(flags[CHILD], MNTPNT); ++ break; ++ ++ default: ++ AuDebugOn(1); ++ } ++ ++ if (wh) ++ h_child_inode = NULL; ++ ++ /* iput() and kfree() will be called in postproc() */ ++ /* ++ * inotify_mutex is already acquired and kmalloc/prune_icache may lock ++ * iprune_mutex. strange. ++ */ ++ lockdep_off(); ++ args = kmalloc(sizeof(*args) + len + 1, GFP_NOFS); ++ lockdep_on(); ++ if (unlikely(!args)) { ++ AuErr1("no memory\n"); ++ iput(dir); ++ return; ++ } ++ args->flags[PARENT] = flags[PARENT]; ++ args->flags[CHILD] = flags[CHILD]; ++ args->mask = mask; ++ args->dir = dir; ++ args->h_dir = igrab(watch->inode); ++ if (h_child_inode) ++ h_child_inode = igrab(h_child_inode); /* can be NULL */ ++ args->h_child_inode = h_child_inode; ++ args->h_child_nlen = len; ++ if (len) { ++ p = (void *)args; ++ p += sizeof(*args); ++ memcpy(p, h_child_name, len + 1); ++ } ++ ++ lockdep_off(); ++ wkq_err = au_wkq_nowait(postproc, args, dir->i_sb); ++ lockdep_on(); ++ if (unlikely(wkq_err)) ++ AuErr("wkq %d\n", wkq_err); ++} ++ ++static void aufs_inotify_destroy(struct inotify_watch *watch __maybe_unused) ++{ ++ return; ++} ++ ++static struct inotify_operations aufs_inotify_ops = { ++ .handle_event = aufs_inotify, ++ .destroy_watch = aufs_inotify_destroy ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_hin_destroy_cache(void) ++{ ++ kmem_cache_destroy(au_cachep[AuCache_HINOTIFY]); ++ au_cachep[AuCache_HINOTIFY] = NULL; ++} ++ ++int __init au_hinotify_init(void) ++{ ++ int err; ++ ++ err = -ENOMEM; ++ au_cachep[AuCache_HINOTIFY] = AuCache(au_hinotify); ++ if (au_cachep[AuCache_HINOTIFY]) { ++ err = 0; ++ au_hin_handle = inotify_init(&aufs_inotify_ops); ++ if (IS_ERR(au_hin_handle)) { ++ err = PTR_ERR(au_hin_handle); ++ au_hin_destroy_cache(); ++ } ++ } ++ AuTraceErr(err); ++ return err; ++} ++ ++void au_hinotify_fin(void) ++{ ++ inotify_destroy(au_hin_handle); ++ if (au_cachep[AuCache_HINOTIFY]) ++ au_hin_destroy_cache(); ++} +diff -Naur a/fs/aufs/iinfo.c b/fs/aufs/iinfo.c +--- a/fs/aufs/iinfo.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/iinfo.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,271 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * inode private data ++ */ ++ ++#include "aufs.h" ++ ++struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex) ++{ ++ struct inode *h_inode; ++ ++ h_inode = au_ii(inode)->ii_hinode[0 + bindex].hi_inode; ++ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); ++ return h_inode; ++} ++ ++/* todo: hard/soft set? */ ++void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex) ++{ ++ struct au_iinfo *iinfo = au_ii(inode); ++ struct inode *h_inode; ++ ++ iinfo->ii_bstart = bindex; ++ h_inode = iinfo->ii_hinode[bindex + 0].hi_inode; ++ if (h_inode) ++ au_cpup_igen(inode, h_inode); ++} ++ ++void au_hiput(struct au_hinode *hinode) ++{ ++ au_hin_free(hinode); ++ dput(hinode->hi_whdentry); ++ iput(hinode->hi_inode); ++} ++ ++unsigned int au_hi_flags(struct inode *inode, int isdir) ++{ ++ unsigned int flags; ++ const unsigned int mnt_flags = au_mntflags(inode->i_sb); ++ ++ flags = 0; ++ if (au_opt_test(mnt_flags, XINO)) ++ au_fset_hi(flags, XINO); ++ if (isdir && au_opt_test(mnt_flags, UDBA_HINOTIFY)) ++ au_fset_hi(flags, HINOTIFY); ++ return flags; ++} ++ ++void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, ++ struct inode *h_inode, unsigned int flags) ++{ ++ struct au_hinode *hinode; ++ struct inode *hi; ++ struct au_iinfo *iinfo = au_ii(inode); ++ ++ hinode = iinfo->ii_hinode + bindex; ++ hi = hinode->hi_inode; ++ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); ++ AuDebugOn(h_inode && hi); ++ ++ if (hi) ++ au_hiput(hinode); ++ hinode->hi_inode = h_inode; ++ if (h_inode) { ++ int err; ++ struct super_block *sb = inode->i_sb; ++ struct au_branch *br; ++ ++ if (bindex == iinfo->ii_bstart) ++ au_cpup_igen(inode, h_inode); ++ br = au_sbr(sb, bindex); ++ hinode->hi_id = br->br_id; ++ if (au_ftest_hi(flags, XINO)) { ++ err = au_xino_write(sb, bindex, h_inode->i_ino, ++ inode->i_ino); ++ if (unlikely(err)) ++ AuIOErr1("failed au_xino_write() %d\n", err); ++ } ++ ++ if (au_ftest_hi(flags, HINOTIFY) ++ && au_br_hinotifyable(br->br_perm)) { ++ err = au_hin_alloc(hinode, inode, h_inode); ++ if (unlikely(err)) ++ AuIOErr1("au_hin_alloc() %d\n", err); ++ } ++ } ++} ++ ++void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_wh) ++{ ++ struct au_hinode *hinode; ++ ++ hinode = au_ii(inode)->ii_hinode + bindex; ++ AuDebugOn(hinode->hi_whdentry); ++ hinode->hi_whdentry = h_wh; ++} ++ ++void au_update_iigen(struct inode *inode) ++{ ++ atomic_set(&au_ii(inode)->ii_generation, au_sigen(inode->i_sb)); ++ /* smp_mb(); */ /* atomic_set */ ++} ++ ++/* it may be called at remount time, too */ ++void au_update_brange(struct inode *inode, int do_put_zero) ++{ ++ struct au_iinfo *iinfo; ++ ++ iinfo = au_ii(inode); ++ if (!iinfo || iinfo->ii_bstart < 0) ++ return; ++ ++ if (do_put_zero) { ++ aufs_bindex_t bindex; ++ ++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; ++ bindex++) { ++ struct inode *h_i; ++ ++ h_i = iinfo->ii_hinode[0 + bindex].hi_inode; ++ if (h_i && !h_i->i_nlink) ++ au_set_h_iptr(inode, bindex, NULL, 0); ++ } ++ } ++ ++ iinfo->ii_bstart = -1; ++ while (++iinfo->ii_bstart <= iinfo->ii_bend) ++ if (iinfo->ii_hinode[0 + iinfo->ii_bstart].hi_inode) ++ break; ++ if (iinfo->ii_bstart > iinfo->ii_bend) { ++ iinfo->ii_bstart = -1; ++ iinfo->ii_bend = -1; ++ return; ++ } ++ ++ iinfo->ii_bend++; ++ while (0 <= --iinfo->ii_bend) ++ if (iinfo->ii_hinode[0 + iinfo->ii_bend].hi_inode) ++ break; ++ AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend || iinfo->ii_bend < 0); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_iinfo_init(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ struct super_block *sb; ++ int nbr, i; ++ ++ sb = inode->i_sb; ++ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); ++ nbr = au_sbend(sb) + 1; ++ if (unlikely(nbr <= 0)) ++ nbr = 1; ++ iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS); ++ if (iinfo->ii_hinode) { ++ for (i = 0; i < nbr; i++) ++ iinfo->ii_hinode[i].hi_id = -1; ++ ++ atomic_set(&iinfo->ii_generation, au_sigen(sb)); ++ /* smp_mb(); */ /* atomic_set */ ++ init_rwsem(&iinfo->ii_rwsem); ++ iinfo->ii_bstart = -1; ++ iinfo->ii_bend = -1; ++ iinfo->ii_vdir = NULL; ++ return 0; ++ } ++ return -ENOMEM; ++} ++ ++int au_ii_realloc(struct au_iinfo *iinfo, int nbr) ++{ ++ int err, sz; ++ struct au_hinode *hip; ++ ++ err = -ENOMEM; ++ sz = sizeof(*hip) * (iinfo->ii_bend + 1); ++ if (!sz) ++ sz = sizeof(*hip); ++ hip = au_kzrealloc(iinfo->ii_hinode, sz, sizeof(*hip) * nbr, GFP_NOFS); ++ if (hip) { ++ iinfo->ii_hinode = hip; ++ err = 0; ++ } ++ ++ return err; ++} ++ ++static int au_iinfo_write0(struct super_block *sb, struct au_hinode *hinode, ++ ino_t ino) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ unsigned char locked; ++ ++ err = 0; ++ locked = !!si_noflush_read_trylock(sb); ++ bindex = au_br_index(sb, hinode->hi_id); ++ if (bindex >= 0) ++ err = au_xino_write0(sb, bindex, hinode->hi_inode->i_ino, ino); ++ /* error action? */ ++ if (locked) ++ si_read_unlock(sb); ++ return err; ++} ++ ++void au_iinfo_fin(struct inode *inode) ++{ ++ ino_t ino; ++ aufs_bindex_t bend; ++ unsigned char unlinked = !inode->i_nlink; ++ struct au_iinfo *iinfo; ++ struct au_hinode *hi; ++ struct super_block *sb; ++ ++ if (unlinked) { ++ int err = au_xigen_inc(inode); ++ if (unlikely(err)) ++ AuWarn1("failed resetting i_generation, %d\n", err); ++ } ++ ++ iinfo = au_ii(inode); ++ /* bad_inode case */ ++ if (!iinfo) ++ return; ++ ++ if (iinfo->ii_vdir) ++ au_vdir_free(iinfo->ii_vdir); ++ ++ if (iinfo->ii_bstart >= 0) { ++ sb = inode->i_sb; ++ ino = 0; ++ if (unlinked) ++ ino = inode->i_ino; ++ hi = iinfo->ii_hinode + iinfo->ii_bstart; ++ bend = iinfo->ii_bend; ++ while (iinfo->ii_bstart++ <= bend) { ++ if (hi->hi_inode) { ++ if (unlinked || !hi->hi_inode->i_nlink) { ++ au_iinfo_write0(sb, hi, ino); ++ /* ignore this error */ ++ ino = 0; ++ } ++ au_hiput(hi); ++ } ++ hi++; ++ } ++ } ++ ++ kfree(iinfo->ii_hinode); ++ au_rwsem_destroy(&iinfo->ii_rwsem); ++} +diff -Naur a/fs/aufs/inode.c b/fs/aufs/inode.c +--- a/fs/aufs/inode.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/inode.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,376 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * inode functions ++ */ ++ ++#include "aufs.h" ++ ++struct inode *au_igrab(struct inode *inode) ++{ ++ if (inode) { ++ AuDebugOn(!atomic_read(&inode->i_count)); ++ atomic_inc(&inode->i_count); ++ } ++ return inode; ++} ++ ++static void au_refresh_hinode_attr(struct inode *inode, int do_version) ++{ ++ au_cpup_attr_all(inode, /*force*/0); ++ au_update_iigen(inode); ++ if (do_version) ++ inode->i_version++; ++} ++ ++int au_refresh_hinode_self(struct inode *inode, int do_attr) ++{ ++ int err; ++ aufs_bindex_t bindex, new_bindex; ++ unsigned char update; ++ struct inode *first; ++ struct au_hinode *p, *q, tmp; ++ struct super_block *sb; ++ struct au_iinfo *iinfo; ++ ++ update = 0; ++ sb = inode->i_sb; ++ iinfo = au_ii(inode); ++ err = au_ii_realloc(iinfo, au_sbend(sb) + 1); ++ if (unlikely(err)) ++ goto out; ++ ++ p = iinfo->ii_hinode + iinfo->ii_bstart; ++ first = p->hi_inode; ++ err = 0; ++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; ++ bindex++, p++) { ++ if (!p->hi_inode) ++ continue; ++ ++ new_bindex = au_br_index(sb, p->hi_id); ++ if (new_bindex == bindex) ++ continue; ++ ++ if (new_bindex < 0) { ++ update++; ++ au_hiput(p); ++ p->hi_inode = NULL; ++ continue; ++ } ++ ++ if (new_bindex < iinfo->ii_bstart) ++ iinfo->ii_bstart = new_bindex; ++ if (iinfo->ii_bend < new_bindex) ++ iinfo->ii_bend = new_bindex; ++ /* swap two lower inode, and loop again */ ++ q = iinfo->ii_hinode + new_bindex; ++ tmp = *q; ++ *q = *p; ++ *p = tmp; ++ if (tmp.hi_inode) { ++ bindex--; ++ p--; ++ } ++ } ++ au_update_brange(inode, /*do_put_zero*/0); ++ if (do_attr) ++ au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode)); ++ ++ out: ++ return err; ++} ++ ++int au_refresh_hinode(struct inode *inode, struct dentry *dentry) ++{ ++ int err, update; ++ unsigned int flags; ++ aufs_bindex_t bindex, bend; ++ unsigned char isdir; ++ struct inode *first; ++ struct au_hinode *p; ++ struct au_iinfo *iinfo; ++ ++ err = au_refresh_hinode_self(inode, /*do_attr*/0); ++ if (unlikely(err)) ++ goto out; ++ ++ update = 0; ++ iinfo = au_ii(inode); ++ p = iinfo->ii_hinode + iinfo->ii_bstart; ++ first = p->hi_inode; ++ isdir = S_ISDIR(inode->i_mode); ++ flags = au_hi_flags(inode, isdir); ++ bend = au_dbend(dentry); ++ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { ++ struct inode *h_i; ++ struct dentry *h_d; ++ ++ h_d = au_h_dptr(dentry, bindex); ++ if (!h_d || !h_d->d_inode) ++ continue; ++ ++ if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) { ++ h_i = au_h_iptr(inode, bindex); ++ if (h_i) { ++ if (h_i == h_d->d_inode) ++ continue; ++ err = -EIO; ++ break; ++ } ++ } ++ if (bindex < iinfo->ii_bstart) ++ iinfo->ii_bstart = bindex; ++ if (iinfo->ii_bend < bindex) ++ iinfo->ii_bend = bindex; ++ au_set_h_iptr(inode, bindex, au_igrab(h_d->d_inode), flags); ++ update = 1; ++ } ++ au_update_brange(inode, /*do_put_zero*/0); ++ ++ if (unlikely(err)) ++ goto out; ++ ++ au_refresh_hinode_attr(inode, update && isdir); ++ ++ out: ++ return err; ++} ++ ++static int set_inode(struct inode *inode, struct dentry *dentry) ++{ ++ int err; ++ unsigned int flags; ++ umode_t mode; ++ aufs_bindex_t bindex, bstart, btail; ++ unsigned char isdir; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ struct au_iinfo *iinfo; ++ ++ err = 0; ++ isdir = 0; ++ bstart = au_dbstart(dentry); ++ h_inode = au_h_dptr(dentry, bstart)->d_inode; ++ mode = h_inode->i_mode; ++ switch (mode & S_IFMT) { ++ case S_IFREG: ++ btail = au_dbtail(dentry); ++ inode->i_op = &aufs_iop; ++ inode->i_fop = &aufs_file_fop; ++ inode->i_mapping->a_ops = &aufs_aop; ++ break; ++ case S_IFDIR: ++ isdir = 1; ++ btail = au_dbtaildir(dentry); ++ inode->i_op = &aufs_dir_iop; ++ inode->i_fop = &aufs_dir_fop; ++ break; ++ case S_IFLNK: ++ btail = au_dbtail(dentry); ++ inode->i_op = &aufs_symlink_iop; ++ break; ++ case S_IFBLK: ++ case S_IFCHR: ++ case S_IFIFO: ++ case S_IFSOCK: ++ btail = au_dbtail(dentry); ++ inode->i_op = &aufs_iop; ++ init_special_inode(inode, mode, h_inode->i_rdev); ++ break; ++ default: ++ AuIOErr("Unknown file type 0%o\n", mode); ++ err = -EIO; ++ goto out; ++ } ++ ++ /* do not set inotify for whiteouted dirs (SHWH mode) */ ++ flags = au_hi_flags(inode, isdir); ++ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH) ++ && au_ftest_hi(flags, HINOTIFY) ++ && dentry->d_name.len > AUFS_WH_PFX_LEN ++ && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) ++ au_fclr_hi(flags, HINOTIFY); ++ iinfo = au_ii(inode); ++ iinfo->ii_bstart = bstart; ++ iinfo->ii_bend = btail; ++ for (bindex = bstart; bindex <= btail; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry) ++ au_set_h_iptr(inode, bindex, ++ au_igrab(h_dentry->d_inode), flags); ++ } ++ au_cpup_attr_all(inode, /*force*/1); ++ ++ out: ++ return err; ++} ++ ++/* successful returns with iinfo write_locked */ ++static int reval_inode(struct inode *inode, struct dentry *dentry, int *matched) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ struct inode *h_inode, *h_dinode; ++ ++ *matched = 0; ++ ++ /* ++ * before this function, if aufs got any iinfo lock, it must be only ++ * one, the parent dir. ++ * it can happen by UDBA and the obsoleted inode number. ++ */ ++ err = -EIO; ++ if (unlikely(inode->i_ino == parent_ino(dentry))) ++ goto out; ++ ++ err = 0; ++ ii_write_lock_new_child(inode); ++ h_dinode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode; ++ bend = au_ibend(inode); ++ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { ++ h_inode = au_h_iptr(inode, bindex); ++ if (h_inode && h_inode == h_dinode) { ++ *matched = 1; ++ err = 0; ++ if (au_iigen(inode) != au_digen(dentry)) ++ err = au_refresh_hinode(inode, dentry); ++ break; ++ } ++ } ++ ++ if (unlikely(err)) ++ ii_write_unlock(inode); ++ out: ++ return err; ++} ++ ++/* successful returns with iinfo write_locked */ ++/* todo: return with unlocked? */ ++struct inode *au_new_inode(struct dentry *dentry, int must_new) ++{ ++ struct inode *inode; ++ struct dentry *h_dentry; ++ struct super_block *sb; ++ ino_t h_ino, ino; ++ int err, match; ++ aufs_bindex_t bstart; ++ ++ sb = dentry->d_sb; ++ bstart = au_dbstart(dentry); ++ h_dentry = au_h_dptr(dentry, bstart); ++ h_ino = h_dentry->d_inode->i_ino; ++ err = au_xino_read(sb, bstart, h_ino, &ino); ++ inode = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ new_ino: ++ if (!ino) { ++ ino = au_xino_new_ino(sb); ++ if (unlikely(!ino)) { ++ inode = ERR_PTR(-EIO); ++ goto out; ++ } ++ } ++ ++ AuDbg("i%lu\n", (unsigned long)ino); ++ inode = au_iget_locked(sb, ino); ++ err = PTR_ERR(inode); ++ if (IS_ERR(inode)) ++ goto out; ++ ++ AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); ++ if (inode->i_state & I_NEW) { ++ ii_write_lock_new_child(inode); ++ err = set_inode(inode, dentry); ++ unlock_new_inode(inode); ++ if (!err) ++ goto out; /* success */ ++ ++ iget_failed(inode); ++ ii_write_unlock(inode); ++ goto out_iput; ++ } else if (!must_new) { ++ err = reval_inode(inode, dentry, &match); ++ if (!err) ++ goto out; /* success */ ++ else if (match) ++ goto out_iput; ++ } ++ ++ if (unlikely(au_test_fs_unique_ino(h_dentry->d_inode))) ++ AuWarn1("Un-notified UDBA or repeatedly renamed dir," ++ " b%d, %s, %.*s, hi%lu, i%lu.\n", ++ bstart, au_sbtype(h_dentry->d_sb), AuDLNPair(dentry), ++ (unsigned long)h_ino, (unsigned long)ino); ++ ino = 0; ++ err = au_xino_write0(sb, bstart, h_ino, 0); ++ if (!err) { ++ iput(inode); ++ goto new_ino; ++ } ++ ++ out_iput: ++ iput(inode); ++ inode = ERR_PTR(err); ++ out: ++ return inode; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, ++ struct inode *inode) ++{ ++ int err; ++ ++ err = au_br_rdonly(au_sbr(sb, bindex)); ++ ++ /* pseudo-link after flushed may happen out of bounds */ ++ if (!err ++ && inode ++ && au_ibstart(inode) <= bindex ++ && bindex <= au_ibend(inode)) { ++ /* ++ * permission check is unnecessary since vfsub routine ++ * will be called later ++ */ ++ struct inode *hi = au_h_iptr(inode, bindex); ++ if (hi) ++ err = IS_IMMUTABLE(hi) ? -EROFS : 0; ++ } ++ ++ return err; ++} ++ ++int au_test_h_perm(struct inode *h_inode, int mask) ++{ ++ if (!current_fsuid()) ++ return 0; ++ return inode_permission(h_inode, mask); ++} ++ ++int au_test_h_perm_sio(struct inode *h_inode, int mask) ++{ ++ if (au_test_nfs(h_inode->i_sb) ++ && (mask & MAY_WRITE) ++ && S_ISDIR(h_inode->i_mode)) ++ mask |= MAY_READ; /* force permission check */ ++ return au_test_h_perm(h_inode, mask); ++} +diff -Naur a/fs/aufs/inode.h b/fs/aufs/inode.h +--- a/fs/aufs/inode.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/inode.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,473 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * inode operations ++ */ ++ ++#ifndef __AUFS_INODE_H__ ++#define __AUFS_INODE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include "rwsem.h" ++ ++struct vfsmount; ++ ++struct au_hinotify { ++#ifdef CONFIG_AUFS_HINOTIFY ++ struct inotify_watch hin_watch; ++ struct inode *hin_aufs_inode; /* no get/put */ ++#endif ++}; ++ ++struct au_hinode { ++ struct inode *hi_inode; ++ aufs_bindex_t hi_id; ++#ifdef CONFIG_AUFS_HINOTIFY ++ struct au_hinotify *hi_notify; ++#endif ++ ++ /* reference to the copied-up whiteout with get/put */ ++ struct dentry *hi_whdentry; ++}; ++ ++struct au_vdir; ++struct au_iinfo { ++ atomic_t ii_generation; ++ struct super_block *ii_hsb1; /* no get/put */ ++ ++ struct rw_semaphore ii_rwsem; ++ aufs_bindex_t ii_bstart, ii_bend; ++ __u32 ii_higen; ++ struct au_hinode *ii_hinode; ++ struct au_vdir *ii_vdir; ++}; ++ ++struct au_icntnr { ++ struct au_iinfo iinfo; ++ struct inode vfs_inode; ++}; ++ ++/* au_pin flags */ ++#define AuPin_DI_LOCKED 1 ++#define AuPin_MNT_WRITE (1 << 1) ++#define au_ftest_pin(flags, name) ((flags) & AuPin_##name) ++#define au_fset_pin(flags, name) { (flags) |= AuPin_##name; } ++#define au_fclr_pin(flags, name) { (flags) &= ~AuPin_##name; } ++ ++struct au_pin { ++ /* input */ ++ struct dentry *dentry; ++ unsigned int udba; ++ unsigned char lsc_di, lsc_hi, flags; ++ aufs_bindex_t bindex; ++ ++ /* output */ ++ struct dentry *parent; ++ struct au_hinode *hdir; ++ struct vfsmount *h_mnt; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct au_iinfo *au_ii(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ ++ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); ++ if (iinfo->ii_hinode) ++ return iinfo; ++ return NULL; /* debugging bad_inode case */ ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* inode.c */ ++struct inode *au_igrab(struct inode *inode); ++int au_refresh_hinode_self(struct inode *inode, int do_attr); ++int au_refresh_hinode(struct inode *inode, struct dentry *dentry); ++struct inode *au_new_inode(struct dentry *dentry, int must_new); ++int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, ++ struct inode *inode); ++int au_test_h_perm(struct inode *h_inode, int mask); ++int au_test_h_perm_sio(struct inode *h_inode, int mask); ++ ++/* i_op.c */ ++extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop; ++ ++/* au_wr_dir flags */ ++#define AuWrDir_ADD_ENTRY 1 ++#define AuWrDir_ISDIR (1 << 1) ++#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name) ++#define au_fset_wrdir(flags, name) { (flags) |= AuWrDir_##name; } ++#define au_fclr_wrdir(flags, name) { (flags) &= ~AuWrDir_##name; } ++ ++struct au_wr_dir_args { ++ aufs_bindex_t force_btgt; ++ unsigned char flags; ++}; ++int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, ++ struct au_wr_dir_args *args); ++ ++struct dentry *au_pinned_h_parent(struct au_pin *pin); ++void au_pin_init(struct au_pin *pin, struct dentry *dentry, ++ aufs_bindex_t bindex, int lsc_di, int lsc_hi, ++ unsigned int udba, unsigned char flags); ++int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int udba, unsigned char flags) __must_check; ++int au_do_pin(struct au_pin *pin) __must_check; ++void au_unpin(struct au_pin *pin); ++ ++/* i_op_add.c */ ++int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir); ++int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev); ++int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname); ++int aufs_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *nd); ++int aufs_link(struct dentry *src_dentry, struct inode *dir, ++ struct dentry *dentry); ++int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode); ++ ++/* i_op_del.c */ ++int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup); ++int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir); ++int aufs_unlink(struct inode *dir, struct dentry *dentry); ++int aufs_rmdir(struct inode *dir, struct dentry *dentry); ++ ++/* i_op_ren.c */ ++int au_wbr(struct dentry *dentry, aufs_bindex_t btgt); ++int aufs_rename(struct inode *src_dir, struct dentry *src_dentry, ++ struct inode *dir, struct dentry *dentry); ++ ++/* iinfo.c */ ++struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex); ++void au_hiput(struct au_hinode *hinode); ++void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex); ++void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_wh); ++unsigned int au_hi_flags(struct inode *inode, int isdir); ++ ++/* hinode flags */ ++#define AuHi_XINO 1 ++#define AuHi_HINOTIFY (1 << 1) ++#define au_ftest_hi(flags, name) ((flags) & AuHi_##name) ++#define au_fset_hi(flags, name) { (flags) |= AuHi_##name; } ++#define au_fclr_hi(flags, name) { (flags) &= ~AuHi_##name; } ++ ++#ifndef CONFIG_AUFS_HINOTIFY ++#undef AuHi_HINOTIFY ++#define AuHi_HINOTIFY 0 ++#endif ++ ++void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, ++ struct inode *h_inode, unsigned int flags); ++ ++void au_update_iigen(struct inode *inode); ++void au_update_brange(struct inode *inode, int do_put_zero); ++ ++int au_iinfo_init(struct inode *inode); ++void au_iinfo_fin(struct inode *inode); ++int au_ii_realloc(struct au_iinfo *iinfo, int nbr); ++ ++/* plink.c */ ++void au_plink_block_maintain(struct super_block *sb); ++#ifdef CONFIG_AUFS_DEBUG ++void au_plink_list(struct super_block *sb); ++#else ++static inline void au_plink_list(struct super_block *sb) ++{ ++ /* nothing */ ++} ++#endif ++int au_plink_test(struct inode *inode); ++struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex); ++void au_plink_append(struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_dentry); ++void au_plink_put(struct super_block *sb); ++void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* lock subclass for iinfo */ ++enum { ++ AuLsc_II_CHILD, /* child first */ ++ AuLsc_II_CHILD2, /* rename(2), link(2), and cpup at hinotify */ ++ AuLsc_II_CHILD3, /* copyup dirs */ ++ AuLsc_II_PARENT, /* see AuLsc_I_PARENT in vfsub.h */ ++ AuLsc_II_PARENT2, ++ AuLsc_II_PARENT3, /* copyup dirs */ ++ AuLsc_II_NEW_CHILD ++}; ++ ++/* ++ * ii_read_lock_child, ii_write_lock_child, ++ * ii_read_lock_child2, ii_write_lock_child2, ++ * ii_read_lock_child3, ii_write_lock_child3, ++ * ii_read_lock_parent, ii_write_lock_parent, ++ * ii_read_lock_parent2, ii_write_lock_parent2, ++ * ii_read_lock_parent3, ii_write_lock_parent3, ++ * ii_read_lock_new_child, ii_write_lock_new_child, ++ */ ++#define AuReadLockFunc(name, lsc) \ ++static inline void ii_read_lock_##name(struct inode *i) \ ++{ \ ++ down_read_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ ++} ++ ++#define AuWriteLockFunc(name, lsc) \ ++static inline void ii_write_lock_##name(struct inode *i) \ ++{ \ ++ down_write_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ ++} ++ ++#define AuRWLockFuncs(name, lsc) \ ++ AuReadLockFunc(name, lsc) \ ++ AuWriteLockFunc(name, lsc) ++ ++AuRWLockFuncs(child, CHILD); ++AuRWLockFuncs(child2, CHILD2); ++AuRWLockFuncs(child3, CHILD3); ++AuRWLockFuncs(parent, PARENT); ++AuRWLockFuncs(parent2, PARENT2); ++AuRWLockFuncs(parent3, PARENT3); ++AuRWLockFuncs(new_child, NEW_CHILD); ++ ++#undef AuReadLockFunc ++#undef AuWriteLockFunc ++#undef AuRWLockFuncs ++ ++/* ++ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock ++ */ ++AuSimpleUnlockRwsemFuncs(ii, struct inode *i, &au_ii(i)->ii_rwsem); ++ ++#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem) ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline unsigned int au_iigen(struct inode *inode) ++{ ++ return atomic_read(&au_ii(inode)->ii_generation); ++} ++ ++/* tiny test for inode number */ ++/* tmpfs generation is too rough */ ++static inline int au_test_higen(struct inode *inode, struct inode *h_inode) ++{ ++ struct au_iinfo *iinfo; ++ ++ iinfo = au_ii(inode); ++ return !(iinfo->ii_hsb1 == h_inode->i_sb ++ && iinfo->ii_higen == h_inode->i_generation); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline aufs_bindex_t au_ii_br_id(struct inode *inode, ++ aufs_bindex_t bindex) ++{ ++ return au_ii(inode)->ii_hinode[0 + bindex].hi_id; ++} ++ ++static inline aufs_bindex_t au_ibstart(struct inode *inode) ++{ ++ return au_ii(inode)->ii_bstart; ++} ++ ++static inline aufs_bindex_t au_ibend(struct inode *inode) ++{ ++ return au_ii(inode)->ii_bend; ++} ++ ++static inline struct au_vdir *au_ivdir(struct inode *inode) ++{ ++ return au_ii(inode)->ii_vdir; ++} ++ ++static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex) ++{ ++ return au_ii(inode)->ii_hinode[0 + bindex].hi_whdentry; ++} ++ ++static inline void au_set_ibend(struct inode *inode, aufs_bindex_t bindex) ++{ ++ au_ii(inode)->ii_bend = bindex; ++} ++ ++static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir) ++{ ++ au_ii(inode)->ii_vdir = vdir; ++} ++ ++static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex) ++{ ++ return au_ii(inode)->ii_hinode + bindex; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct dentry *au_pinned_parent(struct au_pin *pin) ++{ ++ if (pin) ++ return pin->parent; ++ return NULL; ++} ++ ++static inline struct inode *au_pinned_h_dir(struct au_pin *pin) ++{ ++ if (pin && pin->hdir) ++ return pin->hdir->hi_inode; ++ return NULL; ++} ++ ++static inline struct au_hinode *au_pinned_hdir(struct au_pin *pin) ++{ ++ if (pin) ++ return pin->hdir; ++ return NULL; ++} ++ ++static inline void au_pin_set_dentry(struct au_pin *pin, struct dentry *dentry) ++{ ++ if (pin) ++ pin->dentry = dentry; ++} ++ ++static inline void au_pin_set_parent_lflag(struct au_pin *pin, ++ unsigned char lflag) ++{ ++ if (pin) { ++ /* dirty macros require brackets */ ++ if (lflag) { ++ au_fset_pin(pin->flags, DI_LOCKED); ++ } else { ++ au_fclr_pin(pin->flags, DI_LOCKED); ++ } ++ } ++} ++ ++static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent) ++{ ++ if (pin) { ++ dput(pin->parent); ++ pin->parent = dget(parent); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_HINOTIFY ++/* hinotify.c */ ++int au_hin_alloc(struct au_hinode *hinode, struct inode *inode, ++ struct inode *h_inode); ++void au_hin_free(struct au_hinode *hinode); ++void au_hin_ctl(struct au_hinode *hinode, int do_set); ++void au_reset_hinotify(struct inode *inode, unsigned int flags); ++ ++int __init au_hinotify_init(void); ++void au_hinotify_fin(void); ++ ++static inline ++void au_hin_init(struct au_hinode *hinode, struct au_hinotify *val) ++{ ++ hinode->hi_notify = val; ++} ++ ++static inline void au_iigen_dec(struct inode *inode) ++{ ++ atomic_dec(&au_ii(inode)->ii_generation); ++} ++ ++#else ++static inline ++int au_hin_alloc(struct au_hinode *hinode __maybe_unused, ++ struct inode *inode __maybe_unused, ++ struct inode *h_inode __maybe_unused) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline void au_hin_free(struct au_hinode *hinode __maybe_unused) ++{ ++ /* nothing */ ++} ++ ++static inline void au_hin_ctl(struct au_hinode *hinode __maybe_unused, ++ int do_set __maybe_unused) ++{ ++ /* nothing */ ++} ++ ++static inline void au_reset_hinotify(struct inode *inode __maybe_unused, ++ unsigned int flags __maybe_unused) ++{ ++ /* nothing */ ++} ++ ++static inline int au_hinotify_init(void) ++{ ++ return 0; ++} ++ ++#define au_hinotify_fin() do {} while (0) ++ ++static inline ++void au_hin_init(struct au_hinode *hinode __maybe_unused, ++ struct au_hinotify *val __maybe_unused) ++{ ++ /* empty */ ++} ++#endif /* CONFIG_AUFS_HINOTIFY */ ++ ++static inline void au_hin_suspend(struct au_hinode *hdir) ++{ ++ au_hin_ctl(hdir, /*do_set*/0); ++} ++ ++static inline void au_hin_resume(struct au_hinode *hdir) ++{ ++ au_hin_ctl(hdir, /*do_set*/1); ++} ++ ++static inline void au_hin_imtx_lock(struct au_hinode *hdir) ++{ ++ mutex_lock(&hdir->hi_inode->i_mutex); ++ au_hin_suspend(hdir); ++} ++ ++static inline void au_hin_imtx_lock_nested(struct au_hinode *hdir, ++ unsigned int sc __maybe_unused) ++{ ++ mutex_lock_nested(&hdir->hi_inode->i_mutex, sc); ++ au_hin_suspend(hdir); ++} ++ ++static inline void au_hin_imtx_unlock(struct au_hinode *hdir) ++{ ++ au_hin_resume(hdir); ++ mutex_unlock(&hdir->hi_inode->i_mutex); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_INODE_H__ */ +diff -Naur a/fs/aufs/ioctl.c b/fs/aufs/ioctl.c +--- a/fs/aufs/ioctl.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/ioctl.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,68 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * ioctl ++ * currently plink-management only. ++ */ ++ ++#include ++#include "aufs.h" ++ ++long aufs_ioctl_dir(struct file *file, unsigned int cmd, ++ unsigned long arg __maybe_unused) ++{ ++ long err; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++ err = -EACCES; ++ if (!capable(CAP_SYS_ADMIN)) ++ goto out; ++ ++ err = 0; ++ sb = file->f_dentry->d_sb; ++ sbinfo = au_sbi(sb); ++ switch (cmd) { ++ case AUFS_CTL_PLINK_MAINT: ++ /* ++ * pseudo-link maintenance mode, ++ * cleared by aufs_release_dir() ++ */ ++ si_write_lock(sb); ++ if (!au_ftest_si(sbinfo, MAINTAIN_PLINK)) { ++ au_fset_si(sbinfo, MAINTAIN_PLINK); ++ au_fi(file)->fi_maintain_plink = 1; ++ } else ++ err = -EBUSY; ++ si_write_unlock(sb); ++ break; ++ case AUFS_CTL_PLINK_CLEAN: ++ if (au_opt_test(sbinfo->si_mntflags, PLINK)) { ++ aufs_write_lock(sb->s_root); ++ au_plink_put(sb); ++ aufs_write_unlock(sb->s_root); ++ } ++ break; ++ default: ++ err = -EINVAL; ++ } ++ ++ out: ++ return err; ++} +diff -Naur a/fs/aufs/i_op_add.c b/fs/aufs/i_op_add.c +--- a/fs/aufs/i_op_add.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/i_op_add.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,638 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * inode operations (add entry) ++ */ ++ ++#include "aufs.h" ++ ++/* ++ * final procedure of adding a new entry, except link(2). ++ * remove whiteout, instantiate, copyup the parent dir's times and size ++ * and update version. ++ * if it failed, re-create the removed whiteout. ++ */ ++static int epilog(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct dentry *dentry) ++{ ++ int err, rerr; ++ aufs_bindex_t bwh; ++ struct path h_path; ++ struct inode *inode, *h_dir; ++ struct dentry *wh; ++ ++ bwh = -1; ++ if (wh_dentry) { ++ h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ ++ IMustLock(h_dir); ++ AuDebugOn(au_h_iptr(dir, bindex) != h_dir); ++ bwh = au_dbwh(dentry); ++ h_path.dentry = wh_dentry; ++ h_path.mnt = au_sbr_mnt(dir->i_sb, bindex); ++ err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, ++ dentry); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ inode = au_new_inode(dentry, /*must_new*/1); ++ if (!IS_ERR(inode)) { ++ d_instantiate(dentry, inode); ++ dir = dentry->d_parent->d_inode; /* dir inode is locked */ ++ IMustLock(dir); ++ if (au_ibstart(dir) == au_dbstart(dentry)) ++ au_cpup_attr_timesizes(dir); ++ dir->i_version++; ++ return 0; /* success */ ++ } ++ ++ err = PTR_ERR(inode); ++ if (!wh_dentry) ++ goto out; ++ ++ /* revert */ ++ /* dir inode is locked */ ++ wh = au_wh_create(dentry, bwh, wh_dentry->d_parent); ++ rerr = PTR_ERR(wh); ++ if (IS_ERR(wh)) { ++ AuIOErr("%.*s reverting whiteout failed(%d, %d)\n", ++ AuDLNPair(dentry), err, rerr); ++ err = -EIO; ++ } else ++ dput(wh); ++ ++ out: ++ return err; ++} ++ ++/* ++ * simple tests for the adding inode operations. ++ * following the checks in vfs, plus the parent-child relationship. ++ */ ++int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir) ++{ ++ int err; ++ umode_t h_mode; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ h_inode = h_dentry->d_inode; ++ if (!dentry->d_inode) { ++ err = -EEXIST; ++ if (unlikely(h_inode)) ++ goto out; ++ } else { ++ /* rename(2) case */ ++ err = -EIO; ++ if (unlikely(!h_inode || !h_inode->i_nlink)) ++ goto out; ++ ++ h_mode = h_inode->i_mode; ++ if (!isdir) { ++ err = -EISDIR; ++ if (unlikely(S_ISDIR(h_mode))) ++ goto out; ++ } else if (unlikely(!S_ISDIR(h_mode))) { ++ err = -ENOTDIR; ++ goto out; ++ } ++ } ++ ++ err = -EIO; ++ /* expected parent dir is locked */ ++ if (unlikely(h_parent != h_dentry->d_parent)) ++ goto out; ++ err = 0; ++ ++ out: ++ return err; ++} ++ ++/* ++ * initial procedure of adding a new entry. ++ * prepare writable branch and the parent dir, lock it, ++ * and lookup whiteout for the new entry. ++ */ ++static struct dentry* ++lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt, ++ struct dentry *src_dentry, struct au_pin *pin, ++ struct au_wr_dir_args *wr_dir_args) ++{ ++ struct dentry *wh_dentry, *h_parent; ++ struct super_block *sb; ++ struct au_branch *br; ++ int err; ++ unsigned int udba; ++ aufs_bindex_t bcpup; ++ ++ err = au_wr_dir(dentry, src_dentry, wr_dir_args); ++ bcpup = err; ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err < 0)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ udba = au_opt_udba(sb); ++ err = au_pin(pin, dentry, bcpup, udba, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ h_parent = au_pinned_h_parent(pin); ++ if (udba != AuOpt_UDBA_NONE ++ && au_dbstart(dentry) == bcpup) { ++ err = au_may_add(dentry, bcpup, h_parent, ++ au_ftest_wrdir(wr_dir_args->flags, ISDIR)); ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_unpin; ++ } ++ ++ br = au_sbr(sb, bcpup); ++ if (dt) { ++ struct path tmp = { ++ .dentry = h_parent, ++ .mnt = br->br_mnt ++ }; ++ au_dtime_store(dt, au_pinned_parent(pin), &tmp); ++ } ++ ++ wh_dentry = NULL; ++ if (bcpup != au_dbwh(dentry)) ++ goto out; /* success */ ++ ++ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br); ++ ++ out_unpin: ++ if (IS_ERR(wh_dentry)) ++ au_unpin(pin); ++ out: ++ return wh_dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++enum { Mknod, Symlink, Creat }; ++struct simple_arg { ++ int type; ++ union { ++ struct { ++ int mode; ++ struct nameidata *nd; ++ } c; ++ struct { ++ const char *symname; ++ } s; ++ struct { ++ int mode; ++ dev_t dev; ++ } m; ++ } u; ++}; ++ ++static int add_simple(struct inode *dir, struct dentry *dentry, ++ struct simple_arg *arg) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ unsigned char created; ++ struct au_dtime dt; ++ struct au_pin pin; ++ struct path h_path; ++ struct dentry *wh_dentry, *parent; ++ struct inode *h_dir; ++ struct au_wr_dir_args wr_dir_args = { ++ .force_btgt = -1, ++ .flags = AuWrDir_ADD_ENTRY ++ }; ++ ++ IMustLock(dir); ++ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ aufs_read_lock(dentry, AuLock_DW); ++ di_write_lock_parent(parent); ++ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL, &pin, ++ &wr_dir_args); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out; ++ ++ bstart = au_dbstart(dentry); ++ h_path.dentry = au_h_dptr(dentry, bstart); ++ h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); ++ h_dir = au_pinned_h_dir(&pin); ++ switch (arg->type) { ++ case Creat: ++ err = vfsub_create(h_dir, &h_path, arg->u.c.mode); ++ break; ++ case Symlink: ++ err = vfsub_symlink(h_dir, &h_path, arg->u.s.symname); ++ break; ++ case Mknod: ++ err = vfsub_mknod(h_dir, &h_path, arg->u.m.mode, arg->u.m.dev); ++ break; ++ default: ++ BUG(); ++ } ++ created = !err; ++ if (!err) ++ err = epilog(dir, bstart, wh_dentry, dentry); ++ ++ /* revert */ ++ if (unlikely(created && err && h_path.dentry->d_inode)) { ++ int rerr; ++ rerr = vfsub_unlink(h_dir, &h_path, /*force*/0); ++ if (rerr) { ++ AuIOErr("%.*s revert failure(%d, %d)\n", ++ AuDLNPair(dentry), err, rerr); ++ err = -EIO; ++ } ++ au_dtime_revert(&dt); ++ d_drop(dentry); ++ } ++ ++ au_unpin(&pin); ++ dput(wh_dentry); ++ ++ out: ++ if (unlikely(err)) { ++ au_update_dbstart(dentry); ++ d_drop(dentry); ++ } ++ di_write_unlock(parent); ++ aufs_read_unlock(dentry, AuLock_DW); ++ return err; ++} ++ ++int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) ++{ ++ struct simple_arg arg = { ++ .type = Mknod, ++ .u.m = { ++ .mode = mode, ++ .dev = dev ++ } ++ }; ++ return add_simple(dir, dentry, &arg); ++} ++ ++int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) ++{ ++ struct simple_arg arg = { ++ .type = Symlink, ++ .u.s.symname = symname ++ }; ++ return add_simple(dir, dentry, &arg); ++} ++ ++int aufs_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *nd) ++{ ++ struct simple_arg arg = { ++ .type = Creat, ++ .u.c = { ++ .mode = mode, ++ .nd = nd ++ } ++ }; ++ return add_simple(dir, dentry, &arg); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_link_args { ++ aufs_bindex_t bdst, bsrc; ++ struct au_pin pin; ++ struct path h_path; ++ struct dentry *src_parent, *parent; ++}; ++ ++static int au_cpup_before_link(struct dentry *src_dentry, ++ struct au_link_args *a) ++{ ++ int err; ++ struct dentry *h_src_dentry; ++ struct mutex *h_mtx; ++ ++ di_read_lock_parent(a->src_parent, AuLock_IR); ++ err = au_test_and_cpup_dirs(src_dentry, a->bdst); ++ if (unlikely(err)) ++ goto out; ++ ++ h_src_dentry = au_h_dptr(src_dentry, a->bsrc); ++ h_mtx = &h_src_dentry->d_inode->i_mutex; ++ err = au_pin(&a->pin, src_dentry, a->bdst, ++ au_opt_udba(src_dentry->d_sb), ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ err = au_sio_cpup_simple(src_dentry, a->bdst, -1, ++ AuCpup_DTIME /* | AuCpup_KEEPLINO */); ++ mutex_unlock(h_mtx); ++ au_unpin(&a->pin); ++ ++ out: ++ di_read_unlock(a->src_parent, AuLock_IR); ++ return err; ++} ++ ++static int au_cpup_or_link(struct dentry *src_dentry, struct au_link_args *a) ++{ ++ int err; ++ unsigned char plink; ++ struct inode *h_inode, *inode; ++ struct dentry *h_src_dentry; ++ struct super_block *sb; ++ ++ plink = 0; ++ h_inode = NULL; ++ sb = src_dentry->d_sb; ++ inode = src_dentry->d_inode; ++ if (au_ibstart(inode) <= a->bdst) ++ h_inode = au_h_iptr(inode, a->bdst); ++ if (!h_inode || !h_inode->i_nlink) { ++ /* copyup src_dentry as the name of dentry. */ ++ au_set_dbstart(src_dentry, a->bdst); ++ au_set_h_dptr(src_dentry, a->bdst, dget(a->h_path.dentry)); ++ h_inode = au_h_dptr(src_dentry, a->bsrc)->d_inode; ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ err = au_sio_cpup_single(src_dentry, a->bdst, a->bsrc, -1, ++ AuCpup_KEEPLINO, a->parent); ++ mutex_unlock(&h_inode->i_mutex); ++ au_set_h_dptr(src_dentry, a->bdst, NULL); ++ au_set_dbstart(src_dentry, a->bsrc); ++ } else { ++ /* the inode of src_dentry already exists on a.bdst branch */ ++ h_src_dentry = d_find_alias(h_inode); ++ if (!h_src_dentry && au_plink_test(inode)) { ++ plink = 1; ++ h_src_dentry = au_plink_lkup(inode, a->bdst); ++ err = PTR_ERR(h_src_dentry); ++ if (IS_ERR(h_src_dentry)) ++ goto out; ++ ++ if (unlikely(!h_src_dentry->d_inode)) { ++ dput(h_src_dentry); ++ h_src_dentry = NULL; ++ } ++ ++ } ++ if (h_src_dentry) { ++ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), ++ &a->h_path); ++ dput(h_src_dentry); ++ } else { ++ AuIOErr("no dentry found for hi%lu on b%d\n", ++ h_inode->i_ino, a->bdst); ++ err = -EIO; ++ } ++ } ++ ++ if (!err && !plink) ++ au_plink_append(inode, a->bdst, a->h_path.dentry); ++ ++out: ++ return err; ++} ++ ++int aufs_link(struct dentry *src_dentry, struct inode *dir, ++ struct dentry *dentry) ++{ ++ int err, rerr; ++ struct au_dtime dt; ++ struct au_link_args *a; ++ struct dentry *wh_dentry, *h_src_dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ struct au_wr_dir_args wr_dir_args = { ++ /* .force_btgt = -1, */ ++ .flags = AuWrDir_ADD_ENTRY ++ }; ++ ++ IMustLock(dir); ++ inode = src_dentry->d_inode; ++ IMustLock(inode); ++ ++ err = -ENOENT; ++ if (unlikely(!inode->i_nlink)) ++ goto out; ++ ++ err = -ENOMEM; ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ a->parent = dentry->d_parent; /* dir inode is locked */ ++ aufs_read_and_write_lock2(dentry, src_dentry, /*AuLock_FLUSH*/0); ++ a->src_parent = dget_parent(src_dentry); ++ wr_dir_args.force_btgt = au_dbstart(src_dentry); ++ ++ di_write_lock_parent(a->parent); ++ wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt); ++ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin, ++ &wr_dir_args); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_unlock; ++ ++ err = 0; ++ sb = dentry->d_sb; ++ a->bdst = au_dbstart(dentry); ++ a->h_path.dentry = au_h_dptr(dentry, a->bdst); ++ a->h_path.mnt = au_sbr_mnt(sb, a->bdst); ++ a->bsrc = au_dbstart(src_dentry); ++ if (au_opt_test(au_mntflags(sb), PLINK)) { ++ if (a->bdst < a->bsrc ++ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) ++ err = au_cpup_or_link(src_dentry, a); ++ else { ++ h_src_dentry = au_h_dptr(src_dentry, a->bdst); ++ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), ++ &a->h_path); ++ } ++ } else { ++ /* ++ * copyup src_dentry to the branch we process, ++ * and then link(2) to it. ++ */ ++ if (a->bdst < a->bsrc ++ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) { ++ au_unpin(&a->pin); ++ di_write_unlock(a->parent); ++ err = au_cpup_before_link(src_dentry, a); ++ if (!err) { ++ di_write_lock_parent(a->parent); ++ err = au_pin(&a->pin, dentry, a->bdst, ++ au_opt_udba(sb), ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out_wh; ++ } ++ } ++ if (!err) { ++ h_src_dentry = au_h_dptr(src_dentry, a->bdst); ++ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), ++ &a->h_path); ++ } ++ } ++ if (unlikely(err)) ++ goto out_unpin; ++ ++ if (wh_dentry) { ++ a->h_path.dentry = wh_dentry; ++ err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path, ++ dentry); ++ if (unlikely(err)) ++ goto out_revert; ++ } ++ ++ dir->i_version++; ++ if (au_ibstart(dir) == au_dbstart(dentry)) ++ au_cpup_attr_timesizes(dir); ++ inc_nlink(inode); ++ inode->i_ctime = dir->i_ctime; ++ if (!d_unhashed(a->h_path.dentry)) ++ d_instantiate(dentry, au_igrab(inode)); ++ else ++ /* some filesystem calls d_drop() */ ++ d_drop(dentry); ++ goto out_unpin; /* success */ ++ ++ out_revert: ++ rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, /*force*/0); ++ if (!rerr) ++ goto out_dt; ++ AuIOErr("%.*s reverting failed(%d, %d)\n", ++ AuDLNPair(dentry), err, rerr); ++ err = -EIO; ++ out_dt: ++ d_drop(dentry); ++ au_dtime_revert(&dt); ++ out_unpin: ++ au_unpin(&a->pin); ++ out_wh: ++ dput(wh_dentry); ++ out_unlock: ++ if (unlikely(err)) { ++ au_update_dbstart(dentry); ++ d_drop(dentry); ++ } ++ di_write_unlock(a->parent); ++ dput(a->src_parent); ++ aufs_read_and_write_unlock2(dentry, src_dentry); ++ kfree(a); ++ out: ++ return err; ++} ++ ++int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++{ ++ int err, rerr; ++ aufs_bindex_t bindex; ++ unsigned char diropq; ++ struct au_pin pin; ++ struct path h_path; ++ struct dentry *wh_dentry, *parent, *opq_dentry; ++ struct mutex *h_mtx; ++ struct super_block *sb; ++ struct au_dtime dt; ++ struct au_wr_dir_args wr_dir_args = { ++ .force_btgt = -1, ++ .flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR ++ }; ++ ++ IMustLock(dir); ++ ++ aufs_read_lock(dentry, AuLock_DW); ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_write_lock_parent(parent); ++ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL, &pin, ++ &wr_dir_args); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ bindex = au_dbstart(dentry); ++ h_path.dentry = au_h_dptr(dentry, bindex); ++ h_path.mnt = au_sbr_mnt(sb, bindex); ++ err = vfsub_mkdir(au_pinned_h_dir(&pin), &h_path, mode); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ /* make the dir opaque */ ++ diropq = 0; ++ h_mtx = &h_path.dentry->d_inode->i_mutex; ++ if (wh_dentry ++ || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) { ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ opq_dentry = au_diropq_create(dentry, bindex); ++ mutex_unlock(h_mtx); ++ err = PTR_ERR(opq_dentry); ++ if (IS_ERR(opq_dentry)) ++ goto out_dir; ++ dput(opq_dentry); ++ diropq = 1; ++ } ++ ++ err = epilog(dir, bindex, wh_dentry, dentry); ++ if (!err) { ++ inc_nlink(dir); ++ goto out_unlock; /* success */ ++ } ++ ++ /* revert */ ++ if (diropq) { ++ AuLabel(revert opq); ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ rerr = au_diropq_remove(dentry, bindex); ++ mutex_unlock(h_mtx); ++ if (rerr) { ++ AuIOErr("%.*s reverting diropq failed(%d, %d)\n", ++ AuDLNPair(dentry), err, rerr); ++ err = -EIO; ++ } ++ } ++ ++ out_dir: ++ AuLabel(revert dir); ++ rerr = vfsub_rmdir(au_pinned_h_dir(&pin), &h_path); ++ if (rerr) { ++ AuIOErr("%.*s reverting dir failed(%d, %d)\n", ++ AuDLNPair(dentry), err, rerr); ++ err = -EIO; ++ } ++ d_drop(dentry); ++ au_dtime_revert(&dt); ++ out_unlock: ++ au_unpin(&pin); ++ dput(wh_dentry); ++ out: ++ if (unlikely(err)) { ++ au_update_dbstart(dentry); ++ d_drop(dentry); ++ } ++ di_write_unlock(parent); ++ aufs_read_unlock(dentry, AuLock_DW); ++ return err; ++} +diff -Naur a/fs/aufs/i_op.c b/fs/aufs/i_op.c +--- a/fs/aufs/i_op.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/i_op.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,874 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * inode operations (except add/del/rename) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++static int h_permission(struct inode *h_inode, int mask, ++ struct vfsmount *h_mnt, int brperm) ++{ ++ int err; ++ const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); ++ ++ err = -EACCES; ++ if ((write_mask && IS_IMMUTABLE(h_inode)) ++ || ((mask & MAY_EXEC) ++ && S_ISREG(h_inode->i_mode) ++ && ((h_mnt->mnt_flags & MNT_NOEXEC) ++ || !(h_inode->i_mode & S_IXUGO)))) ++ goto out; ++ ++ /* ++ * - skip the lower fs test in the case of write to ro branch. ++ * - nfs dir permission write check is optimized, but a policy for ++ * link/rename requires a real check. ++ */ ++ if ((write_mask && !au_br_writable(brperm)) ++ || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode) ++ && write_mask && !(mask & MAY_READ)) ++ || !h_inode->i_op->permission) { ++ /* AuLabel(generic_permission); */ ++ err = generic_permission(h_inode, mask, NULL); ++ } else { ++ /* AuLabel(h_inode->permission); */ ++ err = h_inode->i_op->permission(h_inode, mask); ++ AuTraceErr(err); ++ } ++ ++ if (!err) ++ err = devcgroup_inode_permission(h_inode, mask); ++ if (!err) ++ err = security_inode_permission ++ (h_inode, mask & (MAY_READ | MAY_WRITE | MAY_EXEC ++ | MAY_APPEND)); ++ ++ out: ++ return err; ++} ++ ++static int aufs_permission(struct inode *inode, int mask) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ const unsigned char isdir = !!S_ISDIR(inode->i_mode); ++ const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); ++ struct inode *h_inode; ++ struct super_block *sb; ++ struct au_branch *br; ++ ++ sb = inode->i_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ ii_read_lock_child(inode); ++ ++ if (!isdir || write_mask) { ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ AuDebugOn(!h_inode ++ || ((h_inode->i_mode & S_IFMT) ++ != (inode->i_mode & S_IFMT))); ++ err = 0; ++ bindex = au_ibstart(inode); ++ br = au_sbr(sb, bindex); ++ err = h_permission(h_inode, mask, br->br_mnt, br->br_perm); ++ ++ if (write_mask && !err) { ++ /* test whether the upper writable branch exists */ ++ err = -EROFS; ++ for (; bindex >= 0; bindex--) ++ if (!au_br_rdonly(au_sbr(sb, bindex))) { ++ err = 0; ++ break; ++ } ++ } ++ goto out; ++ } ++ ++ /* non-write to dir */ ++ err = 0; ++ bend = au_ibend(inode); ++ for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) { ++ h_inode = au_h_iptr(inode, bindex); ++ if (h_inode) { ++ AuDebugOn(!S_ISDIR(h_inode->i_mode)); ++ br = au_sbr(sb, bindex); ++ err = h_permission(h_inode, mask, br->br_mnt, ++ br->br_perm); ++ } ++ } ++ ++ out: ++ ii_read_unlock(inode); ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *nd) ++{ ++ struct dentry *ret, *parent; ++ struct inode *inode, *h_inode; ++ struct mutex *mtx; ++ struct super_block *sb; ++ int err, npositive; ++ aufs_bindex_t bstart; ++ ++ /* temporary workaround for a bug in NFSD readdir */ ++ if (!au_test_nfsd(current)) ++ IMustLock(dir); ++ else ++ WARN_ONCE(!mutex_is_locked(&dir->i_mutex), ++ "a known problem of NFSD readdir since 2.6.28\n"); ++ ++ sb = dir->i_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ err = au_alloc_dinfo(dentry); ++ ret = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_read_lock_parent(parent, AuLock_IR); ++ npositive = au_lkup_dentry(dentry, au_dbstart(parent), /*type*/0, nd); ++ di_read_unlock(parent, AuLock_IR); ++ err = npositive; ++ ret = ERR_PTR(err); ++ if (unlikely(err < 0)) ++ goto out_unlock; ++ ++ inode = NULL; ++ if (npositive) { ++ bstart = au_dbstart(dentry); ++ h_inode = au_h_dptr(dentry, bstart)->d_inode; ++ if (!S_ISDIR(h_inode->i_mode)) { ++ /* ++ * stop 'race'-ing between hardlinks under different ++ * parents. ++ */ ++ mtx = &au_sbr(sb, bstart)->br_xino.xi_nondir_mtx; ++ mutex_lock(mtx); ++ inode = au_new_inode(dentry, /*must_new*/0); ++ mutex_unlock(mtx); ++ } else ++ inode = au_new_inode(dentry, /*must_new*/0); ++ ret = (void *)inode; ++ } ++ if (IS_ERR(inode)) ++ goto out_unlock; ++ ++ ret = d_splice_alias(inode, dentry); ++ if (unlikely(IS_ERR(ret) && inode)) ++ ii_write_unlock(inode); ++ au_store_oflag(nd, inode); ++ ++ out_unlock: ++ di_write_unlock(dentry); ++ out: ++ si_read_unlock(sb); ++ return ret; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, ++ const unsigned char add_entry, aufs_bindex_t bcpup, ++ aufs_bindex_t bstart) ++{ ++ int err; ++ struct dentry *h_parent; ++ struct inode *h_dir; ++ ++ if (add_entry) { ++ au_update_dbstart(dentry); ++ IMustLock(parent->d_inode); ++ } else ++ di_write_lock_parent(parent); ++ ++ err = 0; ++ if (!au_h_dptr(parent, bcpup)) { ++ if (bstart < bcpup) ++ err = au_cpdown_dirs(dentry, bcpup); ++ else ++ err = au_cpup_dirs(dentry, bcpup); ++ } ++ if (!err && add_entry) { ++ h_parent = au_h_dptr(parent, bcpup); ++ h_dir = h_parent->d_inode; ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); ++ err = au_lkup_neg(dentry, bcpup); ++ /* todo: no unlock here */ ++ mutex_unlock(&h_dir->i_mutex); ++ if (bstart < bcpup && au_dbstart(dentry) < 0) { ++ au_set_dbstart(dentry, 0); ++ au_update_dbrange(dentry, /*do_put_zero*/0); ++ } ++ } ++ ++ if (!add_entry) ++ di_write_unlock(parent); ++ if (!err) ++ err = bcpup; /* success */ ++ ++ return err; ++} ++ ++/* ++ * decide the branch and the parent dir where we will create a new entry. ++ * returns new bindex or an error. ++ * copyup the parent dir if needed. ++ */ ++int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, ++ struct au_wr_dir_args *args) ++{ ++ int err; ++ aufs_bindex_t bcpup, bstart, src_bstart; ++ const unsigned char add_entry = !!au_ftest_wrdir(args->flags, ++ ADD_ENTRY); ++ struct super_block *sb; ++ struct dentry *parent; ++ struct au_sbinfo *sbinfo; ++ ++ sb = dentry->d_sb; ++ sbinfo = au_sbi(sb); ++ parent = dget_parent(dentry); ++ bstart = au_dbstart(dentry); ++ bcpup = bstart; ++ if (args->force_btgt < 0) { ++ if (src_dentry) { ++ src_bstart = au_dbstart(src_dentry); ++ if (src_bstart < bstart) ++ bcpup = src_bstart; ++ } else if (add_entry) { ++ err = AuWbrCreate(sbinfo, dentry, ++ au_ftest_wrdir(args->flags, ISDIR)); ++ bcpup = err; ++ } ++ ++ if (bcpup < 0 || au_test_ro(sb, bcpup, dentry->d_inode)) { ++ if (add_entry) ++ err = AuWbrCopyup(sbinfo, dentry); ++ else { ++ if (!IS_ROOT(dentry)) { ++ di_read_lock_parent(parent, !AuLock_IR); ++ err = AuWbrCopyup(sbinfo, dentry); ++ di_read_unlock(parent, !AuLock_IR); ++ } else ++ err = AuWbrCopyup(sbinfo, dentry); ++ } ++ bcpup = err; ++ if (unlikely(err < 0)) ++ goto out; ++ } ++ } else { ++ bcpup = args->force_btgt; ++ AuDebugOn(au_test_ro(sb, bcpup, dentry->d_inode)); ++ } ++ AuDbg("bstart %d, bcpup %d\n", bstart, bcpup); ++ if (bstart < bcpup) ++ au_update_dbrange(dentry, /*do_put_zero*/1); ++ ++ err = bcpup; ++ if (bcpup == bstart) ++ goto out; /* success */ ++ ++ /* copyup the new parent into the branch we process */ ++ err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, bstart); ++ ++ out: ++ dput(parent); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct dentry *au_pinned_h_parent(struct au_pin *pin) ++{ ++ if (pin && pin->parent) ++ return au_h_dptr(pin->parent, pin->bindex); ++ return NULL; ++} ++ ++void au_unpin(struct au_pin *p) ++{ ++ if (au_ftest_pin(p->flags, MNT_WRITE)) ++ mnt_drop_write(p->h_mnt); ++ if (!p->hdir) ++ return; ++ ++ au_hin_imtx_unlock(p->hdir); ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_unlock(p->parent, AuLock_IR); ++ iput(p->hdir->hi_inode); ++ dput(p->parent); ++ p->parent = NULL; ++ p->hdir = NULL; ++ p->h_mnt = NULL; ++} ++ ++int au_do_pin(struct au_pin *p) ++{ ++ int err; ++ struct super_block *sb; ++ struct dentry *h_dentry, *h_parent; ++ struct au_branch *br; ++ struct inode *h_dir; ++ ++ err = 0; ++ sb = p->dentry->d_sb; ++ br = au_sbr(sb, p->bindex); ++ if (IS_ROOT(p->dentry)) { ++ if (au_ftest_pin(p->flags, MNT_WRITE)) { ++ p->h_mnt = br->br_mnt; ++ err = mnt_want_write(p->h_mnt); ++ if (unlikely(err)) { ++ au_fclr_pin(p->flags, MNT_WRITE); ++ goto out_err; ++ } ++ } ++ goto out; ++ } ++ ++ h_dentry = NULL; ++ if (p->bindex <= au_dbend(p->dentry)) ++ h_dentry = au_h_dptr(p->dentry, p->bindex); ++ ++ p->parent = dget_parent(p->dentry); ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_lock(p->parent, AuLock_IR, p->lsc_di); ++ ++ h_dir = NULL; ++ h_parent = au_h_dptr(p->parent, p->bindex); ++ p->hdir = au_hi(p->parent->d_inode, p->bindex); ++ if (p->hdir) ++ h_dir = p->hdir->hi_inode; ++ ++ /* udba case */ ++ if (unlikely(!p->hdir || !h_dir)) { ++ err = au_busy_or_stale(); ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_unlock(p->parent, AuLock_IR); ++ dput(p->parent); ++ p->parent = NULL; ++ goto out_err; ++ } ++ ++ au_igrab(h_dir); ++ au_hin_imtx_lock_nested(p->hdir, p->lsc_hi); ++ ++ if (h_dentry) { ++ err = au_h_verify(h_dentry, p->udba, h_dir, h_parent, br); ++ if (unlikely(err)) { ++ au_fclr_pin(p->flags, MNT_WRITE); ++ goto out_unpin; ++ } ++ } ++ ++ if (au_ftest_pin(p->flags, MNT_WRITE)) { ++ p->h_mnt = br->br_mnt; ++ err = mnt_want_write(p->h_mnt); ++ if (unlikely(err)) { ++ au_fclr_pin(p->flags, MNT_WRITE); ++ goto out_unpin; ++ } ++ } ++ goto out; /* success */ ++ ++ out_unpin: ++ au_unpin(p); ++ out_err: ++ AuErr("err %d\n", err); ++ err = au_busy_or_stale(); ++ out: ++ return err; ++} ++ ++void au_pin_init(struct au_pin *p, struct dentry *dentry, ++ aufs_bindex_t bindex, int lsc_di, int lsc_hi, ++ unsigned int udba, unsigned char flags) ++{ ++ p->dentry = dentry; ++ p->udba = udba; ++ p->lsc_di = lsc_di; ++ p->lsc_hi = lsc_hi; ++ p->flags = flags; ++ p->bindex = bindex; ++ ++ p->parent = NULL; ++ p->hdir = NULL; ++ p->h_mnt = NULL; ++} ++ ++int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int udba, unsigned char flags) ++{ ++ au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2, ++ udba, flags); ++ return au_do_pin(pin); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define AuIcpup_DID_CPUP 1 ++#define au_ftest_icpup(flags, name) ((flags) & AuIcpup_##name) ++#define au_fset_icpup(flags, name) { (flags) |= AuIcpup_##name; } ++#define au_fclr_icpup(flags, name) { (flags) &= ~AuIcpup_##name; } ++ ++struct au_icpup_args { ++ unsigned char flags; ++ unsigned char pin_flags; ++ aufs_bindex_t btgt; ++ struct au_pin pin; ++ struct path h_path; ++ struct inode *h_inode; ++}; ++ ++static int au_lock_and_icpup(struct dentry *dentry, struct iattr *ia, ++ struct au_icpup_args *a) ++{ ++ int err; ++ unsigned int udba; ++ loff_t sz; ++ aufs_bindex_t bstart; ++ struct dentry *hi_wh, *parent; ++ struct inode *inode; ++ struct au_wr_dir_args wr_dir_args = { ++ .force_btgt = -1, ++ .flags = 0 ++ }; ++ ++ di_write_lock_child(dentry); ++ bstart = au_dbstart(dentry); ++ inode = dentry->d_inode; ++ if (S_ISDIR(inode->i_mode)) ++ au_fset_wrdir(wr_dir_args.flags, ISDIR); ++ /* plink or hi_wh() case */ ++ if (bstart != au_ibstart(inode)) ++ wr_dir_args.force_btgt = au_ibstart(inode); ++ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); ++ if (unlikely(err < 0)) ++ goto out_dentry; ++ a->btgt = err; ++ if (err != bstart) ++ au_fset_icpup(a->flags, DID_CPUP); ++ ++ err = 0; ++ a->pin_flags = AuPin_MNT_WRITE; ++ parent = NULL; ++ if (!IS_ROOT(dentry)) { ++ au_fset_pin(a->pin_flags, DI_LOCKED); ++ parent = dget_parent(dentry); ++ di_write_lock_parent(parent); ++ } ++ ++ udba = au_opt_udba(dentry->d_sb); ++ if (d_unhashed(dentry) || (ia->ia_valid & ATTR_FILE)) ++ udba = AuOpt_UDBA_NONE; ++ err = au_pin(&a->pin, dentry, a->btgt, udba, a->pin_flags); ++ if (unlikely(err)) { ++ if (parent) { ++ di_write_unlock(parent); ++ dput(parent); ++ } ++ goto out_dentry; ++ } ++ a->h_path.dentry = au_h_dptr(dentry, bstart); ++ a->h_inode = a->h_path.dentry->d_inode; ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ sz = -1; ++ if ((ia->ia_valid & ATTR_SIZE) && ia->ia_size < i_size_read(a->h_inode)) ++ sz = ia->ia_size; ++ ++ hi_wh = NULL; ++ if (au_ftest_icpup(a->flags, DID_CPUP) && d_unhashed(dentry)) { ++ hi_wh = au_hi_wh(inode, a->btgt); ++ if (!hi_wh) { ++ err = au_sio_cpup_wh(dentry, a->btgt, sz, /*file*/NULL); ++ if (unlikely(err)) ++ goto out_unlock; ++ hi_wh = au_hi_wh(inode, a->btgt); ++ /* todo: revalidate hi_wh? */ ++ } ++ } ++ ++ if (parent) { ++ au_pin_set_parent_lflag(&a->pin, /*lflag*/0); ++ di_downgrade_lock(parent, AuLock_IR); ++ dput(parent); ++ } ++ if (!au_ftest_icpup(a->flags, DID_CPUP)) ++ goto out; /* success */ ++ ++ if (!d_unhashed(dentry)) { ++ err = au_sio_cpup_simple(dentry, a->btgt, sz, AuCpup_DTIME); ++ if (!err) ++ a->h_path.dentry = au_h_dptr(dentry, a->btgt); ++ } else if (!hi_wh) ++ a->h_path.dentry = au_h_dptr(dentry, a->btgt); ++ else ++ a->h_path.dentry = hi_wh; /* do not dget here */ ++ ++ out_unlock: ++ mutex_unlock(&a->h_inode->i_mutex); ++ a->h_inode = a->h_path.dentry->d_inode; ++ if (!err) { ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ goto out; /* success */ ++ } ++ ++ au_unpin(&a->pin); ++ ++ out_dentry: ++ di_write_unlock(dentry); ++ out: ++ return err; ++} ++ ++static int aufs_setattr(struct dentry *dentry, struct iattr *ia) ++{ ++ int err; ++ struct inode *inode; ++ struct super_block *sb; ++ struct file *file; ++ struct au_icpup_args *a; ++ ++ err = -ENOMEM; ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ inode = dentry->d_inode; ++ IMustLock(inode); ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ ++ file = NULL; ++ if (ia->ia_valid & ATTR_FILE) { ++ /* currently ftruncate(2) only */ ++ file = ia->ia_file; ++ fi_write_lock(file); ++ ia->ia_file = au_h_fptr(file, au_fbstart(file)); ++ } ++ ++ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) ++ ia->ia_valid &= ~ATTR_MODE; ++ ++ err = au_lock_and_icpup(dentry, ia, a); ++ if (unlikely(err < 0)) ++ goto out_si; ++ if (au_ftest_icpup(a->flags, DID_CPUP)) { ++ ia->ia_file = NULL; ++ ia->ia_valid &= ~ATTR_FILE; ++ } ++ ++ a->h_path.mnt = au_sbr_mnt(sb, a->btgt); ++ if (ia->ia_valid & ATTR_SIZE) { ++ struct file *f; ++ ++ if (ia->ia_size < i_size_read(inode)) { ++ /* unmap only */ ++ err = vmtruncate(inode, ia->ia_size); ++ if (unlikely(err)) ++ goto out_unlock; ++ } ++ ++ f = NULL; ++ if (ia->ia_valid & ATTR_FILE) ++ f = ia->ia_file; ++ mutex_unlock(&a->h_inode->i_mutex); ++ err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f); ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ } else ++ err = vfsub_notify_change(&a->h_path, ia); ++ if (!err) ++ au_cpup_attr_changeable(inode); ++ ++ out_unlock: ++ mutex_unlock(&a->h_inode->i_mutex); ++ au_unpin(&a->pin); ++ di_write_unlock(dentry); ++ out_si: ++ if (file) { ++ fi_write_unlock(file); ++ ia->ia_file = file; ++ ia->ia_valid |= ATTR_FILE; ++ } ++ si_read_unlock(sb); ++ kfree(a); ++ out: ++ return err; ++} ++ ++static int au_getattr_lock_reval(struct dentry *dentry, unsigned int sigen) ++{ ++ int err; ++ struct inode *inode; ++ struct dentry *parent; ++ ++ err = 0; ++ inode = dentry->d_inode; ++ di_write_lock_child(dentry); ++ if (au_digen(dentry) != sigen || au_iigen(inode) != sigen) { ++ parent = dget_parent(dentry); ++ di_read_lock_parent(parent, AuLock_IR); ++ /* returns a number of positive dentries */ ++ err = au_refresh_hdentry(dentry, inode->i_mode & S_IFMT); ++ if (err > 0) ++ err = au_refresh_hinode(inode, dentry); ++ di_read_unlock(parent, AuLock_IR); ++ dput(parent); ++ if (unlikely(!err)) ++ err = -EIO; ++ } ++ di_downgrade_lock(dentry, AuLock_IR); ++ ++ return err; ++} ++ ++static void au_refresh_iattr(struct inode *inode, struct kstat *st, ++ unsigned int nlink) ++{ ++ inode->i_mode = st->mode; ++ inode->i_uid = st->uid; ++ inode->i_gid = st->gid; ++ inode->i_atime = st->atime; ++ inode->i_mtime = st->mtime; ++ inode->i_ctime = st->ctime; ++ ++ au_cpup_attr_nlink(inode, /*force*/0); ++ if (S_ISDIR(inode->i_mode)) { ++ inode->i_nlink -= nlink; ++ inode->i_nlink += st->nlink; ++ } ++ ++ spin_lock(&inode->i_lock); ++ inode->i_blocks = st->blocks; ++ i_size_write(inode, st->size); ++ spin_unlock(&inode->i_lock); ++} ++ ++static int aufs_getattr(struct vfsmount *mnt __maybe_unused, ++ struct dentry *dentry, struct kstat *st) ++{ ++ int err; ++ unsigned int mnt_flags; ++ aufs_bindex_t bindex; ++ unsigned char udba_none, positive, did_lock; ++ struct super_block *sb, *h_sb; ++ struct inode *inode; ++ struct vfsmount *h_mnt; ++ struct dentry *h_dentry; ++ ++ err = 0; ++ did_lock = 0; ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ si_read_lock(sb, AuLock_FLUSH); ++ if (IS_ROOT(dentry)) { ++ /* lock free root dinfo */ ++ h_dentry = dget(au_di(dentry)->di_hdentry->hd_dentry); ++ h_mnt = au_sbr_mnt(sb, 0); ++ goto getattr; ++ } ++ ++ did_lock = 1; ++ mnt_flags = au_mntflags(sb); ++ udba_none = !!au_opt_test(mnt_flags, UDBA_NONE); ++ ++ /* support fstat(2) */ ++ if (!d_unhashed(dentry) && !udba_none) { ++ unsigned int sigen = au_sigen(sb); ++ if (au_digen(dentry) == sigen && au_iigen(inode) == sigen) ++ di_read_lock_child(dentry, AuLock_IR); ++ else { ++ err = au_getattr_lock_reval(dentry, sigen); ++ if (unlikely(err)) ++ goto out; ++ } ++ } else ++ di_read_lock_child(dentry, AuLock_IR); ++ ++ bindex = au_ibstart(inode); ++ h_mnt = au_sbr_mnt(sb, bindex); ++ h_sb = h_mnt->mnt_sb; ++ if (!au_test_fs_bad_iattr(h_sb) && udba_none) ++ goto out_fill; /* success */ ++ ++ if (au_dbstart(dentry) == bindex) ++ h_dentry = dget(au_h_dptr(dentry, bindex)); ++ else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) { ++ h_dentry = au_plink_lkup(inode, bindex); ++ if (IS_ERR(h_dentry)) ++ goto out_fill; /* pretending success */ ++ } else ++ /* illegally overlapped or something */ ++ goto out_fill; /* pretending success */ ++ ++ getattr: ++ positive = !!h_dentry->d_inode; ++ if (positive) ++ err = vfs_getattr(h_mnt, h_dentry, st); ++ dput(h_dentry); ++ if (!err) { ++ if (positive) ++ au_refresh_iattr(inode, st, h_dentry->d_inode->i_nlink); ++ goto out_fill; /* success */ ++ } ++ goto out; ++ ++ out_fill: ++ generic_fillattr(inode, st); ++ out: ++ if (did_lock) ++ di_read_unlock(dentry, AuLock_IR); ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int h_readlink(struct dentry *dentry, int bindex, char __user *buf, ++ int bufsiz) ++{ ++ int err; ++ struct super_block *sb; ++ struct dentry *h_dentry; ++ ++ err = -EINVAL; ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (unlikely(/* !h_dentry ++ || !h_dentry->d_inode ++ || !h_dentry->d_inode->i_op ++ || */ !h_dentry->d_inode->i_op->readlink)) ++ goto out; ++ ++ err = security_inode_readlink(h_dentry); ++ if (unlikely(err)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ if (!au_test_ro(sb, bindex, dentry->d_inode)) { ++ vfsub_touch_atime(au_sbr_mnt(sb, bindex), h_dentry); ++ fsstack_copy_attr_atime(dentry->d_inode, h_dentry->d_inode); ++ } ++ err = h_dentry->d_inode->i_op->readlink(h_dentry, buf, bufsiz); ++ ++ out: ++ return err; ++} ++ ++static int aufs_readlink(struct dentry *dentry, char __user *buf, int bufsiz) ++{ ++ int err; ++ ++ aufs_read_lock(dentry, AuLock_IR); ++ err = h_readlink(dentry, au_dbstart(dentry), buf, bufsiz); ++ aufs_read_unlock(dentry, AuLock_IR); ++ ++ return err; ++} ++ ++static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ int err; ++ char *buf; ++ mm_segment_t old_fs; ++ ++ err = -ENOMEM; ++ buf = __getname(); ++ if (unlikely(!buf)) ++ goto out; ++ ++ aufs_read_lock(dentry, AuLock_IR); ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ err = h_readlink(dentry, au_dbstart(dentry), (char __user *)buf, ++ PATH_MAX); ++ set_fs(old_fs); ++ aufs_read_unlock(dentry, AuLock_IR); ++ ++ if (err >= 0) { ++ buf[err] = 0; ++ /* will be freed by put_link */ ++ nd_set_link(nd, buf); ++ return NULL; /* success */ ++ } ++ __putname(buf); ++ ++ out: ++ path_put(&nd->path); ++ AuTraceErr(err); ++ return ERR_PTR(err); ++} ++ ++static void aufs_put_link(struct dentry *dentry __maybe_unused, ++ struct nameidata *nd, void *cookie __maybe_unused) ++{ ++ __putname(nd_get_link(nd)); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void aufs_truncate_range(struct inode *inode __maybe_unused, ++ loff_t start __maybe_unused, ++ loff_t end __maybe_unused) ++{ ++ AuUnsupport(); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct inode_operations aufs_symlink_iop = { ++ .permission = aufs_permission, ++ .setattr = aufs_setattr, ++ .getattr = aufs_getattr, ++ .readlink = aufs_readlink, ++ .follow_link = aufs_follow_link, ++ .put_link = aufs_put_link ++}; ++ ++struct inode_operations aufs_dir_iop = { ++ .create = aufs_create, ++ .lookup = aufs_lookup, ++ .link = aufs_link, ++ .unlink = aufs_unlink, ++ .symlink = aufs_symlink, ++ .mkdir = aufs_mkdir, ++ .rmdir = aufs_rmdir, ++ .mknod = aufs_mknod, ++ .rename = aufs_rename, ++ ++ .permission = aufs_permission, ++ .setattr = aufs_setattr, ++ .getattr = aufs_getattr ++}; ++ ++struct inode_operations aufs_iop = { ++ .permission = aufs_permission, ++ .setattr = aufs_setattr, ++ .getattr = aufs_getattr, ++ .truncate_range = aufs_truncate_range ++}; +diff -Naur a/fs/aufs/i_op_del.c b/fs/aufs/i_op_del.c +--- a/fs/aufs/i_op_del.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/i_op_del.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,466 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * inode operations (del entry) ++ */ ++ ++#include "aufs.h" ++ ++/* ++ * decide if a new whiteout for @dentry is necessary or not. ++ * when it is necessary, prepare the parent dir for the upper branch whose ++ * branch index is @bcpup for creation. the actual creation of the whiteout will ++ * be done by caller. ++ * return value: ++ * 0: wh is unnecessary ++ * plus: wh is necessary ++ * minus: error ++ */ ++int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) ++{ ++ int need_wh, err; ++ aufs_bindex_t bstart; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ bstart = au_dbstart(dentry); ++ if (*bcpup < 0) { ++ *bcpup = bstart; ++ if (au_test_ro(sb, bstart, dentry->d_inode)) { ++ err = AuWbrCopyup(au_sbi(sb), dentry); ++ *bcpup = err; ++ if (unlikely(err < 0)) ++ goto out; ++ } ++ } else ++ AuDebugOn(bstart < *bcpup ++ || au_test_ro(sb, *bcpup, dentry->d_inode)); ++ AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart); ++ ++ if (*bcpup != bstart) { ++ err = au_cpup_dirs(dentry, *bcpup); ++ if (unlikely(err)) ++ goto out; ++ need_wh = 1; ++ } else { ++ aufs_bindex_t old_bend, new_bend, bdiropq = -1; ++ ++ old_bend = au_dbend(dentry); ++ if (isdir) { ++ bdiropq = au_dbdiropq(dentry); ++ au_set_dbdiropq(dentry, -1); ++ } ++ need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0, ++ /*nd*/NULL); ++ err = need_wh; ++ if (isdir) ++ au_set_dbdiropq(dentry, bdiropq); ++ if (unlikely(err < 0)) ++ goto out; ++ new_bend = au_dbend(dentry); ++ if (!need_wh && old_bend != new_bend) { ++ au_set_h_dptr(dentry, new_bend, NULL); ++ au_set_dbend(dentry, old_bend); ++ } ++ } ++ AuDbg("need_wh %d\n", need_wh); ++ err = need_wh; ++ ++ out: ++ return err; ++} ++ ++/* ++ * simple tests for the del-entry operations. ++ * following the checks in vfs, plus the parent-child relationship. ++ */ ++int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir) ++{ ++ int err; ++ umode_t h_mode; ++ struct dentry *h_dentry, *h_latest; ++ struct inode *h_inode; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ h_inode = h_dentry->d_inode; ++ if (dentry->d_inode) { ++ err = -ENOENT; ++ if (unlikely(!h_inode || !h_inode->i_nlink)) ++ goto out; ++ ++ h_mode = h_inode->i_mode; ++ if (!isdir) { ++ err = -EISDIR; ++ if (unlikely(S_ISDIR(h_mode))) ++ goto out; ++ } else if (unlikely(!S_ISDIR(h_mode))) { ++ err = -ENOTDIR; ++ goto out; ++ } ++ } else { ++ /* rename(2) case */ ++ err = -EIO; ++ if (unlikely(h_inode)) ++ goto out; ++ } ++ ++ err = -ENOENT; ++ /* expected parent dir is locked */ ++ if (unlikely(h_parent != h_dentry->d_parent)) ++ goto out; ++ err = 0; ++ ++ /* ++ * rmdir a dir may break the consistency on some filesystem. ++ * let's try heavy test. ++ */ ++ err = -EACCES; ++ if (unlikely(au_test_h_perm(h_parent->d_inode, MAY_EXEC | MAY_WRITE))) ++ goto out; ++ ++ h_latest = au_sio_lkup_one(&dentry->d_name, h_parent, ++ au_sbr(dentry->d_sb, bindex)); ++ err = -EIO; ++ if (IS_ERR(h_latest)) ++ goto out; ++ if (h_latest == h_dentry) ++ err = 0; ++ dput(h_latest); ++ ++ out: ++ return err; ++} ++ ++/* ++ * decide the branch where we operate for @dentry. the branch index will be set ++ * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent ++ * dir for reverting. ++ * when a new whiteout is necessary, create it. ++ */ ++static struct dentry* ++lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup, ++ struct au_dtime *dt, struct au_pin *pin) ++{ ++ struct dentry *wh_dentry; ++ struct super_block *sb; ++ struct path h_path; ++ int err, need_wh; ++ unsigned int udba; ++ aufs_bindex_t bcpup; ++ ++ need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup); ++ wh_dentry = ERR_PTR(need_wh); ++ if (unlikely(need_wh < 0)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ udba = au_opt_udba(sb); ++ bcpup = *rbcpup; ++ err = au_pin(pin, dentry, bcpup, udba, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ h_path.dentry = au_pinned_h_parent(pin); ++ if (udba != AuOpt_UDBA_NONE ++ && au_dbstart(dentry) == bcpup) { ++ err = au_may_del(dentry, bcpup, h_path.dentry, isdir); ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_unpin; ++ } ++ ++ h_path.mnt = au_sbr_mnt(sb, bcpup); ++ au_dtime_store(dt, au_pinned_parent(pin), &h_path); ++ wh_dentry = NULL; ++ if (!need_wh) ++ goto out; /* success, no need to create whiteout */ ++ ++ wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry); ++ if (!IS_ERR(wh_dentry)) ++ goto out; /* success */ ++ /* returns with the parent is locked and wh_dentry is dget-ed */ ++ ++ out_unpin: ++ au_unpin(pin); ++ out: ++ return wh_dentry; ++} ++ ++/* ++ * when removing a dir, rename it to a unique temporary whiteout-ed name first ++ * in order to be revertible and save time for removing many child whiteouts ++ * under the dir. ++ * returns 1 when there are too many child whiteout and caller should remove ++ * them asynchronously. returns 0 when the number of children is enough small to ++ * remove now or the branch fs is a remote fs. ++ * otherwise return an error. ++ */ ++static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex, ++ struct au_nhash *whlist, struct inode *dir) ++{ ++ int rmdir_later, err, dirwh; ++ struct dentry *h_dentry; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ h_dentry = au_h_dptr(dentry, bindex); ++ err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex)); ++ if (unlikely(err)) ++ goto out; ++ ++ /* stop monitoring */ ++ au_hin_free(au_hi(dentry->d_inode, bindex)); ++ ++ if (!au_test_fs_remote(h_dentry->d_sb)) { ++ dirwh = au_sbi(sb)->si_dirwh; ++ rmdir_later = (dirwh <= 1); ++ if (!rmdir_later) ++ rmdir_later = au_nhash_test_longer_wh(whlist, bindex, ++ dirwh); ++ if (rmdir_later) ++ return rmdir_later; ++ } ++ ++ err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist); ++ if (unlikely(err)) { ++ AuIOErr("rmdir %.*s, b%d failed, %d. ignored\n", ++ AuDLNPair(h_dentry), bindex, err); ++ err = 0; ++ } ++ ++ out: ++ return err; ++} ++ ++/* ++ * final procedure for deleting a entry. ++ * maintain dentry and iattr. ++ */ ++static void epilog(struct inode *dir, struct dentry *dentry, ++ aufs_bindex_t bindex) ++{ ++ struct inode *inode; ++ ++ inode = dentry->d_inode; ++ d_drop(dentry); ++ inode->i_ctime = dir->i_ctime; ++ ++ if (atomic_read(&dentry->d_count) == 1) { ++ au_set_h_dptr(dentry, au_dbstart(dentry), NULL); ++ au_update_dbstart(dentry); ++ } ++ if (au_ibstart(dir) == bindex) ++ au_cpup_attr_timesizes(dir); ++ dir->i_version++; ++} ++ ++/* ++ * when an error happened, remove the created whiteout and revert everything. ++ */ ++static int do_revert(int err, struct inode *dir, aufs_bindex_t bwh, ++ struct dentry *wh_dentry, struct dentry *dentry, ++ struct au_dtime *dt) ++{ ++ int rerr; ++ struct path h_path = { ++ .dentry = wh_dentry, ++ .mnt = au_sbr_mnt(dir->i_sb, bwh) ++ }; ++ ++ rerr = au_wh_unlink_dentry(au_h_iptr(dir, bwh), &h_path, dentry); ++ if (!rerr) { ++ au_set_dbwh(dentry, bwh); ++ au_dtime_revert(dt); ++ return 0; ++ } ++ ++ AuIOErr("%.*s reverting whiteout failed(%d, %d)\n", ++ AuDLNPair(dentry), err, rerr); ++ return -EIO; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int aufs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bwh, bindex, bstart; ++ struct au_dtime dt; ++ struct au_pin pin; ++ struct path h_path; ++ struct inode *inode, *h_dir; ++ struct dentry *parent, *wh_dentry; ++ ++ IMustLock(dir); ++ inode = dentry->d_inode; ++ if (unlikely(!inode)) ++ return -ENOENT; /* possible? */ ++ IMustLock(inode); ++ ++ aufs_read_lock(dentry, AuLock_DW); ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_write_lock_parent(parent); ++ ++ bstart = au_dbstart(dentry); ++ bwh = au_dbwh(dentry); ++ bindex = -1; ++ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &dt, &pin); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out; ++ ++ h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); ++ h_path.dentry = au_h_dptr(dentry, bstart); ++ dget(h_path.dentry); ++ if (bindex == bstart) { ++ h_dir = au_pinned_h_dir(&pin); ++ err = vfsub_unlink(h_dir, &h_path, /*force*/0); ++ } else { ++ /* dir inode is locked */ ++ h_dir = wh_dentry->d_parent->d_inode; ++ IMustLock(h_dir); ++ err = 0; ++ } ++ ++ if (!err) { ++ drop_nlink(inode); ++ epilog(dir, dentry, bindex); ++ ++ /* update target timestamps */ ++ if (bindex == bstart) { ++ vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/ ++ inode->i_ctime = h_path.dentry->d_inode->i_ctime; ++ } else ++ /* todo: this timestamp may be reverted later */ ++ inode->i_ctime = h_dir->i_ctime; ++ goto out_unlock; /* success */ ++ } ++ ++ /* revert */ ++ if (wh_dentry) { ++ int rerr; ++ ++ rerr = do_revert(err, dir, bwh, wh_dentry, dentry, &dt); ++ if (rerr) ++ err = rerr; ++ } ++ ++ out_unlock: ++ au_unpin(&pin); ++ dput(wh_dentry); ++ dput(h_path.dentry); ++ out: ++ di_write_unlock(parent); ++ aufs_read_unlock(dentry, AuLock_DW); ++ return err; ++} ++ ++int aufs_rmdir(struct inode *dir, struct dentry *dentry) ++{ ++ int err, rmdir_later; ++ aufs_bindex_t bwh, bindex, bstart; ++ struct au_dtime dt; ++ struct au_pin pin; ++ struct inode *inode; ++ struct dentry *parent, *wh_dentry, *h_dentry; ++ struct au_whtmp_rmdir *args; ++ ++ IMustLock(dir); ++ inode = dentry->d_inode; ++ err = -ENOENT; /* possible? */ ++ if (unlikely(!inode)) ++ goto out; ++ IMustLock(inode); ++ ++ err = -ENOMEM; ++ args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS); ++ if (unlikely(!args)) ++ goto out; ++ ++ aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH); ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_write_lock_parent(parent); ++ err = au_test_empty(dentry, &args->whlist); ++ if (unlikely(err)) ++ goto out_args; ++ ++ bstart = au_dbstart(dentry); ++ bwh = au_dbwh(dentry); ++ bindex = -1; ++ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &dt, &pin); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_args; ++ ++ h_dentry = au_h_dptr(dentry, bstart); ++ dget(h_dentry); ++ rmdir_later = 0; ++ if (bindex == bstart) { ++ err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir); ++ if (err > 0) { ++ rmdir_later = err; ++ err = 0; ++ } ++ } else { ++ /* stop monitoring */ ++ au_hin_free(au_hi(inode, bstart)); ++ ++ /* dir inode is locked */ ++ IMustLock(wh_dentry->d_parent->d_inode); ++ err = 0; ++ } ++ ++ if (!err) { ++ clear_nlink(inode); ++ au_set_dbdiropq(dentry, -1); ++ epilog(dir, dentry, bindex); ++ ++ if (rmdir_later) { ++ au_whtmp_kick_rmdir(dir, bstart, h_dentry, args); ++ args = NULL; ++ } ++ ++ goto out_unlock; /* success */ ++ } ++ ++ /* revert */ ++ AuLabel(revert); ++ if (wh_dentry) { ++ int rerr; ++ ++ rerr = do_revert(err, dir, bwh, wh_dentry, dentry, &dt); ++ if (rerr) ++ err = rerr; ++ } ++ ++ out_unlock: ++ au_unpin(&pin); ++ dput(wh_dentry); ++ dput(h_dentry); ++ out_args: ++ di_write_unlock(parent); ++ aufs_read_unlock(dentry, AuLock_DW); ++ if (args) ++ au_whtmp_rmdir_free(args); ++ out: ++ return err; ++} +diff -Naur a/fs/aufs/i_op_ren.c b/fs/aufs/i_op_ren.c +--- a/fs/aufs/i_op_ren.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/i_op_ren.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,942 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * inode operation (rename entry) ++ * todo: this is crazy monster ++ */ ++ ++#include "aufs.h" ++ ++enum { AuSRC, AuDST, AuSrcDst }; ++enum { AuPARENT, AuCHILD, AuParentChild }; ++ ++#define AuRen_ISDIR 1 ++#define AuRen_ISSAMEDIR (1 << 1) ++#define AuRen_WHSRC (1 << 2) ++#define AuRen_WHDST (1 << 3) ++#define AuRen_MNT_WRITE (1 << 4) ++#define AuRen_DT_DSTDIR (1 << 5) ++#define AuRen_DIROPQ (1 << 6) ++#define AuRen_CPUP (1 << 7) ++#define au_ftest_ren(flags, name) ((flags) & AuRen_##name) ++#define au_fset_ren(flags, name) { (flags) |= AuRen_##name; } ++#define au_fclr_ren(flags, name) { (flags) &= ~AuRen_##name; } ++ ++struct au_ren_args { ++ struct { ++ struct dentry *dentry, *h_dentry, *parent, *h_parent, ++ *wh_dentry; ++ struct inode *dir, *inode; ++ struct au_hinode *hdir; ++ struct au_dtime dt[AuParentChild]; ++ aufs_bindex_t bstart; ++ } sd[AuSrcDst]; ++ ++#define src_dentry sd[AuSRC].dentry ++#define src_dir sd[AuSRC].dir ++#define src_inode sd[AuSRC].inode ++#define src_h_dentry sd[AuSRC].h_dentry ++#define src_parent sd[AuSRC].parent ++#define src_h_parent sd[AuSRC].h_parent ++#define src_wh_dentry sd[AuSRC].wh_dentry ++#define src_hdir sd[AuSRC].hdir ++#define src_h_dir sd[AuSRC].hdir->hi_inode ++#define src_dt sd[AuSRC].dt ++#define src_bstart sd[AuSRC].bstart ++ ++#define dst_dentry sd[AuDST].dentry ++#define dst_dir sd[AuDST].dir ++#define dst_inode sd[AuDST].inode ++#define dst_h_dentry sd[AuDST].h_dentry ++#define dst_parent sd[AuDST].parent ++#define dst_h_parent sd[AuDST].h_parent ++#define dst_wh_dentry sd[AuDST].wh_dentry ++#define dst_hdir sd[AuDST].hdir ++#define dst_h_dir sd[AuDST].hdir->hi_inode ++#define dst_dt sd[AuDST].dt ++#define dst_bstart sd[AuDST].bstart ++ ++ struct dentry *h_trap; ++ struct au_branch *br; ++ struct au_hinode *src_hinode; ++ struct path h_path; ++ struct au_nhash whlist; ++ aufs_bindex_t btgt; ++ ++ unsigned int flags; ++ ++ struct au_whtmp_rmdir *thargs; ++ struct dentry *h_dst; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * functions for reverting. ++ * when an error happened in a single rename systemcall, we should revert ++ * everything as if nothing happend. ++ * we don't need to revert the copied-up/down the parent dir since they are ++ * harmless. ++ */ ++ ++#define RevertFailure(fmt, args...) do { \ ++ AuIOErr("revert failure: " fmt " (%d, %d)\n", \ ++ ##args, err, rerr); \ ++ err = -EIO; \ ++} while (0) ++ ++static void au_ren_rev_diropq(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ ++ au_hin_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); ++ rerr = au_diropq_remove(a->src_dentry, a->btgt); ++ au_hin_imtx_unlock(a->src_hinode); ++ if (rerr) ++ RevertFailure("remove diropq %.*s", AuDLNPair(a->src_dentry)); ++} ++ ++ ++static void au_ren_rev_rename(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ ++ a->h_path.dentry = au_lkup_one(&a->src_dentry->d_name, a->src_h_parent, ++ a->br, /*nd*/NULL); ++ rerr = PTR_ERR(a->h_path.dentry); ++ if (IS_ERR(a->h_path.dentry)) { ++ RevertFailure("au_lkup_one %.*s", AuDLNPair(a->src_dentry)); ++ return; ++ } ++ ++ rerr = vfsub_rename(a->dst_h_dir, ++ au_h_dptr(a->src_dentry, a->btgt), ++ a->src_h_dir, &a->h_path); ++ d_drop(a->h_path.dentry); ++ dput(a->h_path.dentry); ++ /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */ ++ if (rerr) ++ RevertFailure("rename %.*s", AuDLNPair(a->src_dentry)); ++} ++ ++static void au_ren_rev_cpup(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ ++ a->h_path.dentry = a->dst_h_dentry; ++ rerr = vfsub_unlink(a->dst_h_dir, &a->h_path, /*force*/0); ++ au_set_h_dptr(a->src_dentry, a->btgt, NULL); ++ au_set_dbstart(a->src_dentry, a->src_bstart); ++ if (rerr) ++ RevertFailure("unlink %.*s", AuDLNPair(a->dst_h_dentry)); ++} ++ ++ ++static void au_ren_rev_whtmp(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ ++ a->h_path.dentry = au_lkup_one(&a->dst_dentry->d_name, a->dst_h_parent, ++ a->br, /*nd*/NULL); ++ rerr = PTR_ERR(a->h_path.dentry); ++ if (IS_ERR(a->h_path.dentry)) { ++ RevertFailure("lookup %.*s", AuDLNPair(a->dst_dentry)); ++ return; ++ } ++ if (a->h_path.dentry->d_inode) { ++ d_drop(a->h_path.dentry); ++ dput(a->h_path.dentry); ++ return; ++ } ++ ++ rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path); ++ d_drop(a->h_path.dentry); ++ dput(a->h_path.dentry); ++ if (!rerr) { ++ au_set_h_dptr(a->dst_dentry, a->btgt, NULL); ++ au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst)); ++ } else ++ RevertFailure("rename %.*s", AuDLNPair(a->h_dst)); ++} ++ ++static void au_ren_rev_whsrc(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ ++ a->h_path.dentry = a->src_wh_dentry; ++ rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry); ++ if (rerr) ++ RevertFailure("unlink %.*s", AuDLNPair(a->src_wh_dentry)); ++} ++ ++static void au_ren_rev_drop(struct au_ren_args *a) ++{ ++ struct dentry *d, *h_d; ++ int i; ++ aufs_bindex_t bend, bindex; ++ ++ for (i = 0; i < AuSrcDst; i++) { ++ d = a->sd[i].dentry; ++ d_drop(d); ++ bend = au_dbend(d); ++ for (bindex = au_dbstart(d); bindex <= bend; bindex++) { ++ h_d = au_h_dptr(d, bindex); ++ if (h_d) ++ d_drop(h_d); ++ } ++ } ++ ++ au_update_dbstart(a->dst_dentry); ++ if (a->thargs) ++ d_drop(a->h_dst); ++} ++#undef RevertFailure ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * when we have to copyup the renaming entry, do it with the rename-target name ++ * in order to minimize the cost (the later actual rename is unnecessary). ++ * otherwise rename it on the target branch. ++ */ ++static int au_ren_or_cpup(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *d; ++ ++ d = a->src_dentry; ++ if (au_dbstart(d) == a->btgt) { ++ a->h_path.dentry = a->dst_h_dentry; ++ if (au_ftest_ren(a->flags, DIROPQ) ++ && au_dbdiropq(d) == a->btgt) ++ au_fclr_ren(a->flags, DIROPQ); ++ AuDebugOn(au_dbstart(d) != a->btgt); ++ err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), ++ a->dst_h_dir, &a->h_path); ++ } else { ++ struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex; ++ ++ au_fset_ren(a->flags, CPUP); ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ au_set_dbstart(d, a->btgt); ++ au_set_h_dptr(d, a->btgt, dget(a->dst_h_dentry)); ++ err = au_sio_cpup_single(d, a->btgt, a->src_bstart, -1, ++ !AuCpup_DTIME, a->dst_parent); ++ if (unlikely(err)) { ++ au_set_h_dptr(d, a->btgt, NULL); ++ au_set_dbstart(d, a->src_bstart); ++ } ++ mutex_unlock(h_mtx); ++ } ++ ++ return err; ++} ++ ++/* cf. aufs_rmdir() */ ++static int au_ren_del_whtmp(struct au_ren_args *a) ++{ ++ int err; ++ struct inode *dir; ++ ++ dir = a->dst_dir; ++ if (!au_nhash_test_longer_wh(&a->whlist, a->btgt, ++ au_sbi(dir->i_sb)->si_dirwh) ++ || au_test_fs_remote(a->h_dst->d_sb)) { ++ err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist); ++ if (unlikely(err)) ++ AuWarn("failed removing whtmp dir %.*s (%d), " ++ "ignored.\n", AuDLNPair(a->h_dst), err); ++ } else { ++ au_nhash_wh_free(&a->thargs->whlist); ++ a->thargs->whlist = a->whlist; ++ a->whlist.nh_num = 0; ++ au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs); ++ dput(a->h_dst); ++ a->thargs = NULL; ++ } ++ ++ return 0; ++} ++ ++/* make it 'opaque' dir. */ ++static int au_ren_diropq(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *diropq; ++ ++ err = 0; ++ a->src_hinode = au_hi(a->src_inode, a->btgt); ++ au_hin_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); ++ diropq = au_diropq_create(a->src_dentry, a->btgt); ++ au_hin_imtx_unlock(a->src_hinode); ++ if (IS_ERR(diropq)) ++ err = PTR_ERR(diropq); ++ dput(diropq); ++ ++ return err; ++} ++ ++static int do_rename(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *d, *h_d; ++ ++ /* prepare workqueue args for asynchronous rmdir */ ++ h_d = a->dst_h_dentry; ++ if (au_ftest_ren(a->flags, ISDIR) && h_d->d_inode) { ++ err = -ENOMEM; ++ a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS); ++ if (unlikely(!a->thargs)) ++ goto out; ++ a->h_dst = dget(h_d); ++ } ++ ++ /* create whiteout for src_dentry */ ++ if (au_ftest_ren(a->flags, WHSRC)) { ++ a->src_wh_dentry ++ = au_wh_create(a->src_dentry, a->btgt, a->src_h_parent); ++ err = PTR_ERR(a->src_wh_dentry); ++ if (IS_ERR(a->src_wh_dentry)) ++ goto out_thargs; ++ } ++ ++ /* lookup whiteout for dentry */ ++ if (au_ftest_ren(a->flags, WHDST)) { ++ h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name, ++ a->br); ++ err = PTR_ERR(h_d); ++ if (IS_ERR(h_d)) ++ goto out_whsrc; ++ if (!h_d->d_inode) ++ dput(h_d); ++ else ++ a->dst_wh_dentry = h_d; ++ } ++ ++ /* rename dentry to tmpwh */ ++ if (a->thargs) { ++ err = au_whtmp_ren(a->dst_h_dentry, a->br); ++ if (unlikely(err)) ++ goto out_whdst; ++ ++ d = a->dst_dentry; ++ au_set_h_dptr(d, a->btgt, NULL); ++ err = au_lkup_neg(d, a->btgt); ++ if (unlikely(err)) ++ goto out_whtmp; ++ a->dst_h_dentry = au_h_dptr(d, a->btgt); ++ } ++ ++ /* cpup src */ ++ if (a->dst_h_dentry->d_inode && a->src_bstart != a->btgt) { ++ struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex; ++ ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ err = au_sio_cpup_simple(a->src_dentry, a->btgt, -1, ++ !AuCpup_DTIME); ++ mutex_unlock(h_mtx); ++ if (unlikely(err)) ++ goto out_whtmp; ++ } ++ ++ /* rename by vfs_rename or cpup */ ++ d = a->dst_dentry; ++ if (au_ftest_ren(a->flags, ISDIR) ++ && (a->dst_wh_dentry ++ || au_dbdiropq(d) == a->btgt ++ /* hide the lower to keep xino */ ++ || a->btgt < au_dbend(d) ++ || au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ))) ++ au_fset_ren(a->flags, DIROPQ); ++ err = au_ren_or_cpup(a); ++ if (unlikely(err)) ++ /* leave the copied-up one */ ++ goto out_whtmp; ++ ++ /* make dir opaque */ ++ if (au_ftest_ren(a->flags, DIROPQ)) { ++ err = au_ren_diropq(a); ++ if (unlikely(err)) ++ goto out_rename; ++ } ++ ++ /* update target timestamps */ ++ AuDebugOn(au_dbstart(a->src_dentry) != a->btgt); ++ a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt); ++ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ ++ a->src_inode->i_ctime = a->h_path.dentry->d_inode->i_ctime; ++ ++ /* remove whiteout for dentry */ ++ if (a->dst_wh_dentry) { ++ a->h_path.dentry = a->dst_wh_dentry; ++ err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, ++ a->dst_dentry); ++ if (unlikely(err)) ++ goto out_diropq; ++ } ++ ++ /* remove whtmp */ ++ if (a->thargs) ++ au_ren_del_whtmp(a); /* ignore this error */ ++ ++ err = 0; ++ goto out_success; ++ ++ out_diropq: ++ if (au_ftest_ren(a->flags, DIROPQ)) ++ au_ren_rev_diropq(err, a); ++ out_rename: ++ if (!au_ftest_ren(a->flags, CPUP)) ++ au_ren_rev_rename(err, a); ++ else ++ au_ren_rev_cpup(err, a); ++ out_whtmp: ++ if (a->thargs) ++ au_ren_rev_whtmp(err, a); ++ out_whdst: ++ dput(a->dst_wh_dentry); ++ a->dst_wh_dentry = NULL; ++ out_whsrc: ++ if (a->src_wh_dentry) ++ au_ren_rev_whsrc(err, a); ++ au_ren_rev_drop(a); ++ out_success: ++ dput(a->src_wh_dentry); ++ dput(a->dst_wh_dentry); ++ out_thargs: ++ if (a->thargs) { ++ dput(a->h_dst); ++ au_whtmp_rmdir_free(a->thargs); ++ a->thargs = NULL; ++ } ++ out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * test if @dentry dir can be rename destination or not. ++ * success means, it is a logically empty dir. ++ */ ++static int may_rename_dstdir(struct dentry *dentry, struct au_nhash *whlist) ++{ ++ return au_test_empty(dentry, whlist); ++} ++ ++/* ++ * test if @dentry dir can be rename source or not. ++ * if it can, return 0 and @children is filled. ++ * success means, ++ * - it is a logically empty dir. ++ * - or, it exists on writable branch and has no children including whiteouts ++ * on the lower branch. ++ */ ++static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ ++ bstart = au_dbstart(dentry); ++ if (bstart != btgt) { ++ struct au_nhash whlist; ++ ++ err = au_nhash_alloc(&whlist, au_sbi(dentry->d_sb)->si_rdhash, ++ GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_test_empty(dentry, &whlist); ++ au_nhash_wh_free(&whlist); ++ goto out; ++ } ++ ++ if (bstart == au_dbtaildir(dentry)) ++ return 0; /* success */ ++ ++ err = au_test_empty_lower(dentry); ++ ++ out: ++ if (err == -ENOTEMPTY) { ++ AuWarn1("renaming dir who has child(ren) on multiple branches," ++ " is not supported\n"); ++ err = -EXDEV; ++ } ++ return err; ++} ++ ++/* side effect: sets whlist and h_dentry */ ++static int au_ren_may_dir(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *d; ++ ++ d = a->dst_dentry; ++ err = au_nhash_alloc(&a->whlist, au_sbi(d->d_sb)->si_rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ ++ err = 0; ++ if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) { ++ au_set_dbstart(d, a->dst_bstart); ++ err = may_rename_dstdir(d, &a->whlist); ++ au_set_dbstart(d, a->btgt); ++ } ++ a->dst_h_dentry = au_h_dptr(d, au_dbstart(d)); ++ if (unlikely(err)) ++ goto out; ++ ++ d = a->src_dentry; ++ a->src_h_dentry = au_h_dptr(d, au_dbstart(d)); ++ if (au_ftest_ren(a->flags, ISDIR)) { ++ err = may_rename_srcdir(d, a->btgt); ++ if (unlikely(err)) { ++ au_nhash_wh_free(&a->whlist); ++ a->whlist.nh_num = 0; ++ } ++ } ++ out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * simple tests for rename. ++ * following the checks in vfs, plus the parent-child relationship. ++ */ ++static int au_may_ren(struct au_ren_args *a) ++{ ++ int err, isdir; ++ struct inode *h_inode; ++ ++ if (a->src_bstart == a->btgt) { ++ err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent, ++ au_ftest_ren(a->flags, ISDIR)); ++ if (unlikely(err)) ++ goto out; ++ err = -EINVAL; ++ if (unlikely(a->src_h_dentry == a->h_trap)) ++ goto out; ++ } ++ ++ err = 0; ++ if (a->dst_bstart != a->btgt) ++ goto out; ++ ++ err = -EIO; ++ h_inode = a->dst_h_dentry->d_inode; ++ isdir = !!au_ftest_ren(a->flags, ISDIR); ++ if (!a->dst_dentry->d_inode) { ++ if (unlikely(h_inode)) ++ goto out; ++ err = au_may_add(a->dst_dentry, a->btgt, a->dst_h_parent, ++ isdir); ++ } else { ++ if (unlikely(!h_inode || !h_inode->i_nlink)) ++ goto out; ++ err = au_may_del(a->dst_dentry, a->btgt, a->dst_h_parent, ++ isdir); ++ if (unlikely(err)) ++ goto out; ++ err = -ENOTEMPTY; ++ if (unlikely(a->dst_h_dentry == a->h_trap)) ++ goto out; ++ err = 0; ++ } ++ ++ out: ++ if (unlikely(err == -ENOENT || err == -EEXIST)) ++ err = -EIO; ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * locking order ++ * (VFS) ++ * - src_dir and dir by lock_rename() ++ * - inode if exitsts ++ * (aufs) ++ * - lock all ++ * + src_dentry and dentry by aufs_read_and_write_lock2() which calls, ++ * + si_read_lock ++ * + di_write_lock2_child() ++ * + di_write_lock_child() ++ * + ii_write_lock_child() ++ * + di_write_lock_child2() ++ * + ii_write_lock_child2() ++ * + src_parent and parent ++ * + di_write_lock_parent() ++ * + ii_write_lock_parent() ++ * + di_write_lock_parent2() ++ * + ii_write_lock_parent2() ++ * + lower src_dir and dir by vfsub_lock_rename() ++ * + verify the every relationships between child and parent. if any ++ * of them failed, unlock all and return -EBUSY. ++ */ ++static void au_ren_unlock(struct au_ren_args *a) ++{ ++ struct super_block *sb; ++ ++ sb = a->dst_dentry->d_sb; ++ if (au_ftest_ren(a->flags, MNT_WRITE)) ++ mnt_drop_write(a->br->br_mnt); ++ vfsub_unlock_rename(a->src_h_parent, a->src_hdir, ++ a->dst_h_parent, a->dst_hdir); ++} ++ ++static int au_ren_lock(struct au_ren_args *a) ++{ ++ int err; ++ unsigned int udba; ++ ++ err = 0; ++ a->src_h_parent = au_h_dptr(a->src_parent, a->btgt); ++ a->src_hdir = au_hi(a->src_dir, a->btgt); ++ a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); ++ a->dst_hdir = au_hi(a->dst_dir, a->btgt); ++ a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, ++ a->dst_h_parent, a->dst_hdir); ++ udba = au_opt_udba(a->src_dentry->d_sb); ++ if (au_dbstart(a->src_dentry) == a->btgt) ++ err = au_h_verify(a->src_h_dentry, udba, ++ a->src_h_parent->d_inode, a->src_h_parent, ++ a->br); ++ if (!err && au_dbstart(a->dst_dentry) == a->btgt) ++ err = au_h_verify(a->dst_h_dentry, udba, ++ a->dst_h_parent->d_inode, a->dst_h_parent, ++ a->br); ++ if (!err) { ++ err = mnt_want_write(a->br->br_mnt); ++ if (unlikely(err)) ++ goto out_unlock; ++ au_fset_ren(a->flags, MNT_WRITE); ++ goto out; /* success */ ++ } ++ ++ err = au_busy_or_stale(); ++ ++ out_unlock: ++ au_ren_unlock(a); ++ out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_ren_refresh_dir(struct au_ren_args *a) ++{ ++ struct inode *dir; ++ ++ dir = a->dst_dir; ++ dir->i_version++; ++ if (au_ftest_ren(a->flags, ISDIR)) { ++ /* is this updating defined in POSIX? */ ++ au_cpup_attr_timesizes(a->src_inode); ++ au_cpup_attr_nlink(dir, /*force*/1); ++ if (a->dst_inode) { ++ clear_nlink(a->dst_inode); ++ au_cpup_attr_timesizes(a->dst_inode); ++ } ++ } ++ if (au_ibstart(dir) == a->btgt) ++ au_cpup_attr_timesizes(dir); ++ ++ if (au_ftest_ren(a->flags, ISSAMEDIR)) ++ return; ++ ++ dir = a->src_dir; ++ dir->i_version++; ++ if (au_ftest_ren(a->flags, ISDIR)) ++ au_cpup_attr_nlink(dir, /*force*/1); ++ if (au_ibstart(dir) == a->btgt) ++ au_cpup_attr_timesizes(dir); ++} ++ ++static void au_ren_refresh(struct au_ren_args *a) ++{ ++ aufs_bindex_t bend, bindex; ++ struct dentry *d, *h_d; ++ struct inode *i, *h_i; ++ struct super_block *sb; ++ ++ d = a->src_dentry; ++ au_set_dbwh(d, -1); ++ bend = au_dbend(d); ++ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { ++ h_d = au_h_dptr(d, bindex); ++ if (h_d) ++ au_set_h_dptr(d, bindex, NULL); ++ } ++ au_set_dbend(d, a->btgt); ++ ++ sb = d->d_sb; ++ i = a->src_inode; ++ if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) ++ return; /* success */ ++ ++ bend = au_ibend(i); ++ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { ++ h_i = au_h_iptr(i, bindex); ++ if (h_i) { ++ au_xino_write0(sb, bindex, h_i->i_ino, 0); ++ /* ignore this error */ ++ au_set_h_iptr(i, bindex, NULL, 0); ++ } ++ } ++ au_set_ibend(i, a->btgt); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* mainly for link(2) and rename(2) */ ++int au_wbr(struct dentry *dentry, aufs_bindex_t btgt) ++{ ++ aufs_bindex_t bdiropq, bwh; ++ struct dentry *parent; ++ struct au_branch *br; ++ ++ parent = dentry->d_parent; ++ IMustLock(parent->d_inode); /* dir is locked */ ++ ++ bdiropq = au_dbdiropq(parent); ++ bwh = au_dbwh(dentry); ++ br = au_sbr(dentry->d_sb, btgt); ++ if (au_br_rdonly(br) ++ || (0 <= bdiropq && bdiropq < btgt) ++ || (0 <= bwh && bwh < btgt)) ++ btgt = -1; ++ ++ AuDbg("btgt %d\n", btgt); ++ return btgt; ++} ++ ++/* sets src_bstart, dst_bstart and btgt */ ++static int au_ren_wbr(struct au_ren_args *a) ++{ ++ int err; ++ struct au_wr_dir_args wr_dir_args = { ++ /* .force_btgt = -1, */ ++ .flags = AuWrDir_ADD_ENTRY ++ }; ++ ++ a->src_bstart = au_dbstart(a->src_dentry); ++ a->dst_bstart = au_dbstart(a->dst_dentry); ++ if (au_ftest_ren(a->flags, ISDIR)) ++ au_fset_wrdir(wr_dir_args.flags, ISDIR); ++ wr_dir_args.force_btgt = a->src_bstart; ++ if (a->dst_inode && a->dst_bstart < a->src_bstart) ++ wr_dir_args.force_btgt = a->dst_bstart; ++ wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt); ++ err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args); ++ a->btgt = err; ++ ++ return err; ++} ++ ++static void au_ren_dt(struct au_ren_args *a) ++{ ++ a->h_path.dentry = a->src_h_parent; ++ au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path); ++ if (!au_ftest_ren(a->flags, ISSAMEDIR)) { ++ a->h_path.dentry = a->dst_h_parent; ++ au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path); ++ } ++ ++ au_fclr_ren(a->flags, DT_DSTDIR); ++ if (!au_ftest_ren(a->flags, ISDIR)) ++ return; ++ ++ a->h_path.dentry = a->src_h_dentry; ++ au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path); ++ if (a->dst_h_dentry->d_inode) { ++ au_fset_ren(a->flags, DT_DSTDIR); ++ a->h_path.dentry = a->dst_h_dentry; ++ au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path); ++ } ++} ++ ++static void au_ren_rev_dt(int err, struct au_ren_args *a) ++{ ++ struct dentry *h_d; ++ struct mutex *h_mtx; ++ ++ au_dtime_revert(a->src_dt + AuPARENT); ++ if (!au_ftest_ren(a->flags, ISSAMEDIR)) ++ au_dtime_revert(a->dst_dt + AuPARENT); ++ ++ if (au_ftest_ren(a->flags, ISDIR) && err != -EIO) { ++ h_d = a->src_dt[AuCHILD].dt_h_path.dentry; ++ h_mtx = &h_d->d_inode->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ au_dtime_revert(a->src_dt + AuCHILD); ++ mutex_unlock(h_mtx); ++ ++ if (au_ftest_ren(a->flags, DT_DSTDIR)) { ++ h_d = a->dst_dt[AuCHILD].dt_h_path.dentry; ++ h_mtx = &h_d->d_inode->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ au_dtime_revert(a->dst_dt + AuCHILD); ++ mutex_unlock(h_mtx); ++ } ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, ++ struct inode *_dst_dir, struct dentry *_dst_dentry) ++{ ++ int err; ++ /* reduce stack space */ ++ struct au_ren_args *a; ++ ++ IMustLock(_src_dir); ++ IMustLock(_dst_dir); ++ ++ err = -ENOMEM; ++ BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ a->src_dir = _src_dir; ++ a->src_dentry = _src_dentry; ++ a->src_inode = a->src_dentry->d_inode; ++ a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */ ++ a->dst_dir = _dst_dir; ++ a->dst_dentry = _dst_dentry; ++ a->dst_inode = a->dst_dentry->d_inode; ++ a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ ++ if (a->dst_inode) { ++ IMustLock(a->dst_inode); ++ au_igrab(a->dst_inode); ++ } ++ ++ err = -ENOTDIR; ++ if (S_ISDIR(a->src_inode->i_mode)) { ++ au_fset_ren(a->flags, ISDIR); ++ if (unlikely(a->dst_inode && !S_ISDIR(a->dst_inode->i_mode))) ++ goto out_free; ++ aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, ++ AuLock_DIR | AuLock_FLUSH); ++ } else ++ aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, ++ AuLock_FLUSH); ++ ++ au_fset_ren(a->flags, ISSAMEDIR); /* temporary */ ++ di_write_lock_parent(a->dst_parent); ++ ++ /* which branch we process */ ++ err = au_ren_wbr(a); ++ if (unlikely(err < 0)) ++ goto out_unlock; ++ a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); ++ a->h_path.mnt = a->br->br_mnt; ++ ++ /* are they available to be renamed */ ++ err = au_ren_may_dir(a); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ /* prepare the writable parent dir on the same branch */ ++ if (a->dst_bstart == a->btgt) { ++ au_fset_ren(a->flags, WHDST); ++ } else { ++ err = au_cpup_dirs(a->dst_dentry, a->btgt); ++ if (unlikely(err)) ++ goto out_children; ++ } ++ ++ if (a->src_dir != a->dst_dir) { ++ /* ++ * this temporary unlock is safe, ++ * because both dir->i_mutex are locked. ++ */ ++ di_write_unlock(a->dst_parent); ++ di_write_lock_parent(a->src_parent); ++ err = au_wr_dir_need_wh(a->src_dentry, ++ au_ftest_ren(a->flags, ISDIR), ++ &a->btgt); ++ di_write_unlock(a->src_parent); ++ di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1); ++ au_fclr_ren(a->flags, ISSAMEDIR); ++ } else ++ err = au_wr_dir_need_wh(a->src_dentry, ++ au_ftest_ren(a->flags, ISDIR), ++ &a->btgt); ++ if (unlikely(err < 0)) ++ goto out_children; ++ if (err) ++ au_fset_ren(a->flags, WHSRC); ++ ++ /* lock them all */ ++ err = au_ren_lock(a); ++ if (unlikely(err)) ++ goto out_children; ++ ++ if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) { ++ err = au_may_ren(a); ++ if (unlikely(err)) ++ goto out_hdir; ++ } ++ ++ /* store timestamps to be revertible */ ++ au_ren_dt(a); ++ ++ /* here we go */ ++ err = do_rename(a); ++ if (unlikely(err)) ++ goto out_dt; ++ ++ /* update dir attributes */ ++ au_ren_refresh_dir(a); ++ ++ /* dput/iput all lower dentries */ ++ au_ren_refresh(a); ++ ++ goto out_hdir; /* success */ ++ ++ out_dt: ++ au_ren_rev_dt(err, a); ++ out_hdir: ++ au_ren_unlock(a); ++ out_children: ++ au_nhash_wh_free(&a->whlist); ++ out_unlock: ++ if (unlikely(err && au_ftest_ren(a->flags, ISDIR))) { ++ au_update_dbstart(a->dst_dentry); ++ d_drop(a->dst_dentry); ++ } ++ if (!err) ++ d_move(a->src_dentry, a->dst_dentry); ++ if (au_ftest_ren(a->flags, ISSAMEDIR)) ++ di_write_unlock(a->dst_parent); ++ else ++ di_write_unlock2(a->src_parent, a->dst_parent); ++ aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry); ++ out_free: ++ iput(a->dst_inode); ++ if (a->thargs) ++ au_whtmp_rmdir_free(a->thargs); ++ kfree(a); ++ out: ++ return err; ++} +diff -Naur a/fs/aufs/Kconfig b/fs/aufs/Kconfig +--- a/fs/aufs/Kconfig 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/Kconfig 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,117 @@ ++config AUFS_FS ++ tristate "Aufs (Advanced multi layered unification filesystem) support" ++ depends on EXPERIMENTAL ++ help ++ Aufs is a stackable unification filesystem such as Unionfs, ++ which unifies several directories and provides a merged single ++ directory. ++ In the early days, aufs was entirely re-designed and ++ re-implemented Unionfs Version 1.x series. Introducing many ++ original ideas, approaches and improvements, it becomes totally ++ different from Unionfs while keeping the basic features. ++ ++if AUFS_FS ++choice ++ prompt "Maximum number of branches" ++ default AUFS_BRANCH_MAX_127 ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_127 ++ bool "127" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_511 ++ bool "511" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_1023 ++ bool "1023" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_32767 ++ bool "32767" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++endchoice ++ ++config AUFS_HINOTIFY ++ bool "Use inotify to detect actions on a branch" ++ depends on INOTIFY ++ help ++ If you want to modify files on branches directly, eg. bypassing aufs, ++ and want aufs to detect the changes of them fully, then enable this ++ option and use 'udba=inotify' mount option. ++ It will have a negative impact to the performance. ++ See detail in aufs.5. ++ ++config AUFS_EXPORT ++ bool "NFS-exportable aufs" ++ depends on (AUFS_FS = y && EXPORTFS = y) || (AUFS_FS = m && EXPORTFS) ++ help ++ If you want to export your mounted aufs via NFS, then enable this ++ option. There are several requirements for this configuration. ++ See detail in aufs.5. ++ ++config AUFS_SHWH ++ bool "Show whiteouts" ++ help ++ If you want to make the whiteouts in aufs visible, then enable ++ this option and specify 'shwh' mount option. Although it may ++ sounds like philosophy or something, but in technically it ++ simply shows the name of whiteout with keeping its behaviour. ++ ++config AUFS_BR_RAMFS ++ bool "Ramfs (initramfs/rootfs) as an aufs branch" ++ help ++ If you want to use ramfs as an aufs branch fs, then enable this ++ option. Generally tmpfs is recommended. ++ Aufs prohibited them to be a branch fs by default, because ++ initramfs becomes unusable after switch_root or something ++ generally. If you sets initramfs as an aufs branch and boot your ++ system by switch_root, you will meet a problem easily since the ++ files in initramfs may be inaccessible. ++ Unless you are going to use ramfs as an aufs branch fs without ++ switch_root or something, leave it N. ++ ++config AUFS_DEBUG ++ bool "Debug aufs" ++ help ++ Enable this to compile aufs internal debug code. ++ It will have a negative impact to the performance. ++ ++config AUFS_MAGIC_SYSRQ ++ bool ++ depends on AUFS_DEBUG && MAGIC_SYSRQ ++ default y ++ help ++ Automatic configuration for internal use. ++ When aufs supports Magic SysRq, enabled automatically. ++ ++config AUFS_BDEV_LOOP ++ bool ++ depends on BLK_DEV_LOOP ++ default y ++ help ++ Automatic configuration for internal use. ++ Convert =[ym] into =y. ++ ++config AUFS_INO_T_64 ++ bool ++ depends on AUFS_EXPORT ++ depends on 64BIT && !(ALPHA || S390) ++ default y ++ help ++ Automatic configuration for internal use. ++ /* typedef unsigned long/int __kernel_ino_t */ ++ /* alpha and s390x are int */ ++endif +diff -Naur a/fs/aufs/loop.c b/fs/aufs/loop.c +--- a/fs/aufs/loop.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/loop.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,55 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * support for loopback block device as a branch ++ */ ++ ++#include ++#include "aufs.h" ++ ++/* ++ * test if two lower dentries have overlapping branches. ++ */ ++int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_d1, ++ struct dentry *h_d2) ++{ ++ struct inode *h_inode; ++ struct loop_device *l; ++ ++ h_inode = h_d1->d_inode; ++ if (MAJOR(h_inode->i_sb->s_dev) != LOOP_MAJOR) ++ return 0; ++ ++ l = h_inode->i_sb->s_bdev->bd_disk->private_data; ++ h_d1 = l->lo_backing_file->f_dentry; ++ /* h_d1 can be local NFS. in this case aufs cannot detect the loop */ ++ if (unlikely(h_d1->d_sb == sb)) ++ return 1; ++ return !!au_test_subdir(h_d1, h_d2); ++} ++ ++/* true if a kernel thread named 'loop[0-9].*' accesses a file */ ++int au_test_loopback_kthread(void) ++{ ++ const char c = current->comm[4]; ++ ++ return current->mm == NULL ++ && '0' <= c && c <= '9' ++ && strncmp(current->comm, "loop", 4) == 0; ++} +diff -Naur a/fs/aufs/loop.h b/fs/aufs/loop.h +--- a/fs/aufs/loop.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/loop.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 ++ */ ++ ++/* ++ * support for loopback mount as a branch ++ */ ++ ++#ifndef __AUFS_LOOP_H__ ++#define __AUFS_LOOP_H__ ++ ++#ifdef __KERNEL__ ++ ++struct dentry; ++struct super_block; ++ ++#ifdef CONFIG_AUFS_BDEV_LOOP ++/* loop.c */ ++int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_d1, ++ struct dentry *h_d2); ++int au_test_loopback_kthread(void); ++#else ++static inline ++int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_d1, ++ struct dentry *h_d2) ++{ ++ return 0; ++} ++ ++static inline int au_test_loopback_kthread(void) ++{ ++ return 0; ++} ++#endif /* BLK_DEV_LOOP */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_LOOP_H__ */ +diff -Naur a/fs/aufs/magic.mk b/fs/aufs/magic.mk +--- a/fs/aufs/magic.mk 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/magic.mk 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,52 @@ ++ ++# defined in ${srctree}/fs/fuse/inode.c ++# tristate ++ifdef CONFIG_FUSE_FS ++ccflags-y += -DFUSE_SUPER_MAGIC=0x65735546 ++endif ++ ++# defined in ${srctree}/fs/ocfs2/ocfs2_fs.h ++# tristate ++ifdef CONFIG_OCFS2_FS ++ccflags-y += -DOCFS2_SUPER_MAGIC=0x7461636f ++endif ++ ++# defined in ${srctree}/fs/ocfs2/dlm/userdlm.h ++# tristate ++ifdef CONFIG_OCFS2_FS_O2CB ++ccflags-y += -DDLMFS_MAGIC=0x76a9f425 ++endif ++ ++# defined in ${srctree}/fs/ramfs/inode.c ++# always true ++ccflags-y += -DRAMFS_MAGIC=0x858458f6 ++ ++# defined in ${srctree}/fs/cifs/cifsfs.c ++# tristate ++ifdef CONFIG_CIFS_FS ++ccflags-y += -DCIFS_MAGIC_NUMBER=0xFF534D42 ++endif ++ ++# defined in ${srctree}/fs/xfs/xfs_sb.h ++# tristate ++ifdef CONFIG_XFS_FS ++ccflags-y += -DXFS_SB_MAGIC=0x58465342 ++endif ++ ++# defined in ${srctree}/fs/configfs/mount.c ++# tristate ++ifdef CONFIG_CONFIGFS_FS ++ccflags-y += -DCONFIGFS_MAGIC=0x62656570 ++endif ++ ++# defined in ${srctree}/fs/9p/v9fs.h ++# tristate ++ifdef CONFIG_9P_FS ++ccflags-y += -DV9FS_MAGIC=0x01021997 ++endif ++ ++# defined in ${srctree}/fs/ubifs/ubifs.h ++# tristate ++ifdef CONFIG_UBIFS_FS ++ccflags-y += -DUBIFS_SUPER_MAGIC=0x24051905 ++endif +diff -Naur a/fs/aufs/Makefile b/fs/aufs/Makefile +--- a/fs/aufs/Makefile 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/Makefile 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,22 @@ ++ ++include ${src}/magic.mk ++-include ${src}/priv_def.mk ++ ++obj-$(CONFIG_AUFS_FS) += aufs.o ++aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \ ++ wkq.o vfsub.o dcsub.o \ ++ cpup.o whout.o plink.o wbr_policy.o \ ++ dinfo.o dentry.o \ ++ finfo.o file.o f_op.o \ ++ dir.o vdir.o \ ++ iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \ ++ ioctl.o ++ ++# all are boolean ++aufs-$(CONFIG_SYSFS) += sysfs.o ++aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o ++aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o ++aufs-$(CONFIG_AUFS_HINOTIFY) += hinotify.o ++aufs-$(CONFIG_AUFS_EXPORT) += export.o ++aufs-$(CONFIG_AUFS_DEBUG) += debug.o ++aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o +diff -Naur a/fs/aufs/module.c b/fs/aufs/module.c +--- a/fs/aufs/module.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/module.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,173 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * module global variables and operations ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp) ++{ ++ if (new_sz <= nused) ++ return p; ++ ++ p = krealloc(p, new_sz, gfp); ++ if (p) ++ memset(p + nused, 0, new_sz - nused); ++ return p; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * aufs caches ++ */ ++struct kmem_cache *au_cachep[AuCache_Last]; ++static int __init au_cache_init(void) ++{ ++ au_cachep[AuCache_DINFO] = AuCache(au_dinfo); ++ if (au_cachep[AuCache_DINFO]) ++ au_cachep[AuCache_ICNTNR] = AuCache(au_icntnr); ++ if (au_cachep[AuCache_ICNTNR]) ++ au_cachep[AuCache_FINFO] = AuCache(au_finfo); ++ if (au_cachep[AuCache_FINFO]) ++ au_cachep[AuCache_VDIR] = AuCache(au_vdir); ++ if (au_cachep[AuCache_VDIR]) ++ au_cachep[AuCache_DEHSTR] = AuCache(au_vdir_dehstr); ++ if (au_cachep[AuCache_DEHSTR]) ++ return 0; ++ ++ return -ENOMEM; ++} ++ ++static void au_cache_fin(void) ++{ ++ int i; ++ for (i = 0; i < AuCache_Last; i++) ++ if (au_cachep[i]) { ++ kmem_cache_destroy(au_cachep[i]); ++ au_cachep[i] = NULL; ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_dir_roflags; ++ ++/* ++ * functions for module interface. ++ */ ++MODULE_LICENSE("GPL"); ++/* MODULE_LICENSE("GPL v2"); */ ++MODULE_AUTHOR("Junjiro R. Okajima "); ++MODULE_DESCRIPTION(AUFS_NAME ++ " -- Advanced multi layered unification filesystem"); ++MODULE_VERSION(AUFS_VERSION); ++ ++/* it should be 'byte', but param_set_byte() prints it by "%c" */ ++short aufs_nwkq = AUFS_NWKQ_DEF; ++MODULE_PARM_DESC(nwkq, "the number of workqueue thread, " AUFS_WKQ_NAME); ++module_param_named(nwkq, aufs_nwkq, short, S_IRUGO); ++ ++/* this module parameter has no meaning when SYSFS is disabled */ ++int sysaufs_brs = 1; ++MODULE_PARM_DESC(brs, "use /fs/aufs/si_*/brN"); ++module_param_named(brs, sysaufs_brs, int, S_IRUGO); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */ ++ ++int au_seq_path(struct seq_file *seq, struct path *path) ++{ ++ return seq_path(seq, path, au_esc_chars); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int __init aufs_init(void) ++{ ++ int err, i; ++ char *p; ++ ++ p = au_esc_chars; ++ for (i = 1; i <= ' '; i++) ++ *p++ = i; ++ *p++ = '\\'; ++ *p++ = '\x7f'; ++ *p = 0; ++ ++ au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE); ++ ++ sysaufs_brs_init(); ++ au_debug_init(); ++ ++ err = -EINVAL; ++ if (unlikely(aufs_nwkq <= 0)) ++ goto out; ++ ++ err = sysaufs_init(); ++ if (unlikely(err)) ++ goto out; ++ err = au_wkq_init(); ++ if (unlikely(err)) ++ goto out_sysaufs; ++ err = au_hinotify_init(); ++ if (unlikely(err)) ++ goto out_wkq; ++ err = au_sysrq_init(); ++ if (unlikely(err)) ++ goto out_hin; ++ err = au_cache_init(); ++ if (unlikely(err)) ++ goto out_sysrq; ++ err = register_filesystem(&aufs_fs_type); ++ if (unlikely(err)) ++ goto out_cache; ++ pr_info(AUFS_NAME " " AUFS_VERSION "\n"); ++ goto out; /* success */ ++ ++ out_cache: ++ au_cache_fin(); ++ out_sysrq: ++ au_sysrq_fin(); ++ out_hin: ++ au_hinotify_fin(); ++ out_wkq: ++ au_wkq_fin(); ++ out_sysaufs: ++ sysaufs_fin(); ++ out: ++ return err; ++} ++ ++static void __exit aufs_exit(void) ++{ ++ unregister_filesystem(&aufs_fs_type); ++ au_cache_fin(); ++ au_sysrq_fin(); ++ au_hinotify_fin(); ++ au_wkq_fin(); ++ sysaufs_fin(); ++} ++ ++module_init(aufs_init); ++module_exit(aufs_exit); +diff -Naur a/fs/aufs/module.h b/fs/aufs/module.h +--- a/fs/aufs/module.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/module.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,78 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * module initialization and module-global ++ */ ++ ++#ifndef __AUFS_MODULE_H__ ++#define __AUFS_MODULE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++ ++struct path; ++struct seq_file; ++ ++/* module parameters */ ++extern short aufs_nwkq; ++extern int sysaufs_brs; ++ ++/* ---------------------------------------------------------------------- */ ++ ++extern int au_dir_roflags; ++ ++void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp); ++int au_seq_path(struct seq_file *seq, struct path *path); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* kmem cache */ ++enum { ++ AuCache_DINFO, ++ AuCache_ICNTNR, ++ AuCache_FINFO, ++ AuCache_VDIR, ++ AuCache_DEHSTR, ++#ifdef CONFIG_AUFS_HINOTIFY ++ AuCache_HINOTIFY, ++#endif ++ AuCache_Last ++}; ++ ++#define AuCache(type) KMEM_CACHE(type, SLAB_RECLAIM_ACCOUNT) ++ ++extern struct kmem_cache *au_cachep[]; ++ ++#define AuCacheFuncs(name, index) \ ++static inline void *au_cache_alloc_##name(void) \ ++{ return kmem_cache_alloc(au_cachep[AuCache_##index], GFP_NOFS); } \ ++static inline void au_cache_free_##name(void *p) \ ++{ kmem_cache_free(au_cachep[AuCache_##index], p); } ++ ++AuCacheFuncs(dinfo, DINFO); ++AuCacheFuncs(icntnr, ICNTNR); ++AuCacheFuncs(finfo, FINFO); ++AuCacheFuncs(vdir, VDIR); ++AuCacheFuncs(dehstr, DEHSTR); ++ ++/* ---------------------------------------------------------------------- */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_MODULE_H__ */ +diff -Naur a/fs/aufs/opts.c b/fs/aufs/opts.c +--- a/fs/aufs/opts.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/opts.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,1533 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * mount options/flags ++ */ ++ ++#include ++#include ++#include /* a distribution requires */ ++#include ++#include "aufs.h" ++ ++/* ---------------------------------------------------------------------- */ ++ ++enum { ++ Opt_br, ++ Opt_add, Opt_del, Opt_mod, Opt_reorder, Opt_append, Opt_prepend, ++ Opt_idel, Opt_imod, Opt_ireorder, ++ Opt_dirwh, Opt_rdcache, Opt_rdblk, Opt_rdhash, Opt_rendir, ++ Opt_rdblk_def, Opt_rdhash_def, ++ Opt_xino, Opt_zxino, Opt_noxino, ++ Opt_trunc_xino, Opt_trunc_xino_v, Opt_notrunc_xino, ++ Opt_trunc_xino_path, Opt_itrunc_xino, ++ Opt_trunc_xib, Opt_notrunc_xib, ++ Opt_shwh, Opt_noshwh, ++ Opt_plink, Opt_noplink, Opt_list_plink, ++ Opt_udba, ++ /* Opt_lock, Opt_unlock, */ ++ Opt_cmd, Opt_cmd_args, ++ Opt_diropq_a, Opt_diropq_w, ++ Opt_warn_perm, Opt_nowarn_perm, ++ Opt_wbr_copyup, Opt_wbr_create, ++ Opt_refrof, Opt_norefrof, ++ Opt_verbose, Opt_noverbose, ++ Opt_sum, Opt_nosum, Opt_wsum, ++ Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err ++}; ++ ++static match_table_t options = { ++ {Opt_br, "br=%s"}, ++ {Opt_br, "br:%s"}, ++ ++ {Opt_add, "add=%d:%s"}, ++ {Opt_add, "add:%d:%s"}, ++ {Opt_add, "ins=%d:%s"}, ++ {Opt_add, "ins:%d:%s"}, ++ {Opt_append, "append=%s"}, ++ {Opt_append, "append:%s"}, ++ {Opt_prepend, "prepend=%s"}, ++ {Opt_prepend, "prepend:%s"}, ++ ++ {Opt_del, "del=%s"}, ++ {Opt_del, "del:%s"}, ++ /* {Opt_idel, "idel:%d"}, */ ++ {Opt_mod, "mod=%s"}, ++ {Opt_mod, "mod:%s"}, ++ /* {Opt_imod, "imod:%d:%s"}, */ ++ ++ {Opt_dirwh, "dirwh=%d"}, ++ ++ {Opt_xino, "xino=%s"}, ++ {Opt_noxino, "noxino"}, ++ {Opt_trunc_xino, "trunc_xino"}, ++ {Opt_trunc_xino_v, "trunc_xino_v=%d:%d"}, ++ {Opt_notrunc_xino, "notrunc_xino"}, ++ {Opt_trunc_xino_path, "trunc_xino=%s"}, ++ {Opt_itrunc_xino, "itrunc_xino=%d"}, ++ /* {Opt_zxino, "zxino=%s"}, */ ++ {Opt_trunc_xib, "trunc_xib"}, ++ {Opt_notrunc_xib, "notrunc_xib"}, ++ ++ {Opt_plink, "plink"}, ++ {Opt_noplink, "noplink"}, ++#ifdef CONFIG_AUFS_DEBUG ++ {Opt_list_plink, "list_plink"}, ++#endif ++ ++ {Opt_udba, "udba=%s"}, ++ ++ {Opt_diropq_a, "diropq=always"}, ++ {Opt_diropq_a, "diropq=a"}, ++ {Opt_diropq_w, "diropq=whiteouted"}, ++ {Opt_diropq_w, "diropq=w"}, ++ ++ {Opt_warn_perm, "warn_perm"}, ++ {Opt_nowarn_perm, "nowarn_perm"}, ++ ++ /* keep them temporary */ ++ {Opt_ignore_silent, "coo=%s"}, ++ {Opt_ignore_silent, "nodlgt"}, ++ {Opt_ignore_silent, "nodirperm1"}, ++ {Opt_ignore_silent, "clean_plink"}, ++ ++#ifdef CONFIG_AUFS_SHWH ++ {Opt_shwh, "shwh"}, ++#endif ++ {Opt_noshwh, "noshwh"}, ++ ++ {Opt_rendir, "rendir=%d"}, ++ ++ {Opt_refrof, "refrof"}, ++ {Opt_norefrof, "norefrof"}, ++ ++ {Opt_verbose, "verbose"}, ++ {Opt_verbose, "v"}, ++ {Opt_noverbose, "noverbose"}, ++ {Opt_noverbose, "quiet"}, ++ {Opt_noverbose, "q"}, ++ {Opt_noverbose, "silent"}, ++ ++ {Opt_sum, "sum"}, ++ {Opt_nosum, "nosum"}, ++ {Opt_wsum, "wsum"}, ++ ++ {Opt_rdcache, "rdcache=%d"}, ++ {Opt_rdblk, "rdblk=%d"}, ++ {Opt_rdblk_def, "rdblk=def"}, ++ {Opt_rdhash, "rdhash=%d"}, ++ {Opt_rdhash_def, "rdhash=def"}, ++ ++ {Opt_wbr_create, "create=%s"}, ++ {Opt_wbr_create, "create_policy=%s"}, ++ {Opt_wbr_copyup, "cpup=%s"}, ++ {Opt_wbr_copyup, "copyup=%s"}, ++ {Opt_wbr_copyup, "copyup_policy=%s"}, ++ ++ /* internal use for the scripts */ ++ {Opt_ignore_silent, "si=%s"}, ++ ++ {Opt_br, "dirs=%s"}, ++ {Opt_ignore, "debug=%d"}, ++ {Opt_ignore, "delete=whiteout"}, ++ {Opt_ignore, "delete=all"}, ++ {Opt_ignore, "imap=%s"}, ++ ++ {Opt_err, NULL} ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static const char *au_parser_pattern(int val, struct match_token *token) ++{ ++ while (token->pattern) { ++ if (token->token == val) ++ return token->pattern; ++ token++; ++ } ++ BUG(); ++ return "??"; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static match_table_t brperms = { ++ {AuBrPerm_RO, AUFS_BRPERM_RO}, ++ {AuBrPerm_RR, AUFS_BRPERM_RR}, ++ {AuBrPerm_RW, AUFS_BRPERM_RW}, ++ ++ {AuBrPerm_ROWH, AUFS_BRPERM_ROWH}, ++ {AuBrPerm_RRWH, AUFS_BRPERM_RRWH}, ++ {AuBrPerm_RWNoLinkWH, AUFS_BRPERM_RWNLWH}, ++ ++ {AuBrPerm_ROWH, "nfsro"}, ++ {AuBrPerm_RO, NULL} ++}; ++ ++static int br_perm_val(char *perm) ++{ ++ int val; ++ substring_t args[MAX_OPT_ARGS]; ++ ++ val = match_token(perm, brperms, args); ++ return val; ++} ++ ++const char *au_optstr_br_perm(int brperm) ++{ ++ return au_parser_pattern(brperm, (void *)brperms); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static match_table_t udbalevel = { ++ {AuOpt_UDBA_REVAL, "reval"}, ++ {AuOpt_UDBA_NONE, "none"}, ++#ifdef CONFIG_AUFS_HINOTIFY ++ {AuOpt_UDBA_HINOTIFY, "inotify"}, ++#endif ++ {-1, NULL} ++}; ++ ++static int udba_val(char *str) ++{ ++ substring_t args[MAX_OPT_ARGS]; ++ ++ return match_token(str, udbalevel, args); ++} ++ ++const char *au_optstr_udba(int udba) ++{ ++ return au_parser_pattern(udba, (void *)udbalevel); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static match_table_t au_wbr_create_policy = { ++ {AuWbrCreate_TDP, "tdp"}, ++ {AuWbrCreate_TDP, "top-down-parent"}, ++ {AuWbrCreate_RR, "rr"}, ++ {AuWbrCreate_RR, "round-robin"}, ++ {AuWbrCreate_MFS, "mfs"}, ++ {AuWbrCreate_MFS, "most-free-space"}, ++ {AuWbrCreate_MFSV, "mfs:%d"}, ++ {AuWbrCreate_MFSV, "most-free-space:%d"}, ++ ++ {AuWbrCreate_MFSRR, "mfsrr:%d"}, ++ {AuWbrCreate_MFSRRV, "mfsrr:%d:%d"}, ++ {AuWbrCreate_PMFS, "pmfs"}, ++ {AuWbrCreate_PMFSV, "pmfs:%d"}, ++ ++ {-1, NULL} ++}; ++ ++/* ++ * cf. linux/lib/parser.c and cmdline.c ++ * gave up calling memparse() since it uses simple_strtoull() instead of ++ * strict_...(). ++ */ ++static int au_match_ull(substring_t *s, unsigned long long *result) ++{ ++ int err; ++ unsigned int len; ++ char a[32]; ++ ++ err = -ERANGE; ++ len = s->to - s->from; ++ if (len + 1 <= sizeof(a)) { ++ memcpy(a, s->from, len); ++ a[len] = '\0'; ++ err = strict_strtoull(a, 0, result); ++ } ++ return err; ++} ++ ++static int au_wbr_mfs_wmark(substring_t *arg, char *str, ++ struct au_opt_wbr_create *create) ++{ ++ int err; ++ unsigned long long ull; ++ ++ err = 0; ++ if (!au_match_ull(arg, &ull)) ++ create->mfsrr_watermark = ull; ++ else { ++ AuErr("bad integer in %s\n", str); ++ err = -EINVAL; ++ } ++ ++ return err; ++} ++ ++static int au_wbr_mfs_sec(substring_t *arg, char *str, ++ struct au_opt_wbr_create *create) ++{ ++ int n, err; ++ ++ err = 0; ++ if (!match_int(arg, &n) && 0 <= n) ++ create->mfs_second = n; ++ else { ++ AuErr("bad integer in %s\n", str); ++ err = -EINVAL; ++ } ++ ++ return err; ++} ++ ++static int au_wbr_create_val(char *str, struct au_opt_wbr_create *create) ++{ ++ int err, e; ++ substring_t args[MAX_OPT_ARGS]; ++ ++ err = match_token(str, au_wbr_create_policy, args); ++ create->wbr_create = err; ++ switch (err) { ++ case AuWbrCreate_MFSRRV: ++ e = au_wbr_mfs_wmark(&args[0], str, create); ++ if (!e) ++ e = au_wbr_mfs_sec(&args[1], str, create); ++ if (unlikely(e)) ++ err = e; ++ break; ++ case AuWbrCreate_MFSRR: ++ e = au_wbr_mfs_wmark(&args[0], str, create); ++ if (unlikely(e)) { ++ err = e; ++ break; ++ } ++ /*FALLTHROUGH*/ ++ case AuWbrCreate_MFS: ++ case AuWbrCreate_PMFS: ++ create->mfs_second = AUFS_MFS_SECOND_DEF; ++ break; ++ case AuWbrCreate_MFSV: ++ case AuWbrCreate_PMFSV: ++ e = au_wbr_mfs_sec(&args[0], str, create); ++ if (unlikely(e)) ++ err = e; ++ break; ++ } ++ ++ return err; ++} ++ ++const char *au_optstr_wbr_create(int wbr_create) ++{ ++ return au_parser_pattern(wbr_create, (void *)au_wbr_create_policy); ++} ++ ++static match_table_t au_wbr_copyup_policy = { ++ {AuWbrCopyup_TDP, "tdp"}, ++ {AuWbrCopyup_TDP, "top-down-parent"}, ++ {AuWbrCopyup_BUP, "bup"}, ++ {AuWbrCopyup_BUP, "bottom-up-parent"}, ++ {AuWbrCopyup_BU, "bu"}, ++ {AuWbrCopyup_BU, "bottom-up"}, ++ {-1, NULL} ++}; ++ ++static int au_wbr_copyup_val(char *str) ++{ ++ substring_t args[MAX_OPT_ARGS]; ++ ++ return match_token(str, au_wbr_copyup_policy, args); ++} ++ ++const char *au_optstr_wbr_copyup(int wbr_copyup) ++{ ++ return au_parser_pattern(wbr_copyup, (void *)au_wbr_copyup_policy); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; ++ ++static void dump_opts(struct au_opts *opts) ++{ ++#ifdef CONFIG_AUFS_DEBUG ++ /* reduce stack space */ ++ union { ++ struct au_opt_add *add; ++ struct au_opt_del *del; ++ struct au_opt_mod *mod; ++ struct au_opt_xino *xino; ++ struct au_opt_xino_itrunc *xino_itrunc; ++ struct au_opt_wbr_create *create; ++ } u; ++ struct au_opt *opt; ++ ++ opt = opts->opt; ++ while (opt->type != Opt_tail) { ++ switch (opt->type) { ++ case Opt_add: ++ u.add = &opt->add; ++ AuDbg("add {b%d, %s, 0x%x, %p}\n", ++ u.add->bindex, u.add->pathname, u.add->perm, ++ u.add->path.dentry); ++ break; ++ case Opt_del: ++ case Opt_idel: ++ u.del = &opt->del; ++ AuDbg("del {%s, %p}\n", ++ u.del->pathname, u.del->h_path.dentry); ++ break; ++ case Opt_mod: ++ case Opt_imod: ++ u.mod = &opt->mod; ++ AuDbg("mod {%s, 0x%x, %p}\n", ++ u.mod->path, u.mod->perm, u.mod->h_root); ++ break; ++ case Opt_append: ++ u.add = &opt->add; ++ AuDbg("append {b%d, %s, 0x%x, %p}\n", ++ u.add->bindex, u.add->pathname, u.add->perm, ++ u.add->path.dentry); ++ break; ++ case Opt_prepend: ++ u.add = &opt->add; ++ AuDbg("prepend {b%d, %s, 0x%x, %p}\n", ++ u.add->bindex, u.add->pathname, u.add->perm, ++ u.add->path.dentry); ++ break; ++ case Opt_dirwh: ++ AuDbg("dirwh %d\n", opt->dirwh); ++ break; ++ case Opt_rdcache: ++ AuDbg("rdcache %d\n", opt->rdcache); ++ break; ++ case Opt_rdblk: ++ AuDbg("rdblk %u\n", opt->rdblk); ++ break; ++ case Opt_rdblk_def: ++ AuDbg("rdblk_def\n"); ++ break; ++ case Opt_rdhash: ++ AuDbg("rdhash %u\n", opt->rdhash); ++ break; ++ case Opt_rdhash_def: ++ AuDbg("rdhash_def\n"); ++ break; ++ case Opt_xino: ++ u.xino = &opt->xino; ++ AuDbg("xino {%s %.*s}\n", ++ u.xino->path, ++ AuDLNPair(u.xino->file->f_dentry)); ++ break; ++ case Opt_trunc_xino: ++ AuLabel(trunc_xino); ++ break; ++ case Opt_notrunc_xino: ++ AuLabel(notrunc_xino); ++ break; ++ case Opt_trunc_xino_path: ++ case Opt_itrunc_xino: ++ u.xino_itrunc = &opt->xino_itrunc; ++ AuDbg("trunc_xino %d\n", u.xino_itrunc->bindex); ++ break; ++ ++ case Opt_noxino: ++ AuLabel(noxino); ++ break; ++ case Opt_trunc_xib: ++ AuLabel(trunc_xib); ++ break; ++ case Opt_notrunc_xib: ++ AuLabel(notrunc_xib); ++ break; ++ case Opt_shwh: ++ AuLabel(shwh); ++ break; ++ case Opt_noshwh: ++ AuLabel(noshwh); ++ break; ++ case Opt_plink: ++ AuLabel(plink); ++ break; ++ case Opt_noplink: ++ AuLabel(noplink); ++ break; ++ case Opt_list_plink: ++ AuLabel(list_plink); ++ break; ++ case Opt_udba: ++ AuDbg("udba %d, %s\n", ++ opt->udba, au_optstr_udba(opt->udba)); ++ break; ++ case Opt_diropq_a: ++ AuLabel(diropq_a); ++ break; ++ case Opt_diropq_w: ++ AuLabel(diropq_w); ++ break; ++ case Opt_warn_perm: ++ AuLabel(warn_perm); ++ break; ++ case Opt_nowarn_perm: ++ AuLabel(nowarn_perm); ++ break; ++ case Opt_refrof: ++ AuLabel(refrof); ++ break; ++ case Opt_norefrof: ++ AuLabel(norefrof); ++ break; ++ case Opt_verbose: ++ AuLabel(verbose); ++ break; ++ case Opt_noverbose: ++ AuLabel(noverbose); ++ break; ++ case Opt_sum: ++ AuLabel(sum); ++ break; ++ case Opt_nosum: ++ AuLabel(nosum); ++ break; ++ case Opt_wsum: ++ AuLabel(wsum); ++ break; ++ case Opt_wbr_create: ++ u.create = &opt->wbr_create; ++ AuDbg("create %d, %s\n", u.create->wbr_create, ++ au_optstr_wbr_create(u.create->wbr_create)); ++ switch (u.create->wbr_create) { ++ case AuWbrCreate_MFSV: ++ case AuWbrCreate_PMFSV: ++ AuDbg("%d sec\n", u.create->mfs_second); ++ break; ++ case AuWbrCreate_MFSRR: ++ AuDbg("%llu watermark\n", ++ u.create->mfsrr_watermark); ++ break; ++ case AuWbrCreate_MFSRRV: ++ AuDbg("%llu watermark, %d sec\n", ++ u.create->mfsrr_watermark, ++ u.create->mfs_second); ++ break; ++ } ++ break; ++ case Opt_wbr_copyup: ++ AuDbg("copyup %d, %s\n", opt->wbr_copyup, ++ au_optstr_wbr_copyup(opt->wbr_copyup)); ++ break; ++ default: ++ BUG(); ++ } ++ opt++; ++ } ++#endif ++} ++ ++void au_opts_free(struct au_opts *opts) ++{ ++ struct au_opt *opt; ++ ++ opt = opts->opt; ++ while (opt->type != Opt_tail) { ++ switch (opt->type) { ++ case Opt_add: ++ case Opt_append: ++ case Opt_prepend: ++ path_put(&opt->add.path); ++ break; ++ case Opt_del: ++ case Opt_idel: ++ path_put(&opt->del.h_path); ++ break; ++ case Opt_mod: ++ case Opt_imod: ++ dput(opt->mod.h_root); ++ break; ++ case Opt_xino: ++ fput(opt->xino.file); ++ break; ++ } ++ opt++; ++ } ++} ++ ++static int opt_add(struct au_opt *opt, char *opt_str, unsigned long sb_flags, ++ aufs_bindex_t bindex) ++{ ++ int err; ++ struct au_opt_add *add = &opt->add; ++ char *p; ++ ++ add->bindex = bindex; ++ add->perm = AuBrPerm_Last; ++ add->pathname = opt_str; ++ p = strchr(opt_str, '='); ++ if (p) { ++ *p++ = 0; ++ if (*p) ++ add->perm = br_perm_val(p); ++ } ++ ++ err = vfsub_kern_path(add->pathname, lkup_dirflags, &add->path); ++ if (!err) { ++ if (!p) { ++ add->perm = AuBrPerm_RO; ++ if (au_test_fs_rr(add->path.dentry->d_sb)) ++ add->perm = AuBrPerm_RR; ++ else if (!bindex && !(sb_flags & MS_RDONLY)) ++ add->perm = AuBrPerm_RW; ++ } ++ opt->type = Opt_add; ++ goto out; ++ } ++ AuErr("lookup failed %s (%d)\n", add->pathname, err); ++ err = -EINVAL; ++ ++ out: ++ return err; ++} ++ ++static int au_opts_parse_del(struct au_opt_del *del, substring_t args[]) ++{ ++ int err; ++ ++ del->pathname = args[0].from; ++ AuDbg("del path %s\n", del->pathname); ++ ++ err = vfsub_kern_path(del->pathname, lkup_dirflags, &del->h_path); ++ if (unlikely(err)) ++ AuErr("lookup failed %s (%d)\n", del->pathname, err); ++ ++ return err; ++} ++ ++#if 0 /* reserved for future use */ ++static int au_opts_parse_idel(struct super_block *sb, aufs_bindex_t bindex, ++ struct au_opt_del *del, substring_t args[]) ++{ ++ int err; ++ struct dentry *root; ++ ++ err = -EINVAL; ++ root = sb->s_root; ++ aufs_read_lock(root, AuLock_FLUSH); ++ if (bindex < 0 || au_sbend(sb) < bindex) { ++ AuErr("out of bounds, %d\n", bindex); ++ goto out; ++ } ++ ++ err = 0; ++ del->h_path.dentry = dget(au_h_dptr(root, bindex)); ++ del->h_path.mnt = mntget(au_sbr_mnt(sb, bindex)); ++ ++ out: ++ aufs_read_unlock(root, !AuLock_IR); ++ return err; ++} ++#endif ++ ++static int au_opts_parse_mod(struct au_opt_mod *mod, substring_t args[]) ++{ ++ int err; ++ struct path path; ++ char *p; ++ ++ err = -EINVAL; ++ mod->path = args[0].from; ++ p = strchr(mod->path, '='); ++ if (unlikely(!p)) { ++ AuErr("no permssion %s\n", args[0].from); ++ goto out; ++ } ++ ++ *p++ = 0; ++ err = vfsub_kern_path(mod->path, lkup_dirflags, &path); ++ if (unlikely(err)) { ++ AuErr("lookup failed %s (%d)\n", mod->path, err); ++ goto out; ++ } ++ ++ mod->perm = br_perm_val(p); ++ AuDbg("mod path %s, perm 0x%x, %s\n", mod->path, mod->perm, p); ++ mod->h_root = dget(path.dentry); ++ path_put(&path); ++ ++ out: ++ return err; ++} ++ ++#if 0 /* reserved for future use */ ++static int au_opts_parse_imod(struct super_block *sb, aufs_bindex_t bindex, ++ struct au_opt_mod *mod, substring_t args[]) ++{ ++ int err; ++ struct dentry *root; ++ ++ err = -EINVAL; ++ root = sb->s_root; ++ aufs_read_lock(root, AuLock_FLUSH); ++ if (bindex < 0 || au_sbend(sb) < bindex) { ++ AuErr("out of bounds, %d\n", bindex); ++ goto out; ++ } ++ ++ err = 0; ++ mod->perm = br_perm_val(args[1].from); ++ AuDbg("mod path %s, perm 0x%x, %s\n", ++ mod->path, mod->perm, args[1].from); ++ mod->h_root = dget(au_h_dptr(root, bindex)); ++ ++ out: ++ aufs_read_unlock(root, !AuLock_IR); ++ return err; ++} ++#endif ++ ++static int au_opts_parse_xino(struct super_block *sb, struct au_opt_xino *xino, ++ substring_t args[]) ++{ ++ int err; ++ struct file *file; ++ ++ file = au_xino_create(sb, args[0].from, /*silent*/0); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ ++ err = -EINVAL; ++ if (unlikely(file->f_dentry->d_sb == sb)) { ++ fput(file); ++ AuErr("%s must be outside\n", args[0].from); ++ goto out; ++ } ++ ++ err = 0; ++ xino->file = file; ++ xino->path = args[0].from; ++ ++ out: ++ return err; ++} ++ ++static ++int au_opts_parse_xino_itrunc_path(struct super_block *sb, ++ struct au_opt_xino_itrunc *xino_itrunc, ++ substring_t args[]) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct path path; ++ struct dentry *root; ++ ++ err = vfsub_kern_path(args[0].from, lkup_dirflags, &path); ++ if (unlikely(err)) { ++ AuErr("lookup failed %s (%d)\n", args[0].from, err); ++ goto out; ++ } ++ ++ xino_itrunc->bindex = -1; ++ root = sb->s_root; ++ aufs_read_lock(root, AuLock_FLUSH); ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ if (au_h_dptr(root, bindex) == path.dentry) { ++ xino_itrunc->bindex = bindex; ++ break; ++ } ++ } ++ aufs_read_unlock(root, !AuLock_IR); ++ path_put(&path); ++ ++ if (unlikely(xino_itrunc->bindex < 0)) { ++ AuErr("no such branch %s\n", args[0].from); ++ err = -EINVAL; ++ } ++ ++ out: ++ return err; ++} ++ ++/* called without aufs lock */ ++int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts) ++{ ++ int err, n, token; ++ aufs_bindex_t bindex; ++ unsigned char skipped; ++ struct dentry *root; ++ struct au_opt *opt, *opt_tail; ++ char *opt_str; ++ /* reduce the stack space */ ++ union { ++ struct au_opt_xino_itrunc *xino_itrunc; ++ struct au_opt_wbr_create *create; ++ } u; ++ struct { ++ substring_t args[MAX_OPT_ARGS]; ++ } *a; ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ root = sb->s_root; ++ err = 0; ++ bindex = 0; ++ opt = opts->opt; ++ opt_tail = opt + opts->max_opt - 1; ++ opt->type = Opt_tail; ++ while (!err && (opt_str = strsep(&str, ",")) && *opt_str) { ++ err = -EINVAL; ++ skipped = 0; ++ token = match_token(opt_str, options, a->args); ++ switch (token) { ++ case Opt_br: ++ err = 0; ++ while (!err && (opt_str = strsep(&a->args[0].from, ":")) ++ && *opt_str) { ++ err = opt_add(opt, opt_str, opts->sb_flags, ++ bindex++); ++ if (unlikely(!err && ++opt > opt_tail)) { ++ err = -E2BIG; ++ break; ++ } ++ opt->type = Opt_tail; ++ skipped = 1; ++ } ++ break; ++ case Opt_add: ++ if (unlikely(match_int(&a->args[0], &n))) { ++ AuErr("bad integer in %s\n", opt_str); ++ break; ++ } ++ bindex = n; ++ err = opt_add(opt, a->args[1].from, opts->sb_flags, ++ bindex); ++ if (!err) ++ opt->type = token; ++ break; ++ case Opt_append: ++ err = opt_add(opt, a->args[0].from, opts->sb_flags, ++ /*dummy bindex*/1); ++ if (!err) ++ opt->type = token; ++ break; ++ case Opt_prepend: ++ err = opt_add(opt, a->args[0].from, opts->sb_flags, ++ /*bindex*/0); ++ if (!err) ++ opt->type = token; ++ break; ++ case Opt_del: ++ err = au_opts_parse_del(&opt->del, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++#if 0 /* reserved for future use */ ++ case Opt_idel: ++ del->pathname = "(indexed)"; ++ if (unlikely(match_int(&args[0], &n))) { ++ AuErr("bad integer in %s\n", opt_str); ++ break; ++ } ++ err = au_opts_parse_idel(sb, n, &opt->del, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++#endif ++ case Opt_mod: ++ err = au_opts_parse_mod(&opt->mod, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++#ifdef IMOD /* reserved for future use */ ++ case Opt_imod: ++ u.mod->path = "(indexed)"; ++ if (unlikely(match_int(&a->args[0], &n))) { ++ AuErr("bad integer in %s\n", opt_str); ++ break; ++ } ++ err = au_opts_parse_imod(sb, n, &opt->mod, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++#endif ++ case Opt_xino: ++ err = au_opts_parse_xino(sb, &opt->xino, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++ ++ case Opt_trunc_xino_path: ++ err = au_opts_parse_xino_itrunc_path ++ (sb, &opt->xino_itrunc, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++ ++ case Opt_itrunc_xino: ++ u.xino_itrunc = &opt->xino_itrunc; ++ if (unlikely(match_int(&a->args[0], &n))) { ++ AuErr("bad integer in %s\n", opt_str); ++ break; ++ } ++ u.xino_itrunc->bindex = n; ++ aufs_read_lock(root, AuLock_FLUSH); ++ if (n < 0 || au_sbend(sb) < n) { ++ AuErr("out of bounds, %d\n", n); ++ aufs_read_unlock(root, !AuLock_IR); ++ break; ++ } ++ aufs_read_unlock(root, !AuLock_IR); ++ err = 0; ++ opt->type = token; ++ break; ++ ++ case Opt_dirwh: ++ if (unlikely(match_int(&a->args[0], &opt->dirwh))) ++ break; ++ err = 0; ++ opt->type = token; ++ break; ++ ++ case Opt_rdcache: ++ if (unlikely(match_int(&a->args[0], &opt->rdcache))) ++ break; ++ err = 0; ++ opt->type = token; ++ break; ++ case Opt_rdblk: ++ if (unlikely(match_int(&a->args[0], &n) ++ || n <= 0 ++ || n > KMALLOC_MAX_SIZE)) { ++ AuErr("bad integer in %s\n", opt_str); ++ break; ++ } ++ if (unlikely(n < NAME_MAX)) { ++ AuErr("rdblk must be larger than %d\n", ++ NAME_MAX); ++ break; ++ } ++ opt->rdblk = n; ++ err = 0; ++ opt->type = token; ++ break; ++ case Opt_rdhash: ++ if (unlikely(match_int(&a->args[0], &n) ++ || n <= 0 ++ || n * sizeof(struct hlist_head) ++ > KMALLOC_MAX_SIZE)) { ++ AuErr("bad integer in %s\n", opt_str); ++ break; ++ } ++ opt->rdhash = n; ++ err = 0; ++ opt->type = token; ++ break; ++ ++ case Opt_trunc_xino: ++ case Opt_notrunc_xino: ++ case Opt_noxino: ++ case Opt_trunc_xib: ++ case Opt_notrunc_xib: ++ case Opt_shwh: ++ case Opt_noshwh: ++ case Opt_plink: ++ case Opt_noplink: ++ case Opt_list_plink: ++ case Opt_diropq_a: ++ case Opt_diropq_w: ++ case Opt_warn_perm: ++ case Opt_nowarn_perm: ++ case Opt_refrof: ++ case Opt_norefrof: ++ case Opt_verbose: ++ case Opt_noverbose: ++ case Opt_sum: ++ case Opt_nosum: ++ case Opt_wsum: ++ case Opt_rdblk_def: ++ case Opt_rdhash_def: ++ err = 0; ++ opt->type = token; ++ break; ++ ++ case Opt_udba: ++ opt->udba = udba_val(a->args[0].from); ++ if (opt->udba >= 0) { ++ err = 0; ++ opt->type = token; ++ } else ++ AuErr("wrong value, %s\n", opt_str); ++ break; ++ ++ case Opt_wbr_create: ++ u.create = &opt->wbr_create; ++ u.create->wbr_create ++ = au_wbr_create_val(a->args[0].from, u.create); ++ if (u.create->wbr_create >= 0) { ++ err = 0; ++ opt->type = token; ++ } else ++ AuErr("wrong value, %s\n", opt_str); ++ break; ++ case Opt_wbr_copyup: ++ opt->wbr_copyup = au_wbr_copyup_val(a->args[0].from); ++ if (opt->wbr_copyup >= 0) { ++ err = 0; ++ opt->type = token; ++ } else ++ AuErr("wrong value, %s\n", opt_str); ++ break; ++ ++ case Opt_ignore: ++ AuWarn("ignored %s\n", opt_str); ++ /*FALLTHROUGH*/ ++ case Opt_ignore_silent: ++ skipped = 1; ++ err = 0; ++ break; ++ case Opt_err: ++ AuErr("unknown option %s\n", opt_str); ++ break; ++ } ++ ++ if (!err && !skipped) { ++ if (unlikely(++opt > opt_tail)) { ++ err = -E2BIG; ++ opt--; ++ opt->type = Opt_tail; ++ break; ++ } ++ opt->type = Opt_tail; ++ } ++ } ++ ++ kfree(a); ++ dump_opts(opts); ++ if (unlikely(err)) ++ au_opts_free(opts); ++ ++ out: ++ return err; ++} ++ ++static int au_opt_wbr_create(struct super_block *sb, ++ struct au_opt_wbr_create *create) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ ++ err = 1; /* handled */ ++ sbinfo = au_sbi(sb); ++ if (sbinfo->si_wbr_create_ops->fin) { ++ err = sbinfo->si_wbr_create_ops->fin(sb); ++ if (!err) ++ err = 1; ++ } ++ ++ sbinfo->si_wbr_create = create->wbr_create; ++ sbinfo->si_wbr_create_ops = au_wbr_create_ops + create->wbr_create; ++ switch (create->wbr_create) { ++ case AuWbrCreate_MFSRRV: ++ case AuWbrCreate_MFSRR: ++ sbinfo->si_wbr_mfs.mfsrr_watermark = create->mfsrr_watermark; ++ /*FALLTHROUGH*/ ++ case AuWbrCreate_MFS: ++ case AuWbrCreate_MFSV: ++ case AuWbrCreate_PMFS: ++ case AuWbrCreate_PMFSV: ++ sbinfo->si_wbr_mfs.mfs_expire = create->mfs_second * HZ; ++ break; ++ } ++ ++ if (sbinfo->si_wbr_create_ops->init) ++ sbinfo->si_wbr_create_ops->init(sb); /* ignore */ ++ ++ return err; ++} ++ ++/* ++ * returns, ++ * plus: processed without an error ++ * zero: unprocessed ++ */ ++static int au_opt_simple(struct super_block *sb, struct au_opt *opt, ++ struct au_opts *opts) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ ++ err = 1; /* handled */ ++ sbinfo = au_sbi(sb); ++ switch (opt->type) { ++ case Opt_udba: ++ sbinfo->si_mntflags &= ~AuOptMask_UDBA; ++ sbinfo->si_mntflags |= opt->udba; ++ opts->given_udba |= opt->udba; ++ break; ++ ++ case Opt_plink: ++ au_opt_set(sbinfo->si_mntflags, PLINK); ++ break; ++ case Opt_noplink: ++ if (au_opt_test(sbinfo->si_mntflags, PLINK)) ++ au_plink_put(sb); ++ au_opt_clr(sbinfo->si_mntflags, PLINK); ++ break; ++ case Opt_list_plink: ++ if (au_opt_test(sbinfo->si_mntflags, PLINK)) ++ au_plink_list(sb); ++ break; ++ ++ case Opt_diropq_a: ++ au_opt_set(sbinfo->si_mntflags, ALWAYS_DIROPQ); ++ break; ++ case Opt_diropq_w: ++ au_opt_clr(sbinfo->si_mntflags, ALWAYS_DIROPQ); ++ break; ++ ++ case Opt_warn_perm: ++ au_opt_set(sbinfo->si_mntflags, WARN_PERM); ++ break; ++ case Opt_nowarn_perm: ++ au_opt_clr(sbinfo->si_mntflags, WARN_PERM); ++ break; ++ ++ case Opt_refrof: ++ au_opt_set(sbinfo->si_mntflags, REFROF); ++ break; ++ case Opt_norefrof: ++ au_opt_clr(sbinfo->si_mntflags, REFROF); ++ break; ++ ++ case Opt_verbose: ++ au_opt_set(sbinfo->si_mntflags, VERBOSE); ++ break; ++ case Opt_noverbose: ++ au_opt_clr(sbinfo->si_mntflags, VERBOSE); ++ break; ++ ++ case Opt_sum: ++ au_opt_set(sbinfo->si_mntflags, SUM); ++ break; ++ case Opt_wsum: ++ au_opt_clr(sbinfo->si_mntflags, SUM); ++ au_opt_set(sbinfo->si_mntflags, SUM_W); ++ case Opt_nosum: ++ au_opt_clr(sbinfo->si_mntflags, SUM); ++ au_opt_clr(sbinfo->si_mntflags, SUM_W); ++ break; ++ ++ case Opt_wbr_create: ++ err = au_opt_wbr_create(sb, &opt->wbr_create); ++ break; ++ case Opt_wbr_copyup: ++ sbinfo->si_wbr_copyup = opt->wbr_copyup; ++ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + opt->wbr_copyup; ++ break; ++ ++ case Opt_dirwh: ++ sbinfo->si_dirwh = opt->dirwh; ++ break; ++ ++ case Opt_rdcache: ++ sbinfo->si_rdcache = opt->rdcache * HZ; ++ break; ++ case Opt_rdblk: ++ sbinfo->si_rdblk = opt->rdblk; ++ break; ++ case Opt_rdblk_def: ++ sbinfo->si_rdblk = AUFS_RDBLK_DEF; ++ break; ++ case Opt_rdhash: ++ sbinfo->si_rdhash = opt->rdhash; ++ break; ++ case Opt_rdhash_def: ++ sbinfo->si_rdhash = AUFS_RDHASH_DEF; ++ break; ++ ++ case Opt_shwh: ++ au_opt_set(sbinfo->si_mntflags, SHWH); ++ break; ++ case Opt_noshwh: ++ au_opt_clr(sbinfo->si_mntflags, SHWH); ++ break; ++ ++ case Opt_trunc_xino: ++ au_opt_set(sbinfo->si_mntflags, TRUNC_XINO); ++ break; ++ case Opt_notrunc_xino: ++ au_opt_clr(sbinfo->si_mntflags, TRUNC_XINO); ++ break; ++ ++ case Opt_trunc_xino_path: ++ case Opt_itrunc_xino: ++ err = au_xino_trunc(sb, opt->xino_itrunc.bindex); ++ if (!err) ++ err = 1; ++ break; ++ ++ case Opt_trunc_xib: ++ au_fset_opts(opts->flags, TRUNC_XIB); ++ break; ++ case Opt_notrunc_xib: ++ au_fclr_opts(opts->flags, TRUNC_XIB); ++ break; ++ ++ default: ++ err = 0; ++ break; ++ } ++ ++ return err; ++} ++ ++/* ++ * returns tri-state. ++ * plus: processed without an error ++ * zero: unprocessed ++ * minus: error ++ */ ++static int au_opt_br(struct super_block *sb, struct au_opt *opt, ++ struct au_opts *opts) ++{ ++ int err, do_refresh; ++ ++ err = 0; ++ switch (opt->type) { ++ case Opt_append: ++ opt->add.bindex = au_sbend(sb) + 1; ++ if (opt->add.bindex < 0) ++ opt->add.bindex = 0; ++ goto add; ++ case Opt_prepend: ++ opt->add.bindex = 0; ++ add: ++ case Opt_add: ++ err = au_br_add(sb, &opt->add, ++ au_ftest_opts(opts->flags, REMOUNT)); ++ if (!err) { ++ err = 1; ++ au_fset_opts(opts->flags, REFRESH_DIR); ++ if (au_br_whable(opt->add.perm)) ++ au_fset_opts(opts->flags, REFRESH_NONDIR); ++ } ++ break; ++ ++ case Opt_del: ++ case Opt_idel: ++ err = au_br_del(sb, &opt->del, ++ au_ftest_opts(opts->flags, REMOUNT)); ++ if (!err) { ++ err = 1; ++ au_fset_opts(opts->flags, TRUNC_XIB); ++ au_fset_opts(opts->flags, REFRESH_DIR); ++ au_fset_opts(opts->flags, REFRESH_NONDIR); ++ } ++ break; ++ ++ case Opt_mod: ++ case Opt_imod: ++ err = au_br_mod(sb, &opt->mod, ++ au_ftest_opts(opts->flags, REMOUNT), ++ &do_refresh); ++ if (!err) { ++ err = 1; ++ if (do_refresh) { ++ au_fset_opts(opts->flags, REFRESH_DIR); ++ au_fset_opts(opts->flags, REFRESH_NONDIR); ++ } ++ } ++ break; ++ } ++ ++ return err; ++} ++ ++static int au_opt_xino(struct super_block *sb, struct au_opt *opt, ++ struct au_opt_xino **opt_xino, ++ struct au_opts *opts) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct dentry *root, *parent, *h_root; ++ ++ err = 0; ++ switch (opt->type) { ++ case Opt_xino: ++ err = au_xino_set(sb, &opt->xino, ++ !!au_ftest_opts(opts->flags, REMOUNT)); ++ if (unlikely(err)) ++ break; ++ ++ *opt_xino = &opt->xino; ++ au_xino_brid_set(sb, -1); ++ ++ /* safe d_parent access */ ++ parent = opt->xino.file->f_dentry->d_parent; ++ root = sb->s_root; ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ h_root = au_h_dptr(root, bindex); ++ if (h_root == parent) { ++ au_xino_brid_set(sb, au_sbr_id(sb, bindex)); ++ break; ++ } ++ } ++ break; ++ ++ case Opt_noxino: ++ au_xino_clr(sb); ++ au_xino_brid_set(sb, -1); ++ *opt_xino = (void *)-1; ++ break; ++ } ++ ++ return err; ++} ++ ++int au_opts_verify(struct super_block *sb, unsigned long sb_flags, ++ unsigned int pending) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ unsigned char do_plink, skip, do_free; ++ struct au_branch *br; ++ struct au_wbr *wbr; ++ struct dentry *root; ++ struct inode *dir, *h_dir; ++ struct au_sbinfo *sbinfo; ++ struct au_hinode *hdir; ++ ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!(sbinfo->si_mntflags & AuOptMask_UDBA)); ++ ++ if (!(sb_flags & MS_RDONLY)) { ++ if (unlikely(!au_br_writable(au_sbr_perm(sb, 0)))) ++ AuWarn("first branch should be rw\n"); ++ if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH))) ++ AuWarn("shwh should be used with ro\n"); ++ } ++ ++ if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HINOTIFY) ++ && !au_opt_test(sbinfo->si_mntflags, XINO)) ++ AuWarn("udba=inotify requires xino\n"); ++ ++ err = 0; ++ root = sb->s_root; ++ dir = sb->s_root->d_inode; ++ do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK); ++ bend = au_sbend(sb); ++ for (bindex = 0; !err && bindex <= bend; bindex++) { ++ skip = 0; ++ h_dir = au_h_iptr(dir, bindex); ++ br = au_sbr(sb, bindex); ++ do_free = 0; ++ ++ wbr = br->br_wbr; ++ if (wbr) ++ wbr_wh_read_lock(wbr); ++ ++ switch (br->br_perm) { ++ case AuBrPerm_RO: ++ case AuBrPerm_ROWH: ++ case AuBrPerm_RR: ++ case AuBrPerm_RRWH: ++ do_free = !!wbr; ++ skip = (!wbr ++ || (!wbr->wbr_whbase ++ && !wbr->wbr_plink ++ && !wbr->wbr_orph)); ++ break; ++ ++ case AuBrPerm_RWNoLinkWH: ++ /* skip = (!br->br_whbase && !br->br_orph); */ ++ skip = (!wbr || !wbr->wbr_whbase); ++ if (skip && wbr) { ++ if (do_plink) ++ skip = !!wbr->wbr_plink; ++ else ++ skip = !wbr->wbr_plink; ++ } ++ break; ++ ++ case AuBrPerm_RW: ++ /* skip = (br->br_whbase && br->br_ohph); */ ++ skip = (wbr && wbr->wbr_whbase); ++ if (skip) { ++ if (do_plink) ++ skip = !!wbr->wbr_plink; ++ else ++ skip = !wbr->wbr_plink; ++ } ++ break; ++ ++ default: ++ BUG(); ++ } ++ if (wbr) ++ wbr_wh_read_unlock(wbr); ++ ++ if (skip) ++ continue; ++ ++ hdir = au_hi(dir, bindex); ++ au_hin_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ if (wbr) ++ wbr_wh_write_lock(wbr); ++ err = au_wh_init(au_h_dptr(root, bindex), br, sb); ++ if (wbr) ++ wbr_wh_write_unlock(wbr); ++ au_hin_imtx_unlock(hdir); ++ ++ if (!err && do_free) { ++ kfree(wbr); ++ br->br_wbr = NULL; ++ } ++ } ++ ++ return err; ++} ++ ++int au_opts_mount(struct super_block *sb, struct au_opts *opts) ++{ ++ int err; ++ unsigned int tmp; ++ aufs_bindex_t bend; ++ struct au_opt *opt; ++ struct au_opt_xino *opt_xino, xino; ++ struct au_sbinfo *sbinfo; ++ ++ err = 0; ++ opt_xino = NULL; ++ opt = opts->opt; ++ while (err >= 0 && opt->type != Opt_tail) ++ err = au_opt_simple(sb, opt++, opts); ++ if (err > 0) ++ err = 0; ++ else if (unlikely(err < 0)) ++ goto out; ++ ++ /* disable xino and udba temporary */ ++ sbinfo = au_sbi(sb); ++ tmp = sbinfo->si_mntflags; ++ au_opt_clr(sbinfo->si_mntflags, XINO); ++ au_opt_set_udba(sbinfo->si_mntflags, UDBA_REVAL); ++ ++ opt = opts->opt; ++ while (err >= 0 && opt->type != Opt_tail) ++ err = au_opt_br(sb, opt++, opts); ++ if (err > 0) ++ err = 0; ++ else if (unlikely(err < 0)) ++ goto out; ++ ++ bend = au_sbend(sb); ++ if (unlikely(bend < 0)) { ++ err = -EINVAL; ++ AuErr("no branches\n"); ++ goto out; ++ } ++ ++ if (au_opt_test(tmp, XINO)) ++ au_opt_set(sbinfo->si_mntflags, XINO); ++ opt = opts->opt; ++ while (!err && opt->type != Opt_tail) ++ err = au_opt_xino(sb, opt++, &opt_xino, opts); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_opts_verify(sb, sb->s_flags, tmp); ++ if (unlikely(err)) ++ goto out; ++ ++ /* restore xino */ ++ if (au_opt_test(tmp, XINO) && !opt_xino) { ++ xino.file = au_xino_def(sb); ++ err = PTR_ERR(xino.file); ++ if (IS_ERR(xino.file)) ++ goto out; ++ ++ err = au_xino_set(sb, &xino, /*remount*/0); ++ fput(xino.file); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ /* restore udba */ ++ sbinfo->si_mntflags &= ~AuOptMask_UDBA; ++ sbinfo->si_mntflags |= (tmp & AuOptMask_UDBA); ++ if (au_opt_test(tmp, UDBA_HINOTIFY)) { ++ struct inode *dir = sb->s_root->d_inode; ++ au_reset_hinotify(dir, ++ au_hi_flags(dir, /*isdir*/1) & ~AuHi_XINO); ++ } ++ ++ out: ++ return err; ++} ++ ++int au_opts_remount(struct super_block *sb, struct au_opts *opts) ++{ ++ int err, rerr; ++ struct inode *dir; ++ struct au_opt_xino *opt_xino; ++ struct au_opt *opt; ++ struct au_sbinfo *sbinfo; ++ ++ dir = sb->s_root->d_inode; ++ sbinfo = au_sbi(sb); ++ err = 0; ++ opt_xino = NULL; ++ opt = opts->opt; ++ while (err >= 0 && opt->type != Opt_tail) { ++ err = au_opt_simple(sb, opt, opts); ++ if (!err) ++ err = au_opt_br(sb, opt, opts); ++ if (!err) ++ err = au_opt_xino(sb, opt, &opt_xino, opts); ++ opt++; ++ } ++ if (err > 0) ++ err = 0; ++ AuTraceErr(err); ++ /* go on even err */ ++ ++ rerr = au_opts_verify(sb, opts->sb_flags, /*pending*/0); ++ if (unlikely(rerr && !err)) ++ err = rerr; ++ ++ if (au_ftest_opts(opts->flags, TRUNC_XIB)) { ++ rerr = au_xib_trunc(sb); ++ if (unlikely(rerr && !err)) ++ err = rerr; ++ } ++ ++ /* will be handled by the caller */ ++ if (!au_ftest_opts(opts->flags, REFRESH_DIR) ++ && (opts->given_udba || au_opt_test(sbinfo->si_mntflags, XINO))) ++ au_fset_opts(opts->flags, REFRESH_DIR); ++ ++ AuDbg("status 0x%x\n", opts->flags); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++unsigned int au_opt_udba(struct super_block *sb) ++{ ++ return au_mntflags(sb) & AuOptMask_UDBA; ++} +diff -Naur a/fs/aufs/opts.h b/fs/aufs/opts.h +--- a/fs/aufs/opts.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/opts.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,196 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * mount options/flags ++ */ ++ ++#ifndef __AUFS_OPTS_H__ ++#define __AUFS_OPTS_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++ ++struct file; ++struct super_block; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* mount flags */ ++#define AuOpt_XINO 1 /* external inode number bitmap ++ and translation table */ ++#define AuOpt_TRUNC_XINO (1 << 1) /* truncate xino files */ ++#define AuOpt_UDBA_NONE (1 << 2) /* users direct branch access */ ++#define AuOpt_UDBA_REVAL (1 << 3) ++#define AuOpt_UDBA_HINOTIFY (1 << 4) ++#define AuOpt_SHWH (1 << 5) /* show whiteout */ ++#define AuOpt_PLINK (1 << 6) /* pseudo-link */ ++#define AuOpt_DIRPERM1 (1 << 7) /* unimplemented */ ++#define AuOpt_REFROF (1 << 8) /* unimplemented */ ++#define AuOpt_ALWAYS_DIROPQ (1 << 9) /* policy to creating diropq */ ++#define AuOpt_SUM (1 << 10) /* summation for statfs(2) */ ++#define AuOpt_SUM_W (1 << 11) /* unimplemented */ ++#define AuOpt_WARN_PERM (1 << 12) /* warn when add-branch */ ++#define AuOpt_VERBOSE (1 << 13) /* busy inode when del-branch */ ++ ++#ifndef CONFIG_AUFS_HINOTIFY ++#undef AuOpt_UDBA_HINOTIFY ++#define AuOpt_UDBA_HINOTIFY 0 ++#endif ++#ifndef CONFIG_AUFS_SHWH ++#undef AuOpt_SHWH ++#define AuOpt_SHWH 0 ++#endif ++ ++#define AuOpt_Def (AuOpt_XINO \ ++ | AuOpt_UDBA_REVAL \ ++ | AuOpt_PLINK \ ++ /* | AuOpt_DIRPERM1 */ \ ++ | AuOpt_WARN_PERM) ++#define AuOptMask_UDBA (AuOpt_UDBA_NONE \ ++ | AuOpt_UDBA_REVAL \ ++ | AuOpt_UDBA_HINOTIFY) ++ ++#define au_opt_test(flags, name) (flags & AuOpt_##name) ++#define au_opt_set(flags, name) do { \ ++ BUILD_BUG_ON(AuOpt_##name & AuOptMask_UDBA); \ ++ ((flags) |= AuOpt_##name); \ ++} while (0) ++#define au_opt_set_udba(flags, name) do { \ ++ (flags) &= ~AuOptMask_UDBA; \ ++ ((flags) |= AuOpt_##name); \ ++} while (0) ++#define au_opt_clr(flags, name) { ((flags) &= ~AuOpt_##name); } ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* policies to select one among multiple writable branches */ ++enum { ++ AuWbrCreate_TDP, /* top down parent */ ++ AuWbrCreate_RR, /* round robin */ ++ AuWbrCreate_MFS, /* most free space */ ++ AuWbrCreate_MFSV, /* mfs with seconds */ ++ AuWbrCreate_MFSRR, /* mfs then rr */ ++ AuWbrCreate_MFSRRV, /* mfs then rr with seconds */ ++ AuWbrCreate_PMFS, /* parent and mfs */ ++ AuWbrCreate_PMFSV, /* parent and mfs with seconds */ ++ ++ AuWbrCreate_Def = AuWbrCreate_TDP ++}; ++ ++enum { ++ AuWbrCopyup_TDP, /* top down parent */ ++ AuWbrCopyup_BUP, /* bottom up parent */ ++ AuWbrCopyup_BU, /* bottom up */ ++ ++ AuWbrCopyup_Def = AuWbrCopyup_TDP ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_opt_add { ++ aufs_bindex_t bindex; ++ char *pathname; ++ int perm; ++ struct path path; ++}; ++ ++struct au_opt_del { ++ char *pathname; ++ struct path h_path; ++}; ++ ++struct au_opt_mod { ++ char *path; ++ int perm; ++ struct dentry *h_root; ++}; ++ ++struct au_opt_xino { ++ char *path; ++ struct file *file; ++}; ++ ++struct au_opt_xino_itrunc { ++ aufs_bindex_t bindex; ++}; ++ ++struct au_opt_wbr_create { ++ int wbr_create; ++ int mfs_second; ++ unsigned long long mfsrr_watermark; ++}; ++ ++struct au_opt { ++ int type; ++ union { ++ struct au_opt_xino xino; ++ struct au_opt_xino_itrunc xino_itrunc; ++ struct au_opt_add add; ++ struct au_opt_del del; ++ struct au_opt_mod mod; ++ int dirwh; ++ int rdcache; ++ unsigned int rdblk; ++ unsigned int rdhash; ++ int udba; ++ struct au_opt_wbr_create wbr_create; ++ int wbr_copyup; ++ }; ++}; ++ ++/* opts flags */ ++#define AuOpts_REMOUNT 1 ++#define AuOpts_REFRESH_DIR (1 << 1) ++#define AuOpts_REFRESH_NONDIR (1 << 2) ++#define AuOpts_TRUNC_XIB (1 << 3) ++#define au_ftest_opts(flags, name) ((flags) & AuOpts_##name) ++#define au_fset_opts(flags, name) { (flags) |= AuOpts_##name; } ++#define au_fclr_opts(flags, name) { (flags) &= ~AuOpts_##name; } ++ ++struct au_opts { ++ struct au_opt *opt; ++ int max_opt; ++ ++ unsigned int given_udba; ++ unsigned int flags; ++ unsigned long sb_flags; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++const char *au_optstr_br_perm(int brperm); ++const char *au_optstr_udba(int udba); ++const char *au_optstr_wbr_copyup(int wbr_copyup); ++const char *au_optstr_wbr_create(int wbr_create); ++ ++void au_opts_free(struct au_opts *opts); ++int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts); ++int au_opts_verify(struct super_block *sb, unsigned long sb_flags, ++ unsigned int pending); ++int au_opts_mount(struct super_block *sb, struct au_opts *opts); ++int au_opts_remount(struct super_block *sb, struct au_opts *opts); ++ ++unsigned int au_opt_udba(struct super_block *sb); ++ ++/* ---------------------------------------------------------------------- */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_OPTS_H__ */ +diff -Naur a/fs/aufs/plink.c b/fs/aufs/plink.c +--- a/fs/aufs/plink.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/plink.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,344 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * pseudo-link ++ */ ++ ++#include "aufs.h" ++ ++/* ++ * during a user process maintains the pseudo-links, ++ * prohibit adding a new plink and branch manipulation. ++ */ ++void au_plink_block_maintain(struct super_block *sb) ++{ ++ struct au_sbinfo *sbi = au_sbi(sb); ++ /* gave up wake_up_bit() */ ++ wait_event(sbi->si_plink_wq, !au_ftest_si(sbi, MAINTAIN_PLINK)); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct pseudo_link { ++ struct list_head list; ++ struct inode *inode; ++}; ++ ++#ifdef CONFIG_AUFS_DEBUG ++void au_plink_list(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ struct list_head *plink_list; ++ struct pseudo_link *plink; ++ ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); ++ ++ plink_list = &sbinfo->si_plink.head; ++ spin_lock(&sbinfo->si_plink.spin); ++ list_for_each_entry(plink, plink_list, list) ++ AuDbg("%lu\n", plink->inode->i_ino); ++ spin_unlock(&sbinfo->si_plink.spin); ++} ++#endif ++ ++/* is the inode pseudo-linked? */ ++int au_plink_test(struct inode *inode) ++{ ++ int found; ++ struct au_sbinfo *sbinfo; ++ struct list_head *plink_list; ++ struct pseudo_link *plink; ++ ++ sbinfo = au_sbi(inode->i_sb); ++ AuDebugOn(!au_opt_test(au_mntflags(inode->i_sb), PLINK)); ++ ++ found = 0; ++ plink_list = &sbinfo->si_plink.head; ++ spin_lock(&sbinfo->si_plink.spin); ++ list_for_each_entry(plink, plink_list, list) ++ if (plink->inode == inode) { ++ found = 1; ++ break; ++ } ++ spin_unlock(&sbinfo->si_plink.spin); ++ return found; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * generate a name for plink. ++ * the file will be stored under AUFS_WH_PLINKDIR. ++ */ ++/* 20 is max digits length of ulong 64 */ ++#define PLINK_NAME_LEN ((20 + 1) * 2) ++ ++static int plink_name(char *name, int len, struct inode *inode, ++ aufs_bindex_t bindex) ++{ ++ int rlen; ++ struct inode *h_inode; ++ ++ h_inode = au_h_iptr(inode, bindex); ++ rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino); ++ return rlen; ++} ++ ++/* lookup the plink-ed @inode under the branch at @bindex */ ++struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex) ++{ ++ struct dentry *h_dentry, *h_parent; ++ struct au_branch *br; ++ struct inode *h_dir; ++ char a[PLINK_NAME_LEN]; ++ struct qstr tgtname = { ++ .name = a ++ }; ++ ++ br = au_sbr(inode->i_sb, bindex); ++ h_parent = br->br_wbr->wbr_plink; ++ h_dir = h_parent->d_inode; ++ tgtname.len = plink_name(a, sizeof(a), inode, bindex); ++ ++ /* always superio. */ ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2); ++ h_dentry = au_sio_lkup_one(&tgtname, h_parent, br); ++ mutex_unlock(&h_dir->i_mutex); ++ return h_dentry; ++} ++ ++/* create a pseudo-link */ ++static int do_whplink(struct qstr *tgt, struct dentry *h_parent, ++ struct dentry *h_dentry, struct au_branch *br) ++{ ++ int err; ++ struct path h_path = { ++ .mnt = br->br_mnt ++ }; ++ struct inode *h_dir; ++ ++ h_dir = h_parent->d_inode; ++ again: ++ h_path.dentry = au_lkup_one(tgt, h_parent, br, /*nd*/NULL); ++ err = PTR_ERR(h_path.dentry); ++ if (IS_ERR(h_path.dentry)) ++ goto out; ++ ++ err = 0; ++ /* wh.plink dir is not monitored */ ++ if (h_path.dentry->d_inode ++ && h_path.dentry->d_inode != h_dentry->d_inode) { ++ err = vfsub_unlink(h_dir, &h_path, /*force*/0); ++ dput(h_path.dentry); ++ h_path.dentry = NULL; ++ if (!err) ++ goto again; ++ } ++ if (!err && !h_path.dentry->d_inode) ++ err = vfsub_link(h_dentry, h_dir, &h_path); ++ dput(h_path.dentry); ++ ++ out: ++ return err; ++} ++ ++struct do_whplink_args { ++ int *errp; ++ struct qstr *tgt; ++ struct dentry *h_parent; ++ struct dentry *h_dentry; ++ struct au_branch *br; ++}; ++ ++static void call_do_whplink(void *args) ++{ ++ struct do_whplink_args *a = args; ++ *a->errp = do_whplink(a->tgt, a->h_parent, a->h_dentry, a->br); ++} ++ ++static int whplink(struct dentry *h_dentry, struct inode *inode, ++ aufs_bindex_t bindex, struct au_branch *br) ++{ ++ int err, wkq_err; ++ struct au_wbr *wbr; ++ struct dentry *h_parent; ++ struct inode *h_dir; ++ char a[PLINK_NAME_LEN]; ++ struct qstr tgtname = { ++ .name = a ++ }; ++ ++ wbr = au_sbr(inode->i_sb, bindex)->br_wbr; ++ h_parent = wbr->wbr_plink; ++ h_dir = h_parent->d_inode; ++ tgtname.len = plink_name(a, sizeof(a), inode, bindex); ++ ++ /* always superio. */ ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2); ++ if (!au_test_wkq(current)) { ++ struct do_whplink_args args = { ++ .errp = &err, ++ .tgt = &tgtname, ++ .h_parent = h_parent, ++ .h_dentry = h_dentry, ++ .br = br ++ }; ++ wkq_err = au_wkq_wait(call_do_whplink, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } else ++ err = do_whplink(&tgtname, h_parent, h_dentry, br); ++ mutex_unlock(&h_dir->i_mutex); ++ ++ return err; ++} ++ ++/* free a single plink */ ++static void do_put_plink(struct pseudo_link *plink, int do_del) ++{ ++ iput(plink->inode); ++ if (do_del) ++ list_del(&plink->list); ++ kfree(plink); ++} ++ ++/* ++ * create a new pseudo-link for @h_dentry on @bindex. ++ * the linked inode is held in aufs @inode. ++ */ ++void au_plink_append(struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_dentry) ++{ ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ struct list_head *plink_list; ++ struct pseudo_link *plink; ++ int found, err, cnt; ++ ++ sb = inode->i_sb; ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); ++ ++ err = 0; ++ cnt = 0; ++ found = 0; ++ plink_list = &sbinfo->si_plink.head; ++ spin_lock(&sbinfo->si_plink.spin); ++ list_for_each_entry(plink, plink_list, list) { ++ cnt++; ++ if (plink->inode == inode) { ++ found = 1; ++ break; ++ } ++ } ++ if (found) { ++ spin_unlock(&sbinfo->si_plink.spin); ++ return; ++ } ++ ++ plink = NULL; ++ if (!found) { ++ plink = kmalloc(sizeof(*plink), GFP_ATOMIC); ++ if (plink) { ++ plink->inode = au_igrab(inode); ++ list_add(&plink->list, plink_list); ++ cnt++; ++ } else ++ err = -ENOMEM; ++ } ++ spin_unlock(&sbinfo->si_plink.spin); ++ ++ if (!err) { ++ au_plink_block_maintain(sb); ++ err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex)); ++ } ++ ++ if (unlikely(cnt > AUFS_PLINK_WARN)) ++ AuWarn1("unexpectedly many pseudo links, %d\n", cnt); ++ if (unlikely(err)) { ++ AuWarn("err %d, damaged pseudo link.\n", err); ++ if (!found && plink) ++ do_put_plink(plink, /*do_del*/1); ++ } ++} ++ ++/* free all plinks */ ++void au_plink_put(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ struct list_head *plink_list; ++ struct pseudo_link *plink, *tmp; ++ ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); ++ ++ plink_list = &sbinfo->si_plink.head; ++ /* no spin_lock since sbinfo is write-locked */ ++ list_for_each_entry_safe(plink, tmp, plink_list, list) ++ do_put_plink(plink, 0); ++ INIT_LIST_HEAD(plink_list); ++} ++ ++/* free the plinks on a branch specified by @br_id */ ++void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id) ++{ ++ struct au_sbinfo *sbinfo; ++ struct list_head *plink_list; ++ struct pseudo_link *plink, *tmp; ++ struct inode *inode; ++ aufs_bindex_t bstart, bend, bindex; ++ unsigned char do_put; ++ ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); ++ ++ plink_list = &sbinfo->si_plink.head; ++ /* no spin_lock since sbinfo is write-locked */ ++ list_for_each_entry_safe(plink, tmp, plink_list, list) { ++ do_put = 0; ++ inode = au_igrab(plink->inode); ++ ii_write_lock_child(inode); ++ bstart = au_ibstart(inode); ++ bend = au_ibend(inode); ++ if (bstart >= 0) { ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ if (!au_h_iptr(inode, bindex) ++ || au_ii_br_id(inode, bindex) != br_id) ++ continue; ++ au_set_h_iptr(inode, bindex, NULL, 0); ++ do_put = 1; ++ break; ++ } ++ } else ++ do_put_plink(plink, 1); ++ ++ if (do_put) { ++ for (bindex = bstart; bindex <= bend; bindex++) ++ if (au_h_iptr(inode, bindex)) { ++ do_put = 0; ++ break; ++ } ++ if (do_put) ++ do_put_plink(plink, 1); ++ } ++ ii_write_unlock(inode); ++ iput(inode); ++ } ++} +diff -Naur a/fs/aufs/rwsem.h b/fs/aufs/rwsem.h +--- a/fs/aufs/rwsem.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/rwsem.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,61 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * simple read-write semaphore wrappers ++ */ ++ ++#ifndef __AUFS_RWSEM_H__ ++#define __AUFS_RWSEM_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++ ++#define au_rwsem_destroy(rw) AuDebugOn(rwsem_is_locked(rw)) ++#define AuRwMustNoWaiters(rw) AuDebugOn(!list_empty(&(rw)->wait_list)) ++ ++#define AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ ++static inline void prefix##_read_lock(param) \ ++{ down_read(rwsem); } \ ++static inline void prefix##_write_lock(param) \ ++{ down_write(rwsem); } \ ++static inline int prefix##_read_trylock(param) \ ++{ return down_read_trylock(rwsem); } \ ++static inline int prefix##_write_trylock(param) \ ++{ return down_write_trylock(rwsem); } ++/* why is not _nested version defined */ ++/* static inline void prefix##_read_trylock_nested(param, lsc) ++{ down_write_trylock_nested(rwsem, lsc)); } ++static inline void prefix##_write_trylock_nestd(param, lsc) ++{ down_write_trylock_nested(rwsem, lsc); } */ ++ ++#define AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) \ ++static inline void prefix##_read_unlock(param) \ ++{ up_read(rwsem); } \ ++static inline void prefix##_write_unlock(param) \ ++{ up_write(rwsem); } \ ++static inline void prefix##_downgrade_lock(param) \ ++{ downgrade_write(rwsem); } ++ ++#define AuSimpleRwsemFuncs(prefix, param, rwsem) \ ++ AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ ++ AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_RWSEM_H__ */ +diff -Naur a/fs/aufs/sbinfo.c b/fs/aufs/sbinfo.c +--- a/fs/aufs/sbinfo.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/sbinfo.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,203 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * superblock private data ++ */ ++ ++#include "aufs.h" ++ ++/* ++ * they are necessary regardless sysfs is disabled. ++ */ ++void au_si_free(struct kobject *kobj) ++{ ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ ++ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); ++ AuDebugOn(!list_empty(&sbinfo->si_plink.head)); ++ ++ sb = sbinfo->si_sb; ++ si_write_lock(sb); ++ au_xino_clr(sb); ++ au_br_free(sbinfo); ++ kfree(sbinfo->si_branch); ++ mutex_destroy(&sbinfo->si_xib_mtx); ++ si_write_unlock(sb); ++ au_rwsem_destroy(&sbinfo->si_rwsem); ++ ++ kfree(sbinfo); ++} ++ ++int au_si_alloc(struct super_block *sb) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ ++ err = -ENOMEM; ++ sbinfo = kmalloc(sizeof(*sbinfo), GFP_NOFS); ++ if (unlikely(!sbinfo)) ++ goto out; ++ ++ /* will be reallocated separately */ ++ sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_NOFS); ++ if (unlikely(!sbinfo->si_branch)) ++ goto out_sbinfo; ++ ++ memset(&sbinfo->si_kobj, 0, sizeof(sbinfo->si_kobj)); ++ err = sysaufs_si_init(sbinfo); ++ if (unlikely(err)) ++ goto out_br; ++ ++ au_nwt_init(&sbinfo->si_nowait); ++ init_rwsem(&sbinfo->si_rwsem); ++ down_write(&sbinfo->si_rwsem); ++ sbinfo->si_generation = 0; ++ sbinfo->au_si_status = 0; ++ sbinfo->si_bend = -1; ++ sbinfo->si_last_br_id = 0; ++ ++ sbinfo->si_wbr_copyup = AuWbrCopyup_Def; ++ sbinfo->si_wbr_create = AuWbrCreate_Def; ++ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + AuWbrCopyup_Def; ++ sbinfo->si_wbr_create_ops = au_wbr_create_ops + AuWbrCreate_Def; ++ ++ sbinfo->si_mntflags = AuOpt_Def; ++ ++ sbinfo->si_xread = NULL; ++ sbinfo->si_xwrite = NULL; ++ sbinfo->si_xib = NULL; ++ mutex_init(&sbinfo->si_xib_mtx); ++ sbinfo->si_xib_buf = NULL; ++ sbinfo->si_xino_brid = -1; ++ /* leave si_xib_last_pindex and si_xib_next_bit */ ++ ++ sbinfo->si_rdcache = AUFS_RDCACHE_DEF * HZ; ++ sbinfo->si_rdblk = AUFS_RDBLK_DEF; ++ sbinfo->si_rdhash = AUFS_RDHASH_DEF; ++ sbinfo->si_dirwh = AUFS_DIRWH_DEF; ++ ++ au_spl_init(&sbinfo->si_plink); ++ init_waitqueue_head(&sbinfo->si_plink_wq); ++ ++ /* leave other members for sysaufs and si_mnt. */ ++ sbinfo->si_sb = sb; ++ sb->s_fs_info = sbinfo; ++ au_debug_sbinfo_init(sbinfo); ++ return 0; /* success */ ++ ++ out_br: ++ kfree(sbinfo->si_branch); ++ out_sbinfo: ++ kfree(sbinfo); ++ out: ++ return err; ++} ++ ++int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr) ++{ ++ int err, sz; ++ struct au_branch **brp; ++ ++ err = -ENOMEM; ++ sz = sizeof(*brp) * (sbinfo->si_bend + 1); ++ if (unlikely(!sz)) ++ sz = sizeof(*brp); ++ brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS); ++ if (brp) { ++ sbinfo->si_branch = brp; ++ err = 0; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++unsigned int au_sigen_inc(struct super_block *sb) ++{ ++ unsigned int gen; ++ ++ gen = ++au_sbi(sb)->si_generation; ++ au_update_digen(sb->s_root); ++ au_update_iigen(sb->s_root->d_inode); ++ sb->s_root->d_inode->i_version++; ++ return gen; ++} ++ ++aufs_bindex_t au_new_br_id(struct super_block *sb) ++{ ++ aufs_bindex_t br_id; ++ int i; ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = au_sbi(sb); ++ for (i = 0; i <= AUFS_BRANCH_MAX; i++) { ++ br_id = ++sbinfo->si_last_br_id; ++ if (br_id && au_br_index(sb, br_id) < 0) ++ return br_id; ++ } ++ ++ return -1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dentry and super_block lock. call at entry point */ ++void aufs_read_lock(struct dentry *dentry, int flags) ++{ ++ si_read_lock(dentry->d_sb, flags); ++ if (au_ftest_lock(flags, DW)) ++ di_write_lock_child(dentry); ++ else ++ di_read_lock_child(dentry, flags); ++} ++ ++void aufs_read_unlock(struct dentry *dentry, int flags) ++{ ++ if (au_ftest_lock(flags, DW)) ++ di_write_unlock(dentry); ++ else ++ di_read_unlock(dentry, flags); ++ si_read_unlock(dentry->d_sb); ++} ++ ++void aufs_write_lock(struct dentry *dentry) ++{ ++ si_write_lock(dentry->d_sb); ++ di_write_lock_child(dentry); ++} ++ ++void aufs_write_unlock(struct dentry *dentry) ++{ ++ di_write_unlock(dentry); ++ si_write_unlock(dentry->d_sb); ++} ++ ++void aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags) ++{ ++ si_read_lock(d1->d_sb, flags); ++ di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR)); ++} ++ ++void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2) ++{ ++ di_write_unlock2(d1, d2); ++ si_read_unlock(d1->d_sb); ++} +diff -Naur a/fs/aufs/spl.h b/fs/aufs/spl.h +--- a/fs/aufs/spl.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/spl.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,57 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * simple list protected by a spinlock ++ */ ++ ++#ifndef __AUFS_SPL_H__ ++#define __AUFS_SPL_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++ ++struct au_splhead { ++ spinlock_t spin; ++ struct list_head head; ++}; ++ ++static inline void au_spl_init(struct au_splhead *spl) ++{ ++ spin_lock_init(&spl->spin); ++ INIT_LIST_HEAD(&spl->head); ++} ++ ++static inline void au_spl_add(struct list_head *list, struct au_splhead *spl) ++{ ++ spin_lock(&spl->spin); ++ list_add(list, &spl->head); ++ spin_unlock(&spl->spin); ++} ++ ++static inline void au_spl_del(struct list_head *list, struct au_splhead *spl) ++{ ++ spin_lock(&spl->spin); ++ list_del(list); ++ spin_unlock(&spl->spin); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_SPL_H__ */ +diff -Naur a/fs/aufs/super.c b/fs/aufs/super.c +--- a/fs/aufs/super.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/super.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,870 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * mount and super_block operations ++ */ ++ ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++/* ++ * super_operations ++ */ ++static struct inode *aufs_alloc_inode(struct super_block *sb __maybe_unused) ++{ ++ struct au_icntnr *c; ++ ++ c = au_cache_alloc_icntnr(); ++ if (c) { ++ inode_init_once(&c->vfs_inode); ++ c->vfs_inode.i_version = 1; /* sigen(sb); */ ++ c->iinfo.ii_hinode = NULL; ++ return &c->vfs_inode; ++ } ++ return NULL; ++} ++ ++static void aufs_destroy_inode(struct inode *inode) ++{ ++ au_iinfo_fin(inode); ++ au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode)); ++} ++ ++struct inode *au_iget_locked(struct super_block *sb, ino_t ino) ++{ ++ struct inode *inode; ++ int err; ++ ++ inode = iget_locked(sb, ino); ++ if (unlikely(!inode)) { ++ inode = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ if (!(inode->i_state & I_NEW)) ++ goto out; ++ ++ err = au_xigen_new(inode); ++ if (!err) ++ err = au_iinfo_init(inode); ++ if (!err) ++ inode->i_version++; ++ else { ++ iget_failed(inode); ++ inode = ERR_PTR(err); ++ } ++ ++ out: ++ /* never return NULL */ ++ AuDebugOn(!inode); ++ AuTraceErrPtr(inode); ++ return inode; ++} ++ ++/* lock free root dinfo */ ++static int au_show_brs(struct seq_file *seq, struct super_block *sb) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ struct path path; ++ struct au_hdentry *hd; ++ struct au_branch *br; ++ ++ err = 0; ++ bend = au_sbend(sb); ++ hd = au_di(sb->s_root)->di_hdentry; ++ for (bindex = 0; !err && bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ path.mnt = br->br_mnt; ++ path.dentry = hd[bindex].hd_dentry; ++ err = au_seq_path(seq, &path); ++ if (err > 0) ++ err = seq_printf(seq, "=%s", ++ au_optstr_br_perm(br->br_perm)); ++ if (!err && bindex != bend) ++ err = seq_putc(seq, ':'); ++ } ++ ++ return err; ++} ++ ++static void au_show_wbr_create(struct seq_file *m, int v, ++ struct au_sbinfo *sbinfo) ++{ ++ const char *pat; ++ ++ seq_printf(m, ",create="); ++ pat = au_optstr_wbr_create(v); ++ switch (v) { ++ case AuWbrCreate_TDP: ++ case AuWbrCreate_RR: ++ case AuWbrCreate_MFS: ++ case AuWbrCreate_PMFS: ++ seq_printf(m, pat); ++ break; ++ case AuWbrCreate_MFSV: ++ seq_printf(m, /*pat*/"mfs:%lu", ++ sbinfo->si_wbr_mfs.mfs_expire / HZ); ++ break; ++ case AuWbrCreate_PMFSV: ++ seq_printf(m, /*pat*/"pmfs:%lu", ++ sbinfo->si_wbr_mfs.mfs_expire / HZ); ++ break; ++ case AuWbrCreate_MFSRR: ++ seq_printf(m, /*pat*/"mfsrr:%llu", ++ sbinfo->si_wbr_mfs.mfsrr_watermark); ++ break; ++ case AuWbrCreate_MFSRRV: ++ seq_printf(m, /*pat*/"mfsrr:%llu:%lu", ++ sbinfo->si_wbr_mfs.mfsrr_watermark, ++ sbinfo->si_wbr_mfs.mfs_expire / HZ); ++ break; ++ } ++} ++ ++static int au_show_xino(struct seq_file *seq, struct vfsmount *mnt) ++{ ++#ifdef CONFIG_SYSFS ++ return 0; ++#else ++ int err; ++ const int len = sizeof(AUFS_XINO_FNAME) - 1; ++ aufs_bindex_t bindex, brid; ++ struct super_block *sb; ++ struct qstr *name; ++ struct file *f; ++ struct dentry *d, *h_root; ++ ++ err = 0; ++ sb = mnt->mnt_sb; ++ f = au_sbi(sb)->si_xib; ++ if (!f) ++ goto out; ++ ++ /* stop printing the default xino path on the first writable branch */ ++ h_root = NULL; ++ brid = au_xino_brid(sb); ++ if (brid >= 0) { ++ bindex = au_br_index(sb, brid); ++ h_root = au_di(sb->s_root)->di_hdentry[0 + bindex].hd_dentry; ++ } ++ d = f->f_dentry; ++ name = &d->d_name; ++ /* safe ->d_parent because the file is unlinked */ ++ if (d->d_parent == h_root ++ && name->len == len ++ && !memcmp(name->name, AUFS_XINO_FNAME, len)) ++ goto out; ++ ++ seq_puts(seq, ",xino="); ++ err = au_xino_path(seq, f); ++ ++ out: ++ return err; ++#endif ++} ++ ++/* seq_file will re-call me in case of too long string */ ++static int aufs_show_options(struct seq_file *m, struct vfsmount *mnt) ++{ ++ int err, n; ++ unsigned int mnt_flags, v; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++#define AuBool(name, str) do { \ ++ v = au_opt_test(mnt_flags, name); \ ++ if (v != au_opt_test(AuOpt_Def, name)) \ ++ seq_printf(m, ",%s" #str, v ? "" : "no"); \ ++} while (0) ++ ++#define AuStr(name, str) do { \ ++ v = mnt_flags & AuOptMask_##name; \ ++ if (v != (AuOpt_Def & AuOptMask_##name)) \ ++ seq_printf(m, "," #str "=%s", au_optstr_##str(v)); \ ++} while (0) ++ ++#define AuUInt(name, str, val) do { \ ++ if (val != AUFS_##name##_DEF) \ ++ seq_printf(m, "," #str "=%u", val); \ ++} while (0) ++ ++ /* lock free root dinfo */ ++ sb = mnt->mnt_sb; ++ si_noflush_read_lock(sb); ++ sbinfo = au_sbi(sb); ++ seq_printf(m, ",si=%lx", sysaufs_si_id(sbinfo)); ++ ++ mnt_flags = au_mntflags(sb); ++ if (au_opt_test(mnt_flags, XINO)) { ++ err = au_show_xino(m, mnt); ++ if (unlikely(err)) ++ goto out; ++ } else ++ seq_puts(m, ",noxino"); ++ ++ AuBool(TRUNC_XINO, trunc_xino); ++ AuStr(UDBA, udba); ++ AuBool(SHWH, shwh); ++ AuBool(PLINK, plink); ++ /* AuBool(DIRPERM1, dirperm1); */ ++ /* AuBool(REFROF, refrof); */ ++ ++ v = sbinfo->si_wbr_create; ++ if (v != AuWbrCreate_Def) ++ au_show_wbr_create(m, v, sbinfo); ++ ++ v = sbinfo->si_wbr_copyup; ++ if (v != AuWbrCopyup_Def) ++ seq_printf(m, ",cpup=%s", au_optstr_wbr_copyup(v)); ++ ++ v = au_opt_test(mnt_flags, ALWAYS_DIROPQ); ++ if (v != au_opt_test(AuOpt_Def, ALWAYS_DIROPQ)) ++ seq_printf(m, ",diropq=%c", v ? 'a' : 'w'); ++ ++ AuUInt(DIRWH, dirwh, sbinfo->si_dirwh); ++ ++ n = sbinfo->si_rdcache / HZ; ++ AuUInt(RDCACHE, rdcache, n); ++ ++ AuUInt(RDBLK, rdblk, sbinfo->si_rdblk); ++ AuUInt(RDHASH, rdhash, sbinfo->si_rdhash); ++ ++ AuBool(SUM, sum); ++ /* AuBool(SUM_W, wsum); */ ++ AuBool(WARN_PERM, warn_perm); ++ AuBool(VERBOSE, verbose); ++ ++ out: ++ /* be sure to print "br:" last */ ++ if (!sysaufs_brs) { ++ seq_puts(m, ",br:"); ++ au_show_brs(m, sb); ++ } ++ si_read_unlock(sb); ++ return 0; ++ ++#undef Deleted ++#undef AuBool ++#undef AuStr ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* sum mode which returns the summation for statfs(2) */ ++ ++static u64 au_add_till_max(u64 a, u64 b) ++{ ++ u64 old; ++ ++ old = a; ++ a += b; ++ if (old < a) ++ return a; ++ return ULLONG_MAX; ++} ++ ++static int au_statfs_sum(struct super_block *sb, struct kstatfs *buf) ++{ ++ int err; ++ u64 blocks, bfree, bavail, files, ffree; ++ aufs_bindex_t bend, bindex, i; ++ unsigned char shared; ++ struct vfsmount *h_mnt; ++ struct super_block *h_sb; ++ ++ blocks = 0; ++ bfree = 0; ++ bavail = 0; ++ files = 0; ++ ffree = 0; ++ ++ err = 0; ++ bend = au_sbend(sb); ++ for (bindex = bend; bindex >= 0; bindex--) { ++ h_mnt = au_sbr_mnt(sb, bindex); ++ h_sb = h_mnt->mnt_sb; ++ shared = 0; ++ for (i = bindex + 1; !shared && i <= bend; i++) ++ shared = (au_sbr_sb(sb, i) == h_sb); ++ if (shared) ++ continue; ++ ++ /* sb->s_root for NFS is unreliable */ ++ err = vfs_statfs(h_mnt->mnt_root, buf); ++ if (unlikely(err)) ++ goto out; ++ ++ blocks = au_add_till_max(blocks, buf->f_blocks); ++ bfree = au_add_till_max(bfree, buf->f_bfree); ++ bavail = au_add_till_max(bavail, buf->f_bavail); ++ files = au_add_till_max(files, buf->f_files); ++ ffree = au_add_till_max(ffree, buf->f_ffree); ++ } ++ ++ buf->f_blocks = blocks; ++ buf->f_bfree = bfree; ++ buf->f_bavail = bavail; ++ buf->f_files = files; ++ buf->f_ffree = ffree; ++ ++ out: ++ return err; ++} ++ ++static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ int err; ++ struct super_block *sb; ++ ++ /* lock free root dinfo */ ++ sb = dentry->d_sb; ++ si_noflush_read_lock(sb); ++ if (!au_opt_test(au_mntflags(sb), SUM)) ++ /* sb->s_root for NFS is unreliable */ ++ err = vfs_statfs(au_sbr_mnt(sb, 0)->mnt_root, buf); ++ else ++ err = au_statfs_sum(sb, buf); ++ si_read_unlock(sb); ++ ++ if (!err) { ++ buf->f_type = AUFS_SUPER_MAGIC; ++ buf->f_namelen -= AUFS_WH_PFX_LEN; ++ memset(&buf->f_fsid, 0, sizeof(buf->f_fsid)); ++ } ++ /* buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; */ ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* try flushing the lower fs at aufs remount/unmount time */ ++ ++static void au_fsync_br(struct super_block *sb) ++{ ++ aufs_bindex_t bend, bindex; ++ int brperm; ++ struct au_branch *br; ++ struct super_block *h_sb; ++ ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex < bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ brperm = br->br_perm; ++ if (brperm == AuBrPerm_RR || brperm == AuBrPerm_RRWH) ++ continue; ++ h_sb = br->br_mnt->mnt_sb; ++ if (bdev_read_only(h_sb->s_bdev)) ++ continue; ++ ++ lockdep_off(); ++ down_write(&h_sb->s_umount); ++ shrink_dcache_sb(h_sb); ++ fsync_super(h_sb); ++ up_write(&h_sb->s_umount); ++ lockdep_on(); ++ } ++} ++ ++/* ++ * this IS NOT for super_operations. ++ * I guess it will be reverted someday. ++ */ ++static void aufs_umount_begin(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = au_sbi(sb); ++ if (!sbinfo) ++ return; ++ ++ si_write_lock(sb); ++ au_fsync_br(sb); ++ if (au_opt_test(au_mntflags(sb), PLINK)) ++ au_plink_put(sb); ++ if (sbinfo->si_wbr_create_ops->fin) ++ sbinfo->si_wbr_create_ops->fin(sb); ++ si_write_unlock(sb); ++} ++ ++/* final actions when unmounting a file system */ ++static void aufs_put_super(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = au_sbi(sb); ++ if (!sbinfo) ++ return; ++ ++ aufs_umount_begin(sb); ++ dbgaufs_si_fin(sbinfo); ++ kobject_put(&sbinfo->si_kobj); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * refresh dentry and inode at remount time. ++ */ ++static int do_refresh(struct dentry *dentry, mode_t type, ++ unsigned int dir_flags) ++{ ++ int err; ++ struct dentry *parent; ++ ++ di_write_lock_child(dentry); ++ parent = dget_parent(dentry); ++ di_read_lock_parent(parent, AuLock_IR); ++ ++ /* returns the number of positive dentries */ ++ err = au_refresh_hdentry(dentry, type); ++ if (err >= 0) { ++ struct inode *inode = dentry->d_inode; ++ err = au_refresh_hinode(inode, dentry); ++ if (!err && type == S_IFDIR) ++ au_reset_hinotify(inode, dir_flags); ++ } ++ if (unlikely(err)) ++ AuErr("unrecoverable error %d, %.*s\n", err, AuDLNPair(dentry)); ++ ++ di_read_unlock(parent, AuLock_IR); ++ dput(parent); ++ di_write_unlock(dentry); ++ ++ return err; ++} ++ ++static int test_dir(struct dentry *dentry, void *arg __maybe_unused) ++{ ++ return S_ISDIR(dentry->d_inode->i_mode); ++} ++ ++/* gave up consolidating with refresh_nondir() */ ++static int refresh_dir(struct dentry *root, unsigned int sigen) ++{ ++ int err, i, j, ndentry, e; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry **dentries; ++ struct inode *inode; ++ const unsigned int flags = au_hi_flags(root->d_inode, /*isdir*/1); ++ ++ err = 0; ++ list_for_each_entry(inode, &root->d_sb->s_inodes, i_sb_list) ++ if (S_ISDIR(inode->i_mode) && au_iigen(inode) != sigen) { ++ ii_write_lock_child(inode); ++ e = au_refresh_hinode_self(inode, /*do_attr*/1); ++ ii_write_unlock(inode); ++ if (unlikely(e)) { ++ AuDbg("e %d, i%lu\n", e, inode->i_ino); ++ if (!err) ++ err = e; ++ /* go on even if err */ ++ } ++ } ++ ++ e = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(e)) { ++ if (!err) ++ err = e; ++ goto out; ++ } ++ e = au_dcsub_pages(&dpages, root, test_dir, NULL); ++ if (unlikely(e)) { ++ if (!err) ++ err = e; ++ goto out_dpages; ++ } ++ ++ for (i = 0; !e && i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ dentries = dpage->dentries; ++ ndentry = dpage->ndentry; ++ for (j = 0; !e && j < ndentry; j++) { ++ struct dentry *d; ++ ++ d = dentries[j]; ++ au_dbg_verify_dir_parent(d, sigen); ++ if (au_digen(d) != sigen) { ++ e = do_refresh(d, S_IFDIR, flags); ++ if (unlikely(e && !err)) ++ err = e; ++ /* break on err */ ++ } ++ } ++ } ++ ++ out_dpages: ++ au_dpages_free(&dpages); ++ out: ++ return err; ++} ++ ++static int test_nondir(struct dentry *dentry, void *arg __maybe_unused) ++{ ++ return !S_ISDIR(dentry->d_inode->i_mode); ++} ++ ++static int refresh_nondir(struct dentry *root, unsigned int sigen, ++ int do_dentry) ++{ ++ int err, i, j, ndentry, e; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry **dentries; ++ struct inode *inode; ++ ++ err = 0; ++ list_for_each_entry(inode, &root->d_sb->s_inodes, i_sb_list) ++ if (!S_ISDIR(inode->i_mode) && au_iigen(inode) != sigen) { ++ ii_write_lock_child(inode); ++ e = au_refresh_hinode_self(inode, /*do_attr*/1); ++ ii_write_unlock(inode); ++ if (unlikely(e)) { ++ AuDbg("e %d, i%lu\n", e, inode->i_ino); ++ if (!err) ++ err = e; ++ /* go on even if err */ ++ } ++ } ++ ++ if (!do_dentry) ++ goto out; ++ ++ e = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(e)) { ++ if (!err) ++ err = e; ++ goto out; ++ } ++ e = au_dcsub_pages(&dpages, root, test_nondir, NULL); ++ if (unlikely(e)) { ++ if (!err) ++ err = e; ++ goto out_dpages; ++ } ++ ++ for (i = 0; i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ dentries = dpage->dentries; ++ ndentry = dpage->ndentry; ++ for (j = 0; j < ndentry; j++) { ++ struct dentry *d; ++ ++ d = dentries[j]; ++ au_dbg_verify_nondir_parent(d, sigen); ++ inode = d->d_inode; ++ if (inode && au_digen(d) != sigen) { ++ e = do_refresh(d, inode->i_mode & S_IFMT, ++ /*dir_flags*/0); ++ if (unlikely(e && !err)) ++ err = e; ++ /* go on even err */ ++ } ++ } ++ } ++ ++ out_dpages: ++ au_dpages_free(&dpages); ++ out: ++ return err; ++} ++ ++static void au_remount_refresh(struct super_block *sb, unsigned int flags) ++{ ++ int err; ++ unsigned int sigen; ++ struct au_sbinfo *sbinfo; ++ struct dentry *root; ++ struct inode *inode; ++ ++ au_sigen_inc(sb); ++ sigen = au_sigen(sb); ++ sbinfo = au_sbi(sb); ++ au_fclr_si(sbinfo, FAILED_REFRESH_DIRS); ++ ++ root = sb->s_root; ++ DiMustNoWaiters(root); ++ inode = root->d_inode; ++ IiMustNoWaiters(inode); ++ au_reset_hinotify(inode, au_hi_flags(inode, /*isdir*/1)); ++ di_write_unlock(root); ++ ++ err = refresh_dir(root, sigen); ++ if (unlikely(err)) { ++ au_fset_si(sbinfo, FAILED_REFRESH_DIRS); ++ AuWarn("Refreshing directories failed, ignored (%d)\n", err); ++ } ++ ++ if (au_ftest_opts(flags, REFRESH_NONDIR)) { ++ err = refresh_nondir(root, sigen, !err); ++ if (unlikely(err)) ++ AuWarn("Refreshing non-directories failed, ignored" ++ "(%d)\n", err); ++ } ++ ++ /* aufs_write_lock() calls ..._child() */ ++ di_write_lock_child(root); ++ au_cpup_attr_all(root->d_inode, /*force*/1); ++} ++ ++/* stop extra interpretation of errno in mount(8), and strange error messages */ ++static int cvt_err(int err) ++{ ++ AuTraceErr(err); ++ ++ switch (err) { ++ case -ENOENT: ++ case -ENOTDIR: ++ case -EEXIST: ++ case -EIO: ++ err = -EINVAL; ++ } ++ return err; ++} ++ ++static int aufs_remount_fs(struct super_block *sb, int *flags, char *data) ++{ ++ int err; ++ struct au_opts opts; ++ struct dentry *root; ++ struct inode *inode; ++ struct au_sbinfo *sbinfo; ++ ++ err = 0; ++ root = sb->s_root; ++ if (!data || !*data) { ++ aufs_write_lock(root); ++ err = au_opts_verify(sb, *flags, /*pending*/0); ++ if (!err) ++ au_fsync_br(sb); ++ aufs_write_unlock(root); ++ goto out; ++ } ++ ++ err = -ENOMEM; ++ memset(&opts, 0, sizeof(opts)); ++ opts.opt = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!opts.opt)) ++ goto out; ++ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); ++ opts.flags = AuOpts_REMOUNT; ++ opts.sb_flags = *flags; ++ ++ /* parse it before aufs lock */ ++ err = au_opts_parse(sb, data, &opts); ++ if (unlikely(err)) ++ goto out_opts; ++ ++ sbinfo = au_sbi(sb); ++ inode = root->d_inode; ++ mutex_lock(&inode->i_mutex); ++ aufs_write_lock(root); ++ au_fsync_br(sb); ++ ++ /* au_opts_remount() may return an error */ ++ err = au_opts_remount(sb, &opts); ++ au_opts_free(&opts); ++ ++ if (au_ftest_opts(opts.flags, REFRESH_DIR) ++ || au_ftest_opts(opts.flags, REFRESH_NONDIR)) ++ au_remount_refresh(sb, opts.flags); ++ ++ aufs_write_unlock(root); ++ mutex_unlock(&inode->i_mutex); ++ ++ out_opts: ++ free_page((unsigned long)opts.opt); ++ out: ++ err = cvt_err(err); ++ AuTraceErr(err); ++ return err; ++} ++ ++static struct super_operations aufs_sop = { ++ .alloc_inode = aufs_alloc_inode, ++ .destroy_inode = aufs_destroy_inode, ++ .drop_inode = generic_delete_inode, ++ .show_options = aufs_show_options, ++ .statfs = aufs_statfs, ++ .put_super = aufs_put_super, ++ .remount_fs = aufs_remount_fs ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int alloc_root(struct super_block *sb) ++{ ++ int err; ++ struct inode *inode; ++ struct dentry *root; ++ ++ err = -ENOMEM; ++ inode = au_iget_locked(sb, AUFS_ROOT_INO); ++ err = PTR_ERR(inode); ++ if (IS_ERR(inode)) ++ goto out; ++ ++ inode->i_op = &aufs_dir_iop; ++ inode->i_fop = &aufs_dir_fop; ++ inode->i_mode = S_IFDIR; ++ inode->i_nlink = 2; ++ unlock_new_inode(inode); ++ ++ root = d_alloc_root(inode); ++ if (unlikely(!root)) ++ goto out_iput; ++ err = PTR_ERR(root); ++ if (IS_ERR(root)) ++ goto out_iput; ++ ++ err = au_alloc_dinfo(root); ++ if (!err) { ++ sb->s_root = root; ++ return 0; /* success */ ++ } ++ dput(root); ++ goto out; /* do not iput */ ++ ++ out_iput: ++ iget_failed(inode); ++ iput(inode); ++ out: ++ return err; ++ ++} ++ ++static int aufs_fill_super(struct super_block *sb, void *raw_data, ++ int silent __maybe_unused) ++{ ++ int err; ++ struct au_opts opts; ++ struct dentry *root; ++ struct inode *inode; ++ char *arg = raw_data; ++ ++ if (unlikely(!arg || !*arg)) { ++ err = -EINVAL; ++ AuErr("no arg\n"); ++ goto out; ++ } ++ ++ err = -ENOMEM; ++ memset(&opts, 0, sizeof(opts)); ++ opts.opt = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!opts.opt)) ++ goto out; ++ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); ++ opts.sb_flags = sb->s_flags; ++ ++ err = au_si_alloc(sb); ++ if (unlikely(err)) ++ goto out_opts; ++ ++ /* all timestamps always follow the ones on the branch */ ++ sb->s_flags |= MS_NOATIME | MS_NODIRATIME; ++ sb->s_op = &aufs_sop; ++ sb->s_magic = AUFS_SUPER_MAGIC; ++ sb->s_maxbytes = 0; ++ au_export_init(sb); ++ ++ err = alloc_root(sb); ++ if (unlikely(err)) { ++ si_write_unlock(sb); ++ goto out_info; ++ } ++ root = sb->s_root; ++ inode = root->d_inode; ++ ++ /* ++ * actually we can parse options regardless aufs lock here. ++ * but at remount time, parsing must be done before aufs lock. ++ * so we follow the same rule. ++ */ ++ ii_write_lock_parent(inode); ++ aufs_write_unlock(root); ++ err = au_opts_parse(sb, arg, &opts); ++ if (unlikely(err)) ++ goto out_root; ++ ++ /* lock vfs_inode first, then aufs. */ ++ mutex_lock(&inode->i_mutex); ++ inode->i_op = &aufs_dir_iop; ++ inode->i_fop = &aufs_dir_fop; ++ aufs_write_lock(root); ++ err = au_opts_mount(sb, &opts); ++ au_opts_free(&opts); ++ if (unlikely(err)) ++ goto out_unlock; ++ aufs_write_unlock(root); ++ mutex_unlock(&inode->i_mutex); ++ goto out_opts; /* success */ ++ ++ out_unlock: ++ aufs_write_unlock(root); ++ mutex_unlock(&inode->i_mutex); ++ out_root: ++ dput(root); ++ sb->s_root = NULL; ++ out_info: ++ kobject_put(&au_sbi(sb)->si_kobj); ++ sb->s_fs_info = NULL; ++ out_opts: ++ free_page((unsigned long)opts.opt); ++ out: ++ AuTraceErr(err); ++ err = cvt_err(err); ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_get_sb(struct file_system_type *fs_type, int flags, ++ const char *dev_name __maybe_unused, void *raw_data, ++ struct vfsmount *mnt) ++{ ++ int err; ++ struct super_block *sb; ++ ++ /* all timestamps always follow the ones on the branch */ ++ /* mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME; */ ++ err = get_sb_nodev(fs_type, flags, raw_data, aufs_fill_super, mnt); ++ if (!err) { ++ sb = mnt->mnt_sb; ++ si_write_lock(sb); ++ sysaufs_brs_add(sb, 0); ++ si_write_unlock(sb); ++ } ++ return err; ++} ++ ++struct file_system_type aufs_fs_type = { ++ .name = AUFS_FSTYPE, ++ .fs_flags = ++ FS_RENAME_DOES_D_MOVE /* a race between rename and others */ ++ | FS_REVAL_DOT, /* for NFS branch and udba */ ++ .get_sb = aufs_get_sb, ++ .kill_sb = generic_shutdown_super, ++ /* no need to __module_get() and module_put(). */ ++ .owner = THIS_MODULE, ++}; +diff -Naur a/fs/aufs/super.h b/fs/aufs/super.h +--- a/fs/aufs/super.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/super.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,359 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * super_block operations ++ */ ++ ++#ifndef __AUFS_SUPER_H__ ++#define __AUFS_SUPER_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include "rwsem.h" ++#include "spl.h" ++#include "wkq.h" ++ ++typedef ssize_t (*au_readf_t)(struct file *, char __user *, size_t, loff_t *); ++typedef ssize_t (*au_writef_t)(struct file *, const char __user *, size_t, ++ loff_t *); ++ ++/* policies to select one among multiple writable branches */ ++struct au_wbr_copyup_operations { ++ int (*copyup)(struct dentry *dentry); ++}; ++ ++struct au_wbr_create_operations { ++ int (*create)(struct dentry *dentry, int isdir); ++ int (*init)(struct super_block *sb); ++ int (*fin)(struct super_block *sb); ++}; ++ ++struct au_wbr_mfs { ++ struct mutex mfs_lock; /* protect this structure */ ++ unsigned long mfs_jiffy; ++ unsigned long mfs_expire; ++ aufs_bindex_t mfs_bindex; ++ ++ unsigned long long mfsrr_bytes; ++ unsigned long long mfsrr_watermark; ++}; ++ ++/* sbinfo status flags */ ++/* ++ * set true when refresh_dirs() failed at remount time. ++ * then try refreshing dirs at access time again. ++ * if it is false, refreshing dirs at access time is unnecesary ++ */ ++#define AuSi_FAILED_REFRESH_DIRS 1 ++#define AuSi_MAINTAIN_PLINK (1 << 1) /* ioctl */ ++#define au_ftest_si(sbinfo, name) ((sbinfo)->au_si_status & AuSi_##name) ++#define au_fset_si(sbinfo, name) \ ++ { (sbinfo)->au_si_status |= AuSi_##name; } ++#define au_fclr_si(sbinfo, name) \ ++ { (sbinfo)->au_si_status &= ~AuSi_##name; } ++ ++struct au_branch; ++struct au_sbinfo { ++ /* nowait tasks in the system-wide workqueue */ ++ struct au_nowait_tasks si_nowait; ++ ++ struct rw_semaphore si_rwsem; ++ ++ /* branch management */ ++ unsigned int si_generation; ++ ++ /* see above flags */ ++ unsigned char au_si_status; ++ ++ aufs_bindex_t si_bend; ++ aufs_bindex_t si_last_br_id; ++ struct au_branch **si_branch; ++ ++ /* policy to select a writable branch */ ++ unsigned char si_wbr_copyup; ++ unsigned char si_wbr_create; ++ struct au_wbr_copyup_operations *si_wbr_copyup_ops; ++ struct au_wbr_create_operations *si_wbr_create_ops; ++ ++ /* round robin */ ++ atomic_t si_wbr_rr_next; ++ ++ /* most free space */ ++ struct au_wbr_mfs si_wbr_mfs; ++ ++ /* mount flags */ ++ /* include/asm-ia64/siginfo.h defines a macro named si_flags */ ++ unsigned int si_mntflags; ++ ++ /* external inode number (bitmap and translation table) */ ++ au_readf_t si_xread; ++ au_writef_t si_xwrite; ++ struct file *si_xib; ++ struct mutex si_xib_mtx; /* protect xib members */ ++ unsigned long *si_xib_buf; ++ unsigned long si_xib_last_pindex; ++ int si_xib_next_bit; ++ aufs_bindex_t si_xino_brid; ++ /* reserved for future use */ ++ /* unsigned long long si_xib_limit; */ /* Max xib file size */ ++ ++#ifdef CONFIG_AUFS_EXPORT ++ /* i_generation */ ++ struct file *si_xigen; ++ atomic_t si_xigen_next; ++#endif ++ ++ /* vdir parameters */ ++ unsigned long si_rdcache; /* max cache time in HZ */ ++ unsigned int si_rdblk; /* deblk size */ ++ unsigned int si_rdhash; /* hash size */ ++ ++ /* ++ * If the number of whiteouts are larger than si_dirwh, leave all of ++ * them after au_whtmp_ren to reduce the cost of rmdir(2). ++ * future fsck.aufs or kernel thread will remove them later. ++ * Otherwise, remove all whiteouts and the dir in rmdir(2). ++ */ ++ unsigned int si_dirwh; ++ ++ /* ++ * rename(2) a directory with all children. ++ */ ++ /* reserved for future use */ ++ /* int si_rendir; */ ++ ++ /* pseudo_link list */ ++ struct au_splhead si_plink; ++ wait_queue_head_t si_plink_wq; ++ ++ /* ++ * sysfs and lifetime management. ++ * this is not a small structure and it may be a waste of memory in case ++ * of sysfs is disabled, particulary when many aufs-es are mounted. ++ * but using sysfs is majority. ++ */ ++ struct kobject si_kobj; ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *si_dbgaufs, *si_dbgaufs_xib; ++#ifdef CONFIG_AUFS_EXPORT ++ struct dentry *si_dbgaufs_xigen; ++#endif ++#endif ++ ++ /* dirty, necessary for unmounting, sysfs and sysrq */ ++ struct super_block *si_sb; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* policy to select one among writable branches */ ++#define AuWbrCopyup(sbinfo, args...) \ ++ ((sbinfo)->si_wbr_copyup_ops->copyup(args)) ++#define AuWbrCreate(sbinfo, args...) \ ++ ((sbinfo)->si_wbr_create_ops->create(args)) ++ ++/* flags for si_read_lock()/aufs_read_lock()/di_read_lock() */ ++#define AuLock_DW 1 /* write-lock dentry */ ++#define AuLock_IR (1 << 1) /* read-lock inode */ ++#define AuLock_IW (1 << 2) /* write-lock inode */ ++#define AuLock_FLUSH (1 << 3) /* wait for 'nowait' tasks */ ++#define AuLock_DIR (1 << 4) /* target is a dir */ ++#define au_ftest_lock(flags, name) ((flags) & AuLock_##name) ++#define au_fset_lock(flags, name) { (flags) |= AuLock_##name; } ++#define au_fclr_lock(flags, name) { (flags) &= ~AuLock_##name; } ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* super.c */ ++extern struct file_system_type aufs_fs_type; ++struct inode *au_iget_locked(struct super_block *sb, ino_t ino); ++ ++/* sbinfo.c */ ++void au_si_free(struct kobject *kobj); ++int au_si_alloc(struct super_block *sb); ++int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr); ++ ++unsigned int au_sigen_inc(struct super_block *sb); ++aufs_bindex_t au_new_br_id(struct super_block *sb); ++ ++void aufs_read_lock(struct dentry *dentry, int flags); ++void aufs_read_unlock(struct dentry *dentry, int flags); ++void aufs_write_lock(struct dentry *dentry); ++void aufs_write_unlock(struct dentry *dentry); ++void aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int isdir); ++void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2); ++ ++/* wbr_policy.c */ ++extern struct au_wbr_copyup_operations au_wbr_copyup_ops[]; ++extern struct au_wbr_create_operations au_wbr_create_ops[]; ++int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct au_sbinfo *au_sbi(struct super_block *sb) ++{ ++ return sb->s_fs_info; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_EXPORT ++void au_export_init(struct super_block *sb); ++ ++static inline int au_test_nfsd(struct task_struct *tsk) ++{ ++ return !tsk->mm && !strcmp(tsk->comm, "nfsd"); ++} ++ ++int au_xigen_inc(struct inode *inode); ++int au_xigen_new(struct inode *inode); ++int au_xigen_set(struct super_block *sb, struct file *base); ++void au_xigen_clr(struct super_block *sb); ++ ++static inline int au_busy_or_stale(void) ++{ ++ if (!au_test_nfsd(current)) ++ return -EBUSY; ++ return -ESTALE; ++} ++#else ++static inline void au_export_init(struct super_block *sb) ++{ ++ /* nothing */ ++} ++ ++static inline int au_test_nfsd(struct task_struct *tsk) ++{ ++ return 0; ++} ++ ++static inline int au_xigen_inc(struct inode *inode) ++{ ++ return 0; ++} ++ ++static inline int au_xigen_new(struct inode *inode) ++{ ++ return 0; ++} ++ ++static inline int au_xigen_set(struct super_block *sb, struct file *base) ++{ ++ return 0; ++} ++ ++static inline void au_xigen_clr(struct super_block *sb) ++{ ++ /* empty */ ++} ++ ++static inline int au_busy_or_stale(void) ++{ ++ return -EBUSY; ++} ++#endif /* CONFIG_AUFS_EXPORT */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline void dbgaufs_si_null(struct au_sbinfo *sbinfo) ++{ ++#ifdef CONFIG_DEBUG_FS ++ sbinfo->si_dbgaufs = NULL; ++ sbinfo->si_dbgaufs_xib = NULL; ++#ifdef CONFIG_AUFS_EXPORT ++ sbinfo->si_dbgaufs_xigen = NULL; ++#endif ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* lock superblock. mainly for entry point functions */ ++/* ++ * si_noflush_read_lock, si_noflush_write_lock, ++ * si_read_unlock, si_write_unlock, si_downgrade_lock ++ */ ++AuSimpleLockRwsemFuncs(si_noflush, struct super_block *sb, ++ &au_sbi(sb)->si_rwsem); ++AuSimpleUnlockRwsemFuncs(si, struct super_block *sb, &au_sbi(sb)->si_rwsem); ++ ++static inline void si_read_lock(struct super_block *sb, int flags) ++{ ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&au_sbi(sb)->si_nowait); ++ si_noflush_read_lock(sb); ++} ++ ++static inline void si_write_lock(struct super_block *sb) ++{ ++ au_nwt_flush(&au_sbi(sb)->si_nowait); ++ si_noflush_write_lock(sb); ++} ++ ++static inline int si_read_trylock(struct super_block *sb, int flags) ++{ ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&au_sbi(sb)->si_nowait); ++ return si_noflush_read_trylock(sb); ++} ++ ++static inline int si_write_trylock(struct super_block *sb, int flags) ++{ ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&au_sbi(sb)->si_nowait); ++ return si_noflush_write_trylock(sb); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline aufs_bindex_t au_sbend(struct super_block *sb) ++{ ++ return au_sbi(sb)->si_bend; ++} ++ ++static inline unsigned int au_mntflags(struct super_block *sb) ++{ ++ return au_sbi(sb)->si_mntflags; ++} ++ ++static inline unsigned int au_sigen(struct super_block *sb) ++{ ++ return au_sbi(sb)->si_generation; ++} ++ ++static inline struct au_branch *au_sbr(struct super_block *sb, ++ aufs_bindex_t bindex) ++{ ++ return au_sbi(sb)->si_branch[0 + bindex]; ++} ++ ++static inline void au_xino_brid_set(struct super_block *sb, aufs_bindex_t brid) ++{ ++ au_sbi(sb)->si_xino_brid = brid; ++} ++ ++static inline aufs_bindex_t au_xino_brid(struct super_block *sb) ++{ ++ return au_sbi(sb)->si_xino_brid; ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_SUPER_H__ */ +diff -Naur a/fs/aufs/sysaufs.c b/fs/aufs/sysaufs.c +--- a/fs/aufs/sysaufs.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/sysaufs.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,104 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * sysfs interface and lifetime management ++ * they are necessary regardless sysfs is disabled. ++ */ ++ ++#include ++#include ++#include ++#include "aufs.h" ++ ++unsigned long sysaufs_si_mask; ++struct kset *sysaufs_ket; ++ ++#define AuSiAttr(_name) { \ ++ .attr = { .name = __stringify(_name), .mode = 0444 }, \ ++ .show = sysaufs_si_##_name, \ ++} ++ ++static struct sysaufs_si_attr sysaufs_si_attr_xi_path = AuSiAttr(xi_path); ++struct attribute *sysaufs_si_attrs[] = { ++ &sysaufs_si_attr_xi_path.attr, ++ NULL, ++}; ++ ++static struct sysfs_ops au_sbi_ops = { ++ .show = sysaufs_si_show ++}; ++ ++static struct kobj_type au_sbi_ktype = { ++ .release = au_si_free, ++ .sysfs_ops = &au_sbi_ops, ++ .default_attrs = sysaufs_si_attrs ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++int sysaufs_si_init(struct au_sbinfo *sbinfo) ++{ ++ int err; ++ ++ sbinfo->si_kobj.kset = sysaufs_ket; ++ /* cf. sysaufs_name() */ ++ err = kobject_init_and_add ++ (&sbinfo->si_kobj, &au_sbi_ktype, /*&sysaufs_ket->kobj*/NULL, ++ SysaufsSiNamePrefix "%lx", sysaufs_si_id(sbinfo)); ++ ++ dbgaufs_si_null(sbinfo); ++ if (!err) { ++ err = dbgaufs_si_init(sbinfo); ++ if (unlikely(err)) ++ kobject_put(&sbinfo->si_kobj); ++ } ++ return err; ++} ++ ++void sysaufs_fin(void) ++{ ++ dbgaufs_fin(); ++ sysfs_remove_group(&sysaufs_ket->kobj, sysaufs_attr_group); ++ kset_unregister(sysaufs_ket); ++} ++ ++int __init sysaufs_init(void) ++{ ++ int err; ++ ++ do { ++ get_random_bytes(&sysaufs_si_mask, sizeof(sysaufs_si_mask)); ++ } while (!sysaufs_si_mask); ++ ++ sysaufs_ket = kset_create_and_add(AUFS_NAME, NULL, fs_kobj); ++ err = PTR_ERR(sysaufs_ket); ++ if (IS_ERR(sysaufs_ket)) ++ goto out; ++ err = sysfs_create_group(&sysaufs_ket->kobj, sysaufs_attr_group); ++ if (unlikely(err)) { ++ kset_unregister(sysaufs_ket); ++ goto out; ++ } ++ ++ err = dbgaufs_init(); ++ if (unlikely(err)) ++ sysaufs_fin(); ++ out: ++ return err; ++} +diff -Naur a/fs/aufs/sysaufs.h b/fs/aufs/sysaufs.h +--- a/fs/aufs/sysaufs.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/sysaufs.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,120 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * sysfs interface and mount lifetime management ++ */ ++ ++#ifndef __SYSAUFS_H__ ++#define __SYSAUFS_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include "module.h" ++ ++struct super_block; ++struct au_sbinfo; ++ ++struct sysaufs_si_attr { ++ struct attribute attr; ++ int (*show)(struct seq_file *seq, struct super_block *sb); ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* sysaufs.c */ ++extern unsigned long sysaufs_si_mask; ++extern struct kset *sysaufs_ket; ++extern struct attribute *sysaufs_si_attrs[]; ++int sysaufs_si_init(struct au_sbinfo *sbinfo); ++int __init sysaufs_init(void); ++void sysaufs_fin(void); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* some people doesn't like to show a pointer in kernel */ ++static inline unsigned long sysaufs_si_id(struct au_sbinfo *sbinfo) ++{ ++ return sysaufs_si_mask ^ (unsigned long)sbinfo; ++} ++ ++#define SysaufsSiNamePrefix "si_" ++#define SysaufsSiNameLen (sizeof(SysaufsSiNamePrefix) + 16) ++static inline void sysaufs_name(struct au_sbinfo *sbinfo, char *name) ++{ ++ snprintf(name, SysaufsSiNameLen, SysaufsSiNamePrefix "%lx", ++ sysaufs_si_id(sbinfo)); ++} ++ ++struct au_branch; ++#ifdef CONFIG_SYSFS ++/* sysfs.c */ ++extern struct attribute_group *sysaufs_attr_group; ++ ++int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb); ++ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, ++ char *buf); ++ ++void sysaufs_br_init(struct au_branch *br); ++void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); ++void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); ++ ++#define sysaufs_brs_init() do {} while (0) ++ ++#else ++#define sysaufs_attr_group NULL ++ ++static inline ++int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb) ++{ ++ return 0; ++} ++ ++static inline ++ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, ++ char *buf) ++{ ++ return 0; ++} ++ ++static inline void sysaufs_br_init(struct au_branch *br) ++{ ++ /* empty */ ++} ++ ++static inline void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ /* nothing */ ++} ++ ++static inline void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ /* nothing */ ++} ++ ++static inline void sysaufs_brs_init(void) ++{ ++ sysaufs_brs = 0; ++} ++ ++#endif /* CONFIG_SYSFS */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __SYSAUFS_H__ */ +diff -Naur a/fs/aufs/sysfs.c b/fs/aufs/sysfs.c +--- a/fs/aufs/sysfs.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/sysfs.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,208 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * sysfs interface ++ */ ++ ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++static struct attribute *au_attr[] = { ++ NULL, /* need to NULL terminate the list of attributes */ ++}; ++ ++static struct attribute_group sysaufs_attr_group_body = { ++ .attrs = au_attr ++}; ++ ++struct attribute_group *sysaufs_attr_group = &sysaufs_attr_group_body; ++ ++/* ---------------------------------------------------------------------- */ ++ ++int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb) ++{ ++ int err; ++ ++ err = 0; ++ if (au_opt_test(au_mntflags(sb), XINO)) { ++ err = au_xino_path(seq, au_sbi(sb)->si_xib); ++ seq_putc(seq, '\n'); ++ } ++ return err; ++} ++ ++/* ++ * the lifetime of branch is independent from the entry under sysfs. ++ * sysfs handles the lifetime of the entry, and never call ->show() after it is ++ * unlinked. ++ */ ++static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, ++ aufs_bindex_t bindex) ++{ ++ struct path path; ++ struct dentry *root; ++ struct au_branch *br; ++ ++ AuDbg("b%d\n", bindex); ++ ++ root = sb->s_root; ++ di_read_lock_parent(root, !AuLock_IR); ++ br = au_sbr(sb, bindex); ++ path.mnt = br->br_mnt; ++ path.dentry = au_h_dptr(root, bindex); ++ au_seq_path(seq, &path); ++ di_read_unlock(root, !AuLock_IR); ++ seq_printf(seq, "=%s\n", au_optstr_br_perm(br->br_perm)); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct seq_file *au_seq(char *p, ssize_t len) ++{ ++ struct seq_file *seq; ++ ++ seq = kzalloc(sizeof(*seq), GFP_NOFS); ++ if (seq) { ++ /* mutex_init(&seq.lock); */ ++ seq->buf = p; ++ seq->size = len; ++ return seq; /* success */ ++ } ++ ++ seq = ERR_PTR(-ENOMEM); ++ return seq; ++} ++ ++#define SysaufsBr_PREFIX "br" ++ ++/* todo: file size may exceed PAGE_SIZE */ ++ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, ++ char *buf) ++{ ++ ssize_t err; ++ long l; ++ aufs_bindex_t bend; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ struct seq_file *seq; ++ char *name; ++ struct attribute **cattr; ++ ++ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ ++ seq = au_seq(buf, PAGE_SIZE); ++ err = PTR_ERR(seq); ++ if (IS_ERR(seq)) ++ goto out; ++ ++ name = (void *)attr->name; ++ cattr = sysaufs_si_attrs; ++ while (*cattr) { ++ if (!strcmp(name, (*cattr)->name)) { ++ err = container_of(*cattr, struct sysaufs_si_attr, attr) ++ ->show(seq, sb); ++ goto out_seq; ++ } ++ cattr++; ++ } ++ ++ bend = au_sbend(sb); ++ if (!strncmp(name, SysaufsBr_PREFIX, sizeof(SysaufsBr_PREFIX) - 1)) { ++ name += sizeof(SysaufsBr_PREFIX) - 1; ++ err = strict_strtol(name, 10, &l); ++ if (!err) { ++ if (l <= bend) ++ err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l); ++ else ++ err = -ENOENT; ++ } ++ goto out_seq; ++ } ++ BUG(); ++ ++ out_seq: ++ if (!err) { ++ err = seq->count; ++ /* sysfs limit */ ++ if (unlikely(err == PAGE_SIZE)) ++ err = -EFBIG; ++ } ++ kfree(seq); ++ out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void sysaufs_br_init(struct au_branch *br) ++{ ++ br->br_attr.name = br->br_name; ++ br->br_attr.mode = S_IRUGO; ++ br->br_attr.owner = THIS_MODULE; ++} ++ ++void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ struct au_branch *br; ++ struct kobject *kobj; ++ aufs_bindex_t bend; ++ ++ dbgaufs_brs_del(sb, bindex); ++ ++ if (!sysaufs_brs) ++ return; ++ ++ kobj = &au_sbi(sb)->si_kobj; ++ bend = au_sbend(sb); ++ for (; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ sysfs_remove_file(kobj, &br->br_attr); ++ } ++} ++ ++void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ int err; ++ aufs_bindex_t bend; ++ struct kobject *kobj; ++ struct au_branch *br; ++ ++ dbgaufs_brs_add(sb, bindex); ++ ++ if (!sysaufs_brs) ++ return; ++ ++ kobj = &au_sbi(sb)->si_kobj; ++ bend = au_sbend(sb); ++ for (; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ snprintf(br->br_name, sizeof(br->br_name), SysaufsBr_PREFIX ++ "%d", bindex); ++ err = sysfs_create_file(kobj, &br->br_attr); ++ if (unlikely(err)) ++ AuWarn("failed %s under sysfs(%d)\n", br->br_name, err); ++ } ++} +diff -Naur a/fs/aufs/sysrq.c b/fs/aufs/sysrq.c +--- a/fs/aufs/sysrq.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/sysrq.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,115 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * magic sysrq hanlder ++ */ ++ ++#include ++#include ++#include ++/* #include */ ++#include "aufs.h" ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void sysrq_sb(struct super_block *sb) ++{ ++ char *plevel; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ ++ plevel = au_plevel; ++ au_plevel = KERN_WARNING; ++ au_debug(1); ++ ++ sbinfo = au_sbi(sb); ++ pr_warning("si=%lx\n", sysaufs_si_id(sbinfo)); ++ pr_warning(AUFS_NAME ": superblock\n"); ++ au_dpri_sb(sb); ++ pr_warning(AUFS_NAME ": root dentry\n"); ++ au_dpri_dentry(sb->s_root); ++ pr_warning(AUFS_NAME ": root inode\n"); ++ au_dpri_inode(sb->s_root->d_inode); ++#if 0 ++ struct inode *i; ++ pr_warning(AUFS_NAME ": isolated inode\n"); ++ list_for_each_entry(i, &sb->s_inodes, i_sb_list) ++ if (list_empty(&i->i_dentry)) ++ au_dpri_inode(i); ++#endif ++ pr_warning(AUFS_NAME ": files\n"); ++ list_for_each_entry(file, &sb->s_files, f_u.fu_list) ++ if (!special_file(file->f_dentry->d_inode->i_mode)) ++ au_dpri_file(file); ++ ++ au_plevel = plevel; ++ au_debug(0); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* module parameter */ ++static char *aufs_sysrq_key = "a"; ++module_param_named(sysrq, aufs_sysrq_key, charp, S_IRUGO); ++MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME); ++ ++static void au_sysrq(int key __maybe_unused, ++ struct tty_struct *tty __maybe_unused) ++{ ++ struct kobject *kobj; ++ struct au_sbinfo *sbinfo; ++ ++ /* spin_lock(&sysaufs_ket->list_lock); */ ++ list_for_each_entry(kobj, &sysaufs_ket->list, entry) { ++ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); ++ sysrq_sb(sbinfo->si_sb); ++ } ++ /* spin_unlock(&sysaufs_ket->list_lock); */ ++} ++ ++static struct sysrq_key_op au_sysrq_op = { ++ .handler = au_sysrq, ++ .help_msg = "Aufs", ++ .action_msg = "Aufs", ++ .enable_mask = SYSRQ_ENABLE_DUMP ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++int __init au_sysrq_init(void) ++{ ++ int err; ++ char key; ++ ++ err = -1; ++ key = *aufs_sysrq_key; ++ if ('a' <= key && key <= 'z') ++ err = register_sysrq_key(key, &au_sysrq_op); ++ if (unlikely(err)) ++ AuErr("err %d, sysrq=%c\n", err, key); ++ return err; ++} ++ ++void au_sysrq_fin(void) ++{ ++ int err; ++ err = unregister_sysrq_key(*aufs_sysrq_key, &au_sysrq_op); ++ if (unlikely(err)) ++ AuErr("err %d (ignored)\n", err); ++} +diff -Naur a/fs/aufs/vdir.c b/fs/aufs/vdir.c +--- a/fs/aufs/vdir.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/vdir.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,876 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * virtual or vertical directory ++ */ ++ ++#include ++#include "aufs.h" ++ ++static unsigned int calc_size(int nlen) ++{ ++ BUILD_BUG_ON(sizeof(ino_t) != sizeof(long)); ++ return ALIGN(sizeof(struct au_vdir_de) + nlen, sizeof(ino_t)); ++} ++ ++static int set_deblk_end(union au_vdir_deblk_p *p, ++ union au_vdir_deblk_p *deblk_end) ++{ ++ if (calc_size(0) <= deblk_end->deblk - p->deblk) { ++ p->de->de_str.len = 0; ++ /* smp_mb(); */ ++ return 0; ++ } ++ return -1; /* error */ ++} ++ ++/* returns true or false */ ++static int is_deblk_end(union au_vdir_deblk_p *p, ++ union au_vdir_deblk_p *deblk_end) ++{ ++ if (calc_size(0) <= deblk_end->deblk - p->deblk) ++ return !p->de->de_str.len; ++ return 1; ++} ++ ++static unsigned char *last_deblk(struct au_vdir *vdir) ++{ ++ return vdir->vd_deblk[vdir->vd_nblk - 1]; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * the allocated memory has to be freed by ++ * au_nhash_wh_free() or au_nhash_de_free(). ++ */ ++int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp) ++{ ++ struct hlist_head *head; ++ unsigned int u; ++ ++ head = kmalloc(sizeof(*nhash->nh_head) * num_hash, gfp); ++ if (head) { ++ nhash->nh_num = num_hash; ++ nhash->nh_head = head; ++ for (u = 0; u < num_hash; u++) ++ INIT_HLIST_HEAD(head++); ++ return 0; /* success */ ++ } ++ ++ return -ENOMEM; ++} ++ ++static void nhash_count(struct hlist_head *head) ++{ ++#if 0 ++ unsigned long n; ++ struct hlist_node *pos; ++ ++ n = 0; ++ hlist_for_each(pos, head) ++ n++; ++ AuInfo("%lu\n", n); ++#endif ++} ++ ++static void au_nhash_wh_do_free(struct hlist_head *head) ++{ ++ struct au_vdir_wh *tpos; ++ struct hlist_node *pos, *node; ++ ++ hlist_for_each_entry_safe(tpos, pos, node, head, wh_hash) { ++ /* hlist_del(pos); */ ++ kfree(tpos); ++ } ++} ++ ++static void au_nhash_de_do_free(struct hlist_head *head) ++{ ++ struct au_vdir_dehstr *tpos; ++ struct hlist_node *pos, *node; ++ ++ hlist_for_each_entry_safe(tpos, pos, node, head, hash) { ++ /* hlist_del(pos); */ ++ au_cache_free_dehstr(tpos); ++ } ++} ++ ++static void au_nhash_do_free(struct au_nhash *nhash, ++ void (*free)(struct hlist_head *head)) ++{ ++ unsigned int u, n; ++ struct hlist_head *head; ++ ++ n = nhash->nh_num; ++ head = nhash->nh_head; ++ for (u = 0; u < n; u++) { ++ nhash_count(head); ++ free(head++); ++ } ++ kfree(nhash->nh_head); ++} ++ ++void au_nhash_wh_free(struct au_nhash *whlist) ++{ ++ au_nhash_do_free(whlist, au_nhash_wh_do_free); ++} ++ ++static void au_nhash_de_free(struct au_nhash *delist) ++{ ++ au_nhash_do_free(delist, au_nhash_de_do_free); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, ++ int limit) ++{ ++ int num; ++ unsigned int u, n; ++ struct hlist_head *head; ++ struct au_vdir_wh *tpos; ++ struct hlist_node *pos; ++ ++ num = 0; ++ n = whlist->nh_num; ++ head = whlist->nh_head; ++ for (u = 0; u < n; u++) { ++ hlist_for_each_entry(tpos, pos, head, wh_hash) ++ if (tpos->wh_bindex == btgt && ++num > limit) ++ return 1; ++ head++; ++ } ++ return 0; ++} ++ ++static struct hlist_head *au_name_hash(struct au_nhash *nhash, ++ unsigned char *name, ++ unsigned int len) ++{ ++ unsigned int v; ++ /* const unsigned int magic_bit = 12; */ ++ ++ v = 0; ++ while (len--) ++ v += *name++; ++ /* v = hash_long(v, magic_bit); */ ++ v %= nhash->nh_num; ++ return nhash->nh_head + v; ++} ++ ++static int au_nhash_test_name(struct au_vdir_destr *str, const char *name, ++ int nlen) ++{ ++ return str->len == nlen && !memcmp(str->name, name, nlen); ++} ++ ++/* returns found or not */ ++int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen) ++{ ++ struct hlist_head *head; ++ struct au_vdir_wh *tpos; ++ struct hlist_node *pos; ++ struct au_vdir_destr *str; ++ ++ head = au_name_hash(whlist, name, nlen); ++ hlist_for_each_entry(tpos, pos, head, wh_hash) { ++ str = &tpos->wh_str; ++ AuDbg("%.*s\n", str->len, str->name); ++ if (au_nhash_test_name(str, name, nlen)) ++ return 1; ++ } ++ return 0; ++} ++ ++/* returns found(true) or not */ ++static int test_known(struct au_nhash *delist, char *name, int nlen) ++{ ++ struct hlist_head *head; ++ struct au_vdir_dehstr *tpos; ++ struct hlist_node *pos; ++ struct au_vdir_destr *str; ++ ++ head = au_name_hash(delist, name, nlen); ++ hlist_for_each_entry(tpos, pos, head, hash) { ++ str = tpos->str; ++ AuDbg("%.*s\n", str->len, str->name); ++ if (au_nhash_test_name(str, name, nlen)) ++ return 1; ++ } ++ return 0; ++} ++ ++static void au_shwh_init_wh(struct au_vdir_wh *wh, ino_t ino, ++ unsigned char d_type) ++{ ++#ifdef CONFIG_AUFS_SHWH ++ wh->wh_ino = ino; ++ wh->wh_type = d_type; ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, ++ unsigned int d_type, aufs_bindex_t bindex, ++ unsigned char shwh) ++{ ++ int err; ++ struct au_vdir_destr *str; ++ struct au_vdir_wh *wh; ++ ++ AuDbg("%.*s\n", nlen, name); ++ err = -ENOMEM; ++ wh = kmalloc(sizeof(*wh) + nlen, GFP_NOFS); ++ if (unlikely(!wh)) ++ goto out; ++ ++ err = 0; ++ wh->wh_bindex = bindex; ++ if (shwh) ++ au_shwh_init_wh(wh, ino, d_type); ++ str = &wh->wh_str; ++ str->len = nlen; ++ memcpy(str->name, name, nlen); ++ hlist_add_head(&wh->wh_hash, au_name_hash(whlist, name, nlen)); ++ /* smp_mb(); */ ++ ++ out: ++ return err; ++} ++ ++static int append_deblk(struct au_vdir *vdir) ++{ ++ int err; ++ unsigned long ul; ++ const unsigned int deblk_sz = vdir->vd_deblk_sz; ++ union au_vdir_deblk_p p, deblk_end; ++ unsigned char **o; ++ ++ err = -ENOMEM; ++ o = krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1), ++ GFP_NOFS); ++ if (unlikely(!o)) ++ goto out; ++ ++ vdir->vd_deblk = o; ++ p.deblk = kmalloc(deblk_sz, GFP_NOFS); ++ if (p.deblk) { ++ ul = vdir->vd_nblk++; ++ vdir->vd_deblk[ul] = p.deblk; ++ vdir->vd_last.ul = ul; ++ vdir->vd_last.p.deblk = p.deblk; ++ deblk_end.deblk = p.deblk + deblk_sz; ++ err = set_deblk_end(&p, &deblk_end); ++ } ++ ++ out: ++ return err; ++} ++ ++static int append_de(struct au_vdir *vdir, char *name, int nlen, ino_t ino, ++ unsigned int d_type, struct au_nhash *delist) ++{ ++ int err; ++ unsigned int sz; ++ const unsigned int deblk_sz = vdir->vd_deblk_sz; ++ union au_vdir_deblk_p p, *room, deblk_end; ++ struct au_vdir_dehstr *dehstr; ++ ++ p.deblk = last_deblk(vdir); ++ deblk_end.deblk = p.deblk + deblk_sz; ++ room = &vdir->vd_last.p; ++ AuDebugOn(room->deblk < p.deblk || deblk_end.deblk <= room->deblk ++ || !is_deblk_end(room, &deblk_end)); ++ ++ sz = calc_size(nlen); ++ if (unlikely(sz > deblk_end.deblk - room->deblk)) { ++ err = append_deblk(vdir); ++ if (unlikely(err)) ++ goto out; ++ ++ p.deblk = last_deblk(vdir); ++ deblk_end.deblk = p.deblk + deblk_sz; ++ /* smp_mb(); */ ++ AuDebugOn(room->deblk != p.deblk); ++ } ++ ++ err = -ENOMEM; ++ dehstr = au_cache_alloc_dehstr(); ++ if (unlikely(!dehstr)) ++ goto out; ++ ++ dehstr->str = &room->de->de_str; ++ hlist_add_head(&dehstr->hash, au_name_hash(delist, name, nlen)); ++ room->de->de_ino = ino; ++ room->de->de_type = d_type; ++ room->de->de_str.len = nlen; ++ memcpy(room->de->de_str.name, name, nlen); ++ ++ err = 0; ++ room->deblk += sz; ++ if (unlikely(set_deblk_end(room, &deblk_end))) ++ err = append_deblk(vdir); ++ /* smp_mb(); */ ++ ++ out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_vdir_free(struct au_vdir *vdir) ++{ ++ unsigned char **deblk; ++ ++ deblk = vdir->vd_deblk; ++ while (vdir->vd_nblk--) ++ kfree(*deblk++); ++ kfree(vdir->vd_deblk); ++ au_cache_free_vdir(vdir); ++} ++ ++static struct au_vdir *alloc_vdir(struct super_block *sb) ++{ ++ struct au_vdir *vdir; ++ int err; ++ ++ err = -ENOMEM; ++ vdir = au_cache_alloc_vdir(); ++ if (unlikely(!vdir)) ++ goto out; ++ ++ vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_NOFS); ++ if (unlikely(!vdir->vd_deblk)) ++ goto out_free; ++ ++ vdir->vd_deblk_sz = au_sbi(sb)->si_rdblk; ++ vdir->vd_nblk = 0; ++ vdir->vd_version = 0; ++ vdir->vd_jiffy = 0; ++ err = append_deblk(vdir); ++ if (!err) ++ return vdir; /* success */ ++ ++ kfree(vdir->vd_deblk); ++ ++ out_free: ++ au_cache_free_vdir(vdir); ++ out: ++ vdir = ERR_PTR(err); ++ return vdir; ++} ++ ++static int reinit_vdir(struct au_vdir *vdir) ++{ ++ int err; ++ union au_vdir_deblk_p p, deblk_end; ++ ++ while (vdir->vd_nblk > 1) { ++ kfree(vdir->vd_deblk[vdir->vd_nblk - 1]); ++ /* vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; */ ++ vdir->vd_nblk--; ++ } ++ p.deblk = vdir->vd_deblk[0]; ++ deblk_end.deblk = p.deblk + vdir->vd_deblk_sz; ++ err = set_deblk_end(&p, &deblk_end); ++ /* keep vd_dblk_sz */ ++ vdir->vd_last.ul = 0; ++ vdir->vd_last.p.deblk = vdir->vd_deblk[0]; ++ vdir->vd_version = 0; ++ vdir->vd_jiffy = 0; ++ /* smp_mb(); */ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ unsigned int d_type, ino_t *ino) ++{ ++ int err; ++ struct mutex *mtx; ++ const int isdir = (d_type == DT_DIR); ++ ++ /* prevent hardlinks from race condition */ ++ mtx = NULL; ++ if (!isdir) { ++ mtx = &au_sbr(sb, bindex)->br_xino.xi_nondir_mtx; ++ mutex_lock(mtx); ++ } ++ err = au_xino_read(sb, bindex, h_ino, ino); ++ if (unlikely(err)) ++ goto out; ++ ++ if (!*ino) { ++ err = -EIO; ++ *ino = au_xino_new_ino(sb); ++ if (unlikely(!*ino)) ++ goto out; ++ err = au_xino_write(sb, bindex, h_ino, *ino); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ out: ++ if (!isdir) ++ mutex_unlock(mtx); ++ return err; ++} ++ ++static int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ unsigned int d_type, ino_t *ino) ++{ ++#ifdef CONFIG_AUFS_SHWH ++ return au_ino(sb, bindex, h_ino, d_type, ino); ++#else ++ return 0; ++#endif ++} ++ ++#define AuFillVdir_CALLED 1 ++#define AuFillVdir_WHABLE (1 << 1) ++#define AuFillVdir_SHWH (1 << 2) ++#define au_ftest_fillvdir(flags, name) ((flags) & AuFillVdir_##name) ++#define au_fset_fillvdir(flags, name) { (flags) |= AuFillVdir_##name; } ++#define au_fclr_fillvdir(flags, name) { (flags) &= ~AuFillVdir_##name; } ++ ++#ifndef CONFIG_AUFS_SHWH ++#undef AuFillVdir_SHWH ++#define AuFillVdir_SHWH 0 ++#endif ++ ++struct fillvdir_arg { ++ struct file *file; ++ struct au_vdir *vdir; ++ struct au_nhash delist; ++ struct au_nhash whlist; ++ aufs_bindex_t bindex; ++ unsigned int flags; ++ int err; ++}; ++ ++static int fillvdir(void *__arg, const char *__name, int nlen, ++ loff_t offset __maybe_unused, u64 h_ino, ++ unsigned int d_type) ++{ ++ struct fillvdir_arg *arg = __arg; ++ char *name = (void *)__name; ++ struct super_block *sb; ++ ino_t ino; ++ const unsigned char shwh = !!au_ftest_fillvdir(arg->flags, SHWH); ++ ++ arg->err = 0; ++ sb = arg->file->f_dentry->d_sb; ++ au_fset_fillvdir(arg->flags, CALLED); ++ /* smp_mb(); */ ++ if (nlen <= AUFS_WH_PFX_LEN ++ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { ++ if (test_known(&arg->delist, name, nlen) ++ || au_nhash_test_known_wh(&arg->whlist, name, nlen)) ++ goto out; /* already exists or whiteouted */ ++ ++ sb = arg->file->f_dentry->d_sb; ++ arg->err = au_ino(sb, arg->bindex, h_ino, d_type, &ino); ++ if (!arg->err) ++ arg->err = append_de(arg->vdir, name, nlen, ino, ++ d_type, &arg->delist); ++ } else if (au_ftest_fillvdir(arg->flags, WHABLE)) { ++ name += AUFS_WH_PFX_LEN; ++ nlen -= AUFS_WH_PFX_LEN; ++ if (au_nhash_test_known_wh(&arg->whlist, name, nlen)) ++ goto out; /* already whiteouted */ ++ ++ if (shwh) ++ arg->err = au_wh_ino(sb, arg->bindex, h_ino, d_type, ++ &ino); ++ if (!arg->err) ++ arg->err = au_nhash_append_wh ++ (&arg->whlist, name, nlen, ino, d_type, ++ arg->bindex, shwh); ++ } ++ ++ out: ++ if (!arg->err) ++ arg->vdir->vd_jiffy = jiffies; ++ /* smp_mb(); */ ++ AuTraceErr(arg->err); ++ return arg->err; ++} ++ ++static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir, ++ struct au_nhash *whlist, struct au_nhash *delist) ++{ ++#ifdef CONFIG_AUFS_SHWH ++ int err; ++ unsigned int nh, u; ++ struct hlist_head *head; ++ struct au_vdir_wh *tpos; ++ struct hlist_node *pos, *n; ++ char *p, *o; ++ struct au_vdir_destr *destr; ++ ++ AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH)); ++ ++ err = -ENOMEM; ++ o = p = __getname(); ++ if (unlikely(!p)) ++ goto out; ++ ++ err = 0; ++ nh = whlist->nh_num; ++ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); ++ p += AUFS_WH_PFX_LEN; ++ for (u = 0; u < nh; u++) { ++ head = whlist->nh_head + u; ++ hlist_for_each_entry_safe(tpos, pos, n, head, wh_hash) { ++ destr = &tpos->wh_str; ++ memcpy(p, destr->name, destr->len); ++ err = append_de(vdir, o, destr->len + AUFS_WH_PFX_LEN, ++ tpos->wh_ino, tpos->wh_type, delist); ++ if (unlikely(err)) ++ break; ++ } ++ } ++ ++ __putname(o); ++ ++ out: ++ AuTraceErr(err); ++ return err; ++#else ++ return 0; ++#endif ++} ++ ++static int au_do_read_vdir(struct fillvdir_arg *arg) ++{ ++ int err; ++ unsigned int rdhash; ++ loff_t offset; ++ aufs_bindex_t bend, bindex, bstart; ++ unsigned char shwh; ++ struct file *hf, *file; ++ struct super_block *sb; ++ ++ file = arg->file; ++ sb = file->f_dentry->d_sb; ++ rdhash = au_sbi(sb)->si_rdhash; ++ err = au_nhash_alloc(&arg->delist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_nhash_alloc(&arg->whlist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out_delist; ++ ++ err = 0; ++ arg->flags = 0; ++ shwh = 0; ++ if (au_opt_test(au_mntflags(sb), SHWH)) { ++ shwh = 1; ++ au_fset_fillvdir(arg->flags, SHWH); ++ } ++ bstart = au_fbstart(file); ++ bend = au_fbend(file); ++ for (bindex = bstart; !err && bindex <= bend; bindex++) { ++ hf = au_h_fptr(file, bindex); ++ if (!hf) ++ continue; ++ ++ offset = vfsub_llseek(hf, 0, SEEK_SET); ++ err = offset; ++ if (unlikely(offset)) ++ break; ++ ++ arg->bindex = bindex; ++ au_fclr_fillvdir(arg->flags, WHABLE); ++ if (shwh ++ || (bindex != bend ++ && au_br_whable(au_sbr_perm(sb, bindex)))) ++ au_fset_fillvdir(arg->flags, WHABLE); ++ do { ++ arg->err = 0; ++ au_fclr_fillvdir(arg->flags, CALLED); ++ /* smp_mb(); */ ++ err = vfsub_readdir(hf, fillvdir, arg); ++ if (err >= 0) ++ err = arg->err; ++ } while (!err && au_ftest_fillvdir(arg->flags, CALLED)); ++ } ++ ++ if (!err && shwh) ++ err = au_handle_shwh(sb, arg->vdir, &arg->whlist, &arg->delist); ++ ++ au_nhash_wh_free(&arg->whlist); ++ ++ out_delist: ++ au_nhash_de_free(&arg->delist); ++ out: ++ return err; ++} ++ ++static int read_vdir(struct file *file, int may_read) ++{ ++ int err; ++ unsigned long expire; ++ unsigned char do_read; ++ struct fillvdir_arg arg; ++ struct inode *inode; ++ struct au_vdir *vdir, *allocated; ++ ++ err = 0; ++ inode = file->f_dentry->d_inode; ++ IMustLock(inode); ++ allocated = NULL; ++ do_read = 0; ++ expire = au_sbi(inode->i_sb)->si_rdcache; ++ vdir = au_ivdir(inode); ++ if (!vdir) { ++ do_read = 1; ++ vdir = alloc_vdir(inode->i_sb); ++ err = PTR_ERR(vdir); ++ if (IS_ERR(vdir)) ++ goto out; ++ err = 0; ++ allocated = vdir; ++ } else if (may_read ++ && (inode->i_version != vdir->vd_version ++ || time_after(jiffies, vdir->vd_jiffy + expire))) { ++ do_read = 1; ++ err = reinit_vdir(vdir); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ if (!do_read) ++ return 0; /* success */ ++ ++ arg.file = file; ++ arg.vdir = vdir; ++ err = au_do_read_vdir(&arg); ++ if (!err) { ++ /* file->f_pos = 0; */ ++ vdir->vd_version = inode->i_version; ++ vdir->vd_last.ul = 0; ++ vdir->vd_last.p.deblk = vdir->vd_deblk[0]; ++ if (allocated) ++ au_set_ivdir(inode, allocated); ++ } else if (allocated) ++ au_vdir_free(allocated); ++ ++ out: ++ return err; ++} ++ ++static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src) ++{ ++ int err, rerr; ++ unsigned long ul, n; ++ const unsigned int deblk_sz = src->vd_deblk_sz; ++ ++ AuDebugOn(tgt->vd_nblk != 1); ++ ++ err = -ENOMEM; ++ if (tgt->vd_nblk < src->vd_nblk) { ++ unsigned char **p; ++ ++ p = krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk, ++ GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ tgt->vd_deblk = p; ++ } ++ ++ tgt->vd_nblk = src->vd_nblk; ++ tgt->vd_deblk_sz = deblk_sz; ++ memcpy(tgt->vd_deblk[0], src->vd_deblk[0], deblk_sz); ++ /* tgt->vd_last.i = 0; */ ++ /* tgt->vd_last.p.deblk = tgt->vd_deblk[0]; */ ++ tgt->vd_version = src->vd_version; ++ tgt->vd_jiffy = src->vd_jiffy; ++ ++ n = src->vd_nblk; ++ for (ul = 1; ul < n; ul++) { ++ tgt->vd_deblk[ul] = kmemdup(src->vd_deblk[ul], deblk_sz, ++ GFP_NOFS); ++ if (unlikely(!tgt->vd_deblk[ul])) ++ goto out; ++ } ++ /* smp_mb(); */ ++ return 0; /* success */ ++ ++ out: ++ rerr = reinit_vdir(tgt); ++ BUG_ON(rerr); ++ return err; ++} ++ ++int au_vdir_init(struct file *file) ++{ ++ int err; ++ struct inode *inode; ++ struct au_vdir *vdir_cache, *allocated; ++ ++ err = read_vdir(file, !file->f_pos); ++ if (unlikely(err)) ++ goto out; ++ ++ allocated = NULL; ++ vdir_cache = au_fvdir_cache(file); ++ if (!vdir_cache) { ++ vdir_cache = alloc_vdir(file->f_dentry->d_sb); ++ err = PTR_ERR(vdir_cache); ++ if (IS_ERR(vdir_cache)) ++ goto out; ++ allocated = vdir_cache; ++ } else if (!file->f_pos && vdir_cache->vd_version != file->f_version) { ++ err = reinit_vdir(vdir_cache); ++ if (unlikely(err)) ++ goto out; ++ } else ++ return 0; /* success */ ++ ++ inode = file->f_dentry->d_inode; ++ err = copy_vdir(vdir_cache, au_ivdir(inode)); ++ if (!err) { ++ file->f_version = inode->i_version; ++ if (allocated) ++ au_set_fvdir_cache(file, allocated); ++ } else if (allocated) ++ au_vdir_free(allocated); ++ ++ out: ++ return err; ++} ++ ++static loff_t calc_offset(struct au_vdir *vdir) ++{ ++ loff_t offset; ++ union au_vdir_deblk_p p; ++ ++ p.deblk = vdir->vd_deblk[vdir->vd_last.ul]; ++ offset = vdir->vd_last.p.deblk - p.deblk; ++ offset += vdir->vd_deblk_sz * vdir->vd_last.ul; ++ return offset; ++} ++ ++/* returns true or false */ ++static int seek_vdir(struct file *file) ++{ ++ int valid; ++ unsigned int deblk_sz; ++ unsigned long ul, n; ++ loff_t offset; ++ union au_vdir_deblk_p p, deblk_end; ++ struct au_vdir *vdir_cache; ++ ++ valid = 1; ++ vdir_cache = au_fvdir_cache(file); ++ offset = calc_offset(vdir_cache); ++ AuDbg("offset %lld\n", offset); ++ if (file->f_pos == offset) ++ goto out; ++ ++ vdir_cache->vd_last.ul = 0; ++ vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0]; ++ if (!file->f_pos) ++ goto out; ++ ++ valid = 0; ++ deblk_sz = vdir_cache->vd_deblk_sz; ++ ul = div64_u64(file->f_pos, deblk_sz); ++ AuDbg("ul %lu\n", ul); ++ if (ul >= vdir_cache->vd_nblk) ++ goto out; ++ ++ n = vdir_cache->vd_nblk; ++ for (; ul < n; ul++) { ++ p.deblk = vdir_cache->vd_deblk[ul]; ++ deblk_end.deblk = p.deblk + deblk_sz; ++ offset = ul; ++ offset *= deblk_sz; ++ while (!is_deblk_end(&p, &deblk_end) && offset < file->f_pos) { ++ unsigned int l; ++ ++ l = calc_size(p.de->de_str.len); ++ offset += l; ++ p.deblk += l; ++ } ++ if (!is_deblk_end(&p, &deblk_end)) { ++ valid = 1; ++ vdir_cache->vd_last.ul = ul; ++ vdir_cache->vd_last.p = p; ++ break; ++ } ++ } ++ ++ out: ++ /* smp_mb(); */ ++ AuTraceErr(!valid); ++ return valid; ++} ++ ++int au_vdir_fill_de(struct file *file, void *dirent, filldir_t filldir) ++{ ++ int err; ++ unsigned int l, deblk_sz; ++ union au_vdir_deblk_p deblk_end; ++ struct au_vdir *vdir_cache; ++ struct au_vdir_de *de; ++ ++ vdir_cache = au_fvdir_cache(file); ++ if (!seek_vdir(file)) ++ return 0; ++ ++ deblk_sz = vdir_cache->vd_deblk_sz; ++ while (1) { ++ deblk_end.deblk = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; ++ deblk_end.deblk += deblk_sz; ++ while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) { ++ de = vdir_cache->vd_last.p.de; ++ AuDbg("%.*s, off%lld, i%lu, dt%d\n", ++ de->de_str.len, de->de_str.name, file->f_pos, ++ (unsigned long)de->de_ino, de->de_type); ++ err = filldir(dirent, de->de_str.name, de->de_str.len, ++ file->f_pos, de->de_ino, de->de_type); ++ if (unlikely(err)) { ++ AuTraceErr(err); ++ /* todo: ignore the error caused by udba? */ ++ /* return err; */ ++ return 0; ++ } ++ ++ l = calc_size(de->de_str.len); ++ vdir_cache->vd_last.p.deblk += l; ++ file->f_pos += l; ++ } ++ if (vdir_cache->vd_last.ul < vdir_cache->vd_nblk - 1) { ++ vdir_cache->vd_last.ul++; ++ vdir_cache->vd_last.p.deblk ++ = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; ++ file->f_pos = deblk_sz * vdir_cache->vd_last.ul; ++ continue; ++ } ++ break; ++ } ++ ++ /* smp_mb(); */ ++ return 0; ++} +diff -Naur a/fs/aufs/vfsub.c b/fs/aufs/vfsub.c +--- a/fs/aufs/vfsub.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/vfsub.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,736 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * sub-routines for VFS ++ */ ++ ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++int vfsub_update_h_iattr(struct path *h_path, int *did) ++{ ++ int err; ++ struct kstat st; ++ struct super_block *h_sb; ++ ++ /* for remote fs, leave work for its getattr or d_revalidate */ ++ /* for bad i_attr fs, handle them in aufs_getattr() */ ++ /* still some fs may acquire i_mutex. we need to skip them */ ++ err = 0; ++ if (!did) ++ did = &err; ++ h_sb = h_path->dentry->d_sb; ++ *did = (!au_test_fs_remote(h_sb) && au_test_fs_refresh_iattr(h_sb)); ++ if (*did) ++ err = vfs_getattr(h_path->mnt, h_path->dentry, &st); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct file *vfsub_filp_open(const char *path, int oflags, int mode) ++{ ++ struct file *file; ++ ++ lockdep_off(); ++ file = filp_open(path, oflags, mode); ++ lockdep_on(); ++ if (IS_ERR(file)) ++ goto out; ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ ++ out: ++ return file; ++} ++ ++int vfsub_kern_path(const char *name, unsigned int flags, struct path *path) ++{ ++ int err; ++ ++ /* lockdep_off(); */ ++ err = kern_path(name, flags, path); ++ /* lockdep_on(); */ ++ if (!err && path->dentry->d_inode) ++ vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, ++ int len) ++{ ++ struct path path = { ++ .mnt = NULL ++ }; ++ ++ IMustLock(parent->d_inode); ++ ++ path.dentry = lookup_one_len(name, parent, len); ++ if (IS_ERR(path.dentry)) ++ goto out; ++ if (path.dentry->d_inode) ++ vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ ++ ++ out: ++ return path.dentry; ++} ++ ++struct dentry *vfsub_lookup_hash(struct nameidata *nd) ++{ ++ struct path path = { ++ .mnt = nd->path.mnt ++ }; ++ ++ IMustLock(nd->path.dentry->d_inode); ++ ++ path.dentry = lookup_hash(nd); ++ if (!IS_ERR(path.dentry) && path.dentry->d_inode) ++ vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ ++ ++ return path.dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, ++ struct dentry *d2, struct au_hinode *hdir2) ++{ ++ struct dentry *d; ++ ++ lockdep_off(); ++ d = lock_rename(d1, d2); ++ lockdep_on(); ++ au_hin_suspend(hdir1); ++ if (hdir1 != hdir2) ++ au_hin_suspend(hdir2); ++ ++ return d; ++} ++ ++void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, ++ struct dentry *d2, struct au_hinode *hdir2) ++{ ++ au_hin_resume(hdir1); ++ if (hdir1 != hdir2) ++ au_hin_resume(hdir2); ++ lockdep_off(); ++ unlock_rename(d1, d2); ++ lockdep_on(); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int vfsub_create(struct inode *dir, struct path *path, int mode) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_mknod(path, path->dentry, mode, 0); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ if (au_test_fs_null_nd(dir->i_sb)) ++ err = vfs_create(dir, path->dentry, mode, NULL); ++ else { ++ struct nameidata h_nd; ++ ++ memset(&h_nd, 0, sizeof(h_nd)); ++ h_nd.flags = LOOKUP_CREATE; ++ h_nd.intent.open.flags = O_CREAT ++ | vfsub_fmode_to_uint(FMODE_READ); ++ h_nd.intent.open.create_mode = mode; ++ h_nd.path.dentry = path->dentry->d_parent; ++ h_nd.path.mnt = path->mnt; ++ path_get(&h_nd.path); ++ err = vfs_create(dir, path->dentry, mode, &h_nd); ++ path_put(&h_nd.path); ++ } ++ ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++ out: ++ return err; ++} ++ ++int vfsub_symlink(struct inode *dir, struct path *path, const char *symname) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_symlink(path, path->dentry, symname); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ err = vfs_symlink(dir, path->dentry, symname); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++ out: ++ return err; ++} ++ ++int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_mknod(path, path->dentry, mode, dev); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ err = vfs_mknod(dir, path->dentry, mode, dev); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++ out: ++ return err; ++} ++ ++static int au_test_nlink(struct inode *inode) ++{ ++ const unsigned int link_max = UINT_MAX >> 1; /* rough margin */ ++ ++ if (!au_test_fs_no_limit_nlink(inode->i_sb) ++ || inode->i_nlink < link_max) ++ return 0; ++ return -EMLINK; ++} ++ ++int vfsub_link(struct dentry *src_dentry, struct inode *dir, struct path *path) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ err = au_test_nlink(src_dentry->d_inode); ++ if (unlikely(err)) ++ return err; ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_link(src_dentry, path, path->dentry); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_link(src_dentry, dir, path->dentry); ++ lockdep_on(); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ /* fuse has different memory inode for the same inumber */ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ tmp.dentry = src_dentry; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++ out: ++ return err; ++} ++ ++int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, ++ struct inode *dir, struct path *path) ++{ ++ int err; ++ struct path tmp = { ++ .mnt = path->mnt ++ }; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ IMustLock(src_dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ tmp.dentry = src_dentry->d_parent; ++ err = security_path_rename(&tmp, src_dentry, path, path->dentry); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_rename(src_dir, src_dentry, dir, path->dentry); ++ lockdep_on(); ++ if (!err) { ++ int did; ++ ++ tmp.dentry = d->d_parent; ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = src_dentry; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ tmp.dentry = src_dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++ out: ++ return err; ++} ++ ++int vfsub_mkdir(struct inode *dir, struct path *path, int mode) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_mkdir(path, path->dentry, mode); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ err = vfs_mkdir(dir, path->dentry, mode); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++ out: ++ return err; ++} ++ ++int vfsub_rmdir(struct inode *dir, struct path *path) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_rmdir(path, path->dentry); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_rmdir(dir, path->dentry); ++ lockdep_on(); ++ if (!err) { ++ struct path tmp = { ++ .dentry = path->dentry->d_parent, ++ .mnt = path->mnt ++ }; ++ ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ ++ } ++ ++ out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ ++ err = vfs_read(file, ubuf, count, ppos); ++ if (err >= 0) ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++/* todo: kernel_read()? */ ++ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ err = vfsub_read_u(file, (char __user *)kbuf, count, ppos); ++ set_fs(oldfs); ++ return err; ++} ++ ++ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ ++ lockdep_off(); ++ err = vfs_write(file, ubuf, count, ppos); ++ lockdep_on(); ++ if (err >= 0) ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ err = vfsub_write_u(file, (const char __user *)kbuf, count, ppos); ++ set_fs(oldfs); ++ return err; ++} ++ ++int vfsub_readdir(struct file *file, filldir_t filldir, void *arg) ++{ ++ int err; ++ ++ lockdep_off(); ++ err = vfs_readdir(file, filldir, arg); ++ lockdep_on(); ++ if (err >= 0) ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++long vfsub_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) ++{ ++ long err; ++ ++ lockdep_off(); ++ err = do_splice_to(in, ppos, pipe, len, flags); ++ lockdep_on(); ++ if (err >= 0) ++ vfsub_update_h_iattr(&in->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags) ++{ ++ long err; ++ ++ lockdep_off(); ++ err = do_splice_from(pipe, out, ppos, len, flags); ++ lockdep_on(); ++ if (err >= 0) ++ vfsub_update_h_iattr(&out->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++/* cf. open.c:do_sys_truncate() and do_sys_ftruncate() */ ++int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, ++ struct file *h_file) ++{ ++ int err; ++ struct inode *h_inode; ++ ++ h_inode = h_path->dentry->d_inode; ++ if (!h_file) { ++ err = mnt_want_write(h_path->mnt); ++ if (err) ++ goto out; ++ err = inode_permission(h_inode, MAY_WRITE); ++ if (err) ++ goto out_mnt; ++ err = get_write_access(h_inode); ++ if (err) ++ goto out_mnt; ++ err = break_lease(h_inode, vfsub_fmode_to_uint(FMODE_WRITE)); ++ if (err) ++ goto out_inode; ++ } ++ ++ err = locks_verify_truncate(h_inode, h_file, length); ++ if (!err) ++ err = security_path_truncate(h_path, length, attr); ++ if (!err) { ++ lockdep_off(); ++ err = do_truncate(h_path->dentry, length, attr, h_file); ++ lockdep_on(); ++ } ++ ++ out_inode: ++ if (!h_file) ++ put_write_access(h_inode); ++ out_mnt: ++ if (!h_file) ++ mnt_drop_write(h_path->mnt); ++ out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_vfsub_mkdir_args { ++ int *errp; ++ struct inode *dir; ++ struct path *path; ++ int mode; ++}; ++ ++static void au_call_vfsub_mkdir(void *args) ++{ ++ struct au_vfsub_mkdir_args *a = args; ++ *a->errp = vfsub_mkdir(a->dir, a->path, a->mode); ++} ++ ++int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode) ++{ ++ int err, do_sio, wkq_err; ++ ++ do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); ++ if (!do_sio) ++ err = vfsub_mkdir(dir, path, mode); ++ else { ++ struct au_vfsub_mkdir_args args = { ++ .errp = &err, ++ .dir = dir, ++ .path = path, ++ .mode = mode ++ }; ++ wkq_err = au_wkq_wait(au_call_vfsub_mkdir, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} ++ ++struct au_vfsub_rmdir_args { ++ int *errp; ++ struct inode *dir; ++ struct path *path; ++}; ++ ++static void au_call_vfsub_rmdir(void *args) ++{ ++ struct au_vfsub_rmdir_args *a = args; ++ *a->errp = vfsub_rmdir(a->dir, a->path); ++} ++ ++int vfsub_sio_rmdir(struct inode *dir, struct path *path) ++{ ++ int err, do_sio, wkq_err; ++ ++ do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); ++ if (!do_sio) ++ err = vfsub_rmdir(dir, path); ++ else { ++ struct au_vfsub_rmdir_args args = { ++ .errp = &err, ++ .dir = dir, ++ .path = path ++ }; ++ wkq_err = au_wkq_wait(au_call_vfsub_rmdir, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct notify_change_args { ++ int *errp; ++ struct path *path; ++ struct iattr *ia; ++}; ++ ++static void call_notify_change(void *args) ++{ ++ struct notify_change_args *a = args; ++ struct inode *h_inode; ++ ++ h_inode = a->path->dentry->d_inode; ++ IMustLock(h_inode); ++ ++ *a->errp = -EPERM; ++ if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) { ++ lockdep_off(); ++ *a->errp = notify_change(a->path->dentry, a->ia); ++ lockdep_on(); ++ if (!*a->errp) ++ vfsub_update_h_iattr(a->path, /*did*/NULL); /*ignore*/ ++ } ++ AuTraceErr(*a->errp); ++} ++ ++int vfsub_notify_change(struct path *path, struct iattr *ia) ++{ ++ int err; ++ struct notify_change_args args = { ++ .errp = &err, ++ .path = path, ++ .ia = ia ++ }; ++ ++ call_notify_change(&args); ++ ++ return err; ++} ++ ++int vfsub_sio_notify_change(struct path *path, struct iattr *ia) ++{ ++ int err, wkq_err; ++ struct notify_change_args args = { ++ .errp = &err, ++ .path = path, ++ .ia = ia ++ }; ++ ++ wkq_err = au_wkq_wait(call_notify_change, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct unlink_args { ++ int *errp; ++ struct inode *dir; ++ struct path *path; ++}; ++ ++static void call_unlink(void *args) ++{ ++ struct unlink_args *a = args; ++ struct dentry *d = a->path->dentry; ++ struct inode *h_inode; ++ const int stop_sillyrename = (au_test_nfs(d->d_sb) ++ && atomic_read(&d->d_count) == 1); ++ ++ IMustLock(a->dir); ++ ++ a->path->dentry = d->d_parent; ++ *a->errp = security_path_unlink(a->path, d); ++ a->path->dentry = d; ++ if (unlikely(*a->errp)) ++ return; ++ ++ if (!stop_sillyrename) ++ dget(d); ++ h_inode = d->d_inode; ++ if (h_inode) ++ atomic_inc(&h_inode->i_count); ++ ++ lockdep_off(); ++ *a->errp = vfs_unlink(a->dir, d); ++ lockdep_on(); ++ if (!*a->errp) { ++ struct path tmp = { ++ .dentry = d->d_parent, ++ .mnt = a->path->mnt ++ }; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ ++ } ++ ++ if (!stop_sillyrename) ++ dput(d); ++ if (h_inode) ++ iput(h_inode); ++ ++ AuTraceErr(*a->errp); ++} ++ ++/* ++ * @dir: must be locked. ++ * @dentry: target dentry. ++ */ ++int vfsub_unlink(struct inode *dir, struct path *path, int force) ++{ ++ int err; ++ struct unlink_args args = { ++ .errp = &err, ++ .dir = dir, ++ .path = path ++ }; ++ ++ if (!force) ++ call_unlink(&args); ++ else { ++ int wkq_err; ++ ++ wkq_err = au_wkq_wait(call_unlink, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} +diff -Naur a/fs/aufs/vfsub.h b/fs/aufs/vfsub.h +--- a/fs/aufs/vfsub.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/vfsub.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,171 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * sub-routines for VFS ++ */ ++ ++#ifndef __AUFS_VFSUB_H__ ++#define __AUFS_VFSUB_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* lock subclass for lower inode */ ++/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */ ++/* reduce? gave up. */ ++enum { ++ AuLsc_I_Begin = I_MUTEX_QUOTA, /* 4 */ ++ AuLsc_I_PARENT, /* lower inode, parent first */ ++ AuLsc_I_PARENT2, /* copyup dirs */ ++ AuLsc_I_CHILD, ++ AuLsc_I_CHILD2, ++ AuLsc_I_End ++}; ++ ++/* to debug easier, do not make them inlined functions */ ++#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx)) ++#define IMustLock(i) MtxMustLock(&(i)->i_mutex) ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline void vfsub_copy_inode_size(struct inode *inode, ++ struct inode *h_inode) ++{ ++ spin_lock(&inode->i_lock); ++ fsstack_copy_inode_size(inode, h_inode); ++ spin_unlock(&inode->i_lock); ++} ++ ++int vfsub_update_h_iattr(struct path *h_path, int *did); ++struct file *vfsub_filp_open(const char *path, int oflags, int mode); ++int vfsub_kern_path(const char *name, unsigned int flags, struct path *path); ++struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, ++ int len); ++struct dentry *vfsub_lookup_hash(struct nameidata *nd); ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_hinode; ++struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, ++ struct dentry *d2, struct au_hinode *hdir2); ++void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, ++ struct dentry *d2, struct au_hinode *hdir2); ++ ++int vfsub_create(struct inode *dir, struct path *path, int mode); ++int vfsub_symlink(struct inode *dir, struct path *path, ++ const char *symname); ++int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev); ++int vfsub_link(struct dentry *src_dentry, struct inode *dir, ++ struct path *path); ++int vfsub_rename(struct inode *src_hdir, struct dentry *src_dentry, ++ struct inode *hdir, struct path *path); ++int vfsub_mkdir(struct inode *dir, struct path *path, int mode); ++int vfsub_rmdir(struct inode *dir, struct path *path); ++ ++/* ---------------------------------------------------------------------- */ ++ ++ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, ++ loff_t *ppos); ++ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, ++ loff_t *ppos); ++ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, ++ loff_t *ppos); ++ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, ++ loff_t *ppos); ++int vfsub_readdir(struct file *file, filldir_t filldir, void *arg); ++ ++static inline void vfsub_file_accessed(struct file *h_file) ++{ ++ file_accessed(h_file); ++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); /*ignore*/ ++} ++ ++static inline void vfsub_touch_atime(struct vfsmount *h_mnt, ++ struct dentry *h_dentry) ++{ ++ struct path h_path = { ++ .dentry = h_dentry, ++ .mnt = h_mnt ++ }; ++ touch_atime(h_mnt, h_dentry); ++ vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/ ++} ++ ++long vfsub_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags); ++long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags); ++int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, ++ struct file *h_file); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin) ++{ ++ loff_t err; ++ ++ lockdep_off(); ++ err = vfs_llseek(file, offset, origin); ++ lockdep_on(); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dirty workaround for strict type of fmode_t */ ++union vfsub_fmu { ++ fmode_t fm; ++ unsigned int ui; ++}; ++ ++static inline unsigned int vfsub_fmode_to_uint(fmode_t fm) ++{ ++ union vfsub_fmu u = { ++ .fm = fm ++ }; ++ ++ BUILD_BUG_ON(sizeof(u.fm) != sizeof(u.ui)); ++ ++ return u.ui; ++} ++ ++static inline fmode_t vfsub_uint_to_fmode(unsigned int ui) ++{ ++ union vfsub_fmu u = { ++ .ui = ui ++ }; ++ ++ return u.fm; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode); ++int vfsub_sio_rmdir(struct inode *dir, struct path *path); ++int vfsub_sio_notify_change(struct path *path, struct iattr *ia); ++int vfsub_notify_change(struct path *path, struct iattr *ia); ++int vfsub_unlink(struct inode *dir, struct path *path, int force); ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_VFSUB_H__ */ +diff -Naur a/fs/aufs/wbr_policy.c b/fs/aufs/wbr_policy.c +--- a/fs/aufs/wbr_policy.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/wbr_policy.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,637 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * policies for selecting one among multiple writable branches ++ */ ++ ++#include ++#include "aufs.h" ++ ++/* subset of cpup_attr() */ ++static noinline_for_stack ++int au_cpdown_attr(struct path *h_path, struct dentry *h_src) ++{ ++ int err, sbits; ++ struct iattr ia; ++ struct inode *h_isrc; ++ ++ h_isrc = h_src->d_inode; ++ ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID; ++ ia.ia_mode = h_isrc->i_mode; ++ ia.ia_uid = h_isrc->i_uid; ++ ia.ia_gid = h_isrc->i_gid; ++ sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); ++ au_cpup_attr_flags(h_path->dentry->d_inode, h_isrc); ++ err = vfsub_sio_notify_change(h_path, &ia); ++ ++ /* is this nfs only? */ ++ if (!err && sbits && au_test_nfs(h_path->dentry->d_sb)) { ++ ia.ia_valid = ATTR_FORCE | ATTR_MODE; ++ ia.ia_mode = h_isrc->i_mode; ++ err = vfsub_sio_notify_change(h_path, &ia); ++ } ++ ++ return err; ++} ++ ++#define AuCpdown_PARENT_OPQ 1 ++#define AuCpdown_WHED (1 << 1) ++#define AuCpdown_MADE_DIR (1 << 2) ++#define AuCpdown_DIROPQ (1 << 3) ++#define au_ftest_cpdown(flags, name) ((flags) & AuCpdown_##name) ++#define au_fset_cpdown(flags, name) { (flags) |= AuCpdown_##name; } ++#define au_fclr_cpdown(flags, name) { (flags) &= ~AuCpdown_##name; } ++ ++struct au_cpdown_dir_args { ++ struct dentry *parent; ++ unsigned int flags; ++}; ++ ++static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_cpdown_dir_args *a) ++{ ++ int err; ++ struct dentry *opq_dentry; ++ ++ opq_dentry = au_diropq_create(dentry, bdst); ++ err = PTR_ERR(opq_dentry); ++ if (IS_ERR(opq_dentry)) ++ goto out; ++ dput(opq_dentry); ++ au_fset_cpdown(a->flags, DIROPQ); ++ ++ out: ++ return err; ++} ++ ++static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent, ++ struct inode *dir, aufs_bindex_t bdst) ++{ ++ int err; ++ struct path h_path; ++ struct au_branch *br; ++ ++ br = au_sbr(dentry->d_sb, bdst); ++ h_path.dentry = au_wh_lkup(h_parent, &dentry->d_name, br); ++ err = PTR_ERR(h_path.dentry); ++ if (IS_ERR(h_path.dentry)) ++ goto out; ++ ++ err = 0; ++ if (h_path.dentry->d_inode) { ++ h_path.mnt = br->br_mnt; ++ err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path, ++ dentry); ++ } ++ dput(h_path.dentry); ++ ++ out: ++ return err; ++} ++ ++static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, ++ struct dentry *h_parent, void *arg) ++{ ++ int err, rerr; ++ aufs_bindex_t bend, bopq, bstart; ++ unsigned char parent_opq; ++ struct path h_path; ++ struct dentry *parent; ++ struct inode *h_dir, *h_inode, *inode, *dir; ++ struct au_cpdown_dir_args *args = arg; ++ ++ bstart = au_dbstart(dentry); ++ /* dentry is di-locked */ ++ parent = dget_parent(dentry); ++ dir = parent->d_inode; ++ h_dir = h_parent->d_inode; ++ AuDebugOn(h_dir != au_h_iptr(dir, bdst)); ++ IMustLock(h_dir); ++ ++ err = au_lkup_neg(dentry, bdst); ++ if (unlikely(err < 0)) ++ goto out; ++ h_path.dentry = au_h_dptr(dentry, bdst); ++ h_path.mnt = au_sbr_mnt(dentry->d_sb, bdst); ++ err = vfsub_sio_mkdir(au_h_iptr(dir, bdst), &h_path, ++ S_IRWXU | S_IRUGO | S_IXUGO); ++ if (unlikely(err)) ++ goto out_put; ++ au_fset_cpdown(args->flags, MADE_DIR); ++ ++ bend = au_dbend(dentry); ++ bopq = au_dbdiropq(dentry); ++ au_fclr_cpdown(args->flags, WHED); ++ au_fclr_cpdown(args->flags, DIROPQ); ++ if (au_dbwh(dentry) == bdst) ++ au_fset_cpdown(args->flags, WHED); ++ if (!au_ftest_cpdown(args->flags, PARENT_OPQ) && bopq <= bdst) ++ au_fset_cpdown(args->flags, PARENT_OPQ); ++ parent_opq = (au_ftest_cpdown(args->flags, PARENT_OPQ) ++ && args->parent == dentry); ++ h_inode = h_path.dentry->d_inode; ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ if (au_ftest_cpdown(args->flags, WHED)) { ++ err = au_cpdown_dir_opq(dentry, bdst, args); ++ if (unlikely(err)) { ++ mutex_unlock(&h_inode->i_mutex); ++ goto out_dir; ++ } ++ } ++ ++ err = au_cpdown_attr(&h_path, au_h_dptr(dentry, bstart)); ++ mutex_unlock(&h_inode->i_mutex); ++ if (unlikely(err)) ++ goto out_opq; ++ ++ if (au_ftest_cpdown(args->flags, WHED)) { ++ err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst); ++ if (unlikely(err)) ++ goto out_opq; ++ } ++ ++ inode = dentry->d_inode; ++ if (au_ibend(inode) < bdst) ++ au_set_ibend(inode, bdst); ++ au_set_h_iptr(inode, bdst, au_igrab(h_inode), ++ au_hi_flags(inode, /*isdir*/1)); ++ goto out; /* success */ ++ ++ /* revert */ ++ out_opq: ++ if (au_ftest_cpdown(args->flags, DIROPQ)) { ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ rerr = au_diropq_remove(dentry, bdst); ++ mutex_unlock(&h_inode->i_mutex); ++ if (unlikely(rerr)) { ++ AuIOErr("failed removing diropq for %.*s b%d (%d)\n", ++ AuDLNPair(dentry), bdst, rerr); ++ err = -EIO; ++ goto out; ++ } ++ } ++ out_dir: ++ if (au_ftest_cpdown(args->flags, MADE_DIR)) { ++ rerr = vfsub_sio_rmdir(au_h_iptr(dir, bdst), &h_path); ++ if (unlikely(rerr)) { ++ AuIOErr("failed removing %.*s b%d (%d)\n", ++ AuDLNPair(dentry), bdst, rerr); ++ err = -EIO; ++ } ++ } ++ out_put: ++ au_set_h_dptr(dentry, bdst, NULL); ++ if (au_dbend(dentry) == bdst) ++ au_update_dbend(dentry); ++ out: ++ dput(parent); ++ return err; ++} ++ ++int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst) ++{ ++ int err; ++ struct au_cpdown_dir_args args = { ++ .parent = dget_parent(dentry), ++ .flags = 0 ++ }; ++ ++ err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &args); ++ dput(args.parent); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* policies for create */ ++ ++static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ for (; bindex >= 0; bindex--) ++ if (!au_br_rdonly(au_sbr(sb, bindex))) ++ return bindex; ++ return -EROFS; ++} ++ ++/* top down parent */ ++static int au_wbr_create_tdp(struct dentry *dentry, int isdir __maybe_unused) ++{ ++ int err; ++ aufs_bindex_t bstart, bindex; ++ struct super_block *sb; ++ struct dentry *parent, *h_parent; ++ ++ sb = dentry->d_sb; ++ bstart = au_dbstart(dentry); ++ err = bstart; ++ if (!au_br_rdonly(au_sbr(sb, bstart))) ++ goto out; ++ ++ err = -EROFS; ++ parent = dget_parent(dentry); ++ for (bindex = au_dbstart(parent); bindex < bstart; bindex++) { ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent || !h_parent->d_inode) ++ continue; ++ ++ if (!au_br_rdonly(au_sbr(sb, bindex))) { ++ err = bindex; ++ break; ++ } ++ } ++ dput(parent); ++ ++ /* bottom up here */ ++ if (unlikely(err < 0)) ++ err = au_wbr_bu(sb, bstart - 1); ++ ++ out: ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* an exception for the policy other than tdp */ ++static int au_wbr_create_exp(struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bwh, bdiropq; ++ struct dentry *parent; ++ ++ err = -1; ++ bwh = au_dbwh(dentry); ++ parent = dget_parent(dentry); ++ bdiropq = au_dbdiropq(parent); ++ if (bwh >= 0) { ++ if (bdiropq >= 0) ++ err = min(bdiropq, bwh); ++ else ++ err = bwh; ++ AuDbg("%d\n", err); ++ } else if (bdiropq >= 0) { ++ err = bdiropq; ++ AuDbg("%d\n", err); ++ } ++ dput(parent); ++ ++ if (err >= 0 && au_br_rdonly(au_sbr(dentry->d_sb, err))) ++ err = -1; ++ ++ AuDbg("%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* round robin */ ++static int au_wbr_create_init_rr(struct super_block *sb) ++{ ++ int err; ++ ++ err = au_wbr_bu(sb, au_sbend(sb)); ++ atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */ ++ ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++static int au_wbr_create_rr(struct dentry *dentry, int isdir) ++{ ++ int err, nbr; ++ unsigned int u; ++ aufs_bindex_t bindex, bend; ++ struct super_block *sb; ++ atomic_t *next; ++ ++ err = au_wbr_create_exp(dentry); ++ if (err >= 0) ++ goto out; ++ ++ sb = dentry->d_sb; ++ next = &au_sbi(sb)->si_wbr_rr_next; ++ bend = au_sbend(sb); ++ nbr = bend + 1; ++ for (bindex = 0; bindex <= bend; bindex++) { ++ if (!isdir) { ++ err = atomic_dec_return(next) + 1; ++ /* modulo for 0 is meaningless */ ++ if (unlikely(!err)) ++ err = atomic_dec_return(next) + 1; ++ } else ++ err = atomic_read(next); ++ AuDbg("%d\n", err); ++ u = err; ++ err = u % nbr; ++ AuDbg("%d\n", err); ++ if (!au_br_rdonly(au_sbr(sb, err))) ++ break; ++ err = -EROFS; ++ } ++ ++ out: ++ AuDbg("%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* most free space */ ++static void au_mfs(struct dentry *dentry) ++{ ++ struct super_block *sb; ++ struct au_branch *br; ++ struct au_wbr_mfs *mfs; ++ aufs_bindex_t bindex, bend; ++ int err; ++ unsigned long long b, bavail; ++ /* reduce the stack usage */ ++ struct kstatfs *st; ++ ++ st = kmalloc(sizeof(*st), GFP_NOFS); ++ if (unlikely(!st)) { ++ AuWarn1("failed updating mfs(%d), ignored\n", -ENOMEM); ++ return; ++ } ++ ++ bavail = 0; ++ sb = dentry->d_sb; ++ mfs = &au_sbi(sb)->si_wbr_mfs; ++ mfs->mfs_bindex = -EROFS; ++ mfs->mfsrr_bytes = 0; ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_rdonly(br)) ++ continue; ++ ++ /* sb->s_root for NFS is unreliable */ ++ err = vfs_statfs(br->br_mnt->mnt_root, st); ++ if (unlikely(err)) { ++ AuWarn1("failed statfs, b%d, %d\n", bindex, err); ++ continue; ++ } ++ ++ /* when the available size is equal, select the lower one */ ++ BUILD_BUG_ON(sizeof(b) < sizeof(st->f_bavail) ++ || sizeof(b) < sizeof(st->f_bsize)); ++ b = st->f_bavail * st->f_bsize; ++ br->br_wbr->wbr_bytes = b; ++ if (b >= bavail) { ++ bavail = b; ++ mfs->mfs_bindex = bindex; ++ mfs->mfs_jiffy = jiffies; ++ } ++ } ++ ++ mfs->mfsrr_bytes = bavail; ++ AuDbg("b%d\n", mfs->mfs_bindex); ++ kfree(st); ++} ++ ++static int au_wbr_create_mfs(struct dentry *dentry, int isdir __maybe_unused) ++{ ++ int err; ++ struct super_block *sb; ++ struct au_wbr_mfs *mfs; ++ ++ err = au_wbr_create_exp(dentry); ++ if (err >= 0) ++ goto out; ++ ++ sb = dentry->d_sb; ++ mfs = &au_sbi(sb)->si_wbr_mfs; ++ mutex_lock(&mfs->mfs_lock); ++ if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire) ++ || mfs->mfs_bindex < 0 ++ || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex))) ++ au_mfs(dentry); ++ mutex_unlock(&mfs->mfs_lock); ++ err = mfs->mfs_bindex; ++ ++ out: ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++static int au_wbr_create_init_mfs(struct super_block *sb) ++{ ++ struct au_wbr_mfs *mfs; ++ ++ mfs = &au_sbi(sb)->si_wbr_mfs; ++ mutex_init(&mfs->mfs_lock); ++ mfs->mfs_jiffy = 0; ++ mfs->mfs_bindex = -EROFS; ++ ++ return 0; ++} ++ ++static int au_wbr_create_fin_mfs(struct super_block *sb __maybe_unused) ++{ ++ mutex_destroy(&au_sbi(sb)->si_wbr_mfs.mfs_lock); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* most free space and then round robin */ ++static int au_wbr_create_mfsrr(struct dentry *dentry, int isdir) ++{ ++ int err; ++ struct au_wbr_mfs *mfs; ++ ++ err = au_wbr_create_mfs(dentry, isdir); ++ if (err >= 0) { ++ mfs = &au_sbi(dentry->d_sb)->si_wbr_mfs; ++ if (mfs->mfsrr_bytes < mfs->mfsrr_watermark) ++ err = au_wbr_create_rr(dentry, isdir); ++ } ++ ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++static int au_wbr_create_init_mfsrr(struct super_block *sb) ++{ ++ int err; ++ ++ au_wbr_create_init_mfs(sb); /* ignore */ ++ err = au_wbr_create_init_rr(sb); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* top down parent and most free space */ ++static int au_wbr_create_pmfs(struct dentry *dentry, int isdir) ++{ ++ int err, e2; ++ unsigned long long b; ++ aufs_bindex_t bindex, bstart, bend; ++ struct super_block *sb; ++ struct dentry *parent, *h_parent; ++ struct au_branch *br; ++ ++ err = au_wbr_create_tdp(dentry, isdir); ++ if (unlikely(err < 0)) ++ goto out; ++ parent = dget_parent(dentry); ++ bstart = au_dbstart(parent); ++ bend = au_dbtaildir(parent); ++ if (bstart == bend) ++ goto out_parent; /* success */ ++ ++ e2 = au_wbr_create_mfs(dentry, isdir); ++ if (e2 < 0) ++ goto out_parent; /* success */ ++ ++ /* when the available size is equal, select upper one */ ++ sb = dentry->d_sb; ++ br = au_sbr(sb, err); ++ b = br->br_wbr->wbr_bytes; ++ AuDbg("b%d, %llu\n", err, b); ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent || !h_parent->d_inode) ++ continue; ++ ++ br = au_sbr(sb, bindex); ++ if (!au_br_rdonly(br) && br->br_wbr->wbr_bytes > b) { ++ b = br->br_wbr->wbr_bytes; ++ err = bindex; ++ AuDbg("b%d, %llu\n", err, b); ++ } ++ } ++ ++ out_parent: ++ dput(parent); ++ out: ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* policies for copyup */ ++ ++/* top down parent */ ++static int au_wbr_copyup_tdp(struct dentry *dentry) ++{ ++ return au_wbr_create_tdp(dentry, /*isdir, anything is ok*/0); ++} ++ ++/* bottom up parent */ ++static int au_wbr_copyup_bup(struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bindex, bstart; ++ struct dentry *parent, *h_parent; ++ struct super_block *sb; ++ ++ err = -EROFS; ++ sb = dentry->d_sb; ++ parent = dget_parent(dentry); ++ bstart = au_dbstart(parent); ++ for (bindex = au_dbstart(dentry); bindex >= bstart; bindex--) { ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent || !h_parent->d_inode) ++ continue; ++ ++ if (!au_br_rdonly(au_sbr(sb, bindex))) { ++ err = bindex; ++ break; ++ } ++ } ++ dput(parent); ++ ++ /* bottom up here */ ++ if (unlikely(err < 0)) ++ err = au_wbr_bu(sb, bstart - 1); ++ ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++/* bottom up */ ++static int au_wbr_copyup_bu(struct dentry *dentry) ++{ ++ int err; ++ ++ err = au_wbr_bu(dentry->d_sb, au_dbstart(dentry)); ++ ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_wbr_copyup_operations au_wbr_copyup_ops[] = { ++ [AuWbrCopyup_TDP] = { ++ .copyup = au_wbr_copyup_tdp ++ }, ++ [AuWbrCopyup_BUP] = { ++ .copyup = au_wbr_copyup_bup ++ }, ++ [AuWbrCopyup_BU] = { ++ .copyup = au_wbr_copyup_bu ++ } ++}; ++ ++struct au_wbr_create_operations au_wbr_create_ops[] = { ++ [AuWbrCreate_TDP] = { ++ .create = au_wbr_create_tdp ++ }, ++ [AuWbrCreate_RR] = { ++ .create = au_wbr_create_rr, ++ .init = au_wbr_create_init_rr ++ }, ++ [AuWbrCreate_MFS] = { ++ .create = au_wbr_create_mfs, ++ .init = au_wbr_create_init_mfs, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_MFSV] = { ++ .create = au_wbr_create_mfs, ++ .init = au_wbr_create_init_mfs, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_MFSRR] = { ++ .create = au_wbr_create_mfsrr, ++ .init = au_wbr_create_init_mfsrr, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_MFSRRV] = { ++ .create = au_wbr_create_mfsrr, ++ .init = au_wbr_create_init_mfsrr, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_PMFS] = { ++ .create = au_wbr_create_pmfs, ++ .init = au_wbr_create_init_mfs, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_PMFSV] = { ++ .create = au_wbr_create_pmfs, ++ .init = au_wbr_create_init_mfs, ++ .fin = au_wbr_create_fin_mfs ++ } ++}; +diff -Naur a/fs/aufs/whout.c b/fs/aufs/whout.c +--- a/fs/aufs/whout.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/whout.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,1042 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * whiteout for logical deletion and opaque directory ++ */ ++ ++#include ++#include "aufs.h" ++ ++#define WH_MASK S_IRUGO ++ ++/* ++ * If a directory contains this file, then it is opaque. We start with the ++ * .wh. flag so that it is blocked by lookup. ++ */ ++static struct qstr diropq_name = { ++ .name = AUFS_WH_DIROPQ, ++ .len = sizeof(AUFS_WH_DIROPQ) - 1 ++}; ++ ++/* ++ * generate whiteout name, which is NOT terminated by NULL. ++ * @name: original d_name.name ++ * @len: original d_name.len ++ * @wh: whiteout qstr ++ * returns zero when succeeds, otherwise error. ++ * succeeded value as wh->name should be freed by kfree(). ++ */ ++int au_wh_name_alloc(struct qstr *wh, const struct qstr *name) ++{ ++ char *p; ++ ++ if (unlikely(name->len > PATH_MAX - AUFS_WH_PFX_LEN)) ++ return -ENAMETOOLONG; ++ ++ wh->len = name->len + AUFS_WH_PFX_LEN; ++ p = kmalloc(wh->len, GFP_NOFS); ++ wh->name = p; ++ if (p) { ++ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); ++ memcpy(p + AUFS_WH_PFX_LEN, name->name, name->len); ++ /* smp_mb(); */ ++ return 0; ++ } ++ return -ENOMEM; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * test if the @wh_name exists under @h_parent. ++ * @try_sio specifies the necessary of super-io. ++ */ ++int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, ++ struct au_branch *br, int try_sio) ++{ ++ int err; ++ struct dentry *wh_dentry; ++ struct inode *h_dir; ++ ++ h_dir = h_parent->d_inode; ++ if (!try_sio) ++ wh_dentry = au_lkup_one(wh_name, h_parent, br, /*nd*/NULL); ++ else ++ wh_dentry = au_sio_lkup_one(wh_name, h_parent, br); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out; ++ ++ err = 0; ++ if (!wh_dentry->d_inode) ++ goto out_wh; /* success */ ++ ++ err = 1; ++ if (S_ISREG(wh_dentry->d_inode->i_mode)) ++ goto out_wh; /* success */ ++ ++ err = -EIO; ++ AuIOErr("%.*s Invalid whiteout entry type 0%o.\n", ++ AuDLNPair(wh_dentry), wh_dentry->d_inode->i_mode); ++ ++ out_wh: ++ dput(wh_dentry); ++ out: ++ return err; ++} ++ ++/* ++ * test if the @h_dentry sets opaque or not. ++ */ ++int au_diropq_test(struct dentry *h_dentry, struct au_branch *br) ++{ ++ int err; ++ struct inode *h_dir; ++ ++ h_dir = h_dentry->d_inode; ++ err = au_wh_test(h_dentry, &diropq_name, br, ++ au_test_h_perm_sio(h_dir, MAY_EXEC)); ++ return err; ++} ++ ++/* ++ * returns a negative dentry whose name is unique and temporary. ++ */ ++struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, ++ struct qstr *prefix) ++{ ++#define HEX_LEN 4 ++ struct dentry *dentry; ++ int i; ++ char defname[AUFS_WH_PFX_LEN * 2 + DNAME_INLINE_LEN_MIN + 1 ++ + HEX_LEN + 1], *name, *p; ++ static unsigned short cnt; ++ struct qstr qs; ++ ++ name = defname; ++ qs.len = sizeof(defname) - DNAME_INLINE_LEN_MIN + prefix->len - 1; ++ if (unlikely(prefix->len > DNAME_INLINE_LEN_MIN)) { ++ dentry = ERR_PTR(-ENAMETOOLONG); ++ if (unlikely(qs.len >= PATH_MAX)) ++ goto out; ++ dentry = ERR_PTR(-ENOMEM); ++ name = kmalloc(qs.len + 1, GFP_NOFS); ++ if (unlikely(!name)) ++ goto out; ++ } ++ ++ /* doubly whiteout-ed */ ++ memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2); ++ p = name + AUFS_WH_PFX_LEN * 2; ++ memcpy(p, prefix->name, prefix->len); ++ p += prefix->len; ++ *p++ = '.'; ++ AuDebugOn(name + qs.len + 1 - p <= HEX_LEN); ++ ++ qs.name = name; ++ for (i = 0; i < 3; i++) { ++ sprintf(p, "%.*d", HEX_LEN, cnt++); ++ dentry = au_sio_lkup_one(&qs, h_parent, br); ++ if (IS_ERR(dentry) || !dentry->d_inode) ++ goto out_name; ++ dput(dentry); ++ } ++ /* AuWarn("could not get random name\n"); */ ++ dentry = ERR_PTR(-EEXIST); ++ AuDbg("%.*s\n", AuLNPair(&qs)); ++ BUG(); ++ ++ out_name: ++ if (name != defname) ++ kfree(name); ++ out: ++ return dentry; ++#undef HEX_LEN ++} ++ ++/* ++ * rename the @h_dentry on @br to the whiteouted temporary name. ++ */ ++int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br) ++{ ++ int err; ++ struct path h_path = { ++ .mnt = br->br_mnt ++ }; ++ struct inode *h_dir; ++ struct dentry *h_parent; ++ ++ h_parent = h_dentry->d_parent; /* dir inode is locked */ ++ h_dir = h_parent->d_inode; ++ IMustLock(h_dir); ++ ++ h_path.dentry = au_whtmp_lkup(h_parent, br, &h_dentry->d_name); ++ err = PTR_ERR(h_path.dentry); ++ if (IS_ERR(h_path.dentry)) ++ goto out; ++ ++ /* under the same dir, no need to lock_rename() */ ++ err = vfsub_rename(h_dir, h_dentry, h_dir, &h_path); ++ AuTraceErr(err); ++ dput(h_path.dentry); ++ ++ out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * functions for removing a whiteout ++ */ ++ ++static int do_unlink_wh(struct inode *h_dir, struct path *h_path) ++{ ++ int force; ++ ++ /* ++ * forces superio when the dir has a sticky bit. ++ * this may be a violation of unix fs semantics. ++ */ ++ force = (h_dir->i_mode & S_ISVTX) ++ && h_path->dentry->d_inode->i_uid != current_fsuid(); ++ return vfsub_unlink(h_dir, h_path, force); ++} ++ ++int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, ++ struct dentry *dentry) ++{ ++ int err; ++ ++ err = do_unlink_wh(h_dir, h_path); ++ if (!err && dentry) ++ au_set_dbwh(dentry, -1); ++ ++ return err; ++} ++ ++static int unlink_wh_name(struct dentry *h_parent, struct qstr *wh, ++ struct au_branch *br) ++{ ++ int err; ++ struct path h_path = { ++ .mnt = br->br_mnt ++ }; ++ ++ err = 0; ++ h_path.dentry = au_lkup_one(wh, h_parent, br, /*nd*/NULL); ++ if (IS_ERR(h_path.dentry)) ++ err = PTR_ERR(h_path.dentry); ++ else { ++ if (h_path.dentry->d_inode ++ && S_ISREG(h_path.dentry->d_inode->i_mode)) ++ err = do_unlink_wh(h_parent->d_inode, &h_path); ++ dput(h_path.dentry); ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * initialize/clean whiteout for a branch ++ */ ++ ++static void au_wh_clean(struct inode *h_dir, struct path *whpath, ++ const int isdir) ++{ ++ int err; ++ ++ if (!whpath->dentry->d_inode) ++ return; ++ ++ err = mnt_want_write(whpath->mnt); ++ if (!err) { ++ if (isdir) ++ err = vfsub_rmdir(h_dir, whpath); ++ else ++ err = vfsub_unlink(h_dir, whpath, /*force*/0); ++ mnt_drop_write(whpath->mnt); ++ } ++ if (unlikely(err)) ++ AuWarn("failed removing %.*s (%d), ignored.\n", ++ AuDLNPair(whpath->dentry), err); ++} ++ ++static int test_linkable(struct dentry *h_root) ++{ ++ struct inode *h_dir = h_root->d_inode; ++ ++ if (h_dir->i_op->link) ++ return 0; ++ ++ AuErr("%.*s (%s) doesn't support link(2), use noplink and rw+nolwh\n", ++ AuDLNPair(h_root), au_sbtype(h_root->d_sb)); ++ return -ENOSYS; ++} ++ ++/* todo: should this mkdir be done in /sbin/mount.aufs helper? */ ++static int au_whdir(struct inode *h_dir, struct path *path) ++{ ++ int err; ++ ++ err = -EEXIST; ++ if (!path->dentry->d_inode) { ++ int mode = S_IRWXU; ++ ++ if (au_test_nfs(path->dentry->d_sb)) ++ mode |= S_IXUGO; ++ err = mnt_want_write(path->mnt); ++ if (!err) { ++ err = vfsub_mkdir(h_dir, path, mode); ++ mnt_drop_write(path->mnt); ++ } ++ } else if (S_ISDIR(path->dentry->d_inode->i_mode)) ++ err = 0; ++ else ++ AuErr("unknown %.*s exists\n", AuDLNPair(path->dentry)); ++ ++ return err; ++} ++ ++struct au_wh_base { ++ const struct qstr *name; ++ struct dentry *dentry; ++}; ++ ++static void au_wh_init_ro(struct inode *h_dir, struct au_wh_base base[], ++ struct path *h_path) ++{ ++ h_path->dentry = base[AuBrWh_BASE].dentry; ++ au_wh_clean(h_dir, h_path, /*isdir*/0); ++ h_path->dentry = base[AuBrWh_PLINK].dentry; ++ au_wh_clean(h_dir, h_path, /*isdir*/1); ++ h_path->dentry = base[AuBrWh_ORPH].dentry; ++ au_wh_clean(h_dir, h_path, /*isdir*/1); ++} ++ ++/* ++ * returns tri-state, ++ * minus: error, caller should print the mesage ++ * zero: succuess ++ * plus: error, caller should NOT print the mesage ++ */ ++static int au_wh_init_rw_nolink(struct dentry *h_root, struct au_wbr *wbr, ++ int do_plink, struct au_wh_base base[], ++ struct path *h_path) ++{ ++ int err; ++ struct inode *h_dir; ++ ++ h_dir = h_root->d_inode; ++ h_path->dentry = base[AuBrWh_BASE].dentry; ++ au_wh_clean(h_dir, h_path, /*isdir*/0); ++ h_path->dentry = base[AuBrWh_PLINK].dentry; ++ if (do_plink) { ++ err = test_linkable(h_root); ++ if (unlikely(err)) { ++ err = 1; ++ goto out; ++ } ++ ++ err = au_whdir(h_dir, h_path); ++ if (unlikely(err)) ++ goto out; ++ wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); ++ } else ++ au_wh_clean(h_dir, h_path, /*isdir*/1); ++ h_path->dentry = base[AuBrWh_ORPH].dentry; ++ err = au_whdir(h_dir, h_path); ++ if (unlikely(err)) ++ goto out; ++ wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); ++ ++ out: ++ return err; ++} ++ ++/* ++ * for the moment, aufs supports the branch filesystem which does not support ++ * link(2). testing on FAT which does not support i_op->setattr() fully either, ++ * copyup failed. finally, such filesystem will not be used as the writable ++ * branch. ++ * ++ * returns tri-state, see above. ++ */ ++static int au_wh_init_rw(struct dentry *h_root, struct au_wbr *wbr, ++ int do_plink, struct au_wh_base base[], ++ struct path *h_path) ++{ ++ int err; ++ struct inode *h_dir; ++ ++ err = test_linkable(h_root); ++ if (unlikely(err)) { ++ err = 1; ++ goto out; ++ } ++ ++ /* ++ * todo: should this create be done in /sbin/mount.aufs helper? ++ */ ++ err = -EEXIST; ++ h_dir = h_root->d_inode; ++ if (!base[AuBrWh_BASE].dentry->d_inode) { ++ err = mnt_want_write(h_path->mnt); ++ if (!err) { ++ h_path->dentry = base[AuBrWh_BASE].dentry; ++ err = vfsub_create(h_dir, h_path, WH_MASK); ++ mnt_drop_write(h_path->mnt); ++ } ++ } else if (S_ISREG(base[AuBrWh_BASE].dentry->d_inode->i_mode)) ++ err = 0; ++ else ++ AuErr("unknown %.*s/%.*s exists\n", ++ AuDLNPair(h_root), AuDLNPair(base[AuBrWh_BASE].dentry)); ++ if (unlikely(err)) ++ goto out; ++ ++ h_path->dentry = base[AuBrWh_PLINK].dentry; ++ if (do_plink) { ++ err = au_whdir(h_dir, h_path); ++ if (unlikely(err)) ++ goto out; ++ wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); ++ } else ++ au_wh_clean(h_dir, h_path, /*isdir*/1); ++ wbr->wbr_whbase = dget(base[AuBrWh_BASE].dentry); ++ ++ h_path->dentry = base[AuBrWh_ORPH].dentry; ++ err = au_whdir(h_dir, h_path); ++ if (unlikely(err)) ++ goto out; ++ wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); ++ ++ out: ++ return err; ++} ++ ++/* ++ * initialize the whiteout base file/dir for @br. ++ */ ++int au_wh_init(struct dentry *h_root, struct au_branch *br, ++ struct super_block *sb) ++{ ++ int err, i; ++ const unsigned char do_plink ++ = !!au_opt_test(au_mntflags(sb), PLINK); ++ struct path path = { ++ .mnt = br->br_mnt ++ }; ++ struct inode *h_dir; ++ struct au_wbr *wbr = br->br_wbr; ++ static const struct qstr base_name[] = { ++ [AuBrWh_BASE] = { ++ .name = AUFS_BASE_NAME, ++ .len = sizeof(AUFS_BASE_NAME) - 1 ++ }, ++ [AuBrWh_PLINK] = { ++ .name = AUFS_PLINKDIR_NAME, ++ .len = sizeof(AUFS_PLINKDIR_NAME) - 1 ++ }, ++ [AuBrWh_ORPH] = { ++ .name = AUFS_ORPHDIR_NAME, ++ .len = sizeof(AUFS_ORPHDIR_NAME) - 1 ++ } ++ }; ++ struct au_wh_base base[] = { ++ [AuBrWh_BASE] = { ++ .name = base_name + AuBrWh_BASE, ++ .dentry = NULL ++ }, ++ [AuBrWh_PLINK] = { ++ .name = base_name + AuBrWh_PLINK, ++ .dentry = NULL ++ }, ++ [AuBrWh_ORPH] = { ++ .name = base_name + AuBrWh_ORPH, ++ .dentry = NULL ++ } ++ }; ++ ++ ++ h_dir = h_root->d_inode; ++ for (i = 0; i < AuBrWh_Last; i++) { ++ /* doubly whiteouted */ ++ struct dentry *d; ++ ++ d = au_wh_lkup(h_root, (void *)base[i].name, br); ++ err = PTR_ERR(d); ++ if (IS_ERR(d)) ++ goto out; ++ ++ base[i].dentry = d; ++ AuDebugOn(wbr ++ && wbr->wbr_wh[i] ++ && wbr->wbr_wh[i] != base[i].dentry); ++ } ++ ++ if (wbr) ++ for (i = 0; i < AuBrWh_Last; i++) { ++ dput(wbr->wbr_wh[i]); ++ wbr->wbr_wh[i] = NULL; ++ } ++ ++ err = 0; ++ ++ switch (br->br_perm) { ++ case AuBrPerm_RO: ++ case AuBrPerm_ROWH: ++ case AuBrPerm_RR: ++ case AuBrPerm_RRWH: ++ au_wh_init_ro(h_dir, base, &path); ++ break; ++ ++ case AuBrPerm_RWNoLinkWH: ++ err = au_wh_init_rw_nolink(h_root, wbr, do_plink, base, &path); ++ if (err > 0) ++ goto out; ++ else if (err) ++ goto out_err; ++ break; ++ ++ case AuBrPerm_RW: ++ err = au_wh_init_rw(h_root, wbr, do_plink, base, &path); ++ if (err > 0) ++ goto out; ++ else if (err) ++ goto out_err; ++ break; ++ ++ default: ++ BUG(); ++ } ++ goto out; /* success */ ++ ++ out_err: ++ AuErr("an error(%d) on the writable branch %.*s(%s)\n", ++ err, AuDLNPair(h_root), au_sbtype(h_root->d_sb)); ++ out: ++ for (i = 0; i < AuBrWh_Last; i++) ++ dput(base[i].dentry); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * whiteouts are all hard-linked usually. ++ * when its link count reaches a ceiling, we create a new whiteout base ++ * asynchronously. ++ */ ++ ++struct reinit_br_wh { ++ struct super_block *sb; ++ struct au_branch *br; ++}; ++ ++static void reinit_br_wh(void *arg) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct path h_path; ++ struct reinit_br_wh *a = arg; ++ struct au_wbr *wbr; ++ struct inode *dir; ++ struct dentry *h_root; ++ struct au_hinode *hdir; ++ ++ err = 0; ++ wbr = a->br->br_wbr; ++ /* big aufs lock */ ++ si_noflush_write_lock(a->sb); ++ if (!au_br_writable(a->br->br_perm)) ++ goto out; ++ bindex = au_br_index(a->sb, a->br->br_id); ++ if (unlikely(bindex < 0)) ++ goto out; ++ ++ dir = a->sb->s_root->d_inode; ++ /* ii_read_lock_parent(dir); */ ++ hdir = au_hi(dir, bindex); ++ h_root = au_h_dptr(a->sb->s_root, bindex); ++ ++ au_hin_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ wbr_wh_write_lock(wbr); ++ err = au_h_verify(wbr->wbr_whbase, au_opt_udba(a->sb), hdir->hi_inode, ++ h_root, a->br); ++ if (!err) { ++ err = mnt_want_write(a->br->br_mnt); ++ if (!err) { ++ h_path.dentry = wbr->wbr_whbase; ++ h_path.mnt = a->br->br_mnt; ++ err = vfsub_unlink(hdir->hi_inode, &h_path, /*force*/0); ++ mnt_drop_write(a->br->br_mnt); ++ } ++ } else { ++ AuWarn("%.*s is moved, ignored\n", AuDLNPair(wbr->wbr_whbase)); ++ err = 0; ++ } ++ dput(wbr->wbr_whbase); ++ wbr->wbr_whbase = NULL; ++ if (!err) ++ err = au_wh_init(h_root, a->br, a->sb); ++ wbr_wh_write_unlock(wbr); ++ au_hin_imtx_unlock(hdir); ++ /* ii_read_unlock(dir); */ ++ ++ out: ++ if (wbr) ++ atomic_dec(&wbr->wbr_wh_running); ++ atomic_dec(&a->br->br_count); ++ au_nwt_done(&au_sbi(a->sb)->si_nowait); ++ si_write_unlock(a->sb); ++ kfree(arg); ++ if (unlikely(err)) ++ AuIOErr("err %d\n", err); ++} ++ ++static void kick_reinit_br_wh(struct super_block *sb, struct au_branch *br) ++{ ++ int do_dec, wkq_err; ++ struct reinit_br_wh *arg; ++ ++ do_dec = 1; ++ if (atomic_inc_return(&br->br_wbr->wbr_wh_running) != 1) ++ goto out; ++ ++ /* ignore ENOMEM */ ++ arg = kmalloc(sizeof(*arg), GFP_NOFS); ++ if (arg) { ++ /* ++ * dec(wh_running), kfree(arg) and dec(br_count) ++ * in reinit function ++ */ ++ arg->sb = sb; ++ arg->br = br; ++ atomic_inc(&br->br_count); ++ wkq_err = au_wkq_nowait(reinit_br_wh, arg, sb); ++ if (unlikely(wkq_err)) { ++ atomic_dec(&br->br_wbr->wbr_wh_running); ++ atomic_dec(&br->br_count); ++ kfree(arg); ++ } ++ do_dec = 0; ++ } ++ ++ out: ++ if (do_dec) ++ atomic_dec(&br->br_wbr->wbr_wh_running); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create the whiteout @wh. ++ */ ++static int link_or_create_wh(struct super_block *sb, aufs_bindex_t bindex, ++ struct dentry *wh) ++{ ++ int err; ++ struct path h_path = { ++ .dentry = wh ++ }; ++ struct au_branch *br; ++ struct au_wbr *wbr; ++ struct dentry *h_parent; ++ struct inode *h_dir; ++ ++ h_parent = wh->d_parent; /* dir inode is locked */ ++ h_dir = h_parent->d_inode; ++ IMustLock(h_dir); ++ ++ br = au_sbr(sb, bindex); ++ h_path.mnt = br->br_mnt; ++ wbr = br->br_wbr; ++ wbr_wh_read_lock(wbr); ++ if (wbr->wbr_whbase) { ++ err = vfsub_link(wbr->wbr_whbase, h_dir, &h_path); ++ if (!err || err != -EMLINK) ++ goto out; ++ ++ /* link count full. re-initialize br_whbase. */ ++ kick_reinit_br_wh(sb, br); ++ } ++ ++ /* return this error in this context */ ++ err = vfsub_create(h_dir, &h_path, WH_MASK); ++ ++ out: ++ wbr_wh_read_unlock(wbr); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create or remove the diropq. ++ */ ++static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int flags) ++{ ++ struct dentry *opq_dentry, *h_dentry; ++ struct super_block *sb; ++ struct au_branch *br; ++ int err; ++ ++ sb = dentry->d_sb; ++ br = au_sbr(sb, bindex); ++ h_dentry = au_h_dptr(dentry, bindex); ++ opq_dentry = au_lkup_one(&diropq_name, h_dentry, br, /*nd*/NULL); ++ if (IS_ERR(opq_dentry)) ++ goto out; ++ ++ if (au_ftest_diropq(flags, CREATE)) { ++ err = link_or_create_wh(sb, bindex, opq_dentry); ++ if (!err) { ++ au_set_dbdiropq(dentry, bindex); ++ goto out; /* success */ ++ } ++ } else { ++ struct path tmp = { ++ .dentry = opq_dentry, ++ .mnt = br->br_mnt ++ }; ++ err = do_unlink_wh(au_h_iptr(dentry->d_inode, bindex), &tmp); ++ if (!err) ++ au_set_dbdiropq(dentry, -1); ++ } ++ dput(opq_dentry); ++ opq_dentry = ERR_PTR(err); ++ ++ out: ++ return opq_dentry; ++} ++ ++struct do_diropq_args { ++ struct dentry **errp; ++ struct dentry *dentry; ++ aufs_bindex_t bindex; ++ unsigned int flags; ++}; ++ ++static void call_do_diropq(void *args) ++{ ++ struct do_diropq_args *a = args; ++ *a->errp = do_diropq(a->dentry, a->bindex, a->flags); ++} ++ ++struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int flags) ++{ ++ struct dentry *diropq, *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!au_test_h_perm_sio(h_dentry->d_inode, MAY_EXEC | MAY_WRITE)) ++ diropq = do_diropq(dentry, bindex, flags); ++ else { ++ int wkq_err; ++ struct do_diropq_args args = { ++ .errp = &diropq, ++ .dentry = dentry, ++ .bindex = bindex, ++ .flags = flags ++ }; ++ ++ wkq_err = au_wkq_wait(call_do_diropq, &args); ++ if (unlikely(wkq_err)) ++ diropq = ERR_PTR(wkq_err); ++ } ++ ++ return diropq; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * lookup whiteout dentry. ++ * @h_parent: lower parent dentry which must exist and be locked ++ * @base_name: name of dentry which will be whiteouted ++ * returns dentry for whiteout. ++ */ ++struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, ++ struct au_branch *br) ++{ ++ int err; ++ struct qstr wh_name; ++ struct dentry *wh_dentry; ++ ++ err = au_wh_name_alloc(&wh_name, base_name); ++ wh_dentry = ERR_PTR(err); ++ if (!err) { ++ wh_dentry = au_lkup_one(&wh_name, h_parent, br, /*nd*/NULL); ++ kfree(wh_name.name); ++ } ++ return wh_dentry; ++} ++ ++/* ++ * link/create a whiteout for @dentry on @bindex. ++ */ ++struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent) ++{ ++ struct dentry *wh_dentry; ++ struct super_block *sb; ++ int err; ++ ++ sb = dentry->d_sb; ++ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, au_sbr(sb, bindex)); ++ if (!IS_ERR(wh_dentry) && !wh_dentry->d_inode) { ++ err = link_or_create_wh(sb, bindex, wh_dentry); ++ if (!err) ++ au_set_dbwh(dentry, bindex); ++ else { ++ dput(wh_dentry); ++ wh_dentry = ERR_PTR(err); ++ } ++ } ++ ++ return wh_dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* Delete all whiteouts in this directory on branch bindex. */ ++static int del_wh_children(struct dentry *h_dentry, struct au_nhash *whlist, ++ aufs_bindex_t bindex, struct au_branch *br) ++{ ++ int err; ++ unsigned long ul, n; ++ struct qstr wh_name; ++ char *p; ++ struct hlist_head *head; ++ struct au_vdir_wh *tpos; ++ struct hlist_node *pos; ++ struct au_vdir_destr *str; ++ ++ err = -ENOMEM; ++ p = __getname(); ++ wh_name.name = p; ++ if (unlikely(!wh_name.name)) ++ goto out; ++ ++ err = 0; ++ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); ++ p += AUFS_WH_PFX_LEN; ++ n = whlist->nh_num; ++ head = whlist->nh_head; ++ for (ul = 0; !err && ul < n; ul++, head++) { ++ hlist_for_each_entry(tpos, pos, head, wh_hash) { ++ if (tpos->wh_bindex != bindex) ++ continue; ++ ++ str = &tpos->wh_str; ++ if (str->len + AUFS_WH_PFX_LEN <= PATH_MAX) { ++ memcpy(p, str->name, str->len); ++ wh_name.len = AUFS_WH_PFX_LEN + str->len; ++ err = unlink_wh_name(h_dentry, &wh_name, br); ++ if (!err) ++ continue; ++ break; ++ } ++ AuIOErr("whiteout name too long %.*s\n", ++ str->len, str->name); ++ err = -EIO; ++ break; ++ } ++ } ++ __putname(wh_name.name); ++ ++ out: ++ return err; ++} ++ ++struct del_wh_children_args { ++ int *errp; ++ struct dentry *h_dentry; ++ struct au_nhash whlist; ++ aufs_bindex_t bindex; ++ struct au_branch *br; ++}; ++ ++static void call_del_wh_children(void *args) ++{ ++ struct del_wh_children_args *a = args; ++ *a->errp = del_wh_children(a->h_dentry, &a->whlist, a->bindex, a->br); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp) ++{ ++ struct au_whtmp_rmdir *whtmp; ++ int err; ++ ++ whtmp = kmalloc(sizeof(*whtmp), gfp); ++ if (unlikely(!whtmp)) { ++ whtmp = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ whtmp->dir = NULL; ++ whtmp->wh_dentry = NULL; ++ err = au_nhash_alloc(&whtmp->whlist, au_sbi(sb)->si_rdhash, gfp); ++ if (!err) ++ return whtmp; /* success */ ++ ++ kfree(whtmp); ++ whtmp = ERR_PTR(err); ++ ++ out: ++ return whtmp; ++} ++ ++void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp) ++{ ++ dput(whtmp->wh_dentry); ++ iput(whtmp->dir); ++ au_nhash_wh_free(&whtmp->whlist); ++ kfree(whtmp); ++} ++ ++/* ++ * rmdir the whiteouted temporary named dir @h_dentry. ++ * @whlist: whiteouted children. ++ */ ++int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct au_nhash *whlist) ++{ ++ int err; ++ struct path h_tmp; ++ struct inode *wh_inode, *h_dir; ++ struct au_branch *br; ++ ++ h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ ++ IMustLock(h_dir); ++ ++ br = au_sbr(dir->i_sb, bindex); ++ wh_inode = wh_dentry->d_inode; ++ mutex_lock_nested(&wh_inode->i_mutex, AuLsc_I_CHILD); ++ ++ /* ++ * someone else might change some whiteouts while we were sleeping. ++ * it means this whlist may have an obsoleted entry. ++ */ ++ if (!au_test_h_perm_sio(wh_inode, MAY_EXEC | MAY_WRITE)) ++ err = del_wh_children(wh_dentry, whlist, bindex, br); ++ else { ++ int wkq_err; ++ struct del_wh_children_args args = { ++ .errp = &err, ++ .h_dentry = wh_dentry, ++ .whlist = *whlist, ++ .bindex = bindex, ++ .br = br ++ }; ++ ++ wkq_err = au_wkq_wait(call_del_wh_children, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ mutex_unlock(&wh_inode->i_mutex); ++ ++ if (!err) { ++ h_tmp.dentry = wh_dentry; ++ h_tmp.mnt = br->br_mnt; ++ err = vfsub_rmdir(h_dir, &h_tmp); ++ /* d_drop(h_dentry); */ ++ } ++ ++ if (!err) { ++ if (au_ibstart(dir) == bindex) { ++ au_cpup_attr_timesizes(dir); ++ drop_nlink(dir); ++ } ++ return 0; /* success */ ++ } ++ ++ AuWarn("failed removing %.*s(%d), ignored\n", ++ AuDLNPair(wh_dentry), err); ++ return err; ++} ++ ++static void call_rmdir_whtmp(void *args) ++{ ++ int err; ++ struct au_whtmp_rmdir *a = args; ++ struct super_block *sb; ++ struct dentry *h_parent; ++ struct inode *h_dir; ++ struct au_branch *br; ++ struct au_hinode *hdir; ++ ++ /* rmdir by nfsd may cause deadlock with this i_mutex */ ++ /* mutex_lock(&a->dir->i_mutex); */ ++ sb = a->dir->i_sb; ++ si_noflush_read_lock(sb); ++ err = au_test_ro(sb, a->bindex, NULL); ++ if (unlikely(err)) ++ goto out; ++ ++ err = -EIO; ++ br = au_sbr(sb, a->bindex); ++ ii_write_lock_parent(a->dir); ++ h_parent = dget_parent(a->wh_dentry); ++ h_dir = h_parent->d_inode; ++ hdir = au_hi(a->dir, a->bindex); ++ au_hin_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ err = au_h_verify(a->wh_dentry, au_opt_udba(sb), h_dir, h_parent, br); ++ if (!err) { ++ err = mnt_want_write(br->br_mnt); ++ if (!err) { ++ err = au_whtmp_rmdir(a->dir, a->bindex, a->wh_dentry, ++ &a->whlist); ++ mnt_drop_write(br->br_mnt); ++ } ++ } ++ au_hin_imtx_unlock(hdir); ++ dput(h_parent); ++ ii_write_unlock(a->dir); ++ ++ out: ++ /* mutex_unlock(&a->dir->i_mutex); */ ++ au_nwt_done(&au_sbi(sb)->si_nowait); ++ si_read_unlock(sb); ++ au_whtmp_rmdir_free(a); ++ if (unlikely(err)) ++ AuIOErr("err %d\n", err); ++} ++ ++void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct au_whtmp_rmdir *args) ++{ ++ int wkq_err; ++ ++ IMustLock(dir); ++ ++ /* all post-process will be done in do_rmdir_whtmp(). */ ++ args->dir = au_igrab(dir); ++ args->bindex = bindex; ++ args->wh_dentry = dget(wh_dentry); ++ wkq_err = au_wkq_nowait(call_rmdir_whtmp, args, dir->i_sb); ++ if (unlikely(wkq_err)) { ++ AuWarn("rmdir error %.*s (%d), ignored\n", ++ AuDLNPair(wh_dentry), wkq_err); ++ au_whtmp_rmdir_free(args); ++ } ++} +diff -Naur a/fs/aufs/whout.h b/fs/aufs/whout.h +--- a/fs/aufs/whout.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/whout.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,87 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * whiteout for logical deletion and opaque directory ++ */ ++ ++#ifndef __AUFS_WHOUT_H__ ++#define __AUFS_WHOUT_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include "dir.h" ++ ++/* whout.c */ ++int au_wh_name_alloc(struct qstr *wh, const struct qstr *name); ++struct au_branch; ++int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, ++ struct au_branch *br, int try_sio); ++int au_diropq_test(struct dentry *h_dentry, struct au_branch *br); ++struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, ++ struct qstr *prefix); ++int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br); ++int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, ++ struct dentry *dentry); ++int au_wh_init(struct dentry *h_parent, struct au_branch *br, ++ struct super_block *sb); ++ ++/* diropq flags */ ++#define AuDiropq_CREATE 1 ++#define au_ftest_diropq(flags, name) ((flags) & AuDiropq_##name) ++#define au_fset_diropq(flags, name) { (flags) |= AuDiropq_##name; } ++#define au_fclr_diropq(flags, name) { (flags) &= ~AuDiropq_##name; } ++ ++struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int flags); ++struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, ++ struct au_branch *br); ++struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent); ++ ++/* real rmdir for the whiteout-ed dir */ ++struct au_whtmp_rmdir { ++ struct inode *dir; ++ aufs_bindex_t bindex; ++ struct dentry *wh_dentry; ++ struct au_nhash whlist; ++}; ++ ++struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp); ++void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp); ++int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct au_nhash *whlist); ++void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct au_whtmp_rmdir *args); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct dentry *au_diropq_create(struct dentry *dentry, ++ aufs_bindex_t bindex) ++{ ++ return au_diropq_sio(dentry, bindex, AuDiropq_CREATE); ++} ++ ++static inline int au_diropq_remove(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ return PTR_ERR(au_diropq_sio(dentry, bindex, !AuDiropq_CREATE)); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_WHOUT_H__ */ +diff -Naur a/fs/aufs/wkq.c b/fs/aufs/wkq.c +--- a/fs/aufs/wkq.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/wkq.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,259 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * workqueue for asynchronous/super-io operations ++ * todo: try new dredential scheme ++ */ ++ ++#include ++#include "aufs.h" ++ ++/* internal workqueue named AUFS_WKQ_NAME */ ++static struct au_wkq { ++ struct workqueue_struct *q; ++ ++ /* balancing */ ++ atomic_t busy; ++} *au_wkq; ++ ++struct au_wkinfo { ++ struct work_struct wk; ++ struct super_block *sb; ++ ++ unsigned int flags; /* see wkq.h */ ++ ++ au_wkq_func_t func; ++ void *args; ++ ++ atomic_t *busyp; ++ struct completion *comp; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int enqueue(struct au_wkq *wkq, struct au_wkinfo *wkinfo) ++{ ++ wkinfo->busyp = &wkq->busy; ++ if (au_ftest_wkq(wkinfo->flags, WAIT)) ++ return !queue_work(wkq->q, &wkinfo->wk); ++ else ++ return !schedule_work(&wkinfo->wk); ++} ++ ++static void do_wkq(struct au_wkinfo *wkinfo) ++{ ++ unsigned int idle, n; ++ int i, idle_idx; ++ ++ while (1) { ++ if (au_ftest_wkq(wkinfo->flags, WAIT)) { ++ idle_idx = 0; ++ idle = UINT_MAX; ++ for (i = 0; i < aufs_nwkq; i++) { ++ n = atomic_inc_return(&au_wkq[i].busy); ++ if (n == 1 && !enqueue(au_wkq + i, wkinfo)) ++ return; /* success */ ++ ++ if (n < idle) { ++ idle_idx = i; ++ idle = n; ++ } ++ atomic_dec(&au_wkq[i].busy); ++ } ++ } else ++ idle_idx = aufs_nwkq; ++ ++ atomic_inc(&au_wkq[idle_idx].busy); ++ if (!enqueue(au_wkq + idle_idx, wkinfo)) ++ return; /* success */ ++ ++ /* impossible? */ ++ AuWarn1("failed to queue_work()\n"); ++ yield(); ++ } ++} ++ ++static void wkq_func(struct work_struct *wk) ++{ ++ struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk); ++ ++ wkinfo->func(wkinfo->args); ++ atomic_dec(wkinfo->busyp); ++ if (au_ftest_wkq(wkinfo->flags, WAIT)) ++ complete(wkinfo->comp); ++ else { ++ kobject_put(&au_sbi(wkinfo->sb)->si_kobj); ++ module_put(THIS_MODULE); ++ kfree(wkinfo); ++ } ++} ++ ++/* ++ * Since struct completion is large, try allocating it dynamically. ++ */ ++#if defined(CONFIG_4KSTACKS) || defined(AuTest4KSTACKS) ++#define AuWkqCompDeclare(name) struct completion *comp = NULL ++ ++static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) ++{ ++ *comp = kmalloc(sizeof(**comp), GFP_NOFS); ++ if (*comp) { ++ init_completion(*comp); ++ wkinfo->comp = *comp; ++ return 0; ++ } ++ return -ENOMEM; ++} ++ ++static void au_wkq_comp_free(struct completion *comp) ++{ ++ kfree(comp); ++} ++ ++#else ++ ++/* no braces */ ++#define AuWkqCompDeclare(name) \ ++ DECLARE_COMPLETION_ONSTACK(_ ## name); \ ++ struct completion *comp = &_ ## name ++ ++static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) ++{ ++ wkinfo->comp = *comp; ++ return 0; ++} ++ ++static void au_wkq_comp_free(struct completion *comp __maybe_unused) ++{ ++ /* empty */ ++} ++#endif /* 4KSTACKS */ ++ ++static void au_wkq_run(struct au_wkinfo *wkinfo) ++{ ++ au_dbg_verify_kthread(); ++ INIT_WORK(&wkinfo->wk, wkq_func); ++ do_wkq(wkinfo); ++} ++ ++int au_wkq_wait(au_wkq_func_t func, void *args) ++{ ++ int err; ++ AuWkqCompDeclare(comp); ++ struct au_wkinfo wkinfo = { ++ .flags = AuWkq_WAIT, ++ .func = func, ++ .args = args ++ }; ++ ++ err = au_wkq_comp_alloc(&wkinfo, &comp); ++ if (!err) { ++ au_wkq_run(&wkinfo); ++ /* no timeout, no interrupt */ ++ wait_for_completion(wkinfo.comp); ++ au_wkq_comp_free(comp); ++ } ++ ++ return err; ++ ++} ++ ++int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb) ++{ ++ int err; ++ struct au_wkinfo *wkinfo; ++ ++ atomic_inc(&au_sbi(sb)->si_nowait.nw_len); ++ ++ /* ++ * wkq_func() must free this wkinfo. ++ * it highly depends upon the implementation of workqueue. ++ */ ++ err = 0; ++ wkinfo = kmalloc(sizeof(*wkinfo), GFP_NOFS); ++ if (wkinfo) { ++ wkinfo->sb = sb; ++ wkinfo->flags = !AuWkq_WAIT; ++ wkinfo->func = func; ++ wkinfo->args = args; ++ wkinfo->comp = NULL; ++ kobject_get(&au_sbi(sb)->si_kobj); ++ __module_get(THIS_MODULE); ++ ++ au_wkq_run(wkinfo); ++ } else { ++ err = -ENOMEM; ++ atomic_dec(&au_sbi(sb)->si_nowait.nw_len); ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_nwt_init(struct au_nowait_tasks *nwt) ++{ ++ atomic_set(&nwt->nw_len, 0); ++ /* smp_mb();*/ /* atomic_set */ ++ init_waitqueue_head(&nwt->nw_wq); ++} ++ ++void au_wkq_fin(void) ++{ ++ int i; ++ ++ for (i = 0; i < aufs_nwkq; i++) ++ if (au_wkq[i].q && !IS_ERR(au_wkq[i].q)) ++ destroy_workqueue(au_wkq[i].q); ++ kfree(au_wkq); ++} ++ ++int __init au_wkq_init(void) ++{ ++ int err, i; ++ struct au_wkq *nowaitq; ++ ++ /* '+1' is for accounting of nowait queue */ ++ err = -ENOMEM; ++ au_wkq = kcalloc(aufs_nwkq + 1, sizeof(*au_wkq), GFP_NOFS); ++ if (unlikely(!au_wkq)) ++ goto out; ++ ++ err = 0; ++ for (i = 0; i < aufs_nwkq; i++) { ++ au_wkq[i].q = create_singlethread_workqueue(AUFS_WKQ_NAME); ++ if (au_wkq[i].q && !IS_ERR(au_wkq[i].q)) { ++ atomic_set(&au_wkq[i].busy, 0); ++ continue; ++ } ++ ++ err = PTR_ERR(au_wkq[i].q); ++ au_wkq_fin(); ++ goto out; ++ } ++ ++ /* nowait accounting */ ++ nowaitq = au_wkq + aufs_nwkq; ++ atomic_set(&nowaitq->busy, 0); ++ nowaitq->q = NULL; ++ /* smp_mb(); */ /* atomic_set */ ++ ++ out: ++ return err; ++} +diff -Naur a/fs/aufs/wkq.h b/fs/aufs/wkq.h +--- a/fs/aufs/wkq.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/wkq.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,82 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * workqueue for asynchronous/super-io operations ++ * todo: try new credentials management scheme ++ */ ++ ++#ifndef __AUFS_WKQ_H__ ++#define __AUFS_WKQ_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++ ++struct super_block; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * in the next operation, wait for the 'nowait' tasks in system-wide workqueue ++ */ ++struct au_nowait_tasks { ++ atomic_t nw_len; ++ wait_queue_head_t nw_wq; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++typedef void (*au_wkq_func_t)(void *args); ++ ++/* wkq flags */ ++#define AuWkq_WAIT 1 ++#define au_ftest_wkq(flags, name) ((flags) & AuWkq_##name) ++#define au_fset_wkq(flags, name) { (flags) |= AuWkq_##name; } ++#define au_fclr_wkq(flags, name) { (flags) &= ~AuWkq_##name; } ++ ++/* wkq.c */ ++int au_wkq_wait(au_wkq_func_t func, void *args); ++int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb); ++void au_nwt_init(struct au_nowait_tasks *nwt); ++int __init au_wkq_init(void); ++void au_wkq_fin(void); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline int au_test_wkq(struct task_struct *tsk) ++{ ++ return !tsk->mm && !strcmp(tsk->comm, AUFS_WKQ_NAME); ++} ++ ++static inline void au_nwt_done(struct au_nowait_tasks *nwt) ++{ ++ if (!atomic_dec_return(&nwt->nw_len)) ++ wake_up_all(&nwt->nw_wq); ++} ++ ++static inline int au_nwt_flush(struct au_nowait_tasks *nwt) ++{ ++ wait_event(nwt->nw_wq, !atomic_read(&nwt->nw_len)); ++ return 0; ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_WKQ_H__ */ +diff -Naur a/fs/aufs/xino.c b/fs/aufs/xino.c +--- a/fs/aufs/xino.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/xino.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,1191 @@ ++/* ++ * 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 ++ */ ++ ++/* ++ * external inode number translation table and bitmap ++ */ ++ ++#include ++#include ++#include ++#include "aufs.h" ++ ++ssize_t xino_fread(au_readf_t func, struct file *file, void *buf, size_t size, ++ loff_t *pos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ do { ++ /* todo: signal_pending? */ ++ err = func(file, (char __user *)buf, size, pos); ++ } while (err == -EAGAIN || err == -EINTR); ++ set_fs(oldfs); ++ ++#if 0 /* reserved for future use */ ++ if (err > 0) ++ fsnotify_access(file->f_dentry); ++#endif ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static ssize_t do_xino_fwrite(au_writef_t func, struct file *file, void *buf, ++ size_t size, loff_t *pos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ lockdep_off(); ++ do { ++ /* todo: signal_pending? */ ++ err = func(file, (const char __user *)buf, size, pos); ++ } while (err == -EAGAIN || err == -EINTR); ++ lockdep_on(); ++ set_fs(oldfs); ++ ++#if 0 /* reserved for future use */ ++ if (err > 0) ++ fsnotify_modify(file->f_dentry); ++#endif ++ ++ return err; ++} ++ ++struct do_xino_fwrite_args { ++ ssize_t *errp; ++ au_writef_t func; ++ struct file *file; ++ void *buf; ++ size_t size; ++ loff_t *pos; ++}; ++ ++static void call_do_xino_fwrite(void *args) ++{ ++ struct do_xino_fwrite_args *a = args; ++ *a->errp = do_xino_fwrite(a->func, a->file, a->buf, a->size, a->pos); ++} ++ ++ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size, ++ loff_t *pos) ++{ ++ ssize_t err; ++ ++ /* todo: signal block and no wkq? */ ++ /* todo: new credential scheme */ ++ /* ++ * it breaks RLIMIT_FSIZE and normal user's limit, ++ * users should care about quota and real 'filesystem full.' ++ */ ++ if (!au_test_wkq(current)) { ++ int wkq_err; ++ struct do_xino_fwrite_args args = { ++ .errp = &err, ++ .func = func, ++ .file = file, ++ .buf = buf, ++ .size = size, ++ .pos = pos ++ }; ++ ++ wkq_err = au_wkq_wait(call_do_xino_fwrite, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } else ++ err = do_xino_fwrite(func, file, buf, size, pos); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create a new xinofile at the same place/path as @base_file. ++ */ ++struct file *au_xino_create2(struct file *base_file, struct file *copy_src) ++{ ++ struct file *file; ++ struct dentry *base, *dentry, *parent; ++ struct inode *dir; ++ struct qstr *name; ++ int err; ++ ++ base = base_file->f_dentry; ++ parent = base->d_parent; /* dir inode is locked */ ++ dir = parent->d_inode; ++ IMustLock(dir); ++ ++ file = ERR_PTR(-EINVAL); ++ name = &base->d_name; ++ dentry = vfsub_lookup_one_len(name->name, parent, name->len); ++ if (IS_ERR(dentry)) { ++ file = (void *)dentry; ++ AuErr("%.*s lookup err %ld\n", AuLNPair(name), PTR_ERR(dentry)); ++ goto out; ++ } ++ ++ /* no need to mnt_want_write() since we call dentry_open() later */ ++ err = vfs_create(dir, dentry, S_IRUGO | S_IWUGO, NULL); ++ if (unlikely(err)) { ++ file = ERR_PTR(err); ++ AuErr("%.*s create err %d\n", AuLNPair(name), err); ++ goto out_dput; ++ } ++ ++ file = dentry_open(dget(dentry), mntget(base_file->f_vfsmnt), ++ O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE, ++ current_cred()); ++ if (IS_ERR(file)) { ++ AuErr("%.*s open err %ld\n", AuLNPair(name), PTR_ERR(file)); ++ goto out_dput; ++ } ++ ++ err = vfsub_unlink(dir, &file->f_path, /*force*/0); ++ if (unlikely(err)) { ++ AuErr("%.*s unlink err %d\n", AuLNPair(name), err); ++ goto out_fput; ++ } ++ ++ if (copy_src) { ++ /* no one can touch copy_src xino */ ++ err = au_copy_file(file, copy_src, ++ i_size_read(copy_src->f_dentry->d_inode)); ++ if (unlikely(err)) { ++ AuErr("%.*s copy err %d\n", AuLNPair(name), err); ++ goto out_fput; ++ } ++ } ++ goto out_dput; /* success */ ++ ++ out_fput: ++ fput(file); ++ file = ERR_PTR(err); ++ out_dput: ++ dput(dentry); ++ out: ++ return file; ++} ++ ++struct au_xino_lock_dir { ++ struct au_hinode *hdir; ++ struct dentry *parent; ++ struct mutex *mtx; ++}; ++ ++static void au_xino_lock_dir(struct super_block *sb, struct file *xino, ++ struct au_xino_lock_dir *ldir) ++{ ++ aufs_bindex_t brid, bindex; ++ ++ ldir->hdir = NULL; ++ bindex = -1; ++ brid = au_xino_brid(sb); ++ if (brid >= 0) ++ bindex = au_br_index(sb, brid); ++ if (bindex >= 0) { ++ ldir->hdir = au_hi(sb->s_root->d_inode, bindex); ++ au_hin_imtx_lock_nested(ldir->hdir, AuLsc_I_PARENT); ++ } else { ++ ldir->parent = dget_parent(xino->f_dentry); ++ ldir->mtx = &ldir->parent->d_inode->i_mutex; ++ mutex_lock_nested(ldir->mtx, AuLsc_I_PARENT); ++ } ++} ++ ++static void au_xino_unlock_dir(struct au_xino_lock_dir *ldir) ++{ ++ if (ldir->hdir) ++ au_hin_imtx_unlock(ldir->hdir); ++ else { ++ mutex_unlock(ldir->mtx); ++ dput(ldir->parent); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* trucate xino files asynchronously */ ++ ++int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ int err; ++ aufs_bindex_t bi, bend; ++ struct au_branch *br; ++ struct file *new_xino, *file; ++ struct super_block *h_sb; ++ struct au_xino_lock_dir ldir; ++ ++ err = -EINVAL; ++ bend = au_sbend(sb); ++ if (unlikely(bindex < 0 || bend < bindex)) ++ goto out; ++ br = au_sbr(sb, bindex); ++ file = br->br_xino.xi_file; ++ if (!file) ++ goto out; ++ ++ au_xino_lock_dir(sb, file, &ldir); ++ /* mnt_want_write() is unnecessary here */ ++ new_xino = au_xino_create2(file, file); ++ au_xino_unlock_dir(&ldir); ++ err = PTR_ERR(new_xino); ++ if (IS_ERR(new_xino)) ++ goto out; ++ err = 0; ++ fput(file); ++ br->br_xino.xi_file = new_xino; ++ ++ h_sb = br->br_mnt->mnt_sb; ++ for (bi = 0; bi <= bend; bi++) { ++ if (unlikely(bi == bindex)) ++ continue; ++ br = au_sbr(sb, bi); ++ if (br->br_mnt->mnt_sb != h_sb) ++ continue; ++ ++ fput(br->br_xino.xi_file); ++ br->br_xino.xi_file = new_xino; ++ get_file(new_xino); ++ } ++ ++ out: ++ return err; ++} ++ ++struct xino_do_trunc_args { ++ struct super_block *sb; ++ struct au_branch *br; ++}; ++ ++static void xino_do_trunc(void *_args) ++{ ++ struct xino_do_trunc_args *args = _args; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct inode *dir; ++ int err; ++ aufs_bindex_t bindex; ++ ++ err = 0; ++ sb = args->sb; ++ dir = sb->s_root->d_inode; ++ br = args->br; ++ ++ si_noflush_write_lock(sb); ++ ii_read_lock_parent(dir); ++ bindex = au_br_index(sb, br->br_id); ++ err = au_xino_trunc(sb, bindex); ++ if (unlikely(err)) ++ goto out; ++ ++ if (br->br_xino.xi_file->f_dentry->d_inode->i_blocks ++ >= br->br_xino_upper) ++ br->br_xino_upper += AUFS_XINO_TRUNC_STEP; ++ ++ out: ++ ii_read_unlock(dir); ++ if (unlikely(err)) ++ AuWarn("err b%d, (%d)\n", bindex, err); ++ atomic_dec(&br->br_xino_running); ++ atomic_dec(&br->br_count); ++ au_nwt_done(&au_sbi(sb)->si_nowait); ++ si_write_unlock(sb); ++ kfree(args); ++} ++ ++static void xino_try_trunc(struct super_block *sb, struct au_branch *br) ++{ ++ struct xino_do_trunc_args *args; ++ int wkq_err; ++ ++ if (br->br_xino.xi_file->f_dentry->d_inode->i_blocks ++ < br->br_xino_upper) ++ return; ++ ++ if (atomic_inc_return(&br->br_xino_running) > 1) ++ goto out; ++ ++ /* lock and kfree() will be called in trunc_xino() */ ++ args = kmalloc(sizeof(*args), GFP_NOFS); ++ if (unlikely(!args)) { ++ AuErr1("no memory\n"); ++ goto out_args; ++ } ++ ++ atomic_inc(&br->br_count); ++ args->sb = sb; ++ args->br = br; ++ wkq_err = au_wkq_nowait(xino_do_trunc, args, sb); ++ if (!wkq_err) ++ return; /* success */ ++ ++ AuErr("wkq %d\n", wkq_err); ++ atomic_dec(&br->br_count); ++ ++ out_args: ++ kfree(args); ++ out: ++ atomic_dec(&br->br_xino_running); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_xino_do_write(au_writef_t write, struct file *file, ++ ino_t h_ino, ino_t ino) ++{ ++ loff_t pos; ++ ssize_t sz; ++ ++ pos = h_ino; ++ if (unlikely(au_loff_max / sizeof(ino) - 1 < pos)) { ++ AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); ++ return -EFBIG; ++ } ++ pos *= sizeof(ino); ++ sz = xino_fwrite(write, file, &ino, sizeof(ino), &pos); ++ if (sz == sizeof(ino)) ++ return 0; /* success */ ++ ++ AuIOErr("write failed (%zd)\n", sz); ++ return -EIO; ++} ++ ++/* ++ * write @ino to the xinofile for the specified branch{@sb, @bindex} ++ * at the position of @h_ino. ++ * even if @ino is zero, it is written to the xinofile and means no entry. ++ * if the size of the xino file on a specific filesystem exceeds the watermark, ++ * try truncating it. ++ */ ++int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t ino) ++{ ++ int err; ++ unsigned int mnt_flags; ++ struct au_branch *br; ++ ++ BUILD_BUG_ON(sizeof(long long) != sizeof(au_loff_max) ++ || ((loff_t)-1) > 0); ++ ++ mnt_flags = au_mntflags(sb); ++ if (!au_opt_test(mnt_flags, XINO)) ++ return 0; ++ ++ br = au_sbr(sb, bindex); ++ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, ++ h_ino, ino); ++ if (!err) { ++ if (au_opt_test(mnt_flags, TRUNC_XINO) ++ && au_test_fs_trunc_xino(br->br_mnt->mnt_sb)) ++ xino_try_trunc(sb, br); ++ return 0; /* success */ ++ } ++ ++ AuIOErr("write failed (%d)\n", err); ++ return -EIO; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* aufs inode number bitmap */ ++ ++static const int page_bits = (int)PAGE_SIZE * BITS_PER_BYTE; ++static ino_t xib_calc_ino(unsigned long pindex, int bit) ++{ ++ ino_t ino; ++ ++ AuDebugOn(bit < 0 || page_bits <= bit); ++ ino = AUFS_FIRST_INO + pindex * page_bits + bit; ++ return ino; ++} ++ ++static void xib_calc_bit(ino_t ino, unsigned long *pindex, int *bit) ++{ ++ AuDebugOn(ino < AUFS_FIRST_INO); ++ ino -= AUFS_FIRST_INO; ++ *pindex = ino / page_bits; ++ *bit = ino % page_bits; ++} ++ ++static int xib_pindex(struct super_block *sb, unsigned long pindex) ++{ ++ int err; ++ loff_t pos; ++ ssize_t sz; ++ struct au_sbinfo *sbinfo; ++ struct file *xib; ++ unsigned long *p; ++ ++ sbinfo = au_sbi(sb); ++ MtxMustLock(&sbinfo->si_xib_mtx); ++ AuDebugOn(pindex > ULONG_MAX / PAGE_SIZE ++ || !au_opt_test(sbinfo->si_mntflags, XINO)); ++ ++ if (pindex == sbinfo->si_xib_last_pindex) ++ return 0; ++ ++ xib = sbinfo->si_xib; ++ p = sbinfo->si_xib_buf; ++ pos = sbinfo->si_xib_last_pindex; ++ pos *= PAGE_SIZE; ++ sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); ++ if (unlikely(sz != PAGE_SIZE)) ++ goto out; ++ ++ pos = pindex; ++ pos *= PAGE_SIZE; ++ if (i_size_read(xib->f_dentry->d_inode) >= pos + PAGE_SIZE) ++ sz = xino_fread(sbinfo->si_xread, xib, p, PAGE_SIZE, &pos); ++ else { ++ memset(p, 0, PAGE_SIZE); ++ sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); ++ } ++ if (sz == PAGE_SIZE) { ++ sbinfo->si_xib_last_pindex = pindex; ++ return 0; /* success */ ++ } ++ ++ out: ++ AuIOErr1("write failed (%zd)\n", sz); ++ err = sz; ++ if (sz >= 0) ++ err = -EIO; ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t ino) ++{ ++ int err, bit; ++ unsigned long pindex; ++ struct au_sbinfo *sbinfo; ++ ++ if (!au_opt_test(au_mntflags(sb), XINO)) ++ return 0; ++ ++ err = 0; ++ if (ino) { ++ sbinfo = au_sbi(sb); ++ xib_calc_bit(ino, &pindex, &bit); ++ AuDebugOn(page_bits <= bit); ++ mutex_lock(&sbinfo->si_xib_mtx); ++ err = xib_pindex(sb, pindex); ++ if (!err) { ++ clear_bit(bit, sbinfo->si_xib_buf); ++ sbinfo->si_xib_next_bit = bit; ++ } ++ mutex_unlock(&sbinfo->si_xib_mtx); ++ } ++ ++ if (!err) ++ err = au_xino_write(sb, bindex, h_ino, 0); ++ return err; ++} ++ ++/* get an unused inode number from bitmap */ ++ino_t au_xino_new_ino(struct super_block *sb) ++{ ++ ino_t ino; ++ unsigned long *p, pindex, ul, pend; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ int free_bit, err; ++ ++ if (!au_opt_test(au_mntflags(sb), XINO)) ++ return iunique(sb, AUFS_FIRST_INO); ++ ++ sbinfo = au_sbi(sb); ++ mutex_lock(&sbinfo->si_xib_mtx); ++ p = sbinfo->si_xib_buf; ++ free_bit = sbinfo->si_xib_next_bit; ++ if (free_bit < page_bits && !test_bit(free_bit, p)) ++ goto out; /* success */ ++ free_bit = find_first_zero_bit(p, page_bits); ++ if (free_bit < page_bits) ++ goto out; /* success */ ++ ++ pindex = sbinfo->si_xib_last_pindex; ++ for (ul = pindex - 1; ul < ULONG_MAX; ul--) { ++ err = xib_pindex(sb, ul); ++ if (unlikely(err)) ++ goto out_err; ++ free_bit = find_first_zero_bit(p, page_bits); ++ if (free_bit < page_bits) ++ goto out; /* success */ ++ } ++ ++ file = sbinfo->si_xib; ++ pend = i_size_read(file->f_dentry->d_inode) / PAGE_SIZE; ++ for (ul = pindex + 1; ul <= pend; ul++) { ++ err = xib_pindex(sb, ul); ++ if (unlikely(err)) ++ goto out_err; ++ free_bit = find_first_zero_bit(p, page_bits); ++ if (free_bit < page_bits) ++ goto out; /* success */ ++ } ++ BUG(); ++ ++ out: ++ set_bit(free_bit, p); ++ sbinfo->si_xib_next_bit++; ++ pindex = sbinfo->si_xib_last_pindex; ++ mutex_unlock(&sbinfo->si_xib_mtx); ++ ino = xib_calc_ino(pindex, free_bit); ++ AuDbg("i%lu\n", (unsigned long)ino); ++ return ino; ++ out_err: ++ mutex_unlock(&sbinfo->si_xib_mtx); ++ AuDbg("i0\n"); ++ return 0; ++} ++ ++/* ++ * read @ino from xinofile for the specified branch{@sb, @bindex} ++ * at the position of @h_ino. ++ * if @ino does not exist and @do_new is true, get new one. ++ */ ++int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t *ino) ++{ ++ int err; ++ ssize_t sz; ++ loff_t pos; ++ struct file *file; ++ struct au_sbinfo *sbinfo; ++ ++ *ino = 0; ++ if (!au_opt_test(au_mntflags(sb), XINO)) ++ return 0; /* no xino */ ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ pos = h_ino; ++ if (unlikely(au_loff_max / sizeof(*ino) - 1 < pos)) { ++ AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); ++ return -EFBIG; ++ } ++ pos *= sizeof(*ino); ++ ++ file = au_sbr(sb, bindex)->br_xino.xi_file; ++ if (i_size_read(file->f_dentry->d_inode) < pos + sizeof(*ino)) ++ return 0; /* no ino */ ++ ++ sz = xino_fread(sbinfo->si_xread, file, ino, sizeof(*ino), &pos); ++ if (sz == sizeof(*ino)) ++ return 0; /* success */ ++ ++ err = sz; ++ if (unlikely(sz >= 0)) { ++ err = -EIO; ++ AuIOErr("xino read error (%zd)\n", sz); ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* create and set a new xino file */ ++ ++struct file *au_xino_create(struct super_block *sb, char *fname, int silent) ++{ ++ struct file *file; ++ struct dentry *h_parent, *d; ++ struct inode *h_dir; ++ int err; ++ ++ /* ++ * at mount-time, and the xino file is the default path, ++ * hinotify is disabled so we have no inotify events to ignore. ++ * when a user specified the xino, we cannot get au_hdir to be ignored. ++ */ ++ file = vfsub_filp_open(fname, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE, ++ S_IRUGO | S_IWUGO); ++ if (IS_ERR(file)) { ++ if (!silent) ++ AuErr("open %s(%ld)\n", fname, PTR_ERR(file)); ++ return file; ++ } ++ ++ /* keep file count */ ++ h_parent = dget_parent(file->f_dentry); ++ h_dir = h_parent->d_inode; ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); ++ /* mnt_want_write() is unnecessary here */ ++ err = vfsub_unlink(h_dir, &file->f_path, /*force*/0); ++ mutex_unlock(&h_dir->i_mutex); ++ dput(h_parent); ++ if (unlikely(err)) { ++ if (!silent) ++ AuErr("unlink %s(%d)\n", fname, err); ++ goto out; ++ } ++ ++ err = -EINVAL; ++ d = file->f_dentry; ++ if (unlikely(sb == d->d_sb)) { ++ if (!silent) ++ AuErr("%s must be outside\n", fname); ++ goto out; ++ } ++ if (unlikely(au_test_fs_bad_xino(d->d_sb))) { ++ if (!silent) ++ AuErr("xino doesn't support %s(%s)\n", ++ fname, au_sbtype(d->d_sb)); ++ goto out; ++ } ++ return file; /* success */ ++ ++ out: ++ fput(file); ++ file = ERR_PTR(err); ++ return file; ++} ++ ++/* ++ * find another branch who is on the same filesystem of the specified ++ * branch{@btgt}. search until @bend. ++ */ ++static int is_sb_shared(struct super_block *sb, aufs_bindex_t btgt, ++ aufs_bindex_t bend) ++{ ++ aufs_bindex_t bindex; ++ struct super_block *tgt_sb = au_sbr_sb(sb, btgt); ++ ++ for (bindex = 0; bindex < btgt; bindex++) ++ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) ++ return bindex; ++ for (bindex++; bindex <= bend; bindex++) ++ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) ++ return bindex; ++ return -1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * initialize the xinofile for the specified branch @br ++ * at the place/path where @base_file indicates. ++ * test whether another branch is on the same filesystem or not, ++ * if @do_test is true. ++ */ ++int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t h_ino, ++ struct file *base_file, int do_test) ++{ ++ int err; ++ ino_t ino; ++ aufs_bindex_t bend, bindex; ++ struct au_branch *shared_br, *b; ++ struct file *file; ++ struct super_block *tgt_sb; ++ ++ shared_br = NULL; ++ bend = au_sbend(sb); ++ if (do_test) { ++ tgt_sb = br->br_mnt->mnt_sb; ++ for (bindex = 0; bindex <= bend; bindex++) { ++ b = au_sbr(sb, bindex); ++ if (tgt_sb == b->br_mnt->mnt_sb) { ++ shared_br = b; ++ break; ++ } ++ } ++ } ++ ++ if (!shared_br || !shared_br->br_xino.xi_file) { ++ struct au_xino_lock_dir ldir; ++ ++ au_xino_lock_dir(sb, base_file, &ldir); ++ /* mnt_want_write() is unnecessary here */ ++ file = au_xino_create2(base_file, NULL); ++ au_xino_unlock_dir(&ldir); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ br->br_xino.xi_file = file; ++ } else { ++ br->br_xino.xi_file = shared_br->br_xino.xi_file; ++ get_file(br->br_xino.xi_file); ++ } ++ ++ ino = AUFS_ROOT_INO; ++ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, ++ h_ino, ino); ++ if (!err) ++ return 0; /* success */ ++ ++ ++ out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* trucate a xino bitmap file */ ++ ++/* todo: slow */ ++static int do_xib_restore(struct super_block *sb, struct file *file, void *page) ++{ ++ int err, bit; ++ ssize_t sz; ++ unsigned long pindex; ++ loff_t pos, pend; ++ struct au_sbinfo *sbinfo; ++ au_readf_t func; ++ ino_t *ino; ++ unsigned long *p; ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ p = sbinfo->si_xib_buf; ++ func = sbinfo->si_xread; ++ pend = i_size_read(file->f_dentry->d_inode); ++ pos = 0; ++ while (pos < pend) { ++ sz = xino_fread(func, file, page, PAGE_SIZE, &pos); ++ err = sz; ++ if (unlikely(sz <= 0)) ++ goto out; ++ ++ err = 0; ++ for (ino = page; sz > 0; ino++, sz -= sizeof(ino)) { ++ if (unlikely(*ino < AUFS_FIRST_INO)) ++ continue; ++ ++ xib_calc_bit(*ino, &pindex, &bit); ++ AuDebugOn(page_bits <= bit); ++ err = xib_pindex(sb, pindex); ++ if (!err) ++ set_bit(bit, p); ++ else ++ goto out; ++ } ++ } ++ ++ out: ++ return err; ++} ++ ++static int xib_restore(struct super_block *sb) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ void *page; ++ ++ err = -ENOMEM; ++ page = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!page)) ++ goto out; ++ ++ err = 0; ++ bend = au_sbend(sb); ++ for (bindex = 0; !err && bindex <= bend; bindex++) ++ if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0) ++ err = do_xib_restore ++ (sb, au_sbr(sb, bindex)->br_xino.xi_file, page); ++ else ++ AuDbg("b%d\n", bindex); ++ free_page((unsigned long)page); ++ ++ out: ++ return err; ++} ++ ++int au_xib_trunc(struct super_block *sb) ++{ ++ int err; ++ ssize_t sz; ++ loff_t pos; ++ struct au_xino_lock_dir ldir; ++ struct au_sbinfo *sbinfo; ++ unsigned long *p; ++ struct file *file; ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ if (!au_opt_test(sbinfo->si_mntflags, XINO)) ++ goto out; ++ ++ file = sbinfo->si_xib; ++ if (i_size_read(file->f_dentry->d_inode) <= PAGE_SIZE) ++ goto out; ++ ++ au_xino_lock_dir(sb, file, &ldir); ++ /* mnt_want_write() is unnecessary here */ ++ file = au_xino_create2(sbinfo->si_xib, NULL); ++ au_xino_unlock_dir(&ldir); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ fput(sbinfo->si_xib); ++ sbinfo->si_xib = file; ++ ++ p = sbinfo->si_xib_buf; ++ memset(p, 0, PAGE_SIZE); ++ pos = 0; ++ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xib, p, PAGE_SIZE, &pos); ++ if (unlikely(sz != PAGE_SIZE)) { ++ err = sz; ++ AuIOErr("err %d\n", err); ++ if (sz >= 0) ++ err = -EIO; ++ goto out; ++ } ++ ++ mutex_lock(&sbinfo->si_xib_mtx); ++ /* mnt_want_write() is unnecessary here */ ++ err = xib_restore(sb); ++ mutex_unlock(&sbinfo->si_xib_mtx); ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * xino mount option handlers ++ */ ++static au_readf_t find_readf(struct file *h_file) ++{ ++ const struct file_operations *fop = h_file->f_op; ++ ++ if (fop) { ++ if (fop->read) ++ return fop->read; ++ if (fop->aio_read) ++ return do_sync_read; ++ } ++ return ERR_PTR(-ENOSYS); ++} ++ ++static au_writef_t find_writef(struct file *h_file) ++{ ++ const struct file_operations *fop = h_file->f_op; ++ ++ if (fop) { ++ if (fop->write) ++ return fop->write; ++ if (fop->aio_write) ++ return do_sync_write; ++ } ++ return ERR_PTR(-ENOSYS); ++} ++ ++/* xino bitmap */ ++static void xino_clear_xib(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = au_sbi(sb); ++ sbinfo->si_xread = NULL; ++ sbinfo->si_xwrite = NULL; ++ if (sbinfo->si_xib) ++ fput(sbinfo->si_xib); ++ sbinfo->si_xib = NULL; ++ free_page((unsigned long)sbinfo->si_xib_buf); ++ sbinfo->si_xib_buf = NULL; ++} ++ ++static int au_xino_set_xib(struct super_block *sb, struct file *base) ++{ ++ int err; ++ loff_t pos; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ ++ sbinfo = au_sbi(sb); ++ file = au_xino_create2(base, sbinfo->si_xib); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ if (sbinfo->si_xib) ++ fput(sbinfo->si_xib); ++ sbinfo->si_xib = file; ++ sbinfo->si_xread = find_readf(file); ++ sbinfo->si_xwrite = find_writef(file); ++ ++ err = -ENOMEM; ++ if (!sbinfo->si_xib_buf) ++ sbinfo->si_xib_buf = (void *)get_zeroed_page(GFP_NOFS); ++ if (unlikely(!sbinfo->si_xib_buf)) ++ goto out_unset; ++ ++ sbinfo->si_xib_last_pindex = 0; ++ sbinfo->si_xib_next_bit = 0; ++ if (i_size_read(file->f_dentry->d_inode) < PAGE_SIZE) { ++ pos = 0; ++ err = xino_fwrite(sbinfo->si_xwrite, file, sbinfo->si_xib_buf, ++ PAGE_SIZE, &pos); ++ if (unlikely(err != PAGE_SIZE)) ++ goto out_free; ++ } ++ err = 0; ++ goto out; /* success */ ++ ++ out_free: ++ free_page((unsigned long)sbinfo->si_xib_buf); ++ sbinfo->si_xib_buf = NULL; ++ if (err >= 0) ++ err = -EIO; ++ out_unset: ++ fput(sbinfo->si_xib); ++ sbinfo->si_xib = NULL; ++ sbinfo->si_xread = NULL; ++ sbinfo->si_xwrite = NULL; ++ out: ++ return err; ++} ++ ++/* xino for each branch */ ++static void xino_clear_br(struct super_block *sb) ++{ ++ aufs_bindex_t bindex, bend; ++ struct au_branch *br; ++ ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (!br || !br->br_xino.xi_file) ++ continue; ++ ++ fput(br->br_xino.xi_file); ++ br->br_xino.xi_file = NULL; ++ } ++} ++ ++static int au_xino_set_br(struct super_block *sb, struct file *base) ++{ ++ int err; ++ ino_t ino; ++ aufs_bindex_t bindex, bend, bshared; ++ struct { ++ struct file *old, *new; ++ } *fpair, *p; ++ struct au_branch *br; ++ struct inode *inode; ++ au_writef_t writef; ++ ++ err = -ENOMEM; ++ bend = au_sbend(sb); ++ fpair = kcalloc(bend + 1, sizeof(*fpair), GFP_NOFS); ++ if (unlikely(!fpair)) ++ goto out; ++ ++ inode = sb->s_root->d_inode; ++ ino = AUFS_ROOT_INO; ++ writef = au_sbi(sb)->si_xwrite; ++ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { ++ br = au_sbr(sb, bindex); ++ bshared = is_sb_shared(sb, bindex, bindex - 1); ++ if (bshared >= 0) { ++ /* shared xino */ ++ *p = fpair[bshared]; ++ get_file(p->new); ++ } ++ ++ if (!p->new) { ++ /* new xino */ ++ p->old = br->br_xino.xi_file; ++ p->new = au_xino_create2(base, br->br_xino.xi_file); ++ err = PTR_ERR(p->new); ++ if (IS_ERR(p->new)) { ++ p->new = NULL; ++ goto out_pair; ++ } ++ } ++ ++ err = au_xino_do_write(writef, p->new, ++ au_h_iptr(inode, bindex)->i_ino, ino); ++ if (unlikely(err)) ++ goto out_pair; ++ } ++ ++ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { ++ br = au_sbr(sb, bindex); ++ if (br->br_xino.xi_file) ++ fput(br->br_xino.xi_file); ++ get_file(p->new); ++ br->br_xino.xi_file = p->new; ++ } ++ ++ out_pair: ++ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) ++ if (p->new) ++ fput(p->new); ++ else ++ break; ++ kfree(fpair); ++ out: ++ return err; ++} ++ ++void au_xino_clr(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ au_xigen_clr(sb); ++ xino_clear_xib(sb); ++ xino_clear_br(sb); ++ sbinfo = au_sbi(sb); ++ /* lvalue, do not call au_mntflags() */ ++ au_opt_clr(sbinfo->si_mntflags, XINO); ++} ++ ++int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount) ++{ ++ int err, skip; ++ struct dentry *parent, *cur_parent; ++ struct qstr *dname, *cur_name; ++ struct file *cur_xino; ++ struct inode *dir; ++ struct au_sbinfo *sbinfo; ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ parent = dget_parent(xino->file->f_dentry); ++ if (remount) { ++ skip = 0; ++ dname = &xino->file->f_dentry->d_name; ++ cur_xino = sbinfo->si_xib; ++ if (cur_xino) { ++ cur_parent = dget_parent(cur_xino->f_dentry); ++ cur_name = &cur_xino->f_dentry->d_name; ++ skip = (cur_parent == parent ++ && dname->len == cur_name->len ++ && !memcmp(dname->name, cur_name->name, ++ dname->len)); ++ dput(cur_parent); ++ } ++ if (skip) ++ goto out; ++ } ++ ++ au_opt_set(sbinfo->si_mntflags, XINO); ++ dir = parent->d_inode; ++ mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT); ++ /* mnt_want_write() is unnecessary here */ ++ err = au_xino_set_xib(sb, xino->file); ++ if (!err) ++ err = au_xigen_set(sb, xino->file); ++ if (!err) ++ err = au_xino_set_br(sb, xino->file); ++ mutex_unlock(&dir->i_mutex); ++ if (!err) ++ goto out; /* success */ ++ ++ /* reset all */ ++ AuIOErr("failed creating xino(%d).\n", err); ++ ++ out: ++ dput(parent); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create a xinofile at the default place/path. ++ */ ++struct file *au_xino_def(struct super_block *sb) ++{ ++ struct file *file; ++ char *page, *p; ++ struct au_branch *br; ++ struct super_block *h_sb; ++ struct path path; ++ aufs_bindex_t bend, bindex, bwr; ++ ++ br = NULL; ++ bend = au_sbend(sb); ++ bwr = -1; ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_writable(br->br_perm) ++ && !au_test_fs_bad_xino(br->br_mnt->mnt_sb)) { ++ bwr = bindex; ++ break; ++ } ++ } ++ ++ if (bwr >= 0) { ++ file = ERR_PTR(-ENOMEM); ++ page = __getname(); ++ if (unlikely(!page)) ++ goto out; ++ path.mnt = br->br_mnt; ++ path.dentry = au_h_dptr(sb->s_root, bwr); ++ p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME)); ++ file = (void *)p; ++ if (!IS_ERR(p)) { ++ strcat(p, "/" AUFS_XINO_FNAME); ++ AuDbg("%s\n", p); ++ file = au_xino_create(sb, p, /*silent*/0); ++ if (!IS_ERR(file)) ++ au_xino_brid_set(sb, br->br_id); ++ } ++ __putname(page); ++ } else { ++ file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0); ++ if (IS_ERR(file)) ++ goto out; ++ h_sb = file->f_dentry->d_sb; ++ if (unlikely(au_test_fs_bad_xino(h_sb))) { ++ AuErr("xino doesn't support %s(%s)\n", ++ AUFS_XINO_DEFPATH, au_sbtype(h_sb)); ++ fput(file); ++ file = ERR_PTR(-EINVAL); ++ } ++ if (!IS_ERR(file)) ++ au_xino_brid_set(sb, -1); ++ } ++ ++ out: ++ return file; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_xino_path(struct seq_file *seq, struct file *file) ++{ ++ int err; ++ ++ err = au_seq_path(seq, &file->f_path); ++ if (unlikely(err < 0)) ++ goto out; ++ ++ err = 0; ++#define Deleted "\\040(deleted)" ++ seq->count -= sizeof(Deleted) - 1; ++ AuDebugOn(memcmp(seq->buf + seq->count, Deleted, ++ sizeof(Deleted) - 1)); ++#undef Deleted ++ ++ out: ++ return err; ++} +diff -Naur a/include/linux/aufs_type.h b/include/linux/aufs_type.h +--- a/include/linux/aufs_type.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/include/linux/aufs_type.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,109 @@ ++/* ++ * 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 ++ */ ++ ++#ifndef __AUFS_TYPE_H__ ++#define __AUFS_TYPE_H__ ++ ++#include ++ ++#define AUFS_VERSION "2-standalone.tree-29-20090622" ++ ++/* todo? move this to linux-2.6.19/include/magic.h */ ++#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_BRANCH_MAX_127 ++/* some environments treat 'char' as 'unsigned char' by default */ ++typedef signed char aufs_bindex_t; ++#define AUFS_BRANCH_MAX 127 ++#else ++typedef short aufs_bindex_t; ++#ifdef CONFIG_AUFS_BRANCH_MAX_511 ++#define AUFS_BRANCH_MAX 511 ++#elif defined(CONFIG_AUFS_BRANCH_MAX_1023) ++#define AUFS_BRANCH_MAX 1023 ++#elif defined(CONFIG_AUFS_BRANCH_MAX_32767) ++#define AUFS_BRANCH_MAX 32767 ++#endif ++#endif ++ ++#ifdef __KERNEL__ ++#ifndef AUFS_BRANCH_MAX ++#error unknown CONFIG_AUFS_BRANCH_MAX value ++#endif ++#endif /* __KERNEL__ */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define AUFS_NAME "aufs" ++#define AUFS_FSTYPE AUFS_NAME ++ ++#define AUFS_ROOT_INO 2 ++#define AUFS_FIRST_INO 11 ++ ++#define AUFS_WH_PFX ".wh." ++#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1) ++#define AUFS_XINO_FNAME "." AUFS_NAME ".xino" ++#define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME ++#define AUFS_XINO_TRUNC_INIT 64 /* blocks */ ++#define AUFS_XINO_TRUNC_STEP 4 /* blocks */ ++#define AUFS_DIRWH_DEF 3 ++#define AUFS_RDCACHE_DEF 10 /* seconds */ ++#define AUFS_RDBLK_DEF 512 /* bytes */ ++#define AUFS_RDHASH_DEF 32 ++#define AUFS_WKQ_NAME AUFS_NAME "d" ++#define AUFS_NWKQ_DEF 4 ++#define AUFS_MFS_SECOND_DEF 30 /* seconds */ ++#define AUFS_PLINK_WARN 100 /* number of plinks */ ++ ++#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */ ++#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME ++ ++#define AUFS_BASE_NAME AUFS_WH_PFX AUFS_NAME ++#define AUFS_PLINKDIR_NAME AUFS_WH_PFX "plnk" ++#define AUFS_ORPHDIR_NAME AUFS_WH_PFX "orph" ++ ++/* doubly whiteouted */ ++#define AUFS_WH_BASE AUFS_WH_PFX AUFS_BASE_NAME ++#define AUFS_WH_PLINKDIR AUFS_WH_PFX AUFS_PLINKDIR_NAME ++#define AUFS_WH_ORPHDIR AUFS_WH_PFX AUFS_ORPHDIR_NAME ++ ++/* branch permission */ ++#define AUFS_BRPERM_RW "rw" ++#define AUFS_BRPERM_RO "ro" ++#define AUFS_BRPERM_RR "rr" ++#define AUFS_BRPERM_WH "wh" ++#define AUFS_BRPERM_NLWH "nolwh" ++#define AUFS_BRPERM_ROWH AUFS_BRPERM_RO "+" AUFS_BRPERM_WH ++#define AUFS_BRPERM_RRWH AUFS_BRPERM_RR "+" AUFS_BRPERM_WH ++#define AUFS_BRPERM_RWNLWH AUFS_BRPERM_RW "+" AUFS_BRPERM_NLWH ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ioctl */ ++enum { ++ AuCtl_PLINK_MAINT, ++ AuCtl_PLINK_CLEAN ++}; ++ ++#define AuCtlType 'A' ++#define AUFS_CTL_PLINK_MAINT _IO(AuCtlType, AuCtl_PLINK_MAINT) ++#define AUFS_CTL_PLINK_CLEAN _IO(AuCtlType, AuCtl_PLINK_CLEAN) ++ ++#endif /* __AUFS_TYPE_H__ */ diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/aufs-squashfs-mount-to-avoid-initramfs.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/aufs-squashfs-mount-to-avoid-initramfs.patch new file mode 100644 index 0000000000..9a9b982fef --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/aufs-squashfs-mount-to-avoid-initramfs.patch @@ -0,0 +1,40 @@ +--- linux-omap-2.6/init/do_mounts.c 2008-11-23 01:22:52.000000000 -0800 ++++ linux-omap-2.6/init/do_mounts.c 2008-11-23 02:17:58.000000000 -0800 +@@ -325,6 +325,37 @@ + + void __init mount_root(void) + { ++ int do_special = 0; ++ if (strcmp(root_device_name, "special") == 0) ++ do_special = 1; ++ //do_special = 1; ++ ++ if (do_special) { ++ dev_t ROOT_DEV_RO; ++ dev_t ROOT_DEV_RW; ++ ++ sys_mkdir("/root-ro", 0700); ++ ROOT_DEV_RO = name_to_dev_t("/dev/mtd4"); ++ create_dev("/dev/root-ro", ROOT_DEV_RO); ++ if (sys_mount("/dev/root-ro", "/root-ro", "squashfs", MS_RDONLY | MS_SILENT, NULL) != 0) { ++ ROOT_DEV_RO = name_to_dev_t("/dev/mmcblk0p2"); ++ create_dev("/dev/root-ro2", ROOT_DEV_RO); ++ if (sys_mount("/dev/root-ro2", "/root-ro", "squashfs", MS_RDONLY | MS_SILENT, NULL) != 0) ++ goto no_special; ++ } ++ ++ sys_mkdir("/root-rw", 0700); ++ ROOT_DEV_RW = name_to_dev_t("/dev/mmcblk0p3"); ++ create_dev("/dev/root-rw", ROOT_DEV_RW); ++ if (sys_mount("/dev/root-rw", "/root-rw", "ext3", MS_SILENT, NULL) != 0) ++ goto no_special; ++ sys_mount("none", "/root", "aufs", 0, "br:/root-rw:/root-ro=ro"); ++ sys_chdir("/root"); ++ return; ++ } ++ ++no_special: ++ + #ifdef CONFIG_ROOT_NFS + if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) { + if (mount_nfs_root()) diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/battery1-tps65950-charging-management-1.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/battery1-tps65950-charging-management-1.patch new file mode 100644 index 0000000000..035627ac79 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/battery1-tps65950-charging-management-1.patch @@ -0,0 +1,221 @@ +From 1b3e7e2e095648f244a08b1459ff035bbdc99942 Mon Sep 17 00:00:00 2001 +From: Tim Yamin +Date: Tue, 5 May 2009 23:33:00 -0700 +Subject: [PATCH] TWL BCI: Make charge current controllable and backup battery optional. + +This patch makes the charging current controllable via sysfs and also +makes the backup battery optional. Also, if you don't care about the +temperature readings, you can opt-out from having to supply a +temperature lookup table. + +Signed-off-by: Tim Yamin +--- + drivers/power/twl4030_bci_battery.c | 91 +++++++++++++++++++++++++++++------ + include/linux/i2c/twl4030.h | 2 + + 2 files changed, 78 insertions(+), 15 deletions(-) + +diff --git a/drivers/power/twl4030_bci_battery.c b/drivers/power/twl4030_bci_battery.c +index ddba62b..eab0933 100644 +--- a/drivers/power/twl4030_bci_battery.c ++++ b/drivers/power/twl4030_bci_battery.c +@@ -125,6 +125,14 @@ + /* BCIEDR3 */ + #define VBATLVL_EDRRISIN 0x02 + ++/* BCIIREF1 */ ++#define REG_BCIIREF1 0x027 ++#define REG_BCIIREF2 0x028 ++ ++/* Key */ ++#define KEY_IIREF 0xE7 ++#define REG_BCIMFKEY 0x011 ++ + /* Step size and prescaler ratio */ + #define TEMP_STEP_SIZE 147 + #define TEMP_PSR_R 100 +@@ -142,9 +150,6 @@ + #define ENABLE 1 + #define DISABLE 1 + +-/* Ptr to thermistor table */ +-int *therm_tbl; +- + struct twl4030_bci_device_info { + struct device *dev; + +@@ -160,6 +165,8 @@ struct twl4030_bci_device_info { + struct power_supply bk_bat; + struct delayed_work twl4030_bci_monitor_work; + struct delayed_work twl4030_bk_bci_monitor_work; ++ ++ struct twl4030_bci_platform_data *pdata; + }; + + static int usb_charger_flag; +@@ -518,11 +525,15 @@ int twl4030charger_usb_en(int enable) + * Return battery temperature + * Or < 0 on failure. + */ +-static int twl4030battery_temperature(void) ++static int twl4030battery_temperature(struct twl4030_bci_device_info *di) + { + u8 val; + int temp, curr, volt, res, ret; + ++ /* Is a temperature table specified? */ ++ if (!di->pdata->tblsize) ++ return 0; ++ + /* Getting and calculating the thermistor voltage */ + ret = read_bci_val(T2_BATTERY_TEMP); + if (ret < 0) +@@ -543,7 +554,7 @@ static int twl4030battery_temperature(void) + + /*calculating temperature*/ + for (temp = 58; temp >= 0; temp--) { +- int actual = therm_tbl[temp]; ++ int actual = di->pdata->battery_tmp_tbl[temp]; + if ((actual - res) >= 0) + break; + } +@@ -772,13 +783,14 @@ static void twl4030_bk_bci_battery_work(struct work_struct *work) + struct twl4030_bci_device_info, + twl4030_bk_bci_monitor_work.work); + +- twl4030_bk_bci_battery_read_status(di); ++ if(!di->pdata->no_backup_battery) ++ twl4030_bk_bci_battery_read_status(di); + schedule_delayed_work(&di->twl4030_bk_bci_monitor_work, 500); + } + + static void twl4030_bci_battery_read_status(struct twl4030_bci_device_info *di) + { +- di->temp_C = twl4030battery_temperature(); ++ di->temp_C = twl4030battery_temperature(di); + di->voltage_uV = twl4030battery_voltage(); + di->current_uA = twl4030battery_current(); + } +@@ -819,6 +831,43 @@ static void twl4030_bci_battery_external_power_changed(struct power_supply *psy) + #define to_twl4030_bk_bci_device_info(x) container_of((x), \ + struct twl4030_bci_device_info, bk_bat); + ++static ssize_t ++show_charge_current(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d\n", read_bci_val(REG_BCIIREF1)); ++} ++ ++static ssize_t ++set_charge_current(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) ++{ ++ unsigned long newCurrent; ++ int ret; ++ ++ ret = strict_strtoul(buf, 10, &newCurrent); ++ if (ret) ++ return -EINVAL; ++ ++ ret = twl4030_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, KEY_IIREF, REG_BCIMFKEY); ++ if (ret) ++ return ret; ++ ++ ret = twl4030_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, newCurrent & 0xff, REG_BCIIREF1); ++ if (ret) ++ return ret; ++ ++ ret = twl4030_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, KEY_IIREF, REG_BCIMFKEY); ++ if (ret) ++ return ret; ++ ++ ret = twl4030_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, (newCurrent >> 8) & 0x03, REG_BCIIREF2); ++ if (ret) ++ return ret; ++ ++ return count; ++} ++ ++static DEVICE_ATTR(charge_current, S_IRUGO | S_IWUGO, show_charge_current, set_charge_current); ++ + static int twl4030_bk_bci_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +@@ -912,8 +961,6 @@ static int __init twl4030_bci_battery_probe(struct platform_device *pdev) + int irq; + int ret; + +- therm_tbl = pdata->battery_tmp_tbl; +- + di = kzalloc(sizeof(*di), GFP_KERNEL); + if (!di) + return -ENOMEM; +@@ -937,6 +984,7 @@ static int __init twl4030_bci_battery_probe(struct platform_device *pdev) + di->bk_bat.num_properties = ARRAY_SIZE(twl4030_bk_bci_battery_props); + di->bk_bat.get_property = twl4030_bk_bci_battery_get_property; + di->bk_bat.external_power_changed = NULL; ++ di->pdata = pdata; + + twl4030charger_ac_en(ENABLE); + twl4030charger_usb_en(ENABLE); +@@ -951,9 +999,12 @@ static int __init twl4030_bci_battery_probe(struct platform_device *pdev) + goto temp_setup_fail; + + /* enabling GPCH09 for read back battery voltage */ +- ret = twl4030backupbatt_voltage_setup(); +- if (ret) +- goto voltage_setup_fail; ++ if(!di->pdata->no_backup_battery) ++ { ++ ret = twl4030backupbatt_voltage_setup(); ++ if (ret) ++ goto voltage_setup_fail; ++ } + + /* REVISIT do we need to request both IRQs ?? */ + +@@ -988,9 +1039,18 @@ static int __init twl4030_bci_battery_probe(struct platform_device *pdev) + twl4030_bci_battery_work); + schedule_delayed_work(&di->twl4030_bci_monitor_work, 0); + +- ret = power_supply_register(&pdev->dev, &di->bk_bat); ++ if(!pdata->no_backup_battery) ++ { ++ ret = power_supply_register(&pdev->dev, &di->bk_bat); ++ if (ret) { ++ dev_dbg(&pdev->dev, "failed to register backup battery\n"); ++ goto bk_batt_failed; ++ } ++ } ++ ++ ret = device_create_file(di->bat.dev, &dev_attr_charge_current); + if (ret) { +- dev_dbg(&pdev->dev, "failed to register backup battery\n"); ++ dev_err(&pdev->dev, "failed to create sysfs entries\n"); + goto bk_batt_failed; + } + +@@ -1001,7 +1061,8 @@ static int __init twl4030_bci_battery_probe(struct platform_device *pdev) + return 0; + + bk_batt_failed: +- power_supply_unregister(&di->bat); ++ if(!pdata->no_backup_battery) ++ power_supply_unregister(&di->bat); + batt_failed: + free_irq(irq, di); + chg_irq_fail: +diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h +index 87accda..a188b6c 100644 +--- a/include/linux/i2c/twl4030.h ++++ b/include/linux/i2c/twl4030.h +@@ -299,6 +299,8 @@ int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); + struct twl4030_bci_platform_data { + int *battery_tmp_tbl; + unsigned int tblsize; ++ ++ bool no_backup_battery; + }; + + /* TWL4030_GPIO_MAX (18) GPIOs, with interrupts */ +-- +1.5.6.3 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/battery1-tps65950-charging-management-2.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/battery1-tps65950-charging-management-2.patch new file mode 100644 index 0000000000..ce5a98f69d --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/battery1-tps65950-charging-management-2.patch @@ -0,0 +1,246 @@ +diff --git a/drivers/power/twl4030_bci_battery.c b/drivers/power/twl4030_bci_battery.c +index eab0933..d1220d7 100644 +--- a/drivers/power/twl4030_bci_battery.c ++++ b/drivers/power/twl4030_bci_battery.c +@@ -15,6 +15,9 @@ + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + ++/* Boot with automatic charge */ ++#define CHARGE_MODE 1 ++ + #include + #include + #include +@@ -50,6 +53,7 @@ + /* Boot BCI flag bits */ + #define BCIAUTOWEN 0x020 + #define CONFIG_DONE 0x010 ++#define CVENAC 0x004 + #define BCIAUTOUSB 0x002 + #define BCIAUTOAC 0x001 + #define BCIMSTAT_MASK 0x03F +@@ -81,6 +85,11 @@ + #define REG_BB_CFG 0x012 + #define BBCHEN 0x010 + ++/* GPBR */ ++#define REG_GPBR1 0x0c ++#define MADC_HFCLK_EN 0x80 ++#define DEFAULT_MADC_CLK_EN 0x10 ++ + /* Power supply charge interrupt */ + #define REG_PWR_ISR1 0x00 + #define REG_PWR_IMR1 0x01 +@@ -129,8 +138,12 @@ + #define REG_BCIIREF1 0x027 + #define REG_BCIIREF2 0x028 + ++/* BCIMFTH1 */ ++#define REG_BCIMFTH1 0x016 ++ + /* Key */ + #define KEY_IIREF 0xE7 ++#define KEY_FTH1 0xD2 + #define REG_BCIMFKEY 0x011 + + /* Step size and prescaler ratio */ +@@ -432,15 +445,21 @@ static int twl4030battery_hw_presence_en(int enable) + /* + * Enable/Disable AC Charge funtionality. + */ +-static int twl4030charger_ac_en(int enable) ++static int twl4030charger_ac_en(int enable, int automatic) + { + int ret; + + if (enable) { + /* forcing the field BCIAUTOAC (BOOT_BCI[0) to 1 */ +- ret = clear_n_set(TWL4030_MODULE_PM_MASTER, 0, +- (CONFIG_DONE | BCIAUTOWEN | BCIAUTOAC), +- REG_BOOT_BCI); ++ if(!automatic) { ++ ret = clear_n_set(TWL4030_MODULE_PM_MASTER, BCIAUTOAC | CVENAC, ++ (CONFIG_DONE | BCIAUTOWEN), ++ REG_BOOT_BCI); ++ } else { ++ ret = clear_n_set(TWL4030_MODULE_PM_MASTER, 0, ++ (CONFIG_DONE | BCIAUTOWEN | BCIAUTOAC | CVENAC), ++ REG_BOOT_BCI); ++ } + if (ret) + return ret; + } else { +@@ -672,6 +691,9 @@ static int twl4030bci_status(void) + return ret; + } + ++#ifdef DEBUG ++ printk("BCI DEBUG: BCIMSTATEC Charge state is 0x%x\n", status); ++#endif + return (int) (status & BCIMSTAT_MASK); + } + +@@ -720,14 +742,43 @@ static int twl4030backupbatt_voltage_setup(void) + */ + static int twl4030battery_temp_setup(void) + { +- int ret; ++#ifdef DEBUG ++ u8 i; ++#endif ++ u8 ret; + + /* Enabling thermistor current */ +- ret = clear_n_set(TWL4030_MODULE_MAIN_CHARGE, 0, ITHEN, ++ ret = clear_n_set(TWL4030_MODULE_MAIN_CHARGE, 0, 0x1B, + REG_BCICTL1); + if (ret) + return ret; + ++#ifdef DEBUG ++ twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &ret, REG_BOOT_BCI); ++ printk("BCI DEBUG: BOOT_BCI Value is 0x%x\n", ret); ++ ++ twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &ret, REG_STS_HW_CONDITIONS); ++ printk("BCI DEBUG: STS_HW_CONDITIONS Value is 0x%x\n", ret); ++ ++ twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &ret, REG_BCICTL1); ++ printk("BCI DEBUG: BCICTL1 Value is 0x%x\n", ret); ++ ++ twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &ret, REG_BCICTL2); ++ printk("BCI DEBUG: BCICTL2 Value is 0x%x\n", ret); ++ ++ twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &ret, 0x0); ++ printk("BCI DEBUG: BCIMDEN Value is 0x%x\n", ret); ++ ++ twl4030_i2c_read_u8(TWL4030_MODULE_INTBR, &ret, REG_GPBR1); ++ printk("BCI DEBUG: GPBR1 Value is 0x%x\n", ret); ++ ++ for(i = 0x0; i <= 0x32; i++) ++ { ++ twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &ret, i); ++ printk("BCI DEBUG: BCI 0x%x Value is 0x%x\n", i, ret); ++ } ++#endif ++ + return 0; + } + +@@ -743,7 +794,6 @@ static inline int clear_n_set(u8 mod_no, u8 clear, u8 set, u8 reg) + ret = twl4030_i2c_read_u8(mod_no, &val, reg); + if (ret) + return ret; +- + /* Clearing all those bits to clear */ + val &= ~(clear); + +@@ -834,7 +884,19 @@ static void twl4030_bci_battery_external_power_changed(struct power_supply *psy) + static ssize_t + show_charge_current(struct device *dev, struct device_attribute *attr, char *buf) + { +- return sprintf(buf, "%d\n", read_bci_val(REG_BCIIREF1)); ++ u8 ctl; ++ int ret = read_bci_val(REG_BCIIREF1) & 0x1FF; ++ twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &ctl, REG_BCICTL1); ++ ++ if (ctl & CGAIN) ++ ret |= 0x200; ++ ++#ifdef DEBUG ++ /* Dump debug */ ++ twl4030battery_temp_setup(); ++#endif ++ ++ return sprintf(buf, "%d\n", ret); + } + + static ssize_t +@@ -859,13 +921,45 @@ set_charge_current(struct device *dev, struct device_attribute *attr, const char + if (ret) + return ret; + +- ret = twl4030_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, (newCurrent >> 8) & 0x03, REG_BCIIREF2); ++ ret = twl4030_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, (newCurrent >> 8) & 0x1, REG_BCIIREF2); + if (ret) + return ret; + ++ /* Set software-controlled charge */ ++ twl4030charger_ac_en(ENABLE, 0); ++ ++ /* Set CGAIN = 0 or 1 */ ++ if(newCurrent > 511) { ++ u8 tmp; ++ ++ /* Set CGAIN = 1 -- need to wait until automatic charge turns off */ ++ while(!ret) { ++ clear_n_set(TWL4030_MODULE_MAIN_CHARGE, 0, CGAIN | 0x1B, REG_BCICTL1); ++ twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &tmp, REG_BCICTL1); ++ ++ ret = tmp & CGAIN; ++ if(!ret) ++ mdelay(50); ++ } ++ } else { ++ u8 tmp; ++ ++ /* Set CGAIN = 0 -- need to wait until automatic charge turns off */ ++ while(!ret) { ++ clear_n_set(TWL4030_MODULE_MAIN_CHARGE, CGAIN, 0x1B, REG_BCICTL1); ++ twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &tmp, REG_BCICTL1); ++ ++ ret = !(tmp & CGAIN); ++ if(!ret) ++ mdelay(50); ++ } ++ } ++ ++ /* Set automatic charge (CGAIN = 0/1 persists) */ ++ twl4030charger_ac_en(ENABLE, 1); ++ + return count; + } +- + static DEVICE_ATTR(charge_current, S_IRUGO | S_IWUGO, show_charge_current, set_charge_current); + + static int twl4030_bk_bci_battery_get_property(struct power_supply *psy, +@@ -986,7 +1080,10 @@ static int __init twl4030_bci_battery_probe(struct platform_device *pdev) + di->bk_bat.external_power_changed = NULL; + di->pdata = pdata; + +- twl4030charger_ac_en(ENABLE); ++ /* Set up clocks */ ++ twl4030_i2c_write_u8(TWL4030_MODULE_INTBR, MADC_HFCLK_EN | DEFAULT_MADC_CLK_EN, REG_GPBR1); ++ ++ twl4030charger_ac_en(ENABLE, CHARGE_MODE); + twl4030charger_usb_en(ENABLE); + twl4030battery_hw_level_en(ENABLE); + twl4030battery_hw_presence_en(ENABLE); +@@ -1058,6 +1155,7 @@ static int __init twl4030_bci_battery_probe(struct platform_device *pdev) + twl4030_bk_bci_battery_work); + schedule_delayed_work(&di->twl4030_bk_bci_monitor_work, 500); + ++ set_charge_current (NULL, NULL, "1023", 4); + return 0; + + bk_batt_failed: +@@ -1071,7 +1169,7 @@ chg_irq_fail: + batt_irq_fail: + voltage_setup_fail: + temp_setup_fail: +- twl4030charger_ac_en(DISABLE); ++ twl4030charger_ac_en(DISABLE, CHARGE_MODE); + twl4030charger_usb_en(DISABLE); + twl4030battery_hw_level_en(DISABLE); + twl4030battery_hw_presence_en(DISABLE); +@@ -1085,7 +1183,7 @@ static int __exit twl4030_bci_battery_remove(struct platform_device *pdev) + struct twl4030_bci_device_info *di = platform_get_drvdata(pdev); + int irq; + +- twl4030charger_ac_en(DISABLE); ++ twl4030charger_ac_en(DISABLE, CHARGE_MODE); + twl4030charger_usb_en(DISABLE); + twl4030battery_hw_level_en(DISABLE); + twl4030battery_hw_presence_en(DISABLE); + + diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/battery2-bq27200-gpio-charged.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/battery2-bq27200-gpio-charged.patch new file mode 100644 index 0000000000..fd629078ca --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/battery2-bq27200-gpio-charged.patch @@ -0,0 +1,53 @@ +diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c +index bc6ef06..df7aa70 100644 +--- a/drivers/power/bq27x00_battery.c ++++ b/drivers/power/bq27x00_battery.c +@@ -28,6 +28,7 @@ + + #define DRIVER_VERSION "1.0.0" + ++#define BQ27x00_REG_MODE 0x00 + #define BQ27x00_REG_TEMP 0x06 + #define BQ27x00_REG_VOLT 0x08 + #define BQ27x00_REG_RSOC 0x0B /* Relative State-of-Charge */ +@@ -65,6 +66,7 @@ static enum power_supply_property bq27x00_battery_props[] = { + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, ++ POWER_SUPPLY_PROP_ONLINE, + }; + + /* +@@ -83,6 +85,22 @@ static int bq27x00_read(u8 reg, int *rt_value, int b_single, + } + + /* ++ * Return the GPIO status (0 or 1) ++ * Or < 0 if something fails. ++ */ ++static int bq27x00_gpio(struct bq27x00_device_info *di) ++{ ++ int ret; ++ int gpio = 0; ++ ++ ret = bq27x00_read(BQ27x00_REG_MODE, &gpio, 0, di); ++ if (ret) ++ return ret; ++ ++ return (gpio & 0x40) >> 6; ++} ++ ++/* + * Return the battery temperature in Celcius degrees + * Or < 0 if something fails. + */ +@@ -184,6 +202,9 @@ static int bq27x00_battery_get_property(struct power_supply *psy, + case POWER_SUPPLY_PROP_TEMP: + val->intval = bq27x00_battery_temperature(di); + break; ++ case POWER_SUPPLY_PROP_ONLINE: ++ val->intval = bq27x00_gpio(di); ++ break; + default: + return -EINVAL; + } diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/battery2-bq27200-no-error-message.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/battery2-bq27200-no-error-message.patch new file mode 100644 index 0000000000..eb7c8c9699 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/battery2-bq27200-no-error-message.patch @@ -0,0 +1,39 @@ +--- a/drivers/power/bq27x00_battery.c ++++ b/drivers/power/bq27x00_battery.c +@@ -93,7 +93,6 @@ static int bq27x00_battery_temperature(struct bq27x00_device_info *di) + + ret = bq27x00_read(BQ27x00_REG_TEMP, &temp, 0, di); + if (ret) { +- dev_err(di->dev, "error reading temperature\n"); + return ret; + } + +@@ -111,7 +110,6 @@ static int bq27x00_battery_voltage(struct bq27x00_device_info *di) + + ret = bq27x00_read(BQ27x00_REG_VOLT, &volt, 0, di); + if (ret) { +- dev_err(di->dev, "error reading voltage\n"); + return ret; + } + +@@ -131,12 +129,10 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di) + + ret = bq27x00_read(BQ27x00_REG_AI, &curr, 0, di); + if (ret) { +- dev_err(di->dev, "error reading current\n"); + return 0; + } + ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di); + if (ret < 0) { +- dev_err(di->dev, "error reading flags\n"); + return 0; + } + if ((flags & (1 << 7)) != 0) { +@@ -157,7 +153,6 @@ static int bq27x00_battery_rsoc(struct bq27x00_device_info *di) + + ret = bq27x00_read(BQ27x00_REG_RSOC, &rsoc, 1, di); + if (ret) { +- dev_err(di->dev, "error reading relative State-of-Charge\n"); + return ret; + } + diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/beagle-asoc.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/beagle-asoc.patch new file mode 100644 index 0000000000..b2b920037e --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/beagle-asoc.patch @@ -0,0 +1,35 @@ +diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig +index 4f7f040..ccd8973 100644 +--- a/sound/soc/omap/Kconfig ++++ b/sound/soc/omap/Kconfig +@@ -55,3 +55,13 @@ config SND_OMAP_SOC_OMAP3_PANDORA + select SND_SOC_TWL4030 + help + Say Y if you want to add support for SoC audio on the OMAP3 Pandora. ++ ++config SND_OMAP_SOC_OMAP3_BEAGLE ++ tristate "SoC Audio support for OMAP3 Beagle" ++ depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3_BEAGLE ++ select SND_OMAP_SOC_MCBSP ++ select SND_SOC_TWL4030 ++ help ++ Say Y if you want to add support for SoC audio on the Beagleboard. ++ ++ +diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile +index 76fedd9..0c9e4ac 100644 +--- a/sound/soc/omap/Makefile ++++ b/sound/soc/omap/Makefile +@@ -12,6 +12,7 @@ snd-soc-overo-objs := overo.o + snd-soc-omap2evm-objs := omap2evm.o + snd-soc-sdp3430-objs := sdp3430.o + snd-soc-omap3pandora-objs := omap3pandora.o ++snd-soc-omap3beagle-objs := omap3beagle.o + + obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o + obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o +@@ -19,3 +20,4 @@ obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o + obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o + obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o + obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o ++obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/board-omap3beagle.c b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/board-omap3beagle.c new file mode 100644 index 0000000000..c7867bef2f --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/board-omap3beagle.c @@ -0,0 +1,744 @@ +/* + * linux/arch/arm/mach-omap2/board-omap3beagle.c + * + * Copyright (C) 2008 Texas Instruments + * + * Modified from mach-omap2/board-3430sdp.c + * + * Initial code: Syed Mohammed Khasim + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "twl4030-generic-scripts.h" +#include "mmc-twl4030.h" +#include "pm.h" +#include "omap3-opp.h" + + +#define GPMC_CS0_BASE 0x60 +#define GPMC_CS_SIZE 0x30 + +#define NAND_BLOCK_SIZE SZ_128K + +#define OMAP3_AC_GPIO 136 //Int1 DRDY +#define OMAP3_TS_GPIO 162 +#define TB_BL_PWM_TIMER 9 +#define TB_KILL_POWER_GPIO 168 + +static struct mtd_partition omap3beagle_nand_partitions[] = { + /* All the partition sizes are listed in terms of NAND block size */ + { + .name = "X-Loader", + .offset = 0, + .size = 4 * NAND_BLOCK_SIZE, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "U-Boot", + .offset = MTDPART_OFS_APPEND, /* Offset = 0x80000 */ + .size = 15 * NAND_BLOCK_SIZE, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "U-Boot Env", + .offset = MTDPART_OFS_APPEND, /* Offset = 0x260000 */ + .size = 1 * NAND_BLOCK_SIZE, + }, + { + .name = "Kernel", + .offset = MTDPART_OFS_APPEND, /* Offset = 0x280000 */ + .size = 32 * NAND_BLOCK_SIZE, + }, + { + .name = "File System", + .offset = MTDPART_OFS_APPEND, /* Offset = 0x680000 */ + .size = MTDPART_SIZ_FULL, + }, +}; + +static struct omap_nand_platform_data omap3beagle_nand_data = { + .options = NAND_BUSWIDTH_16, + .parts = omap3beagle_nand_partitions, + .nr_parts = ARRAY_SIZE(omap3beagle_nand_partitions), + .dma_channel = -1, /* disable DMA in OMAP NAND driver */ + .nand_setup = NULL, + .dev_ready = NULL, +}; + +static struct resource omap3beagle_nand_resource = { + .flags = IORESOURCE_MEM, +}; + +static struct platform_device omap3beagle_nand_device = { + .name = "omap2-nand", + .id = -1, + .dev = { + .platform_data = &omap3beagle_nand_data, + }, + .num_resources = 1, + .resource = &omap3beagle_nand_resource, +}; + +#include "sdram-micron-mt46h32m32lf-6.h" + +static struct omap_uart_config omap3_beagle_uart_config __initdata = { + .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)), +}; + +static struct twl4030_usb_data beagle_usb_data = { + .usb_mode = T2_USB_MODE_ULPI, +}; + +static struct twl4030_hsmmc_info mmc[] = { + { + .mmc = 1, + .wires = 8, + .gpio_wp = 29, + }, + {} /* Terminator */ +}; + +static struct regulator_consumer_supply beagle_vmmc1_supply = { + .supply = "vmmc", +}; + +static struct regulator_consumer_supply beagle_vsim_supply = { + .supply = "vmmc_aux", +}; + +static struct gpio_led gpio_leds[]; + +static int beagle_twl_gpio_setup(struct device *dev, + unsigned gpio, unsigned ngpio) +{ + /* gpio + 0 is "mmc0_cd" (input/IRQ) */ + omap_cfg_reg(AH8_34XX_GPIO29); + mmc[0].gpio_cd = gpio + 0; + twl4030_mmc_init(mmc); + + /* link regulators to MMC adapters */ + beagle_vmmc1_supply.dev = mmc[0].dev; + beagle_vsim_supply.dev = mmc[0].dev; + + /* REVISIT: need ehci-omap hooks for external VBUS + * power switch and overcurrent detect + */ + +#if 0 /* TODO: This needs to be modified to not rely on u-boot */ + gpio_request(gpio + 1, "EHCI_nOC"); + gpio_direction_input(gpio + 1); + + /* TWL4030_GPIO_MAX + 0 == ledA, EHCI nEN_USB_PWR (out, active low) */ + gpio_request(gpio + TWL4030_GPIO_MAX, "nEN_USB_PWR"); + gpio_direction_output(gpio + TWL4030_GPIO_MAX, 1); + + /* TWL4030_GPIO_MAX + 1 == ledB, PMU_STAT (out, active low LED) */ + gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1; +#endif + return 0; +} + +static struct twl4030_gpio_platform_data beagle_gpio_data = { + .gpio_base = OMAP_MAX_GPIO_LINES, + .irq_base = TWL4030_GPIO_IRQ_BASE, + .irq_end = TWL4030_GPIO_IRQ_END, + .use_leds = true, + .pullups = BIT(1), + .pulldowns = BIT(2) | BIT(6) | BIT(7) | BIT(8) | BIT(13) + | BIT(15) | BIT(16) | BIT(17), + .setup = beagle_twl_gpio_setup, +}; + +static struct platform_device omap3_beagle_lcd_device = { + .name = "omap3beagle_lcd", + .id = -1, +}; + +static struct regulator_consumer_supply beagle_vdac_supply = { + .supply = "vdac", + .dev = &omap3_beagle_lcd_device.dev, +}; + +static struct regulator_consumer_supply beagle_vdvi_supply = { + .supply = "vdvi", + .dev = &omap3_beagle_lcd_device.dev, +}; + +/* VMMC1 for MMC1 pins CMD, CLK, DAT0..DAT3 (20 mA, plus card == max 220 mA) */ +static struct regulator_init_data beagle_vmmc1 = { + .constraints = { + .min_uV = 1850000, + .max_uV = 3150000, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE + | REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &beagle_vmmc1_supply, +}; + +/* VSIM for MMC1 pins DAT4..DAT7 (2 mA, plus card == max 50 mA) */ +static struct regulator_init_data beagle_vsim = { + .constraints = { + .min_uV = 1800000, + .max_uV = 3000000, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE + | REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &beagle_vsim_supply, +}; + +/* VDAC for DSS driving S-Video (8 mA unloaded, max 65 mA) */ +static struct regulator_init_data beagle_vdac = { + .constraints = { + .min_uV = 1800000, + .max_uV = 1800000, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &beagle_vdac_supply, +}; + +/* VPLL2 for digital video outputs */ +static struct regulator_init_data beagle_vpll2 = { + .constraints = { + .name = "VDVI", + .min_uV = 1800000, + .max_uV = 1800000, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &beagle_vdvi_supply, +}; + +static const struct twl4030_resconfig beagle_resconfig[] = { + /* disable regulators that u-boot left enabled; the + * devices' drivers should be managing these. + */ + { .resource = RES_VAUX3, }, /* not even connected! */ + { .resource = RES_VMMC1, }, + { .resource = RES_VSIM, }, + { .resource = RES_VPLL2, }, + { .resource = RES_VDAC, }, + { .resource = RES_VUSB_1V5, }, + { .resource = RES_VUSB_1V8, }, + { .resource = RES_VUSB_3V1, }, + { 0, }, +}; + +static struct twl4030_power_data beagle_power_data = { + .resource_config = beagle_resconfig, + /* REVISIT can't use GENERIC3430_T2SCRIPTS_DATA; + * among other things, it makes reboot fail. + */ +}; + +static struct twl4030_bci_platform_data touchbook_bci_data = { + .tblsize = 0, + .no_backup_battery = 1, +}; + +static struct twl4030_platform_data beagle_twldata = { + .irq_base = TWL4030_IRQ_BASE, + .irq_end = TWL4030_IRQ_END, + + /* platform_data for children goes here */ + .usb = &beagle_usb_data, + .gpio = &beagle_gpio_data, + .power = &beagle_power_data, + .vmmc1 = &beagle_vmmc1, + .vsim = &beagle_vsim, + .vdac = &beagle_vdac, + .vpll2 = &beagle_vpll2, + + /* TouchBook BCI */ + .bci = &touchbook_bci_data, +}; + +static struct i2c_board_info __initdata beagle_i2c_boardinfo[] = { + { + I2C_BOARD_INFO("twl4030", 0x48), + .flags = I2C_CLIENT_WAKE, + .irq = INT_34XX_SYS_NIRQ, + .platform_data = &beagle_twldata, + }, +}; + +static struct i2c_board_info __initdata touchBook_i2c_boardinfo[] = { + { + I2C_BOARD_INFO("bq27200", 0x55), + }, +}; + +static int __init omap3_beagle_i2c_init(void) +{ + /* Standard BeagleBoard bus */ + omap_register_i2c_bus(1, 2600, beagle_i2c_boardinfo, + ARRAY_SIZE(beagle_i2c_boardinfo)); + + /* TouchBook keyboard bus */ + omap_register_i2c_bus(3, 100, touchBook_i2c_boardinfo, + ARRAY_SIZE(touchBook_i2c_boardinfo)); + + return 0; +} + +static void __init omap3_ads7846_init(void) +{ + if (gpio_request(OMAP3_TS_GPIO, "ads7846_pen_down")) { + printk(KERN_ERR "Failed to request GPIO %d for " + "ads7846 pen down IRQ\n", OMAP3_TS_GPIO); + return; + } + + gpio_direction_input(OMAP3_TS_GPIO); + omap_set_gpio_debounce(OMAP3_TS_GPIO, 1); + omap_set_gpio_debounce_time(OMAP3_TS_GPIO, 0xa); +} + +static struct ads7846_platform_data ads7846_config = { + .x_min = 100, + .y_min = 265, + .x_max = 3950, + .y_max = 3750, + .x_plate_ohms = 40, + .pressure_max = 255, + .debounce_max = 10, + .debounce_tol = 5, + .debounce_rep = 1, + .gpio_pendown = OMAP3_TS_GPIO, + .keep_vref_on = 1, +}; + +static struct omap2_mcspi_device_config ads7846_mcspi_config = { + .turbo_mode = 0, + .single_channel = 1, /* 0: slave, 1: master */ +}; + +static struct spi_board_info omap3_ads7846_spi_board_info[] __initdata = { + { + .modalias = "ads7846", + .bus_num = 4, + .chip_select = 0, + .max_speed_hz = 1500000, + .controller_data = &ads7846_mcspi_config, //(void *) 161, + .irq = OMAP_GPIO_IRQ(OMAP3_TS_GPIO), + .platform_data = &ads7846_config, + } +}; + +static void __init omap3_beagle_init_irq(void) +{ + omap2_init_common_hw(mt46h32m32lf6_sdrc_params, omap3_mpu_rate_table, + omap3_dsp_rate_table, omap3_l3_rate_table); + omap_init_irq(); + omap_gpio_init(); +} + +static struct gpio_led gpio_leds[] = { + { + .name = "beagleboard::usr0", + .default_trigger = "heartbeat", + .gpio = 150, + }, + { + .name = "beagleboard::usr1", + .default_trigger = "mmc0", + .gpio = 149, + }, + { + .name = "beagleboard::pmu_stat", + .gpio = -EINVAL, /* gets replaced */ + .active_low = true, + }, +}; + +static struct gpio_led_platform_data gpio_led_info = { + .leds = gpio_leds, + .num_leds = ARRAY_SIZE(gpio_leds), +}; + +static struct platform_device leds_gpio = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &gpio_led_info, + }, +}; + +static struct gpio_keys_button gpio_buttons[] = { + { + .code = BTN_EXTRA, + .gpio = 7, + .desc = "user", + .wakeup = 1, + }, + { + .code = KEY_POWER, + .gpio = 183, + .desc = "power", + .wakeup = 1, + }, +}; + +static struct gpio_keys_platform_data gpio_key_info = { + .buttons = gpio_buttons, + .nbuttons = ARRAY_SIZE(gpio_buttons), +}; + +static struct platform_device keys_gpio = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &gpio_key_info, + }, +}; + +/* DSS */ + +static int beagle_enable_dvi(struct omap_display *display) +{ + if (display->hw_config.panel_reset_gpio != -1) + gpio_set_value(display->hw_config.panel_reset_gpio, 1); + + return 0; +} + +static void beagle_disable_dvi(struct omap_display *display) +{ + if (display->hw_config.panel_reset_gpio != -1) + gpio_set_value(display->hw_config.panel_reset_gpio, 0); +} + +static struct omap_dss_display_config beagle_display_data_dvi = { + .type = OMAP_DISPLAY_TYPE_DPI, + .name = "dvi", + .panel_name = "panel-generic", + .u.dpi.data_lines = 24, + .panel_reset_gpio = 176, + .panel_enable = beagle_enable_dvi, + .panel_disable = beagle_disable_dvi, +}; + + +static int beagle_panel_enable_tv(struct omap_display *display) +{ +#define ENABLE_VDAC_DEDICATED 0x03 +#define ENABLE_VDAC_DEV_GRP 0x20 + + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + ENABLE_VDAC_DEDICATED, + TWL4030_VDAC_DEDICATED); + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + ENABLE_VDAC_DEV_GRP, TWL4030_VDAC_DEV_GRP); + + return 0; +} + +static void beagle_panel_disable_tv(struct omap_display *display) +{ + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x00, + TWL4030_VDAC_DEDICATED); + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x00, + TWL4030_VDAC_DEV_GRP); +} + +static struct omap_dss_display_config beagle_display_data_tv = { + .type = OMAP_DISPLAY_TYPE_VENC, + .name = "tv", + .u.venc.type = OMAP_DSS_VENC_TYPE_SVIDEO, + .panel_enable = beagle_panel_enable_tv, + .panel_disable = beagle_panel_disable_tv, +}; + +static struct omap_dss_board_info beagle_dss_data = { + .num_displays = 2, + .displays = { + &beagle_display_data_dvi, + &beagle_display_data_tv, + } +}; + +static struct platform_device beagle_dss_device = { + .name = "omapdss", + .id = -1, + .dev = { + .platform_data = &beagle_dss_data, + }, +}; + +static void __init beagle_display_init(void) +{ + int r; + + r = gpio_request(beagle_display_data_dvi.panel_reset_gpio, "DVI reset"); + if (r < 0) { + printk(KERN_ERR "Unable to get DVI reset GPIO\n"); + return; + } + + gpio_direction_output(beagle_display_data_dvi.panel_reset_gpio, 0); +} + +static struct omap_board_config_kernel omap3_beagle_config[] __initdata = { + { OMAP_TAG_UART, &omap3_beagle_uart_config }, +}; + +static struct platform_device *omap3_beagle_devices[] __initdata = { + &beagle_dss_device, + &leds_gpio, + &keys_gpio, +}; + +static void __init omap3beagle_flash_init(void) +{ + u8 cs = 0; + u8 nandcs = GPMC_CS_NUM + 1; + + u32 gpmc_base_add = OMAP34XX_GPMC_VIRT; + + /* find out the chip-select on which NAND exists */ + while (cs < GPMC_CS_NUM) { + u32 ret = 0; + ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); + + if ((ret & 0xC00) == 0x800) { + printk(KERN_INFO "Found NAND on CS%d\n", cs); + if (nandcs > GPMC_CS_NUM) + nandcs = cs; + } + cs++; + } + + if (nandcs > GPMC_CS_NUM) { + printk(KERN_INFO "NAND: Unable to find configuration " + "in GPMC\n "); + return; + } + + if (nandcs < GPMC_CS_NUM) { + omap3beagle_nand_data.cs = nandcs; + omap3beagle_nand_data.gpmc_cs_baseaddr = (void *) + (gpmc_base_add + GPMC_CS0_BASE + nandcs * GPMC_CS_SIZE); + omap3beagle_nand_data.gpmc_baseaddr = (void *) (gpmc_base_add); + + printk(KERN_INFO "Registering NAND on CS%d\n", nandcs); + if (platform_device_register(&omap3beagle_nand_device) < 0) + printk(KERN_ERR "Unable to register NAND device\n"); + } +} + +static void __init omap3_mma7455l_init(void) +{ + int ret; + + ret = gpio_request(OMAP3_AC_GPIO, "mma7455l"); + if (ret < 0) { + printk(KERN_ERR "Failed to request GPIO %d for mma7455l IRQ\n", OMAP3_AC_GPIO); + return; + } + + gpio_direction_input(OMAP3_AC_GPIO); +} + +static struct mma7455l_platform_data mma7455l_config = { + .calibration_x = -4, //26 for Beagleboard + .calibration_y = 28, //44 for Beagleboard + .calibration_z = -28, //26 for Beagleboard +}; + +static struct omap2_mcspi_device_config mma7455l_mcspi_config = { + .turbo_mode = 0, + .single_channel = 1, /* 0: slave, 1: master */ +}; + +static struct spi_board_info omap3_mma7455l_spi_board_info[] __initdata = { + { + .modalias = "mma7455l", + .bus_num = 3, + .chip_select = 0, + .max_speed_hz = 200000, + .irq = OMAP_GPIO_IRQ(OMAP3_AC_GPIO), + .controller_data = &mma7455l_mcspi_config, //(void *) 135, + .platform_data = &mma7455l_config, + } +}; + +static int touchbook_backlight_brightness = 50; +static struct omap_dm_timer *touchbook_backlight_pwm; + +static int touchbook_backlight_read(struct backlight_device *bd) +{ + return touchbook_backlight_brightness; +} + +static int touchbook_backlight_update(struct backlight_device *bd) +{ + int value = bd->props.brightness; + touchbook_backlight_brightness = value; + + /* Frequency calculation: + - For 200Hz PWM, you want to load -164 (=> -32768Hz / 200Hz). + - Minimum duty cycle for the backlight is 15%. + - You have (164*0.85) => ~140 levels of brightness. + */ + + /* Convert from 0-100 range to 0-140 range */ + value = (value * 14) / 10 / 2; + + /* For maximum brightness, just stop the timer... */ + if(value != bd->props.max_brightness) + { + omap_dm_timer_set_load(touchbook_backlight_pwm, 1, -164); + omap_dm_timer_set_match(touchbook_backlight_pwm, 1, -24 - value); + omap_dm_timer_write_counter(touchbook_backlight_pwm, -1); + //omap_dm_timer_stop(touchbook_backlight_pwm); + omap_dm_timer_start(touchbook_backlight_pwm); + } + else + omap_dm_timer_stop(touchbook_backlight_pwm); + + + return 0; +} + +static struct backlight_ops touchbook_backlight_properties = { + .get_brightness = touchbook_backlight_read, + .update_status = touchbook_backlight_update, +}; + +static void __init omap3_touchbook_backlight_init(void) +{ + static struct backlight_device *bd; + bd = backlight_device_register("touchbook", NULL, NULL, &touchbook_backlight_properties); + + if(bd) + { + touchbook_backlight_pwm = omap_dm_timer_request_specific(TB_BL_PWM_TIMER); + omap_dm_timer_enable(touchbook_backlight_pwm); + omap_dm_timer_set_source(touchbook_backlight_pwm, OMAP_TIMER_SRC_32_KHZ); + omap_dm_timer_set_pwm(touchbook_backlight_pwm, 1, 1, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE); + + bd->props.max_brightness = 100; + bd->props.brightness = touchbook_backlight_brightness; + } + + touchbook_backlight_update(bd); +} + +static void omap3_touchbook_poweroff(void) +{ + int r; + + r = gpio_request(TB_KILL_POWER_GPIO, "DVI reset"); + if (r < 0) { + printk(KERN_ERR "Unable to get kill power GPIO\n"); + return; + } + + gpio_direction_output(TB_KILL_POWER_GPIO, 0); +} + +static void __init omap3_beagle_init(void) +{ + pm_power_off = omap3_touchbook_poweroff; + + omap3_beagle_i2c_init(); + platform_add_devices(omap3_beagle_devices, + ARRAY_SIZE(omap3_beagle_devices)); + omap_board_config = omap3_beagle_config; + omap_board_config_size = ARRAY_SIZE(omap3_beagle_config); + omap_serial_init(); + + omap_cfg_reg(J25_34XX_GPIO170); + + omap3beagle_flash_init(); + beagle_display_init(); + omap3_touchbook_backlight_init(); + + /* Touch Book */ + spi_register_board_info(omap3_ads7846_spi_board_info, ARRAY_SIZE(omap3_ads7846_spi_board_info)); + spi_register_board_info(omap3_mma7455l_spi_board_info, ARRAY_SIZE(omap3_mma7455l_spi_board_info)); + + omap3_ads7846_init(); + omap3_mma7455l_init(); + + usb_musb_init(); + usb_ehci_init(); +} + +static void __init omap3_beagle_map_io(void) +{ + omap2_set_globals_343x(); + omap2_map_common_io(); +} + +MACHINE_START(OMAP3_BEAGLE, "OMAP3 Touch Book") + /* Maintainer: Gregoire Gentil - http://www.alwaysinnovating.com */ + .phys_io = 0x48000000, + .io_pg_offst = ((0xd8000000) >> 18) & 0xfffc, + .boot_params = 0x80000100, + .map_io = omap3_beagle_map_io, + .init_irq = omap3_beagle_init_irq, + .init_machine = omap3_beagle_init, + .timer = &omap_timer, +MACHINE_END diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/boot-no-power-message.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/boot-no-power-message.patch new file mode 100644 index 0000000000..4be59a4212 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/boot-no-power-message.patch @@ -0,0 +1,11 @@ +--- a/arch/arm/mach-omap2/pm34xx.c 2009-07-18 10:13:43.000000000 -0700 ++++ b/arch/arm/mach-omap2/pm34xx.c 2009-07-18 10:13:54.000000000 -0700 +@@ -625,7 +625,7 @@ + struct power_state *pwrst, *tmp; + int ret; + +- printk(KERN_ERR "Power Management for TI OMAP3.\n"); ++// printk(KERN_ERR "Power Management for TI OMAP3.\n"); + + /* XXX prcm_setup_regs needs to be before enabling hw + * supervised mode for powerdomains */ diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/defconfig b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/defconfig new file mode 100644 index 0000000000..2ff46a4832 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/defconfig @@ -0,0 +1,2720 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.29-omap1 +# Fri May 29 14:47:52 2009 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_MMU=y +# CONFIG_NO_IOPORT is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_OPROFILE_ARMV7=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +# CONFIG_AUDIT is not set + +# +# RCU Subsystem +# +CONFIG_CLASSIC_RCU=y +# CONFIG_TREE_RCU is not set +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_PREEMPT_RCU_TRACE is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_GROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_RT_GROUP_SCHED is not set +CONFIG_USER_SCHED=y +# CONFIG_CGROUP_SCHED is not set +# CONFIG_CGROUPS is not set +# CONFIG_SYSFS_DEPRECATED_V2 is not set +# CONFIG_RELAY is not set +# CONFIG_NAMESPACES is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_VM_EVENT_COUNTERS=y +# CONFIG_COMPAT_BRK is not set +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +CONFIG_PROFILING=y +CONFIG_TRACEPOINTS=y +# CONFIG_MARKERS is not set +CONFIG_OPROFILE=y +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_BLOCK=y +CONFIG_LBD=y +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_FREEZER=y + +# +# System Type +# +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_DAVINCI is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_W90X900 is not set + +# +# TI OMAP Implementations +# +CONFIG_ARCH_OMAP_OTG=y +# CONFIG_ARCH_OMAP1 is not set +# CONFIG_ARCH_OMAP2 is not set +CONFIG_ARCH_OMAP3=y + +# +# OMAP Feature Selections +# +# CONFIG_OMAP_DEBUG_POWERDOMAIN is not set +# CONFIG_OMAP_DEBUG_CLOCKDOMAIN is not set +CONFIG_OMAP_SMARTREFLEX=y +# CONFIG_OMAP_SMARTREFLEX_TESTING is not set +CONFIG_OMAP_RESET_CLOCKS=y +CONFIG_OMAP_BOOT_TAG=y +CONFIG_OMAP_BOOT_REASON=y +# CONFIG_OMAP_COMPONENT_VERSION is not set +# CONFIG_OMAP_GPIO_SWITCH is not set +# CONFIG_OMAP_MUX is not set +CONFIG_OMAP_MCBSP=y +# CONFIG_OMAP_MBOX_FWK is not set +# CONFIG_OMAP_IOMMU is not set +# CONFIG_OMAP_MPU_TIMER is not set +CONFIG_OMAP_32K_TIMER=y +CONFIG_OMAP_32K_TIMER_HZ=128 +CONFIG_OMAP_TICK_GPTIMER=12 +CONFIG_OMAP_DM_TIMER=y +# CONFIG_OMAP_LL_DEBUG_UART1 is not set +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +CONFIG_OMAP_LL_DEBUG_UART3=y +CONFIG_ARCH_OMAP34XX=y +CONFIG_ARCH_OMAP3430=y + +# +# OMAP Board Type +# +# CONFIG_MACH_NOKIA_RX51 is not set +# CONFIG_MACH_OMAP_LDP is not set +# CONFIG_MACH_OMAP_3430SDP is not set +# CONFIG_MACH_OMAP3EVM is not set +CONFIG_MACH_OMAP3_BEAGLE=y +# CONFIG_MACH_OVERO is not set +# CONFIG_MACH_OMAP3_PANDORA is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_IFAR=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_HAS_TLS_REG=y +# CONFIG_OUTER_CACHE is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +# CONFIG_VMSPLIT_3G is not set +CONFIG_VMSPLIT_2G=y +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0x80000000 +# CONFIG_PREEMPT is not set +CONFIG_HZ=128 +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_ARCH_FLATMEM_HAS_HOLES=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_UNEVICTABLE_LRU=y +CONFIG_LEDS=y +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE=" debug " +# CONFIG_XIP_KERNEL is not set +CONFIG_KEXEC=y +CONFIG_ATAGS_PROC=y + +# +# CPU Power Management +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y +# CONFIG_ARM_ERRATUM_451034 is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +CONFIG_BINFMT_AOUT=m +CONFIG_BINFMT_MISC=y + +# +# Power management options +# +CONFIG_PM=y +CONFIG_PM_DEBUG=y +# CONFIG_PM_VERBOSE is not set +CONFIG_CAN_PM_TRACE=y +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +# CONFIG_PM_TEST_SUSPEND is not set +CONFIG_SUSPEND_FREEZER=y +# CONFIG_APM_EMULATION is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_COMPAT_NET_DEV_OPS=y +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=m +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE=m +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_IPCOMP=m +CONFIG_INET_XFRM_TUNNEL=m +CONFIG_INET_TUNNEL=m +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +CONFIG_INET_LRO=m +CONFIG_INET_DIAG=m +CONFIG_INET_TCP_DIAG=m +CONFIG_TCP_CONG_ADVANCED=y +CONFIG_TCP_CONG_BIC=m +CONFIG_TCP_CONG_CUBIC=y +CONFIG_TCP_CONG_WESTWOOD=m +CONFIG_TCP_CONG_HTCP=m +CONFIG_TCP_CONG_HSTCP=m +CONFIG_TCP_CONG_HYBLA=m +CONFIG_TCP_CONG_VEGAS=m +CONFIG_TCP_CONG_SCALABLE=m +CONFIG_TCP_CONG_LP=m +CONFIG_TCP_CONG_VENO=m +CONFIG_TCP_CONG_YEAH=m +CONFIG_TCP_CONG_ILLINOIS=m +# CONFIG_DEFAULT_BIC is not set +CONFIG_DEFAULT_CUBIC=y +# CONFIG_DEFAULT_HTCP is not set +# CONFIG_DEFAULT_VEGAS is not set +# CONFIG_DEFAULT_WESTWOOD is not set +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=m +# CONFIG_IPV6_PRIVACY is not set +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_IPCOMP=m +CONFIG_IPV6_MIP6=m +CONFIG_INET6_XFRM_TUNNEL=m +CONFIG_INET6_TUNNEL=m +CONFIG_INET6_XFRM_MODE_TRANSPORT=m +CONFIG_INET6_XFRM_MODE_TUNNEL=m +CONFIG_INET6_XFRM_MODE_BEET=m +CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=m +CONFIG_IPV6_SIT=m +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=m +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_IPV6_MROUTE=y +# CONFIG_IPV6_PIMSM_V2 is not set +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=m +CONFIG_NETFILTER_NETLINK_QUEUE=m +CONFIG_NETFILTER_NETLINK_LOG=m +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CT_ACCT=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=m +CONFIG_NF_CT_PROTO_GRE=m +CONFIG_NF_CT_PROTO_SCTP=m +CONFIG_NF_CT_PROTO_UDPLITE=m +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_H323=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NF_CONNTRACK_NETBIOS_NS=m +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_SANE=m +CONFIG_NF_CONNTRACK_SIP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NF_CT_NETLINK=m +# CONFIG_NETFILTER_TPROXY is not set +CONFIG_NETFILTER_XTABLES=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +CONFIG_NETFILTER_XT_TARGET_RATEEST=m +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_DCCP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_RECENT=m +# CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT is not set +CONFIG_NETFILTER_XT_MATCH_SCTP=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=m +CONFIG_NETFILTER_XT_MATCH_U32=m +CONFIG_IP_VS=m +CONFIG_IP_VS_IPV6=y +CONFIG_IP_VS_DEBUG=y +CONFIG_IP_VS_TAB_BITS=12 + +# +# IPVS transport protocol load balancing support +# +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_AH_ESP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y + +# +# IPVS scheduler +# +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +CONFIG_IP_VS_SED=m +CONFIG_IP_VS_NQ=m + +# +# IPVS application helper +# +CONFIG_IP_VS_FTP=m + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=m +CONFIG_NF_CONNTRACK_IPV4=m +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +CONFIG_IP_NF_QUEUE=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_ADDRTYPE=m +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_LOG=m +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_NF_NAT=m +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_TARGET_IDLETIMER=m +CONFIG_NF_NAT_SNMP_BASIC=m +CONFIG_NF_NAT_PROTO_DCCP=m +CONFIG_NF_NAT_PROTO_GRE=m +CONFIG_NF_NAT_PROTO_UDPLITE=m +CONFIG_NF_NAT_PROTO_SCTP=m +CONFIG_NF_NAT_FTP=m +CONFIG_NF_NAT_IRC=m +CONFIG_NF_NAT_TFTP=m +CONFIG_NF_NAT_AMANDA=m +CONFIG_NF_NAT_PPTP=m +CONFIG_NF_NAT_H323=m +CONFIG_NF_NAT_SIP=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_CLUSTERIP=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_CONNTRACK_IPV6=m +CONFIG_IP6_NF_QUEUE=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_AH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_MH=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_TARGET_LOG=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_TARGET_HL=m +CONFIG_IP6_NF_RAW=m +# CONFIG_BRIDGE_NF_EBTABLES is not set +CONFIG_IP_DCCP=m +CONFIG_INET_DCCP_DIAG=m + +# +# DCCP CCIDs Configuration (EXPERIMENTAL) +# +# CONFIG_IP_DCCP_CCID2_DEBUG is not set +CONFIG_IP_DCCP_CCID3=y +# CONFIG_IP_DCCP_CCID3_DEBUG is not set +CONFIG_IP_DCCP_CCID3_RTO=100 +CONFIG_IP_DCCP_TFRC_LIB=y + +# +# DCCP Kernel Hacking +# +# CONFIG_IP_DCCP_DEBUG is not set +CONFIG_IP_SCTP=m +# CONFIG_SCTP_DBG_MSG is not set +# CONFIG_SCTP_DBG_OBJCNT is not set +# CONFIG_SCTP_HMAC_NONE is not set +# CONFIG_SCTP_HMAC_SHA1 is not set +CONFIG_SCTP_HMAC_MD5=y +CONFIG_TIPC=m +# CONFIG_TIPC_ADVANCED is not set +# CONFIG_TIPC_DEBUG is not set +CONFIG_ATM=m +CONFIG_ATM_CLIP=m +# CONFIG_ATM_CLIP_NO_ICMP is not set +CONFIG_ATM_LANE=m +CONFIG_ATM_MPOA=m +CONFIG_ATM_BR2684=m +# CONFIG_ATM_BR2684_IPFILTER is not set +CONFIG_STP=m +CONFIG_GARP=m +CONFIG_BRIDGE=m +# CONFIG_NET_DSA is not set +CONFIG_VLAN_8021Q=m +CONFIG_VLAN_8021Q_GVRP=y +# CONFIG_DECNET is not set +CONFIG_LLC=m +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +CONFIG_WAN_ROUTER=m +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_ATM=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_MULTIQ=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_DRR=m + +# +# Classification +# +CONFIG_NET_CLS=y +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_PERF=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +CONFIG_NET_CLS_FLOW=m +# CONFIG_NET_EMATCH is not set +# CONFIG_NET_CLS_ACT is not set +CONFIG_NET_CLS_IND=y +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +CONFIG_CAN=m +CONFIG_CAN_RAW=m +CONFIG_CAN_BCM=m + +# +# CAN Device Drivers +# +CONFIG_CAN_VCAN=m +# CONFIG_CAN_DEBUG_DEVICES is not set +CONFIG_IRDA=m + +# +# IrDA protocols +# +CONFIG_IRLAN=m +CONFIG_IRNET=m +CONFIG_IRCOMM=m +CONFIG_IRDA_ULTRA=y + +# +# IrDA options +# +CONFIG_IRDA_CACHE_LAST_LSAP=y +CONFIG_IRDA_FAST_RR=y +CONFIG_IRDA_DEBUG=y + +# +# Infrared-port device drivers +# + +# +# SIR device drivers +# +CONFIG_IRTTY_SIR=m + +# +# Dongle support +# +CONFIG_DONGLE=y +CONFIG_ESI_DONGLE=m +CONFIG_ACTISYS_DONGLE=m +CONFIG_TEKRAM_DONGLE=m +CONFIG_TOIM3232_DONGLE=m +CONFIG_LITELINK_DONGLE=m +CONFIG_MA600_DONGLE=m +CONFIG_GIRBIL_DONGLE=m +CONFIG_MCP2120_DONGLE=m +CONFIG_OLD_BELKIN_DONGLE=m +# CONFIG_ACT200L_DONGLE is not set +CONFIG_KINGSUN_DONGLE=m +CONFIG_KSDAZZLE_DONGLE=m +CONFIG_KS959_DONGLE=m + +# +# FIR device drivers +# +CONFIG_USB_IRDA=m +CONFIG_SIGMATEL_FIR=m +CONFIG_MCS_FIR=m +# CONFIG_OMAP_IR is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +CONFIG_BT_HCIBTUSB=y +CONFIG_BT_HCIBTSDIO=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_LL=y +CONFIG_BT_HCIBCM203X=y +CONFIG_BT_HCIBPA10X=y +CONFIG_BT_HCIBFUSB=y +# CONFIG_BT_HCIBRF6150 is not set +# CONFIG_BT_HCIH4P is not set +# CONFIG_BT_HCIVHCI is not set +CONFIG_AF_RXRPC=m +# CONFIG_AF_RXRPC_DEBUG is not set +# CONFIG_RXKAD is not set +# CONFIG_PHONET is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_CFG80211=y +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_NL80211=y +CONFIG_WIRELESS_OLD_REGULATORY=y +CONFIG_WIRELESS_EXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +CONFIG_LIB80211_CRYPT_WEP=y +CONFIG_LIB80211_CRYPT_CCMP=y +CONFIG_LIB80211_CRYPT_TKIP=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_MAC80211=y + +# +# Rate control algorithm selection +# +CONFIG_MAC80211_RC_PID=y +# CONFIG_MAC80211_RC_MINSTREL is not set +CONFIG_MAC80211_RC_DEFAULT_PID=y +# CONFIG_MAC80211_RC_DEFAULT_MINSTREL is not set +CONFIG_MAC80211_RC_DEFAULT="pid" +# CONFIG_MAC80211_MESH is not set +CONFIG_MAC80211_LEDS=y +# CONFIG_MAC80211_DEBUGFS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +CONFIG_WIMAX=m +CONFIG_WIMAX_DEBUG_LEVEL=8 +# CONFIG_RFKILL is not set +CONFIG_NET_9P=m +# CONFIG_NET_9P_DEBUG is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_OMAP2=y +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_NANDSIM is not set +CONFIG_MTD_NAND_PLATFORM=y +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set + +# +# UBI - Unsorted block images +# +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_RESERVE=1 +# CONFIG_MTD_UBI_GLUEBI is not set + +# +# UBI debugging options +# +# CONFIG_MTD_UBI_DEBUG is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=m +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=16384 +# CONFIG_BLK_DEV_XIP is not set +CONFIG_CDROM_PKTCDVD=m +CONFIG_CDROM_PKTCDVD_BUFFERS=8 +# CONFIG_CDROM_PKTCDVD_WCACHE is not set +# CONFIG_ATA_OVER_ETH is not set +CONFIG_MISC_DEVICES=y +# CONFIG_ICS932S401 is not set +# CONFIG_OMAP_STI is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +CONFIG_EEPROM_93CX6=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_RAID_ATTRS=m +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +CONFIG_SCSI_ISCSI_ATTRS=m +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +CONFIG_ISCSI_TCP=m +# CONFIG_LIBFC is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_ATA is not set +CONFIG_MD=y +CONFIG_BLK_DEV_MD=m +CONFIG_MD_LINEAR=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m +CONFIG_MD_RAID456=m +CONFIG_MD_RAID5_RESHAPE=y +CONFIG_MD_MULTIPATH=m +CONFIG_MD_FAULTY=m +CONFIG_BLK_DEV_DM=m +# CONFIG_DM_DEBUG is not set +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m +CONFIG_DM_MULTIPATH=m +CONFIG_DM_DELAY=m +# CONFIG_DM_UEVENT is not set +CONFIG_NETDEVICES=y +CONFIG_DUMMY=m +CONFIG_BONDING=m +CONFIG_MACVLAN=m +CONFIG_EQUALIZER=m +CONFIG_TUN=m +CONFIG_VETH=m +# CONFIG_NET_ETHERNET is not set +CONFIG_MII=y +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set + +# +# Wireless LAN +# +# CONFIG_WLAN_PRE80211 is not set +CONFIG_WLAN_80211=y +CONFIG_LIBERTAS=y +CONFIG_LIBERTAS_USB=y +# CONFIG_LIBERTAS_SDIO is not set +# CONFIG_LIBERTAS_DEBUG is not set +# CONFIG_LIBERTAS_THINFIRM is not set +CONFIG_USB_ZD1201=y +CONFIG_USB_NET_RNDIS_WLAN=y +CONFIG_RTL8187=y +# CONFIG_MAC80211_HWSIM is not set +CONFIG_P54_COMMON=y +CONFIG_P54_USB=y +# CONFIG_IWLWIFI_LEDS is not set +CONFIG_HOSTAP=y +CONFIG_HOSTAP_FIRMWARE=y +CONFIG_HOSTAP_FIRMWARE_NVRAM=y +CONFIG_B43=y +CONFIG_B43_LEDS=y +# CONFIG_B43_DEBUG is not set +# CONFIG_B43LEGACY is not set +CONFIG_ZD1211RW=y +# CONFIG_ZD1211RW_DEBUG is not set +CONFIG_RT2X00=y +CONFIG_RT2500USB=y +CONFIG_RT73USB=y +CONFIG_RT2X00_LIB_USB=y +CONFIG_RT2X00_LIB=y +CONFIG_RT2X00_LIB_FIRMWARE=y +CONFIG_RT2X00_LIB_CRYPTO=y +CONFIG_RT2X00_LIB_LEDS=y +# CONFIG_RT2X00_DEBUG is not set + +# +# WiMAX Wireless Broadband devices +# +# CONFIG_WIMAX_I2400M_USB is not set +# CONFIG_WIMAX_I2400M_SDIO is not set + +# +# USB Network Adapters +# +CONFIG_USB_CATC=y +CONFIG_USB_KAWETH=y +CONFIG_USB_PEGASUS=y +CONFIG_USB_RTL8150=y +CONFIG_USB_USBNET=y +CONFIG_USB_NET_AX8817X=y +CONFIG_USB_NET_CDCETHER=y +CONFIG_USB_NET_DM9601=y +CONFIG_USB_NET_SMSC95XX=y +CONFIG_USB_NET_GL620A=y +CONFIG_USB_NET_NET1080=y +CONFIG_USB_NET_PLUSB=y +CONFIG_USB_NET_MCS7830=y +CONFIG_USB_NET_RNDIS_HOST=y +CONFIG_USB_NET_CDC_SUBSET=y +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_KC2190=y +CONFIG_USB_NET_ZAURUS=y +# CONFIG_WAN is not set +CONFIG_ATM_DRIVERS=y +# CONFIG_ATM_DUMMY is not set +# CONFIG_ATM_TCP is not set +CONFIG_PPP=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_MPPE=m +CONFIG_PPPOE=m +# CONFIG_PPPOATM is not set +CONFIG_PPPOL2TP=m +# CONFIG_SLIP is not set +CONFIG_SLHC=m +CONFIG_NETCONSOLE=m +CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_NETPOLL=y +CONFIG_NETPOLL_TRAP=y +CONFIG_NET_POLL_CONTROLLER=y +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +CONFIG_INPUT_FF_MEMLESS=y +# CONFIG_INPUT_POLLDEV is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_TWL4030 is not set +# CONFIG_KEYBOARD_LM8323 is not set +CONFIG_KEYBOARD_GPIO=y +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +CONFIG_MOUSE_PS2_ALPS=y +CONFIG_MOUSE_PS2_LOGIPS2PP=y +CONFIG_MOUSE_PS2_SYNAPTICS=y +CONFIG_MOUSE_PS2_TRACKPOINT=y +# CONFIG_MOUSE_PS2_ELANTECH is not set +# CONFIG_MOUSE_PS2_TOUCHKIT is not set +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_BCM5974 is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_MOUSE_GPIO is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ADS7846=y +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_TSC2005 is not set +# CONFIG_TOUCHSCREEN_TSC210X is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_TWL4030_PWRBUTTON=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_MMA7455L=y + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=32 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DETECT_IRQ=y +CONFIG_SERIAL_8250_RSA=y + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_OMAP=y +# CONFIG_I2C_SIMTEC is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_STUB is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_DS1682 is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_PCF8575 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +CONFIG_TWL4030_MADC=y +CONFIG_TWL4030_POWEROFF=y +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_TSL2563 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +CONFIG_SPI_OMAP24XX=y + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TSC210X is not set +# CONFIG_SPI_TSC2301 is not set +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO expanders: +# + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +CONFIG_GPIO_TWL4030=y + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_BATTERY_DS2760 is not set +CONFIG_TWL4030_BCI_BATTERY=y +CONFIG_BATTERY_BQ27x00=y +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7473 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_SENSORS_TSC210X is not set +CONFIG_SENSORS_OMAP34XX=y +# CONFIG_HWMON_DEBUG_CHIP is not set +CONFIG_THERMAL=y +CONFIG_THERMAL_HWMON=y +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +CONFIG_OMAP_WATCHDOG=y + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +CONFIG_SSB=y +# CONFIG_SSB_SILENT is not set +# CONFIG_SSB_DEBUG is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_TPS65010 is not set +CONFIG_TWL4030_CORE=y +# CONFIG_TWL4030_POWER is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_PCF50633 is not set + +# +# Multimedia devices +# + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=m +CONFIG_VIDEO_V4L2_COMMON=m +CONFIG_VIDEO_ALLOW_V4L1=y +CONFIG_VIDEO_V4L1_COMPAT=y +CONFIG_DVB_CORE=m +CONFIG_VIDEO_MEDIA=m + +# +# Multimedia drivers +# +CONFIG_MEDIA_ATTACH=y +CONFIG_MEDIA_TUNER=m +# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set +CONFIG_MEDIA_TUNER_SIMPLE=m +CONFIG_MEDIA_TUNER_TDA8290=m +CONFIG_MEDIA_TUNER_TDA827X=m +CONFIG_MEDIA_TUNER_TDA18271=m +CONFIG_MEDIA_TUNER_TDA9887=m +CONFIG_MEDIA_TUNER_TEA5761=m +CONFIG_MEDIA_TUNER_TEA5767=m +CONFIG_MEDIA_TUNER_MT20XX=m +CONFIG_MEDIA_TUNER_MT2060=m +CONFIG_MEDIA_TUNER_MT2266=m +CONFIG_MEDIA_TUNER_QT1010=m +CONFIG_MEDIA_TUNER_XC2028=m +CONFIG_MEDIA_TUNER_XC5000=m +CONFIG_MEDIA_TUNER_MXL5005S=m +CONFIG_VIDEO_V4L2=m +CONFIG_VIDEO_V4L1=m +CONFIG_VIDEOBUF_GEN=m +CONFIG_VIDEOBUF_VMALLOC=m +CONFIG_VIDEOBUF_DVB=m +CONFIG_VIDEO_IR=m +CONFIG_VIDEO_TVEEPROM=m +CONFIG_VIDEO_TUNER=m +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +CONFIG_VIDEO_HELPER_CHIPS_AUTO=y +CONFIG_VIDEO_IR_I2C=m +CONFIG_VIDEO_MSP3400=m +CONFIG_VIDEO_CS53L32A=m +CONFIG_VIDEO_WM8775=m +CONFIG_VIDEO_SAA711X=m +CONFIG_VIDEO_TVP5150=m +CONFIG_VIDEO_CX25840=m +CONFIG_VIDEO_CX2341X=m +CONFIG_VIDEO_VIVI=m +# CONFIG_VIDEO_CPIA is not set +# CONFIG_VIDEO_CPIA2 is not set +# CONFIG_VIDEO_SAA5246A is not set +# CONFIG_VIDEO_SAA5249 is not set +# CONFIG_VIDEO_AU0828 is not set +# CONFIG_VIDEO_OMAP3 is not set +# CONFIG_SOC_CAMERA is not set +CONFIG_V4L_USB_DRIVERS=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +CONFIG_USB_GSPCA=m +CONFIG_USB_M5602=m +CONFIG_USB_STV06XX=m +CONFIG_USB_GSPCA_CONEX=m +CONFIG_USB_GSPCA_ETOMS=m +CONFIG_USB_GSPCA_FINEPIX=m +CONFIG_USB_GSPCA_MARS=m +CONFIG_USB_GSPCA_OV519=m +CONFIG_USB_GSPCA_OV534=m +CONFIG_USB_GSPCA_PAC207=m +CONFIG_USB_GSPCA_PAC7311=m +CONFIG_USB_GSPCA_SONIXB=m +CONFIG_USB_GSPCA_SONIXJ=m +CONFIG_USB_GSPCA_SPCA500=m +CONFIG_USB_GSPCA_SPCA501=m +CONFIG_USB_GSPCA_SPCA505=m +CONFIG_USB_GSPCA_SPCA506=m +CONFIG_USB_GSPCA_SPCA508=m +CONFIG_USB_GSPCA_SPCA561=m +CONFIG_USB_GSPCA_STK014=m +CONFIG_USB_GSPCA_SUNPLUS=m +CONFIG_USB_GSPCA_T613=m +CONFIG_USB_GSPCA_TV8532=m +CONFIG_USB_GSPCA_VC032X=m +CONFIG_USB_GSPCA_ZC3XX=m +CONFIG_VIDEO_PVRUSB2=m +CONFIG_VIDEO_PVRUSB2_SYSFS=y +CONFIG_VIDEO_PVRUSB2_DVB=y +# CONFIG_VIDEO_PVRUSB2_DEBUGIFC is not set +CONFIG_VIDEO_EM28XX=m +CONFIG_VIDEO_EM28XX_ALSA=m +CONFIG_VIDEO_EM28XX_DVB=m +CONFIG_VIDEO_USBVISION=m +CONFIG_VIDEO_USBVIDEO=m +CONFIG_USB_VICAM=m +CONFIG_USB_IBMCAM=m +CONFIG_USB_KONICAWC=m +CONFIG_USB_QUICKCAM_MESSENGER=m +CONFIG_USB_ET61X251=m +CONFIG_VIDEO_OVCAMCHIP=m +CONFIG_USB_W9968CF=m +CONFIG_USB_OV511=m +CONFIG_USB_SE401=m +CONFIG_USB_SN9C102=m +CONFIG_USB_STV680=m +CONFIG_USB_ZC0301=m +CONFIG_USB_PWC=m +# CONFIG_USB_PWC_DEBUG is not set +CONFIG_USB_ZR364XX=m +CONFIG_USB_STKWEBCAM=m +CONFIG_USB_S2255=m +CONFIG_RADIO_ADAPTERS=y +# CONFIG_USB_DSBR is not set +# CONFIG_USB_SI470X is not set +# CONFIG_USB_MR800 is not set +# CONFIG_RADIO_TEA5764 is not set +CONFIG_DVB_DYNAMIC_MINORS=y +CONFIG_DVB_CAPTURE_DRIVERS=y +# CONFIG_TTPCI_EEPROM is not set + +# +# Supported USB Adapters +# +CONFIG_DVB_USB=m +# CONFIG_DVB_USB_DEBUG is not set +CONFIG_DVB_USB_A800=m +CONFIG_DVB_USB_DIBUSB_MB=m +# CONFIG_DVB_USB_DIBUSB_MB_FAULTY is not set +CONFIG_DVB_USB_DIBUSB_MC=m +CONFIG_DVB_USB_DIB0700=m +CONFIG_DVB_USB_UMT_010=m +CONFIG_DVB_USB_CXUSB=m +CONFIG_DVB_USB_M920X=m +CONFIG_DVB_USB_GL861=m +CONFIG_DVB_USB_AU6610=m +CONFIG_DVB_USB_DIGITV=m +CONFIG_DVB_USB_VP7045=m +CONFIG_DVB_USB_VP702X=m +CONFIG_DVB_USB_GP8PSK=m +CONFIG_DVB_USB_NOVA_T_USB2=m +CONFIG_DVB_USB_TTUSB2=m +CONFIG_DVB_USB_DTT200U=m +CONFIG_DVB_USB_OPERA1=m +CONFIG_DVB_USB_AF9005=m +CONFIG_DVB_USB_AF9005_REMOTE=m +CONFIG_DVB_USB_DW2102=m +CONFIG_DVB_USB_CINERGY_T2=m +CONFIG_DVB_USB_ANYSEE=m +CONFIG_DVB_USB_DTV5100=m +CONFIG_DVB_USB_AF9015=m +CONFIG_DVB_SIANO_SMS1XXX=m +CONFIG_DVB_SIANO_SMS1XXX_SMS_IDS=y + +# +# Supported FlexCopII (B2C2) Adapters +# +CONFIG_DVB_B2C2_FLEXCOP=m +CONFIG_DVB_B2C2_FLEXCOP_USB=m +# CONFIG_DVB_B2C2_FLEXCOP_DEBUG is not set + +# +# Supported DVB Frontends +# + +# +# Customise DVB Frontends +# +# CONFIG_DVB_FE_CUSTOMISE is not set + +# +# Multistandard (satellite) frontends +# +CONFIG_DVB_STB0899=m +CONFIG_DVB_STB6100=m + +# +# DVB-S (satellite) frontends +# +CONFIG_DVB_CX24110=m +CONFIG_DVB_CX24123=m +CONFIG_DVB_MT312=m +CONFIG_DVB_S5H1420=m +CONFIG_DVB_STV0288=m +CONFIG_DVB_STB6000=m +CONFIG_DVB_STV0299=m +CONFIG_DVB_TDA8083=m +CONFIG_DVB_TDA10086=m +CONFIG_DVB_TDA8261=m +CONFIG_DVB_VES1X93=m +CONFIG_DVB_TUNER_ITD1000=m +CONFIG_DVB_TUNER_CX24113=m +CONFIG_DVB_TDA826X=m +CONFIG_DVB_TUA6100=m +CONFIG_DVB_CX24116=m +CONFIG_DVB_SI21XX=m + +# +# DVB-T (terrestrial) frontends +# +CONFIG_DVB_SP8870=m +CONFIG_DVB_SP887X=m +CONFIG_DVB_CX22700=m +CONFIG_DVB_CX22702=m +CONFIG_DVB_DRX397XD=m +CONFIG_DVB_L64781=m +CONFIG_DVB_TDA1004X=m +CONFIG_DVB_NXT6000=m +CONFIG_DVB_MT352=m +CONFIG_DVB_ZL10353=m +CONFIG_DVB_DIB3000MB=m +CONFIG_DVB_DIB3000MC=m +CONFIG_DVB_DIB7000M=m +CONFIG_DVB_DIB7000P=m +CONFIG_DVB_TDA10048=m + +# +# DVB-C (cable) frontends +# +CONFIG_DVB_VES1820=m +CONFIG_DVB_TDA10021=m +CONFIG_DVB_TDA10023=m +CONFIG_DVB_STV0297=m + +# +# ATSC (North American/Korean Terrestrial/Cable DTV) frontends +# +CONFIG_DVB_NXT200X=m +CONFIG_DVB_OR51211=m +CONFIG_DVB_OR51132=m +CONFIG_DVB_BCM3510=m +CONFIG_DVB_LGDT330X=m +CONFIG_DVB_LGDT3304=m +CONFIG_DVB_S5H1409=m +CONFIG_DVB_AU8522=m +CONFIG_DVB_S5H1411=m + +# +# ISDB-T (terrestrial) frontends +# +CONFIG_DVB_S921=m + +# +# Digital terrestrial only tuners/PLL +# +CONFIG_DVB_PLL=m +CONFIG_DVB_TUNER_DIB0070=m + +# +# SEC control devices for DVB-S +# +CONFIG_DVB_LNBP21=m +# CONFIG_DVB_ISL6405 is not set +CONFIG_DVB_ISL6421=m +CONFIG_DVB_LGS8GL5=m + +# +# Tools to develop new frontends +# +# CONFIG_DVB_DUMMY_FE is not set +CONFIG_DVB_AF9013=m +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_OMAP_BOOTLOADER_INIT is not set +CONFIG_OMAP2_DSS=y +CONFIG_OMAP2_DSS_VRAM_SIZE=14 +CONFIG_OMAP2_DSS_DEBUG_SUPPORT=y +# CONFIG_OMAP2_DSS_RFBI is not set +CONFIG_OMAP2_DSS_VENC=y +# CONFIG_OMAP2_DSS_SDI is not set +CONFIG_OMAP2_DSS_DSI=y +CONFIG_OMAP2_DSS_USE_DSI_PLL=y +# CONFIG_OMAP2_DSS_FAKE_VSYNC is not set +CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK=1 + +# +# OMAP2/3 Display Device Drivers +# +CONFIG_PANEL_GENERIC=y +CONFIG_PANEL_SAMSUNG_LTE430WQ_F0C=m +# CONFIG_PANEL_SHARP_LS037V7DW01 is not set +# CONFIG_PANEL_N800 is not set +# CONFIG_CTRL_BLIZZARD is not set +CONFIG_FB_OMAP2=y +CONFIG_FB_OMAP2_DEBUG_SUPPORT=y +# CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE is not set +CONFIG_FB_OMAP2_NUM_FBS=3 +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=y + +# +# Display device support +# +CONFIG_DISPLAY_SUPPORT=y + +# +# Display hardware drivers +# + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_SOUND=y +CONFIG_SOUND_OSS_CORE=y +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_HWDEP=y +CONFIG_SND_RAWMIDI=y +CONFIG_SND_SEQUENCER=m +# CONFIG_SND_SEQ_DUMMY is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_PCM_OSS_PLUGINS=y +CONFIG_SND_SEQUENCER_OSS=y +CONFIG_SND_HRTIMER=m +CONFIG_SND_SEQ_HRTIMER_DEFAULT=y +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_VIRMIDI is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +# CONFIG_SND_ARM is not set +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_USB_CAIAQ=m +CONFIG_SND_USB_CAIAQ_INPUT=y +CONFIG_SND_SOC=y +CONFIG_SND_OMAP_SOC=y +CONFIG_SND_OMAP_SOC_MCBSP=y +CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE=y +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_TWL4030=y +# CONFIG_SOUND_PRIME is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +CONFIG_HID_DEBUG=y +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# Special HID drivers +# +CONFIG_HID_COMPAT=y +CONFIG_HID_A4TECH=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_EZKEY=y +CONFIG_HID_GYRATION=y +CONFIG_HID_LOGITECH=y +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_NTRIG=y +CONFIG_HID_PANTHERLORD=y +# CONFIG_PANTHERLORD_FF is not set +CONFIG_HID_PETALYNX=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SUNPLUS=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_TOPSEED=y +# CONFIG_THRUSTMASTER_FF is not set +# CONFIG_ZEROPLUS_FF is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_SUSPEND=y +CONFIG_USB_OTG=y +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +CONFIG_USB_MON=y +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_EHCI_HCD=y +CONFIG_OMAP_EHCI_PHY_MODE=y +# CONFIG_OMAP_EHCI_TLL_MODE is not set +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_TT_NEWSCHED=y +CONFIG_USB_OXU210HP_HCD=y +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_OHCI_HCD is not set +# CONFIG_USB_U132_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_SOC=y + +# +# OMAP 343x high speed USB support +# +# CONFIG_USB_MUSB_HOST is not set +# CONFIG_USB_MUSB_PERIPHERAL is not set +CONFIG_USB_MUSB_OTG=y +CONFIG_USB_GADGET_MUSB_HDRC=y +CONFIG_USB_MUSB_HDRC_HCD=y +# CONFIG_MUSB_PIO_ONLY is not set +CONFIG_USB_INVENTRA_DMA=y +# CONFIG_USB_TI_CPPI_DMA is not set +# CONFIG_USB_MUSB_DEBUG is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m +CONFIG_USB_WDM=m +CONFIG_USB_TMC=m + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed; +# + +# +# see USB_STORAGE Help for more information +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +CONFIG_USB_SERIAL=m +CONFIG_USB_EZUSB=y +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_AIRCABLE=m +CONFIG_USB_SERIAL_ARK3116=m +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_CP2101=m +CONFIG_USB_SERIAL_CYPRESS_M8=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_FUNSOFT=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_GARMIN=m +CONFIG_USB_SERIAL_IPW=m +CONFIG_USB_SERIAL_IUU=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KEYSPAN_MPR=y +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_MOS7720=m +CONFIG_USB_SERIAL_MOS7840=m +CONFIG_USB_SERIAL_MOTOROLA=m +CONFIG_USB_SERIAL_NAVMAN=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OTI6858=m +CONFIG_USB_SERIAL_SPCP8X5=m +CONFIG_USB_SERIAL_HP4X=m +CONFIG_USB_SERIAL_SAFE=m +# CONFIG_USB_SERIAL_SAFE_PADDED is not set +CONFIG_USB_SERIAL_SIEMENS_MPI=m +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_USB_SERIAL_TI=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_SERIAL_OPTICON=m +CONFIG_USB_SERIAL_DEBUG=m + +# +# USB Miscellaneous drivers +# +CONFIG_USB_EMI62=m +CONFIG_USB_EMI26=m +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +CONFIG_USB_LEGOTOWER=m +CONFIG_USB_LCD=m +CONFIG_USB_BERRY_CHARGE=m +CONFIG_USB_LED=m +CONFIG_USB_CYPRESS_CY7C63=m +CONFIG_USB_CYTHERM=m +CONFIG_USB_PHIDGET=m +CONFIG_USB_PHIDGETKIT=m +CONFIG_USB_PHIDGETMOTORCONTROL=m +CONFIG_USB_PHIDGETSERVO=m +CONFIG_USB_IDMOUSE=m +CONFIG_USB_FTDI_ELAN=m +# CONFIG_USB_APPLEDISPLAY is not set +CONFIG_USB_SISUSBVGA=m +CONFIG_USB_SISUSBVGA_CON=y +CONFIG_USB_LD=m +CONFIG_USB_TRANCEVIBRATOR=m +# CONFIG_USB_IOWARRIOR is not set +CONFIG_USB_TEST=m +# CONFIG_USB_ISIGHTFW is not set +CONFIG_USB_VST=m +CONFIG_USB_ATM=m +CONFIG_USB_SPEEDTOUCH=m +CONFIG_USB_CXACRU=m +CONFIG_USB_UEAGLEATM=m +CONFIG_USB_XUSBATM=m +CONFIG_USB_GADGET=m +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_DEBUG_FS=y +CONFIG_USB_GADGET_VBUS_DRAW=2 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_AT91 is not set +# CONFIG_USB_GADGET_ATMEL_USBA is not set +# CONFIG_USB_GADGET_FSL_USB2 is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_PXA25X is not set +# CONFIG_USB_GADGET_PXA27X is not set +# CONFIG_USB_GADGET_S3C2410 is not set +# CONFIG_USB_GADGET_IMX is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_FSL_QE is not set +# CONFIG_USB_GADGET_CI13XXX is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +CONFIG_USB_ZERO=m +CONFIG_USB_ZERO_HNPTEST=y +CONFIG_USB_ETH=m +CONFIG_USB_ETH_RNDIS=y +CONFIG_USB_GADGETFS=m +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_G_SERIAL=m +CONFIG_USB_MIDI_GADGET=m +CONFIG_USB_G_PRINTER=m +CONFIG_USB_CDC_COMPOSITE=m + +# +# OTG and related infrastructure +# +CONFIG_USB_OTG_UTILS=y +CONFIG_USB_GPIO_VBUS=y +# CONFIG_ISP1301_OMAP is not set +CONFIG_TWL4030_USB=y +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y +CONFIG_SDIO_UART=y +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_OMAP_HS=y +CONFIG_MMC_SPI=m +# CONFIG_MEMSTICK is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_OMAP_DEBUG is not set +CONFIG_LEDS_OMAP=y +# CONFIG_LEDS_OMAP_PWM is not set +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_PCA955X is not set + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=m +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_BACKLIGHT=m +CONFIG_LEDS_TRIGGER_DEFAULT_ON=m +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +CONFIG_RTC_DRV_TWL4030=y +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_DMADEVICES is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +CONFIG_REGULATOR_TWL4030=y +CONFIG_UIO=m +CONFIG_UIO_PDRV=m +CONFIG_UIO_PDRV_GENIRQ=m +# CONFIG_UIO_SMX is not set +# CONFIG_UIO_SERCOS3 is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_MEILHAUS is not set +# CONFIG_USB_IP_COMMON is not set +CONFIG_W35UND=m +CONFIG_PRISM2_USB=m +# CONFIG_ECHO is not set +CONFIG_USB_ATMEL=m +# CONFIG_AGNX is not set +CONFIG_OTUS=m +# CONFIG_COMEDI is not set +# CONFIG_ASUS_OLED is not set +# CONFIG_INPUT_MIMIO is not set +# CONFIG_TRANZPORT is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=m +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +# CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION is not set +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_GPIO=m +CONFIG_ANDROID_LOW_MEMORY_KILLER=y + +# +# CBUS support +# +# CONFIG_CBUS is not set +CONFIG_MPU_BRIDGE=m +CONFIG_BRIDGE_MEMPOOL_SIZE=0x600000 +# CONFIG_BRIDGE_DEBUG is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_FS_XATTR is not set +CONFIG_EXT4_FS=m +# CONFIG_EXT4DEV_COMPAT is not set +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_JBD2=m +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=m +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +CONFIG_REISERFS_PROC_INFO=y +CONFIG_REISERFS_FS_XATTR=y +# CONFIG_REISERFS_FS_POSIX_ACL is not set +# CONFIG_REISERFS_FS_SECURITY is not set +CONFIG_JFS_FS=m +# CONFIG_JFS_POSIX_ACL is not set +# CONFIG_JFS_SECURITY is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_FILE_LOCKING=y +CONFIG_XFS_FS=m +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_POSIX_ACL is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_DEBUG is not set +CONFIG_GFS2_FS=m +CONFIG_GFS2_FS_LOCKING_DLM=m +CONFIG_OCFS2_FS=m +CONFIG_OCFS2_FS_O2CB=m +CONFIG_OCFS2_FS_USERSPACE_CLUSTER=m +CONFIG_OCFS2_FS_STATS=y +CONFIG_OCFS2_DEBUG_MASKLOG=y +# CONFIG_OCFS2_DEBUG_FS is not set +# CONFIG_OCFS2_FS_POSIX_ACL is not set +CONFIG_BTRFS_FS=m +# CONFIG_BTRFS_FS_POSIX_ACL is not set +CONFIG_DNOTIFY=y +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +CONFIG_QUOTA=y +# CONFIG_QUOTA_NETLINK_INTERFACE is not set +CONFIG_PRINT_QUOTA_WARNING=y +CONFIG_QUOTA_TREE=y +# CONFIG_QFMT_V1 is not set +CONFIG_QFMT_V2=y +CONFIG_QUOTACTL=y +# CONFIG_AUTOFS_FS is not set +CONFIG_AUTOFS4_FS=m +CONFIG_FUSE_FS=y + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +CONFIG_NTFS_FS=m +# CONFIG_NTFS_DEBUG is not set +CONFIG_NTFS_RW=y + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_CONFIGFS_FS=m +CONFIG_MISC_FILESYSTEMS=y +CONFIG_ADFS_FS=m +# CONFIG_ADFS_FS_RW is not set +CONFIG_AFFS_FS=m +# CONFIG_ECRYPT_FS is not set +CONFIG_HFS_FS=m +CONFIG_HFSPLUS_FS=m +CONFIG_BEFS_FS=m +# CONFIG_BEFS_DEBUG is not set +CONFIG_BFS_FS=m +CONFIG_EFS_FS=m +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +CONFIG_JFFS2_SUMMARY=y +CONFIG_JFFS2_FS_XATTR=y +CONFIG_JFFS2_FS_POSIX_ACL=y +CONFIG_JFFS2_FS_SECURITY=y +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_LZO=y +CONFIG_JFFS2_RTIME=y +CONFIG_JFFS2_RUBIN=y +# CONFIG_JFFS2_CMODE_NONE is not set +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +CONFIG_JFFS2_CMODE_FAVOURLZO=y +CONFIG_UBIFS_FS=y +CONFIG_UBIFS_FS_XATTR=y +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_UBIFS_FS_DEBUG is not set +CONFIG_CRAMFS=m +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +CONFIG_VXFS_FS=m +CONFIG_MINIX_FS=m +CONFIG_OMFS_FS=m +CONFIG_HPFS_FS=m +CONFIG_QNX4FS_FS=m +CONFIG_ROMFS_FS=m +CONFIG_SYSV_FS=m +CONFIG_UFS_FS=m +# CONFIG_UFS_FS_WRITE is not set +# CONFIG_UFS_DEBUG is not set +CONFIG_AUFS_FS=y +CONFIG_AUFS_BRANCH_MAX_127=y +# CONFIG_AUFS_BRANCH_MAX_511 is not set +# CONFIG_AUFS_BRANCH_MAX_1023 is not set +# CONFIG_AUFS_BRANCH_MAX_32767 is not set +# CONFIG_AUFS_HINOTIFY is not set +# CONFIG_AUFS_SHWH is not set +# CONFIG_AUFS_BR_RAMFS is not set +CONFIG_AUFS_DEBUG=y +CONFIG_AUFS_MAGIC_SYSRQ=y +CONFIG_AUFS_BDEV_LOOP=y +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +CONFIG_NFS_V4=y +CONFIG_ROOT_NFS=y +CONFIG_NFSD=m +CONFIG_NFSD_V2_ACL=y +CONFIG_NFSD_V3=y +CONFIG_NFSD_V3_ACL=y +CONFIG_NFSD_V4=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_EXPORTFS=m +CONFIG_NFS_ACL_SUPPORT=m +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +# CONFIG_SUNRPC_REGISTER_V4 is not set +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +CONFIG_SMB_FS=m +# CONFIG_SMB_NLS_DEFAULT is not set +CONFIG_CIFS=m +CONFIG_CIFS_STATS=y +CONFIG_CIFS_STATS2=y +# CONFIG_CIFS_WEAK_PW_HASH is not set +# CONFIG_CIFS_UPCALL is not set +# CONFIG_CIFS_XATTR is not set +# CONFIG_CIFS_DEBUG2 is not set +CONFIG_CIFS_EXPERIMENTAL=y +# CONFIG_CIFS_DFS_UPCALL is not set +CONFIG_NCP_FS=m +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +CONFIG_CODA_FS=m +CONFIG_AFS_FS=m +# CONFIG_AFS_DEBUG is not set +CONFIG_9P_FS=m + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +CONFIG_MAC_PARTITION=y +CONFIG_MSDOS_PARTITION=y +CONFIG_BSD_DISKLABEL=y +CONFIG_MINIX_SUBPARTITION=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_UNIXWARE_DISKLABEL=y +CONFIG_LDM_PARTITION=y +CONFIG_LDM_DEBUG=y +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +CONFIG_EFI_PARTITION=y +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_UTF8=y +CONFIG_DLM=m +# CONFIG_DLM_DEBUG is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +CONFIG_DEBUG_MUTEXES=y +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +CONFIG_NOP_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_RING_BUFFER=y +CONFIG_TRACING=y + +# +# Tracers +# +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_CONTEXT_SWITCH_TRACER is not set +# CONFIG_BOOT_TRACER is not set +# CONFIG_TRACE_BRANCH_PROFILING is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_DYNAMIC_PRINTK_DEBUG is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +CONFIG_KEYS=y +# CONFIG_KEYS_DEBUG_PROC_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_XOR_BLOCKS=m +CONFIG_ASYNC_CORE=m +CONFIG_ASYNC_MEMCPY=m +CONFIG_ASYNC_XOR=m +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_FIPS=y +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=m +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=m +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_GF128MUL=m +CONFIG_CRYPTO_NULL=m +CONFIG_CRYPTO_CRYPTD=m +CONFIG_CRYPTO_AUTHENC=m +CONFIG_CRYPTO_TEST=m + +# +# Authenticated Encryption with Associated Data +# +CONFIG_CRYPTO_CCM=m +CONFIG_CRYPTO_GCM=m +CONFIG_CRYPTO_SEQIV=m + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CTR=m +CONFIG_CRYPTO_CTS=m +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_LRW=m +CONFIG_CRYPTO_PCBC=m +CONFIG_CRYPTO_XTS=m + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=m +CONFIG_CRYPTO_XCBC=m + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_MICHAEL_MIC=y +CONFIG_CRYPTO_RMD128=m +CONFIG_CRYPTO_RMD160=m +CONFIG_CRYPTO_RMD256=m +CONFIG_CRYPTO_RMD320=m +CONFIG_CRYPTO_SHA1=m +CONFIG_CRYPTO_SHA256=m +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_TGR192=m +CONFIG_CRYPTO_WP512=m + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_ANUBIS=m +CONFIG_CRYPTO_ARC4=y +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_CAMELLIA=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_FCRYPT=m +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_SALSA20=m +CONFIG_CRYPTO_SEED=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_TWOFISH_COMMON=m + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_LZO=y + +# +# Random Number Generation +# +CONFIG_CRYPTO_ANSI_CPRNG=m +CONFIG_CRYPTO_HW=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +CONFIG_CRC_T10DIF=y +CONFIG_CRC_ITU_T=y +CONFIG_CRC32=y +CONFIG_CRC7=y +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=m +CONFIG_TEXTSEARCH_BM=m +CONFIG_TEXTSEARCH_FSM=m +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/defconfig.orig b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/defconfig.orig new file mode 100644 index 0000000000..1aa7cf57ac --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/defconfig.orig @@ -0,0 +1,2701 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.29-omap1 +# Fri May 29 14:47:52 2009 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_MMU=y +# CONFIG_NO_IOPORT is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_OPROFILE_ARMV7=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +# CONFIG_AUDIT is not set + +# +# RCU Subsystem +# +CONFIG_CLASSIC_RCU=y +# CONFIG_TREE_RCU is not set +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_PREEMPT_RCU_TRACE is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_GROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_RT_GROUP_SCHED is not set +CONFIG_USER_SCHED=y +# CONFIG_CGROUP_SCHED is not set +# CONFIG_CGROUPS is not set +# CONFIG_SYSFS_DEPRECATED_V2 is not set +# CONFIG_RELAY is not set +# CONFIG_NAMESPACES is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_VM_EVENT_COUNTERS=y +# CONFIG_COMPAT_BRK is not set +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +CONFIG_PROFILING=y +CONFIG_TRACEPOINTS=y +# CONFIG_MARKERS is not set +CONFIG_OPROFILE=y +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_BLOCK=y +CONFIG_LBD=y +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_FREEZER=y + +# +# System Type +# +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_DAVINCI is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_W90X900 is not set + +# +# TI OMAP Implementations +# +CONFIG_ARCH_OMAP_OTG=y +# CONFIG_ARCH_OMAP1 is not set +# CONFIG_ARCH_OMAP2 is not set +CONFIG_ARCH_OMAP3=y + +# +# OMAP Feature Selections +# +# CONFIG_OMAP_DEBUG_POWERDOMAIN is not set +# CONFIG_OMAP_DEBUG_CLOCKDOMAIN is not set +CONFIG_OMAP_SMARTREFLEX=y +# CONFIG_OMAP_SMARTREFLEX_TESTING is not set +CONFIG_OMAP_RESET_CLOCKS=y +CONFIG_OMAP_BOOT_TAG=y +CONFIG_OMAP_BOOT_REASON=y +# CONFIG_OMAP_COMPONENT_VERSION is not set +# CONFIG_OMAP_GPIO_SWITCH is not set +# CONFIG_OMAP_MUX is not set +CONFIG_OMAP_MCBSP=y +# CONFIG_OMAP_MBOX_FWK is not set +# CONFIG_OMAP_IOMMU is not set +# CONFIG_OMAP_MPU_TIMER is not set +CONFIG_OMAP_32K_TIMER=y +CONFIG_OMAP_32K_TIMER_HZ=128 +CONFIG_OMAP_TICK_GPTIMER=12 +CONFIG_OMAP_DM_TIMER=y +# CONFIG_OMAP_LL_DEBUG_UART1 is not set +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +CONFIG_OMAP_LL_DEBUG_UART3=y +CONFIG_ARCH_OMAP34XX=y +CONFIG_ARCH_OMAP3430=y + +# +# OMAP Board Type +# +# CONFIG_MACH_NOKIA_RX51 is not set +# CONFIG_MACH_OMAP_LDP is not set +# CONFIG_MACH_OMAP_3430SDP is not set +# CONFIG_MACH_OMAP3EVM is not set +CONFIG_MACH_OMAP3_BEAGLE=y +# CONFIG_MACH_OVERO is not set +# CONFIG_MACH_OMAP3_PANDORA is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_IFAR=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_HAS_TLS_REG=y +# CONFIG_OUTER_CACHE is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT is not set +CONFIG_HZ=128 +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_ARCH_FLATMEM_HAS_HOLES=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_UNEVICTABLE_LRU=y +CONFIG_LEDS=y +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE=" debug " +# CONFIG_XIP_KERNEL is not set +CONFIG_KEXEC=y +CONFIG_ATAGS_PROC=y + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_DEBUG=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y +# CONFIG_ARM_ERRATUM_451034 is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +CONFIG_BINFMT_AOUT=m +CONFIG_BINFMT_MISC=y + +# +# Power management options +# +CONFIG_PM=y +CONFIG_PM_DEBUG=y +# CONFIG_PM_VERBOSE is not set +CONFIG_CAN_PM_TRACE=y +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +# CONFIG_PM_TEST_SUSPEND is not set +CONFIG_SUSPEND_FREEZER=y +# CONFIG_APM_EMULATION is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_COMPAT_NET_DEV_OPS=y +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=m +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE=m +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_IPCOMP=m +CONFIG_INET_XFRM_TUNNEL=m +CONFIG_INET_TUNNEL=m +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +CONFIG_INET_LRO=m +CONFIG_INET_DIAG=m +CONFIG_INET_TCP_DIAG=m +CONFIG_TCP_CONG_ADVANCED=y +CONFIG_TCP_CONG_BIC=m +CONFIG_TCP_CONG_CUBIC=y +CONFIG_TCP_CONG_WESTWOOD=m +CONFIG_TCP_CONG_HTCP=m +CONFIG_TCP_CONG_HSTCP=m +CONFIG_TCP_CONG_HYBLA=m +CONFIG_TCP_CONG_VEGAS=m +CONFIG_TCP_CONG_SCALABLE=m +CONFIG_TCP_CONG_LP=m +CONFIG_TCP_CONG_VENO=m +CONFIG_TCP_CONG_YEAH=m +CONFIG_TCP_CONG_ILLINOIS=m +# CONFIG_DEFAULT_BIC is not set +CONFIG_DEFAULT_CUBIC=y +# CONFIG_DEFAULT_HTCP is not set +# CONFIG_DEFAULT_VEGAS is not set +# CONFIG_DEFAULT_WESTWOOD is not set +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=m +# CONFIG_IPV6_PRIVACY is not set +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_IPCOMP=m +CONFIG_IPV6_MIP6=m +CONFIG_INET6_XFRM_TUNNEL=m +CONFIG_INET6_TUNNEL=m +CONFIG_INET6_XFRM_MODE_TRANSPORT=m +CONFIG_INET6_XFRM_MODE_TUNNEL=m +CONFIG_INET6_XFRM_MODE_BEET=m +CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=m +CONFIG_IPV6_SIT=m +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=m +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_IPV6_MROUTE=y +# CONFIG_IPV6_PIMSM_V2 is not set +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=m +CONFIG_NETFILTER_NETLINK_QUEUE=m +CONFIG_NETFILTER_NETLINK_LOG=m +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CT_ACCT=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=m +CONFIG_NF_CT_PROTO_GRE=m +CONFIG_NF_CT_PROTO_SCTP=m +CONFIG_NF_CT_PROTO_UDPLITE=m +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_H323=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NF_CONNTRACK_NETBIOS_NS=m +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_SANE=m +CONFIG_NF_CONNTRACK_SIP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NF_CT_NETLINK=m +# CONFIG_NETFILTER_TPROXY is not set +CONFIG_NETFILTER_XTABLES=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +CONFIG_NETFILTER_XT_TARGET_RATEEST=m +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_DCCP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_RECENT=m +# CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT is not set +CONFIG_NETFILTER_XT_MATCH_SCTP=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=m +CONFIG_NETFILTER_XT_MATCH_U32=m +CONFIG_IP_VS=m +CONFIG_IP_VS_IPV6=y +CONFIG_IP_VS_DEBUG=y +CONFIG_IP_VS_TAB_BITS=12 + +# +# IPVS transport protocol load balancing support +# +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_AH_ESP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y + +# +# IPVS scheduler +# +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +CONFIG_IP_VS_SED=m +CONFIG_IP_VS_NQ=m + +# +# IPVS application helper +# +CONFIG_IP_VS_FTP=m + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=m +CONFIG_NF_CONNTRACK_IPV4=m +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +CONFIG_IP_NF_QUEUE=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_ADDRTYPE=m +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_LOG=m +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_NF_NAT=m +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_TARGET_IDLETIMER=m +CONFIG_NF_NAT_SNMP_BASIC=m +CONFIG_NF_NAT_PROTO_DCCP=m +CONFIG_NF_NAT_PROTO_GRE=m +CONFIG_NF_NAT_PROTO_UDPLITE=m +CONFIG_NF_NAT_PROTO_SCTP=m +CONFIG_NF_NAT_FTP=m +CONFIG_NF_NAT_IRC=m +CONFIG_NF_NAT_TFTP=m +CONFIG_NF_NAT_AMANDA=m +CONFIG_NF_NAT_PPTP=m +CONFIG_NF_NAT_H323=m +CONFIG_NF_NAT_SIP=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_CLUSTERIP=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_CONNTRACK_IPV6=m +CONFIG_IP6_NF_QUEUE=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_AH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_MH=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_TARGET_LOG=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_TARGET_HL=m +CONFIG_IP6_NF_RAW=m +# CONFIG_BRIDGE_NF_EBTABLES is not set +CONFIG_IP_DCCP=m +CONFIG_INET_DCCP_DIAG=m + +# +# DCCP CCIDs Configuration (EXPERIMENTAL) +# +# CONFIG_IP_DCCP_CCID2_DEBUG is not set +CONFIG_IP_DCCP_CCID3=y +# CONFIG_IP_DCCP_CCID3_DEBUG is not set +CONFIG_IP_DCCP_CCID3_RTO=100 +CONFIG_IP_DCCP_TFRC_LIB=y + +# +# DCCP Kernel Hacking +# +# CONFIG_IP_DCCP_DEBUG is not set +CONFIG_IP_SCTP=m +# CONFIG_SCTP_DBG_MSG is not set +# CONFIG_SCTP_DBG_OBJCNT is not set +# CONFIG_SCTP_HMAC_NONE is not set +# CONFIG_SCTP_HMAC_SHA1 is not set +CONFIG_SCTP_HMAC_MD5=y +CONFIG_TIPC=m +# CONFIG_TIPC_ADVANCED is not set +# CONFIG_TIPC_DEBUG is not set +CONFIG_ATM=m +CONFIG_ATM_CLIP=m +# CONFIG_ATM_CLIP_NO_ICMP is not set +CONFIG_ATM_LANE=m +CONFIG_ATM_MPOA=m +CONFIG_ATM_BR2684=m +# CONFIG_ATM_BR2684_IPFILTER is not set +CONFIG_STP=m +CONFIG_GARP=m +CONFIG_BRIDGE=m +# CONFIG_NET_DSA is not set +CONFIG_VLAN_8021Q=m +CONFIG_VLAN_8021Q_GVRP=y +# CONFIG_DECNET is not set +CONFIG_LLC=m +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +CONFIG_WAN_ROUTER=m +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_ATM=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_MULTIQ=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_DRR=m + +# +# Classification +# +CONFIG_NET_CLS=y +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_PERF=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +CONFIG_NET_CLS_FLOW=m +# CONFIG_NET_EMATCH is not set +# CONFIG_NET_CLS_ACT is not set +CONFIG_NET_CLS_IND=y +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +CONFIG_CAN=m +CONFIG_CAN_RAW=m +CONFIG_CAN_BCM=m + +# +# CAN Device Drivers +# +CONFIG_CAN_VCAN=m +# CONFIG_CAN_DEBUG_DEVICES is not set +CONFIG_IRDA=m + +# +# IrDA protocols +# +CONFIG_IRLAN=m +CONFIG_IRNET=m +CONFIG_IRCOMM=m +CONFIG_IRDA_ULTRA=y + +# +# IrDA options +# +CONFIG_IRDA_CACHE_LAST_LSAP=y +CONFIG_IRDA_FAST_RR=y +CONFIG_IRDA_DEBUG=y + +# +# Infrared-port device drivers +# + +# +# SIR device drivers +# +CONFIG_IRTTY_SIR=m + +# +# Dongle support +# +CONFIG_DONGLE=y +CONFIG_ESI_DONGLE=m +CONFIG_ACTISYS_DONGLE=m +CONFIG_TEKRAM_DONGLE=m +CONFIG_TOIM3232_DONGLE=m +CONFIG_LITELINK_DONGLE=m +CONFIG_MA600_DONGLE=m +CONFIG_GIRBIL_DONGLE=m +CONFIG_MCP2120_DONGLE=m +CONFIG_OLD_BELKIN_DONGLE=m +# CONFIG_ACT200L_DONGLE is not set +CONFIG_KINGSUN_DONGLE=m +CONFIG_KSDAZZLE_DONGLE=m +CONFIG_KS959_DONGLE=m + +# +# FIR device drivers +# +CONFIG_USB_IRDA=m +CONFIG_SIGMATEL_FIR=m +CONFIG_MCS_FIR=m +# CONFIG_OMAP_IR is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +CONFIG_BT_HCIBTUSB=y +CONFIG_BT_HCIBTSDIO=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_LL=y +CONFIG_BT_HCIBCM203X=y +CONFIG_BT_HCIBPA10X=y +CONFIG_BT_HCIBFUSB=y +# CONFIG_BT_HCIBRF6150 is not set +# CONFIG_BT_HCIH4P is not set +# CONFIG_BT_HCIVHCI is not set +CONFIG_AF_RXRPC=m +# CONFIG_AF_RXRPC_DEBUG is not set +# CONFIG_RXKAD is not set +# CONFIG_PHONET is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_CFG80211=y +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_NL80211=y +CONFIG_WIRELESS_OLD_REGULATORY=y +CONFIG_WIRELESS_EXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +CONFIG_LIB80211_CRYPT_WEP=y +CONFIG_LIB80211_CRYPT_CCMP=y +CONFIG_LIB80211_CRYPT_TKIP=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_MAC80211=y + +# +# Rate control algorithm selection +# +CONFIG_MAC80211_RC_PID=y +# CONFIG_MAC80211_RC_MINSTREL is not set +CONFIG_MAC80211_RC_DEFAULT_PID=y +# CONFIG_MAC80211_RC_DEFAULT_MINSTREL is not set +CONFIG_MAC80211_RC_DEFAULT="pid" +# CONFIG_MAC80211_MESH is not set +CONFIG_MAC80211_LEDS=y +# CONFIG_MAC80211_DEBUGFS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +CONFIG_WIMAX=m +CONFIG_WIMAX_DEBUG_LEVEL=8 +# CONFIG_RFKILL is not set +CONFIG_NET_9P=m +# CONFIG_NET_9P_DEBUG is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_OMAP2=y +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_NANDSIM is not set +CONFIG_MTD_NAND_PLATFORM=y +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set + +# +# UBI - Unsorted block images +# +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_RESERVE=1 +# CONFIG_MTD_UBI_GLUEBI is not set + +# +# UBI debugging options +# +# CONFIG_MTD_UBI_DEBUG is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=m +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=16384 +# CONFIG_BLK_DEV_XIP is not set +CONFIG_CDROM_PKTCDVD=m +CONFIG_CDROM_PKTCDVD_BUFFERS=8 +# CONFIG_CDROM_PKTCDVD_WCACHE is not set +# CONFIG_ATA_OVER_ETH is not set +CONFIG_MISC_DEVICES=y +# CONFIG_ICS932S401 is not set +# CONFIG_OMAP_STI is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +CONFIG_EEPROM_93CX6=y +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_RAID_ATTRS=m +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +CONFIG_SCSI_ISCSI_ATTRS=m +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +CONFIG_ISCSI_TCP=m +# CONFIG_LIBFC is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_ATA is not set +CONFIG_MD=y +CONFIG_BLK_DEV_MD=m +CONFIG_MD_LINEAR=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m +CONFIG_MD_RAID456=m +CONFIG_MD_RAID5_RESHAPE=y +CONFIG_MD_MULTIPATH=m +CONFIG_MD_FAULTY=m +CONFIG_BLK_DEV_DM=m +# CONFIG_DM_DEBUG is not set +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m +CONFIG_DM_MULTIPATH=m +CONFIG_DM_DELAY=m +# CONFIG_DM_UEVENT is not set +CONFIG_NETDEVICES=y +CONFIG_DUMMY=m +CONFIG_BONDING=m +CONFIG_MACVLAN=m +CONFIG_EQUALIZER=m +CONFIG_TUN=m +CONFIG_VETH=m +# CONFIG_NET_ETHERNET is not set +CONFIG_MII=y +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set + +# +# Wireless LAN +# +# CONFIG_WLAN_PRE80211 is not set +CONFIG_WLAN_80211=y +CONFIG_LIBERTAS=y +CONFIG_LIBERTAS_USB=y +# CONFIG_LIBERTAS_SDIO is not set +# CONFIG_LIBERTAS_DEBUG is not set +# CONFIG_LIBERTAS_THINFIRM is not set +CONFIG_USB_ZD1201=y +CONFIG_USB_NET_RNDIS_WLAN=y +CONFIG_RTL8187=y +# CONFIG_MAC80211_HWSIM is not set +CONFIG_P54_COMMON=y +CONFIG_P54_USB=y +# CONFIG_IWLWIFI_LEDS is not set +CONFIG_HOSTAP=y +CONFIG_HOSTAP_FIRMWARE=y +CONFIG_HOSTAP_FIRMWARE_NVRAM=y +CONFIG_B43=y +CONFIG_B43_LEDS=y +# CONFIG_B43_DEBUG is not set +# CONFIG_B43LEGACY is not set +CONFIG_ZD1211RW=y +# CONFIG_ZD1211RW_DEBUG is not set +CONFIG_RT2X00=y +CONFIG_RT2500USB=y +CONFIG_RT73USB=y +CONFIG_RT2X00_LIB_USB=y +CONFIG_RT2X00_LIB=y +CONFIG_RT2X00_LIB_FIRMWARE=y +CONFIG_RT2X00_LIB_CRYPTO=y +CONFIG_RT2X00_LIB_LEDS=y +# CONFIG_RT2X00_DEBUG is not set + +# +# WiMAX Wireless Broadband devices +# +# CONFIG_WIMAX_I2400M_USB is not set +# CONFIG_WIMAX_I2400M_SDIO is not set + +# +# USB Network Adapters +# +CONFIG_USB_CATC=y +CONFIG_USB_KAWETH=y +CONFIG_USB_PEGASUS=y +CONFIG_USB_RTL8150=y +CONFIG_USB_USBNET=y +CONFIG_USB_NET_AX8817X=y +CONFIG_USB_NET_CDCETHER=y +CONFIG_USB_NET_DM9601=y +CONFIG_USB_NET_SMSC95XX=y +CONFIG_USB_NET_GL620A=y +CONFIG_USB_NET_NET1080=y +CONFIG_USB_NET_PLUSB=y +CONFIG_USB_NET_MCS7830=y +CONFIG_USB_NET_RNDIS_HOST=y +CONFIG_USB_NET_CDC_SUBSET=y +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_KC2190=y +CONFIG_USB_NET_ZAURUS=y +# CONFIG_WAN is not set +CONFIG_ATM_DRIVERS=y +# CONFIG_ATM_DUMMY is not set +# CONFIG_ATM_TCP is not set +CONFIG_PPP=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_MPPE=m +CONFIG_PPPOE=m +# CONFIG_PPPOATM is not set +CONFIG_PPPOL2TP=m +# CONFIG_SLIP is not set +CONFIG_SLHC=m +CONFIG_NETCONSOLE=m +CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_NETPOLL=y +CONFIG_NETPOLL_TRAP=y +CONFIG_NET_POLL_CONTROLLER=y +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +CONFIG_INPUT_FF_MEMLESS=y +# CONFIG_INPUT_POLLDEV is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_TWL4030 is not set +# CONFIG_KEYBOARD_LM8323 is not set +CONFIG_KEYBOARD_GPIO=y +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +CONFIG_MOUSE_PS2_ALPS=y +CONFIG_MOUSE_PS2_LOGIPS2PP=y +CONFIG_MOUSE_PS2_SYNAPTICS=y +CONFIG_MOUSE_PS2_TRACKPOINT=y +# CONFIG_MOUSE_PS2_ELANTECH is not set +# CONFIG_MOUSE_PS2_TOUCHKIT is not set +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_BCM5974 is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_MOUSE_GPIO is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_TWL4030_PWRBUTTON=y +CONFIG_INPUT_UINPUT=y + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=32 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DETECT_IRQ=y +CONFIG_SERIAL_8250_RSA=y + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_OMAP=y +# CONFIG_I2C_SIMTEC is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_STUB is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_DS1682 is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_PCF8575 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +CONFIG_TWL4030_MADC=m +CONFIG_TWL4030_POWEROFF=y +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_TSL2563 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OMAP24XX is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_TSC210X is not set +# CONFIG_SPI_TSC2301 is not set +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO expanders: +# + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +CONFIG_GPIO_TWL4030=y + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=m +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_BATTERY_DS2760 is not set +# CONFIG_TWL4030_BCI_BATTERY is not set +# CONFIG_BATTERY_BQ27x00 is not set +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7473 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_SENSORS_TSC210X is not set +CONFIG_SENSORS_OMAP34XX=y +# CONFIG_HWMON_DEBUG_CHIP is not set +CONFIG_THERMAL=y +CONFIG_THERMAL_HWMON=y +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +CONFIG_OMAP_WATCHDOG=y + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +CONFIG_SSB=y +# CONFIG_SSB_SILENT is not set +# CONFIG_SSB_DEBUG is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_TPS65010 is not set +CONFIG_TWL4030_CORE=y +# CONFIG_TWL4030_POWER is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_PCF50633 is not set + +# +# Multimedia devices +# + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=m +CONFIG_VIDEO_V4L2_COMMON=m +CONFIG_VIDEO_ALLOW_V4L1=y +CONFIG_VIDEO_V4L1_COMPAT=y +CONFIG_DVB_CORE=m +CONFIG_VIDEO_MEDIA=m + +# +# Multimedia drivers +# +CONFIG_MEDIA_ATTACH=y +CONFIG_MEDIA_TUNER=m +# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set +CONFIG_MEDIA_TUNER_SIMPLE=m +CONFIG_MEDIA_TUNER_TDA8290=m +CONFIG_MEDIA_TUNER_TDA827X=m +CONFIG_MEDIA_TUNER_TDA18271=m +CONFIG_MEDIA_TUNER_TDA9887=m +CONFIG_MEDIA_TUNER_TEA5761=m +CONFIG_MEDIA_TUNER_TEA5767=m +CONFIG_MEDIA_TUNER_MT20XX=m +CONFIG_MEDIA_TUNER_MT2060=m +CONFIG_MEDIA_TUNER_MT2266=m +CONFIG_MEDIA_TUNER_QT1010=m +CONFIG_MEDIA_TUNER_XC2028=m +CONFIG_MEDIA_TUNER_XC5000=m +CONFIG_MEDIA_TUNER_MXL5005S=m +CONFIG_VIDEO_V4L2=m +CONFIG_VIDEO_V4L1=m +CONFIG_VIDEOBUF_GEN=m +CONFIG_VIDEOBUF_VMALLOC=m +CONFIG_VIDEOBUF_DVB=m +CONFIG_VIDEO_IR=m +CONFIG_VIDEO_TVEEPROM=m +CONFIG_VIDEO_TUNER=m +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +CONFIG_VIDEO_HELPER_CHIPS_AUTO=y +CONFIG_VIDEO_IR_I2C=m +CONFIG_VIDEO_MSP3400=m +CONFIG_VIDEO_CS53L32A=m +CONFIG_VIDEO_WM8775=m +CONFIG_VIDEO_SAA711X=m +CONFIG_VIDEO_TVP5150=m +CONFIG_VIDEO_CX25840=m +CONFIG_VIDEO_CX2341X=m +CONFIG_VIDEO_VIVI=m +# CONFIG_VIDEO_CPIA is not set +# CONFIG_VIDEO_CPIA2 is not set +# CONFIG_VIDEO_SAA5246A is not set +# CONFIG_VIDEO_SAA5249 is not set +# CONFIG_VIDEO_AU0828 is not set +# CONFIG_VIDEO_OMAP3 is not set +# CONFIG_SOC_CAMERA is not set +CONFIG_V4L_USB_DRIVERS=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +CONFIG_USB_GSPCA=m +CONFIG_USB_M5602=m +CONFIG_USB_STV06XX=m +CONFIG_USB_GSPCA_CONEX=m +CONFIG_USB_GSPCA_ETOMS=m +CONFIG_USB_GSPCA_FINEPIX=m +CONFIG_USB_GSPCA_MARS=m +CONFIG_USB_GSPCA_OV519=m +CONFIG_USB_GSPCA_OV534=m +CONFIG_USB_GSPCA_PAC207=m +CONFIG_USB_GSPCA_PAC7311=m +CONFIG_USB_GSPCA_SONIXB=m +CONFIG_USB_GSPCA_SONIXJ=m +CONFIG_USB_GSPCA_SPCA500=m +CONFIG_USB_GSPCA_SPCA501=m +CONFIG_USB_GSPCA_SPCA505=m +CONFIG_USB_GSPCA_SPCA506=m +CONFIG_USB_GSPCA_SPCA508=m +CONFIG_USB_GSPCA_SPCA561=m +CONFIG_USB_GSPCA_STK014=m +CONFIG_USB_GSPCA_SUNPLUS=m +CONFIG_USB_GSPCA_T613=m +CONFIG_USB_GSPCA_TV8532=m +CONFIG_USB_GSPCA_VC032X=m +CONFIG_USB_GSPCA_ZC3XX=m +CONFIG_VIDEO_PVRUSB2=m +CONFIG_VIDEO_PVRUSB2_SYSFS=y +CONFIG_VIDEO_PVRUSB2_DVB=y +# CONFIG_VIDEO_PVRUSB2_DEBUGIFC is not set +CONFIG_VIDEO_EM28XX=m +CONFIG_VIDEO_EM28XX_ALSA=m +CONFIG_VIDEO_EM28XX_DVB=m +CONFIG_VIDEO_USBVISION=m +CONFIG_VIDEO_USBVIDEO=m +CONFIG_USB_VICAM=m +CONFIG_USB_IBMCAM=m +CONFIG_USB_KONICAWC=m +CONFIG_USB_QUICKCAM_MESSENGER=m +CONFIG_USB_ET61X251=m +CONFIG_VIDEO_OVCAMCHIP=m +CONFIG_USB_W9968CF=m +CONFIG_USB_OV511=m +CONFIG_USB_SE401=m +CONFIG_USB_SN9C102=m +CONFIG_USB_STV680=m +CONFIG_USB_ZC0301=m +CONFIG_USB_PWC=m +# CONFIG_USB_PWC_DEBUG is not set +CONFIG_USB_ZR364XX=m +CONFIG_USB_STKWEBCAM=m +CONFIG_USB_S2255=m +CONFIG_RADIO_ADAPTERS=y +# CONFIG_USB_DSBR is not set +# CONFIG_USB_SI470X is not set +# CONFIG_USB_MR800 is not set +# CONFIG_RADIO_TEA5764 is not set +CONFIG_DVB_DYNAMIC_MINORS=y +CONFIG_DVB_CAPTURE_DRIVERS=y +# CONFIG_TTPCI_EEPROM is not set + +# +# Supported USB Adapters +# +CONFIG_DVB_USB=m +# CONFIG_DVB_USB_DEBUG is not set +CONFIG_DVB_USB_A800=m +CONFIG_DVB_USB_DIBUSB_MB=m +# CONFIG_DVB_USB_DIBUSB_MB_FAULTY is not set +CONFIG_DVB_USB_DIBUSB_MC=m +CONFIG_DVB_USB_DIB0700=m +CONFIG_DVB_USB_UMT_010=m +CONFIG_DVB_USB_CXUSB=m +CONFIG_DVB_USB_M920X=m +CONFIG_DVB_USB_GL861=m +CONFIG_DVB_USB_AU6610=m +CONFIG_DVB_USB_DIGITV=m +CONFIG_DVB_USB_VP7045=m +CONFIG_DVB_USB_VP702X=m +CONFIG_DVB_USB_GP8PSK=m +CONFIG_DVB_USB_NOVA_T_USB2=m +CONFIG_DVB_USB_TTUSB2=m +CONFIG_DVB_USB_DTT200U=m +CONFIG_DVB_USB_OPERA1=m +CONFIG_DVB_USB_AF9005=m +CONFIG_DVB_USB_AF9005_REMOTE=m +CONFIG_DVB_USB_DW2102=m +CONFIG_DVB_USB_CINERGY_T2=m +CONFIG_DVB_USB_ANYSEE=m +CONFIG_DVB_USB_DTV5100=m +CONFIG_DVB_USB_AF9015=m +CONFIG_DVB_SIANO_SMS1XXX=m +CONFIG_DVB_SIANO_SMS1XXX_SMS_IDS=y + +# +# Supported FlexCopII (B2C2) Adapters +# +CONFIG_DVB_B2C2_FLEXCOP=m +CONFIG_DVB_B2C2_FLEXCOP_USB=m +# CONFIG_DVB_B2C2_FLEXCOP_DEBUG is not set + +# +# Supported DVB Frontends +# + +# +# Customise DVB Frontends +# +# CONFIG_DVB_FE_CUSTOMISE is not set + +# +# Multistandard (satellite) frontends +# +CONFIG_DVB_STB0899=m +CONFIG_DVB_STB6100=m + +# +# DVB-S (satellite) frontends +# +CONFIG_DVB_CX24110=m +CONFIG_DVB_CX24123=m +CONFIG_DVB_MT312=m +CONFIG_DVB_S5H1420=m +CONFIG_DVB_STV0288=m +CONFIG_DVB_STB6000=m +CONFIG_DVB_STV0299=m +CONFIG_DVB_TDA8083=m +CONFIG_DVB_TDA10086=m +CONFIG_DVB_TDA8261=m +CONFIG_DVB_VES1X93=m +CONFIG_DVB_TUNER_ITD1000=m +CONFIG_DVB_TUNER_CX24113=m +CONFIG_DVB_TDA826X=m +CONFIG_DVB_TUA6100=m +CONFIG_DVB_CX24116=m +CONFIG_DVB_SI21XX=m + +# +# DVB-T (terrestrial) frontends +# +CONFIG_DVB_SP8870=m +CONFIG_DVB_SP887X=m +CONFIG_DVB_CX22700=m +CONFIG_DVB_CX22702=m +CONFIG_DVB_DRX397XD=m +CONFIG_DVB_L64781=m +CONFIG_DVB_TDA1004X=m +CONFIG_DVB_NXT6000=m +CONFIG_DVB_MT352=m +CONFIG_DVB_ZL10353=m +CONFIG_DVB_DIB3000MB=m +CONFIG_DVB_DIB3000MC=m +CONFIG_DVB_DIB7000M=m +CONFIG_DVB_DIB7000P=m +CONFIG_DVB_TDA10048=m + +# +# DVB-C (cable) frontends +# +CONFIG_DVB_VES1820=m +CONFIG_DVB_TDA10021=m +CONFIG_DVB_TDA10023=m +CONFIG_DVB_STV0297=m + +# +# ATSC (North American/Korean Terrestrial/Cable DTV) frontends +# +CONFIG_DVB_NXT200X=m +CONFIG_DVB_OR51211=m +CONFIG_DVB_OR51132=m +CONFIG_DVB_BCM3510=m +CONFIG_DVB_LGDT330X=m +CONFIG_DVB_LGDT3304=m +CONFIG_DVB_S5H1409=m +CONFIG_DVB_AU8522=m +CONFIG_DVB_S5H1411=m + +# +# ISDB-T (terrestrial) frontends +# +CONFIG_DVB_S921=m + +# +# Digital terrestrial only tuners/PLL +# +CONFIG_DVB_PLL=m +CONFIG_DVB_TUNER_DIB0070=m + +# +# SEC control devices for DVB-S +# +CONFIG_DVB_LNBP21=m +# CONFIG_DVB_ISL6405 is not set +CONFIG_DVB_ISL6421=m +CONFIG_DVB_LGS8GL5=m + +# +# Tools to develop new frontends +# +# CONFIG_DVB_DUMMY_FE is not set +CONFIG_DVB_AF9013=m +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_OMAP_BOOTLOADER_INIT is not set +CONFIG_OMAP2_DSS=y +CONFIG_OMAP2_DSS_VRAM_SIZE=14 +CONFIG_OMAP2_DSS_DEBUG_SUPPORT=y +# CONFIG_OMAP2_DSS_RFBI is not set +CONFIG_OMAP2_DSS_VENC=y +# CONFIG_OMAP2_DSS_SDI is not set +CONFIG_OMAP2_DSS_DSI=y +CONFIG_OMAP2_DSS_USE_DSI_PLL=y +# CONFIG_OMAP2_DSS_FAKE_VSYNC is not set +CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK=1 + +# +# OMAP2/3 Display Device Drivers +# +CONFIG_PANEL_GENERIC=y +CONFIG_PANEL_SAMSUNG_LTE430WQ_F0C=m +# CONFIG_PANEL_SHARP_LS037V7DW01 is not set +# CONFIG_PANEL_N800 is not set +# CONFIG_CTRL_BLIZZARD is not set +CONFIG_FB_OMAP2=y +CONFIG_FB_OMAP2_DEBUG_SUPPORT=y +# CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE is not set +CONFIG_FB_OMAP2_NUM_FBS=3 +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +CONFIG_DISPLAY_SUPPORT=y + +# +# Display hardware drivers +# + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_SOUND=y +CONFIG_SOUND_OSS_CORE=y +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_HWDEP=y +CONFIG_SND_RAWMIDI=y +CONFIG_SND_SEQUENCER=m +# CONFIG_SND_SEQ_DUMMY is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_PCM_OSS_PLUGINS=y +CONFIG_SND_SEQUENCER_OSS=y +CONFIG_SND_HRTIMER=m +CONFIG_SND_SEQ_HRTIMER_DEFAULT=y +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_VIRMIDI is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +# CONFIG_SND_ARM is not set +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_USB_CAIAQ=m +CONFIG_SND_USB_CAIAQ_INPUT=y +CONFIG_SND_SOC=y +CONFIG_SND_OMAP_SOC=y +CONFIG_SND_OMAP_SOC_MCBSP=y +CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE=y +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_TWL4030=y +# CONFIG_SOUND_PRIME is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +CONFIG_HID_DEBUG=y +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# Special HID drivers +# +CONFIG_HID_COMPAT=y +CONFIG_HID_A4TECH=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_EZKEY=y +CONFIG_HID_GYRATION=y +CONFIG_HID_LOGITECH=y +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_NTRIG=y +CONFIG_HID_PANTHERLORD=y +# CONFIG_PANTHERLORD_FF is not set +CONFIG_HID_PETALYNX=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SUNPLUS=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_TOPSEED=y +# CONFIG_THRUSTMASTER_FF is not set +# CONFIG_ZEROPLUS_FF is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_SUSPEND=y +CONFIG_USB_OTG=y +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +CONFIG_USB_MON=y +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_EHCI_HCD=y +CONFIG_OMAP_EHCI_PHY_MODE=y +# CONFIG_OMAP_EHCI_TLL_MODE is not set +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_TT_NEWSCHED=y +CONFIG_USB_OXU210HP_HCD=y +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_OHCI_HCD is not set +# CONFIG_USB_U132_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_SOC=y + +# +# OMAP 343x high speed USB support +# +# CONFIG_USB_MUSB_HOST is not set +# CONFIG_USB_MUSB_PERIPHERAL is not set +CONFIG_USB_MUSB_OTG=y +CONFIG_USB_GADGET_MUSB_HDRC=y +CONFIG_USB_MUSB_HDRC_HCD=y +# CONFIG_MUSB_PIO_ONLY is not set +CONFIG_USB_INVENTRA_DMA=y +# CONFIG_USB_TI_CPPI_DMA is not set +# CONFIG_USB_MUSB_DEBUG is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m +CONFIG_USB_WDM=m +CONFIG_USB_TMC=m + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed; +# + +# +# see USB_STORAGE Help for more information +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +CONFIG_USB_SERIAL=m +CONFIG_USB_EZUSB=y +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_AIRCABLE=m +CONFIG_USB_SERIAL_ARK3116=m +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_CP2101=m +CONFIG_USB_SERIAL_CYPRESS_M8=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_FUNSOFT=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_GARMIN=m +CONFIG_USB_SERIAL_IPW=m +CONFIG_USB_SERIAL_IUU=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KEYSPAN_MPR=y +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_MOS7720=m +CONFIG_USB_SERIAL_MOS7840=m +CONFIG_USB_SERIAL_MOTOROLA=m +CONFIG_USB_SERIAL_NAVMAN=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OTI6858=m +CONFIG_USB_SERIAL_SPCP8X5=m +CONFIG_USB_SERIAL_HP4X=m +CONFIG_USB_SERIAL_SAFE=m +# CONFIG_USB_SERIAL_SAFE_PADDED is not set +CONFIG_USB_SERIAL_SIEMENS_MPI=m +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_USB_SERIAL_TI=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_SERIAL_OPTICON=m +CONFIG_USB_SERIAL_DEBUG=m + +# +# USB Miscellaneous drivers +# +CONFIG_USB_EMI62=m +CONFIG_USB_EMI26=m +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +CONFIG_USB_LEGOTOWER=m +CONFIG_USB_LCD=m +CONFIG_USB_BERRY_CHARGE=m +CONFIG_USB_LED=m +CONFIG_USB_CYPRESS_CY7C63=m +CONFIG_USB_CYTHERM=m +CONFIG_USB_PHIDGET=m +CONFIG_USB_PHIDGETKIT=m +CONFIG_USB_PHIDGETMOTORCONTROL=m +CONFIG_USB_PHIDGETSERVO=m +CONFIG_USB_IDMOUSE=m +CONFIG_USB_FTDI_ELAN=m +# CONFIG_USB_APPLEDISPLAY is not set +CONFIG_USB_SISUSBVGA=m +CONFIG_USB_SISUSBVGA_CON=y +CONFIG_USB_LD=m +CONFIG_USB_TRANCEVIBRATOR=m +# CONFIG_USB_IOWARRIOR is not set +CONFIG_USB_TEST=m +# CONFIG_USB_ISIGHTFW is not set +CONFIG_USB_VST=m +CONFIG_USB_ATM=m +CONFIG_USB_SPEEDTOUCH=m +CONFIG_USB_CXACRU=m +CONFIG_USB_UEAGLEATM=m +CONFIG_USB_XUSBATM=m +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_DEBUG_FS=y +CONFIG_USB_GADGET_VBUS_DRAW=2 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_AT91 is not set +# CONFIG_USB_GADGET_ATMEL_USBA is not set +# CONFIG_USB_GADGET_FSL_USB2 is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_PXA25X is not set +# CONFIG_USB_GADGET_PXA27X is not set +# CONFIG_USB_GADGET_S3C2410 is not set +# CONFIG_USB_GADGET_IMX is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_FSL_QE is not set +# CONFIG_USB_GADGET_CI13XXX is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +CONFIG_USB_ZERO=m +CONFIG_USB_ZERO_HNPTEST=y +CONFIG_USB_ETH=m +CONFIG_USB_ETH_RNDIS=y +CONFIG_USB_GADGETFS=m +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_G_SERIAL=m +CONFIG_USB_MIDI_GADGET=m +CONFIG_USB_G_PRINTER=m +CONFIG_USB_CDC_COMPOSITE=m + +# +# OTG and related infrastructure +# +CONFIG_USB_OTG_UTILS=y +CONFIG_USB_GPIO_VBUS=y +# CONFIG_ISP1301_OMAP is not set +CONFIG_TWL4030_USB=y +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y +CONFIG_SDIO_UART=y +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_OMAP_HS=y +CONFIG_MMC_SPI=m +# CONFIG_MEMSTICK is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_OMAP_DEBUG is not set +CONFIG_LEDS_OMAP=y +# CONFIG_LEDS_OMAP_PWM is not set +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_PCA955X is not set + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=m +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_BACKLIGHT=m +CONFIG_LEDS_TRIGGER_DEFAULT_ON=m +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +CONFIG_RTC_DRV_TWL4030=y +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_DMADEVICES is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +CONFIG_REGULATOR_TWL4030=y +CONFIG_UIO=m +CONFIG_UIO_PDRV=m +CONFIG_UIO_PDRV_GENIRQ=m +# CONFIG_UIO_SMX is not set +# CONFIG_UIO_SERCOS3 is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_MEILHAUS is not set +# CONFIG_USB_IP_COMMON is not set +CONFIG_W35UND=m +CONFIG_PRISM2_USB=m +# CONFIG_ECHO is not set +CONFIG_USB_ATMEL=m +# CONFIG_AGNX is not set +CONFIG_OTUS=m +# CONFIG_COMEDI is not set +# CONFIG_ASUS_OLED is not set +# CONFIG_INPUT_MIMIO is not set +# CONFIG_TRANZPORT is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=m +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +# CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION is not set +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_GPIO=m +CONFIG_ANDROID_LOW_MEMORY_KILLER=y + +# +# CBUS support +# +# CONFIG_CBUS is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_FS_XATTR is not set +CONFIG_EXT4_FS=m +# CONFIG_EXT4DEV_COMPAT is not set +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_JBD2=m +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=m +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +CONFIG_REISERFS_PROC_INFO=y +CONFIG_REISERFS_FS_XATTR=y +# CONFIG_REISERFS_FS_POSIX_ACL is not set +# CONFIG_REISERFS_FS_SECURITY is not set +CONFIG_JFS_FS=m +# CONFIG_JFS_POSIX_ACL is not set +# CONFIG_JFS_SECURITY is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_FILE_LOCKING=y +CONFIG_XFS_FS=m +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_POSIX_ACL is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_DEBUG is not set +CONFIG_GFS2_FS=m +CONFIG_GFS2_FS_LOCKING_DLM=m +CONFIG_OCFS2_FS=m +CONFIG_OCFS2_FS_O2CB=m +CONFIG_OCFS2_FS_USERSPACE_CLUSTER=m +CONFIG_OCFS2_FS_STATS=y +CONFIG_OCFS2_DEBUG_MASKLOG=y +# CONFIG_OCFS2_DEBUG_FS is not set +# CONFIG_OCFS2_FS_POSIX_ACL is not set +CONFIG_BTRFS_FS=m +# CONFIG_BTRFS_FS_POSIX_ACL is not set +CONFIG_DNOTIFY=y +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +CONFIG_QUOTA=y +# CONFIG_QUOTA_NETLINK_INTERFACE is not set +CONFIG_PRINT_QUOTA_WARNING=y +CONFIG_QUOTA_TREE=y +# CONFIG_QFMT_V1 is not set +CONFIG_QFMT_V2=y +CONFIG_QUOTACTL=y +# CONFIG_AUTOFS_FS is not set +CONFIG_AUTOFS4_FS=m +CONFIG_FUSE_FS=m + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +CONFIG_NTFS_FS=m +# CONFIG_NTFS_DEBUG is not set +CONFIG_NTFS_RW=y + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_CONFIGFS_FS=m +CONFIG_MISC_FILESYSTEMS=y +CONFIG_ADFS_FS=m +# CONFIG_ADFS_FS_RW is not set +CONFIG_AFFS_FS=m +# CONFIG_ECRYPT_FS is not set +CONFIG_HFS_FS=m +CONFIG_HFSPLUS_FS=m +CONFIG_BEFS_FS=m +# CONFIG_BEFS_DEBUG is not set +CONFIG_BFS_FS=m +CONFIG_EFS_FS=m +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +CONFIG_JFFS2_SUMMARY=y +CONFIG_JFFS2_FS_XATTR=y +CONFIG_JFFS2_FS_POSIX_ACL=y +CONFIG_JFFS2_FS_SECURITY=y +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_LZO=y +CONFIG_JFFS2_RTIME=y +CONFIG_JFFS2_RUBIN=y +# CONFIG_JFFS2_CMODE_NONE is not set +# CONFIG_JFFS2_CMODE_PRIORITY is not set +# CONFIG_JFFS2_CMODE_SIZE is not set +CONFIG_JFFS2_CMODE_FAVOURLZO=y +CONFIG_UBIFS_FS=y +CONFIG_UBIFS_FS_XATTR=y +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_UBIFS_FS_DEBUG is not set +CONFIG_CRAMFS=m +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +CONFIG_VXFS_FS=m +CONFIG_MINIX_FS=m +CONFIG_OMFS_FS=m +CONFIG_HPFS_FS=m +CONFIG_QNX4FS_FS=m +CONFIG_ROMFS_FS=m +CONFIG_SYSV_FS=m +CONFIG_UFS_FS=m +# CONFIG_UFS_FS_WRITE is not set +# CONFIG_UFS_DEBUG is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +CONFIG_NFS_V4=y +CONFIG_ROOT_NFS=y +CONFIG_NFSD=m +CONFIG_NFSD_V2_ACL=y +CONFIG_NFSD_V3=y +CONFIG_NFSD_V3_ACL=y +CONFIG_NFSD_V4=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_EXPORTFS=m +CONFIG_NFS_ACL_SUPPORT=m +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +# CONFIG_SUNRPC_REGISTER_V4 is not set +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +CONFIG_SMB_FS=m +# CONFIG_SMB_NLS_DEFAULT is not set +CONFIG_CIFS=m +CONFIG_CIFS_STATS=y +CONFIG_CIFS_STATS2=y +# CONFIG_CIFS_WEAK_PW_HASH is not set +# CONFIG_CIFS_UPCALL is not set +# CONFIG_CIFS_XATTR is not set +# CONFIG_CIFS_DEBUG2 is not set +CONFIG_CIFS_EXPERIMENTAL=y +# CONFIG_CIFS_DFS_UPCALL is not set +CONFIG_NCP_FS=m +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +CONFIG_CODA_FS=m +CONFIG_AFS_FS=m +# CONFIG_AFS_DEBUG is not set +CONFIG_9P_FS=m + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +CONFIG_MAC_PARTITION=y +CONFIG_MSDOS_PARTITION=y +CONFIG_BSD_DISKLABEL=y +CONFIG_MINIX_SUBPARTITION=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_UNIXWARE_DISKLABEL=y +CONFIG_LDM_PARTITION=y +CONFIG_LDM_DEBUG=y +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +CONFIG_EFI_PARTITION=y +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_UTF8=y +CONFIG_DLM=m +# CONFIG_DLM_DEBUG is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +CONFIG_DEBUG_MUTEXES=y +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +CONFIG_NOP_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_RING_BUFFER=y +CONFIG_TRACING=y + +# +# Tracers +# +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_CONTEXT_SWITCH_TRACER is not set +# CONFIG_BOOT_TRACER is not set +# CONFIG_TRACE_BRANCH_PROFILING is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_DYNAMIC_PRINTK_DEBUG is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +CONFIG_KEYS=y +# CONFIG_KEYS_DEBUG_PROC_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_XOR_BLOCKS=m +CONFIG_ASYNC_CORE=m +CONFIG_ASYNC_MEMCPY=m +CONFIG_ASYNC_XOR=m +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_FIPS=y +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=m +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=m +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_GF128MUL=m +CONFIG_CRYPTO_NULL=m +CONFIG_CRYPTO_CRYPTD=m +CONFIG_CRYPTO_AUTHENC=m +CONFIG_CRYPTO_TEST=m + +# +# Authenticated Encryption with Associated Data +# +CONFIG_CRYPTO_CCM=m +CONFIG_CRYPTO_GCM=m +CONFIG_CRYPTO_SEQIV=m + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CTR=m +CONFIG_CRYPTO_CTS=m +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_LRW=m +CONFIG_CRYPTO_PCBC=m +CONFIG_CRYPTO_XTS=m + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=m +CONFIG_CRYPTO_XCBC=m + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_MICHAEL_MIC=y +CONFIG_CRYPTO_RMD128=m +CONFIG_CRYPTO_RMD160=m +CONFIG_CRYPTO_RMD256=m +CONFIG_CRYPTO_RMD320=m +CONFIG_CRYPTO_SHA1=m +CONFIG_CRYPTO_SHA256=m +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_TGR192=m +CONFIG_CRYPTO_WP512=m + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_ANUBIS=m +CONFIG_CRYPTO_ARC4=y +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_CAMELLIA=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_FCRYPT=m +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_SALSA20=m +CONFIG_CRYPTO_SEED=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_TWOFISH_COMMON=m + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_LZO=y + +# +# Random Number Generation +# +CONFIG_CRYPTO_ANSI_CPRNG=m +CONFIG_CRYPTO_HW=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +CONFIG_CRC_T10DIF=y +CONFIG_CRC_ITU_T=y +CONFIG_CRC32=y +CONFIG_CRC7=y +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=m +CONFIG_TEXTSEARCH_BM=m +CONFIG_TEXTSEARCH_FSM=m +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dspbridge.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dspbridge.patch new file mode 100644 index 0000000000..621ae93d3b --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dspbridge.patch @@ -0,0 +1,66715 @@ +From ea83ad971e877ef8e9b1304f2678469c2fc74c67 Mon Sep 17 00:00:00 2001 +From: Tim Yamin +Date: Sun, 26 Apr 2009 18:58:30 -0700 +Subject: [PATCH] Merge in tidspbridge from git://gitorious.org/tidspbridge/mainline.git + revisions fe30e75..20f5ca. + +Made a few changes so this compiles clean against a 2.6.29 non-PM +tree. + +Signed-off-by: Tim Yamin +--- + Documentation/tidspbridge/README | 70 + + arch/arm/Kconfig | 2 + + arch/arm/mach-omap2/Makefile | 2 + + arch/arm/mach-omap2/dspbridge.c | 74 + + arch/arm/mach-omap2/io.c | 3 + + arch/arm/plat-omap/devices.c | 28 + + arch/arm/plat-omap/include/dspbridge/_chnl_sm.h | 212 ++ + arch/arm/plat-omap/include/dspbridge/_dcd.h | 187 + + arch/arm/plat-omap/include/dspbridge/brddefs.h | 54 + + arch/arm/plat-omap/include/dspbridge/cfg.h | 339 ++ + arch/arm/plat-omap/include/dspbridge/cfgdefs.h | 126 + + arch/arm/plat-omap/include/dspbridge/chnl.h | 170 + + arch/arm/plat-omap/include/dspbridge/chnl_sm.h | 168 + + arch/arm/plat-omap/include/dspbridge/chnldefs.h | 92 + + arch/arm/plat-omap/include/dspbridge/chnlpriv.h | 136 + + arch/arm/plat-omap/include/dspbridge/clk.h | 155 + + arch/arm/plat-omap/include/dspbridge/cmm.h | 420 +++ + arch/arm/plat-omap/include/dspbridge/cmmdefs.h | 135 + + arch/arm/plat-omap/include/dspbridge/cod.h | 433 +++ + arch/arm/plat-omap/include/dspbridge/csl.h | 135 + + arch/arm/plat-omap/include/dspbridge/dbc.h | 66 + + arch/arm/plat-omap/include/dspbridge/dbdcd.h | 388 +++ + arch/arm/plat-omap/include/dspbridge/dbdcddef.h | 94 + + arch/arm/plat-omap/include/dspbridge/dbdefs.h | 580 ++++ + arch/arm/plat-omap/include/dspbridge/dbg.h | 110 + + arch/arm/plat-omap/include/dspbridge/dbl.h | 354 ++ + arch/arm/plat-omap/include/dspbridge/dbldefs.h | 155 + + arch/arm/plat-omap/include/dspbridge/dbll.h | 70 + + arch/arm/plat-omap/include/dspbridge/dblldefs.h | 509 +++ + arch/arm/plat-omap/include/dspbridge/dbof.h | 117 + + arch/arm/plat-omap/include/dspbridge/dbreg.h | 113 + + arch/arm/plat-omap/include/dspbridge/dbtype.h | 103 + + arch/arm/plat-omap/include/dspbridge/dehdefs.h | 42 + + arch/arm/plat-omap/include/dspbridge/dev.h | 785 +++++ + arch/arm/plat-omap/include/dspbridge/devdefs.h | 35 + + arch/arm/plat-omap/include/dspbridge/disp.h | 236 ++ + arch/arm/plat-omap/include/dspbridge/dispdefs.h | 45 + + arch/arm/plat-omap/include/dspbridge/dmm.h | 85 + + arch/arm/plat-omap/include/dspbridge/dpc.h | 167 + + arch/arm/plat-omap/include/dspbridge/drv.h | 449 +++ + arch/arm/plat-omap/include/dspbridge/drvdefs.h | 34 + + arch/arm/plat-omap/include/dspbridge/dspdrv.h | 106 + + .../plat-omap/include/dspbridge/dynamic_loader.h | 505 +++ + arch/arm/plat-omap/include/dspbridge/errbase.h | 509 +++ + arch/arm/plat-omap/include/dspbridge/gb.h | 85 + + arch/arm/plat-omap/include/dspbridge/getsection.h | 118 + + arch/arm/plat-omap/include/dspbridge/gh.h | 37 + + arch/arm/plat-omap/include/dspbridge/gs.h | 64 + + arch/arm/plat-omap/include/dspbridge/gt.h | 315 ++ + arch/arm/plat-omap/include/dspbridge/host_os.h | 96 + + arch/arm/plat-omap/include/dspbridge/io.h | 132 + + arch/arm/plat-omap/include/dspbridge/io_sm.h | 335 ++ + arch/arm/plat-omap/include/dspbridge/iodefs.h | 45 + + arch/arm/plat-omap/include/dspbridge/kfile.h | 216 ++ + arch/arm/plat-omap/include/dspbridge/ldr.h | 51 + + arch/arm/plat-omap/include/dspbridge/list.h | 296 ++ + arch/arm/plat-omap/include/dspbridge/mbx_sh.h | 213 ++ + arch/arm/plat-omap/include/dspbridge/mem.h | 340 ++ + arch/arm/plat-omap/include/dspbridge/memdefs.h | 52 + + arch/arm/plat-omap/include/dspbridge/mgr.h | 234 ++ + arch/arm/plat-omap/include/dspbridge/mgrpriv.h | 55 + + arch/arm/plat-omap/include/dspbridge/msg.h | 106 + + arch/arm/plat-omap/include/dspbridge/msgdefs.h | 43 + + arch/arm/plat-omap/include/dspbridge/nldr.h | 81 + + arch/arm/plat-omap/include/dspbridge/nldrdefs.h | 307 ++ + arch/arm/plat-omap/include/dspbridge/node.h | 619 ++++ + arch/arm/plat-omap/include/dspbridge/nodedefs.h | 40 + + arch/arm/plat-omap/include/dspbridge/nodepriv.h | 202 ++ + arch/arm/plat-omap/include/dspbridge/ntfy.h | 146 + + arch/arm/plat-omap/include/dspbridge/proc.h | 648 ++++ + arch/arm/plat-omap/include/dspbridge/procpriv.h | 35 + + arch/arm/plat-omap/include/dspbridge/pwr.h | 129 + + arch/arm/plat-omap/include/dspbridge/pwr_sh.h | 41 + + arch/arm/plat-omap/include/dspbridge/reg.h | 257 ++ + .../plat-omap/include/dspbridge/resourcecleanup.h | 88 + + arch/arm/plat-omap/include/dspbridge/rmm.h | 199 ++ + arch/arm/plat-omap/include/dspbridge/rms_sh.h | 125 + + arch/arm/plat-omap/include/dspbridge/rmstypes.h | 40 + + arch/arm/plat-omap/include/dspbridge/services.h | 63 + + arch/arm/plat-omap/include/dspbridge/std.h | 143 + + arch/arm/plat-omap/include/dspbridge/strm.h | 441 +++ + arch/arm/plat-omap/include/dspbridge/strmdefs.h | 57 + + arch/arm/plat-omap/include/dspbridge/sync.h | 340 ++ + arch/arm/plat-omap/include/dspbridge/util.h | 122 + + arch/arm/plat-omap/include/dspbridge/utildefs.h | 51 + + arch/arm/plat-omap/include/dspbridge/uuidutil.h | 74 + + arch/arm/plat-omap/include/dspbridge/wcd.h | 61 + + arch/arm/plat-omap/include/dspbridge/wcdioctl.h | 519 +++ + arch/arm/plat-omap/include/dspbridge/wmd.h | 1193 +++++++ + arch/arm/plat-omap/include/dspbridge/wmdchnl.h | 90 + + arch/arm/plat-omap/include/dspbridge/wmddeh.h | 64 + + arch/arm/plat-omap/include/dspbridge/wmdio.h | 53 + + arch/arm/plat-omap/include/dspbridge/wmdioctl.h | 91 + + arch/arm/plat-omap/include/dspbridge/wmdmsg.h | 70 + + drivers/Makefile | 1 + + drivers/dsp/bridge/Kbuild | 39 + + drivers/dsp/bridge/Kconfig | 36 + + drivers/dsp/bridge/dynload/cload.c | 1854 ++++++++++ + drivers/dsp/bridge/dynload/dlclasses_hdr.h | 41 + + drivers/dsp/bridge/dynload/dload_internal.h | 237 ++ + drivers/dsp/bridge/dynload/doff.h | 347 ++ + drivers/dsp/bridge/dynload/getsection.c | 412 +++ + drivers/dsp/bridge/dynload/header.h | 59 + + drivers/dsp/bridge/dynload/module_list.h | 161 + + drivers/dsp/bridge/dynload/params.h | 231 ++ + drivers/dsp/bridge/dynload/reloc.c | 425 +++ + drivers/dsp/bridge/dynload/reloc_table.h | 102 + + drivers/dsp/bridge/dynload/reloc_table_c6000.c | 258 ++ + drivers/dsp/bridge/gen/_gt_para.c | 107 + + drivers/dsp/bridge/gen/gb.c | 182 + + drivers/dsp/bridge/gen/gh.c | 191 ++ + drivers/dsp/bridge/gen/gs.c | 108 + + drivers/dsp/bridge/gen/gt.c | 348 ++ + drivers/dsp/bridge/gen/uuidutil.c | 238 ++ + drivers/dsp/bridge/hw/EasiGlobal.h | 42 + + drivers/dsp/bridge/hw/GlobalTypes.h | 325 ++ + drivers/dsp/bridge/hw/IPIAccInt.h | 41 + + drivers/dsp/bridge/hw/IVA2RegAcM.h | 28 + + drivers/dsp/bridge/hw/MLBAccInt.h | 132 + + drivers/dsp/bridge/hw/MLBRegAcM.h | 200 ++ + drivers/dsp/bridge/hw/MMUAccInt.h | 76 + + drivers/dsp/bridge/hw/MMURegAcM.h | 253 ++ + drivers/dsp/bridge/hw/PRCMAccInt.h | 300 ++ + drivers/dsp/bridge/hw/PRCMRegAcM.h | 669 ++++ + drivers/dsp/bridge/hw/hw_defs.h | 73 + + drivers/dsp/bridge/hw/hw_dspssC64P.c | 55 + + drivers/dsp/bridge/hw/hw_dspssC64P.h | 48 + + drivers/dsp/bridge/hw/hw_mbox.c | 244 ++ + drivers/dsp/bridge/hw/hw_mbox.h | 323 ++ + drivers/dsp/bridge/hw/hw_mmu.c | 598 ++++ + drivers/dsp/bridge/hw/hw_mmu.h | 177 + + drivers/dsp/bridge/hw/hw_prcm.c | 167 + + drivers/dsp/bridge/hw/hw_prcm.h | 168 + + drivers/dsp/bridge/pmgr/chnl.c | 260 ++ + drivers/dsp/bridge/pmgr/chnlobj.h | 71 + + drivers/dsp/bridge/pmgr/cmm.c | 1291 +++++++ + drivers/dsp/bridge/pmgr/cod.c | 683 ++++ + drivers/dsp/bridge/pmgr/dbl.c | 1385 ++++++++ + drivers/dsp/bridge/pmgr/dbll.c | 1564 +++++++++ + drivers/dsp/bridge/pmgr/dev.c | 1476 ++++++++ + drivers/dsp/bridge/pmgr/dmm.c | 692 ++++ + drivers/dsp/bridge/pmgr/io.c | 205 ++ + drivers/dsp/bridge/pmgr/ioobj.h | 52 + + drivers/dsp/bridge/pmgr/msg.c | 173 + + drivers/dsp/bridge/pmgr/msgobj.h | 52 + + drivers/dsp/bridge/pmgr/wcd.c | 1647 +++++++++ + drivers/dsp/bridge/rmgr/dbdcd.c | 1573 +++++++++ + drivers/dsp/bridge/rmgr/disp.c | 916 +++++ + drivers/dsp/bridge/rmgr/drv.c | 1893 +++++++++++ + drivers/dsp/bridge/rmgr/drv_interface.c | 777 +++++ + drivers/dsp/bridge/rmgr/drv_interface.h | 40 + + drivers/dsp/bridge/rmgr/dspdrv.c | 276 ++ + drivers/dsp/bridge/rmgr/mgr.c | 491 +++ + drivers/dsp/bridge/rmgr/nldr.c | 1967 +++++++++++ + drivers/dsp/bridge/rmgr/node.c | 3544 ++++++++++++++++++++ + drivers/dsp/bridge/rmgr/proc.c | 1985 +++++++++++ + drivers/dsp/bridge/rmgr/pwr.c | 184 + + drivers/dsp/bridge/rmgr/rmm.c | 604 ++++ + drivers/dsp/bridge/rmgr/strm.c | 1066 ++++++ + drivers/dsp/bridge/services/cfg.c | 483 +++ + drivers/dsp/bridge/services/clk.c | 375 +++ + drivers/dsp/bridge/services/csl.c | 173 + + drivers/dsp/bridge/services/dbg.c | 119 + + drivers/dsp/bridge/services/dpc.c | 274 ++ + drivers/dsp/bridge/services/kfile.c | 338 ++ + drivers/dsp/bridge/services/list.c | 285 ++ + drivers/dsp/bridge/services/mem.c | 599 ++++ + drivers/dsp/bridge/services/ntfy.c | 329 ++ + drivers/dsp/bridge/services/reg.c | 196 ++ + drivers/dsp/bridge/services/regsup.c | 368 ++ + drivers/dsp/bridge/services/regsup.h | 58 + + drivers/dsp/bridge/services/services.c | 193 ++ + drivers/dsp/bridge/services/sync.c | 602 ++++ + drivers/dsp/bridge/wmd/_cmm.h | 59 + + drivers/dsp/bridge/wmd/_deh.h | 46 + + drivers/dsp/bridge/wmd/_msg_sm.h | 158 + + drivers/dsp/bridge/wmd/_tiomap.h | 384 +++ + drivers/dsp/bridge/wmd/_tiomap_mmu.h | 53 + + drivers/dsp/bridge/wmd/_tiomap_pwr.h | 102 + + drivers/dsp/bridge/wmd/_tiomap_util.h | 46 + + drivers/dsp/bridge/wmd/chnl_sm.c | 1100 ++++++ + drivers/dsp/bridge/wmd/io_sm.c | 2011 +++++++++++ + drivers/dsp/bridge/wmd/mmu_fault.c | 172 + + drivers/dsp/bridge/wmd/mmu_fault.h | 45 + + drivers/dsp/bridge/wmd/msg_sm.c | 643 ++++ + drivers/dsp/bridge/wmd/tiomap3430.c | 2149 ++++++++++++ + drivers/dsp/bridge/wmd/tiomap3430_pwr.c | 731 ++++ + drivers/dsp/bridge/wmd/tiomap_io.c | 427 +++ + drivers/dsp/bridge/wmd/tiomap_io.h | 112 + + drivers/dsp/bridge/wmd/tiomap_sm.c | 195 ++ + drivers/dsp/bridge/wmd/ue_deh.c | 329 ++ + 191 files changed, 65137 insertions(+), 0 deletions(-) + create mode 100644 Documentation/tidspbridge/README + create mode 100644 arch/arm/mach-omap2/dspbridge.c + create mode 100644 arch/arm/plat-omap/include/dspbridge/_chnl_sm.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/_dcd.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/brddefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/cfg.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/cfgdefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/chnl.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/chnl_sm.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/chnldefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/chnlpriv.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/clk.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/cmm.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/cmmdefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/cod.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/csl.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dbc.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dbdcd.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dbdcddef.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dbdefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dbg.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dbl.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dbldefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dbll.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dblldefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dbof.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dbreg.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dbtype.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dehdefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dev.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/devdefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/disp.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dispdefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dmm.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dpc.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/drv.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/drvdefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dspdrv.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/dynamic_loader.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/errbase.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/gb.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/getsection.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/gh.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/gs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/gt.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/host_os.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/io.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/io_sm.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/iodefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/kfile.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/ldr.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/list.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/mbx_sh.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/mem.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/memdefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/mgr.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/mgrpriv.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/msg.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/msgdefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/nldr.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/nldrdefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/node.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/nodedefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/nodepriv.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/ntfy.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/proc.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/procpriv.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/pwr.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/pwr_sh.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/reg.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/resourcecleanup.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/rmm.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/rms_sh.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/rmstypes.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/services.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/std.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/strm.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/strmdefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/sync.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/util.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/utildefs.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/uuidutil.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/wcd.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/wcdioctl.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/wmd.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/wmdchnl.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/wmddeh.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/wmdio.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/wmdioctl.h + create mode 100644 arch/arm/plat-omap/include/dspbridge/wmdmsg.h + create mode 100644 drivers/dsp/bridge/Kbuild + create mode 100644 drivers/dsp/bridge/Kconfig + create mode 100644 drivers/dsp/bridge/dynload/cload.c + create mode 100644 drivers/dsp/bridge/dynload/dlclasses_hdr.h + create mode 100644 drivers/dsp/bridge/dynload/dload_internal.h + create mode 100644 drivers/dsp/bridge/dynload/doff.h + create mode 100644 drivers/dsp/bridge/dynload/getsection.c + create mode 100644 drivers/dsp/bridge/dynload/header.h + create mode 100644 drivers/dsp/bridge/dynload/module_list.h + create mode 100644 drivers/dsp/bridge/dynload/params.h + create mode 100644 drivers/dsp/bridge/dynload/reloc.c + create mode 100644 drivers/dsp/bridge/dynload/reloc_table.h + create mode 100644 drivers/dsp/bridge/dynload/reloc_table_c6000.c + create mode 100644 drivers/dsp/bridge/gen/_gt_para.c + create mode 100644 drivers/dsp/bridge/gen/gb.c + create mode 100644 drivers/dsp/bridge/gen/gh.c + create mode 100644 drivers/dsp/bridge/gen/gs.c + create mode 100644 drivers/dsp/bridge/gen/gt.c + create mode 100644 drivers/dsp/bridge/gen/uuidutil.c + create mode 100644 drivers/dsp/bridge/hw/EasiGlobal.h + create mode 100644 drivers/dsp/bridge/hw/GlobalTypes.h + create mode 100644 drivers/dsp/bridge/hw/IPIAccInt.h + create mode 100644 drivers/dsp/bridge/hw/IVA2RegAcM.h + create mode 100644 drivers/dsp/bridge/hw/MLBAccInt.h + create mode 100644 drivers/dsp/bridge/hw/MLBRegAcM.h + create mode 100644 drivers/dsp/bridge/hw/MMUAccInt.h + create mode 100644 drivers/dsp/bridge/hw/MMURegAcM.h + create mode 100644 drivers/dsp/bridge/hw/PRCMAccInt.h + create mode 100644 drivers/dsp/bridge/hw/PRCMRegAcM.h + create mode 100644 drivers/dsp/bridge/hw/hw_defs.h + create mode 100644 drivers/dsp/bridge/hw/hw_dspssC64P.c + create mode 100644 drivers/dsp/bridge/hw/hw_dspssC64P.h + create mode 100644 drivers/dsp/bridge/hw/hw_mbox.c + create mode 100644 drivers/dsp/bridge/hw/hw_mbox.h + create mode 100644 drivers/dsp/bridge/hw/hw_mmu.c + create mode 100644 drivers/dsp/bridge/hw/hw_mmu.h + create mode 100644 drivers/dsp/bridge/hw/hw_prcm.c + create mode 100644 drivers/dsp/bridge/hw/hw_prcm.h + create mode 100644 drivers/dsp/bridge/pmgr/chnl.c + create mode 100644 drivers/dsp/bridge/pmgr/chnlobj.h + create mode 100644 drivers/dsp/bridge/pmgr/cmm.c + create mode 100644 drivers/dsp/bridge/pmgr/cod.c + create mode 100644 drivers/dsp/bridge/pmgr/dbl.c + create mode 100644 drivers/dsp/bridge/pmgr/dbll.c + create mode 100644 drivers/dsp/bridge/pmgr/dev.c + create mode 100644 drivers/dsp/bridge/pmgr/dmm.c + create mode 100644 drivers/dsp/bridge/pmgr/io.c + create mode 100644 drivers/dsp/bridge/pmgr/ioobj.h + create mode 100644 drivers/dsp/bridge/pmgr/msg.c + create mode 100644 drivers/dsp/bridge/pmgr/msgobj.h + create mode 100644 drivers/dsp/bridge/pmgr/wcd.c + create mode 100644 drivers/dsp/bridge/rmgr/dbdcd.c + create mode 100644 drivers/dsp/bridge/rmgr/disp.c + create mode 100644 drivers/dsp/bridge/rmgr/drv.c + create mode 100644 drivers/dsp/bridge/rmgr/drv_interface.c + create mode 100644 drivers/dsp/bridge/rmgr/drv_interface.h + create mode 100644 drivers/dsp/bridge/rmgr/dspdrv.c + create mode 100644 drivers/dsp/bridge/rmgr/mgr.c + create mode 100644 drivers/dsp/bridge/rmgr/nldr.c + create mode 100644 drivers/dsp/bridge/rmgr/node.c + create mode 100644 drivers/dsp/bridge/rmgr/proc.c + create mode 100644 drivers/dsp/bridge/rmgr/pwr.c + create mode 100644 drivers/dsp/bridge/rmgr/rmm.c + create mode 100644 drivers/dsp/bridge/rmgr/strm.c + create mode 100644 drivers/dsp/bridge/services/cfg.c + create mode 100644 drivers/dsp/bridge/services/clk.c + create mode 100644 drivers/dsp/bridge/services/csl.c + create mode 100644 drivers/dsp/bridge/services/dbg.c + create mode 100644 drivers/dsp/bridge/services/dpc.c + create mode 100644 drivers/dsp/bridge/services/kfile.c + create mode 100644 drivers/dsp/bridge/services/list.c + create mode 100644 drivers/dsp/bridge/services/mem.c + create mode 100644 drivers/dsp/bridge/services/ntfy.c + create mode 100644 drivers/dsp/bridge/services/reg.c + create mode 100644 drivers/dsp/bridge/services/regsup.c + create mode 100644 drivers/dsp/bridge/services/regsup.h + create mode 100644 drivers/dsp/bridge/services/services.c + create mode 100644 drivers/dsp/bridge/services/sync.c + create mode 100644 drivers/dsp/bridge/wmd/_cmm.h + create mode 100644 drivers/dsp/bridge/wmd/_deh.h + create mode 100644 drivers/dsp/bridge/wmd/_msg_sm.h + create mode 100644 drivers/dsp/bridge/wmd/_tiomap.h + create mode 100644 drivers/dsp/bridge/wmd/_tiomap_mmu.h + create mode 100644 drivers/dsp/bridge/wmd/_tiomap_pwr.h + create mode 100644 drivers/dsp/bridge/wmd/_tiomap_util.h + create mode 100644 drivers/dsp/bridge/wmd/chnl_sm.c + create mode 100644 drivers/dsp/bridge/wmd/io_sm.c + create mode 100644 drivers/dsp/bridge/wmd/mmu_fault.c + create mode 100644 drivers/dsp/bridge/wmd/mmu_fault.h + create mode 100644 drivers/dsp/bridge/wmd/msg_sm.c + create mode 100644 drivers/dsp/bridge/wmd/tiomap3430.c + create mode 100644 drivers/dsp/bridge/wmd/tiomap3430_pwr.c + create mode 100644 drivers/dsp/bridge/wmd/tiomap_io.c + create mode 100644 drivers/dsp/bridge/wmd/tiomap_io.h + create mode 100644 drivers/dsp/bridge/wmd/tiomap_sm.c + create mode 100644 drivers/dsp/bridge/wmd/ue_deh.c + +diff --git a/Documentation/tidspbridge/README b/Documentation/tidspbridge/README +new file mode 100644 +index 0000000..df6d371 +--- /dev/null ++++ b/Documentation/tidspbridge/README +@@ -0,0 +1,70 @@ ++ Linux DSP/BIOS Bridge release ++ ++DSP/BIOS Bridge overview ++======================== ++ ++DSP/BIOS Bridge is designed for platforms that contain a GPP and one or more ++attached DSPs. The GPP is considered the master or "host" processor, and the ++attached DSPs are processing resources that can be utilized by applications ++and drivers running on the GPP. ++ ++The abstraction that DSP/BIOS Bridge supplies, is a direct link between a GPP ++program and a DSP task. This communication link is partitioned into two ++types of sub-links: messaging (short, fixed-length packets) and data ++streaming (multiple, large buffers). Each sub-link operates independently, ++and features in-order delivery of data, meaning that messages are delivered ++in the order they were submitted to the message link, and stream buffers are ++delivered in the order they were submitted to the stream link. ++ ++In addition, a GPP client can specify what inputs and outputs a DSP task ++uses. DSP tasks typically use message objects for passing control and status ++information and stream objects for efficient streaming of real-time data. ++ ++GPP Software Architecture ++========================= ++ ++A GPP application communicates with its associated DSP task running on the ++DSP subsystem using the DSP/BIOS Bridge API. For example, a GPP audio ++application can use the API to pass messages to a DSP task that is managing ++data flowing from analog-to-digital converters (ADCs) to digital-to-analog ++converters (DACs). ++ ++From the perspective of the GPP OS, the DSP is treated as just another ++peripheral device. Most high level GPP OS typically support a device driver ++model, whereby applications can safely access and share a hardware peripheral ++through standard driver interfaces. Therefore, to allow multiple GPP ++applications to share access to the DSP, the GPP side of DSP/BIOS Bridge ++implements a device driver for the DSP. ++ ++Since driver interfaces are not always standard across GPP OS, and to provide ++some level of interoperability of application code using DSP/BIOS Bridge ++between GPP OS, DSP/BIOS Bridge provides a standard library of APIs which ++wrap calls into the device driver. So, rather than calling GPP OS specific ++driver interfaces, applications (and even other device drivers) can use the ++standard API library directly. ++ ++DSP Software Architecture ++========================= ++ ++For DSP/BIOS, DSP/BIOS Bridge adds a device-independent streaming I/O (STRM) ++interface, a messaging interface (NODE), and a Resource Manager (RM) Server. ++The RM Server runs as a task of DSP/BIOS and is subservient to commands ++and queries from the GPP. It executes commands to start and stop DSP signal ++processing nodes in response to GPP programs making requests through the ++(GPP-side) API. ++ ++DSP tasks started by the RM Server are similar to any other DSP task with two ++important differences: they must follow a specific task model consisting of ++three C-callable functions (node create, execute, and delete), with specific ++sets of arguments, and they have a pre-defined task environment established ++by the RM Server. ++ ++Tasks started by the RM Server communicate using the STRM and NODE interfaces ++and act as servers for their corresponding GPP clients, performing signal ++processing functions as requested by messages sent by their GPP client. ++Typically, a DSP task moves data from source devices to sink devices using ++device independent I/O streams, performing application-specific processing ++and transformations on the data while it is moved. For example, an audio ++task might perform audio decompression (ADPCM, MPEG, CELP) on data received ++from a GPP audio driver and then send the decompressed linear samples to a ++digital-to-analog converter. +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index e7fb201..2060772 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -1331,6 +1331,8 @@ if ARCH_OMAP + source "drivers/cbus/Kconfig" + endif + ++source "drivers/dsp/bridge/Kconfig" ++ + endmenu + + source "fs/Kconfig" +diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile +index 9b270d8..e52e01e 100644 +--- a/arch/arm/mach-omap2/Makefile ++++ b/arch/arm/mach-omap2/Makefile +@@ -34,6 +34,8 @@ obj-$(CONFIG_OMAP_SMARTREFLEX) += smartreflex.o + obj-$(CONFIG_ARCH_OMAP2) += clock24xx.o + obj-$(CONFIG_ARCH_OMAP3) += clock34xx.o + ++obj-$(CONFIG_MPU_BRIDGE) += dspbridge.o ++ + # DSP + obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox_mach.o + mailbox_mach-objs := mailbox.o +diff --git a/arch/arm/mach-omap2/dspbridge.c b/arch/arm/mach-omap2/dspbridge.c +new file mode 100644 +index 0000000..221e8ff +--- /dev/null ++++ b/arch/arm/mach-omap2/dspbridge.c +@@ -0,0 +1,74 @@ ++/* ++ * TI's dspbridge platform device registration ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * Copyright (C) 2009 Nokia Corporation ++ * ++ * Written by Hiroshi DOYU ++ * ++ * 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 ++#include ++ ++static struct platform_device *dspbridge_pdev; ++ ++#ifdef CONFIG_BRIDGE_DVFS ++#include ++static struct dspbridge_platform_data dspbridge_pdata __initdata = { ++ .dsp_set_min_opp = omap_pm_dsp_set_min_opp, ++ .dsp_get_opp = omap_pm_dsp_get_opp, ++ .cpu_set_freq = omap_pm_cpu_set_freq, ++ .cpu_get_freq = omap_pm_cpu_get_freq, ++}; ++#else ++static struct dspbridge_platform_data dspbridge_pdata; ++#endif ++ ++static int __init dspbridge_init(void) ++{ ++ struct platform_device *pdev; ++ int err = -ENOMEM; ++ struct dspbridge_platform_data *pdata = &dspbridge_pdata; ++ ++ pdata->phys_mempool_base = dspbridge_get_mempool_base(); ++ ++ if (pdata->phys_mempool_base) { ++ pdata->phys_mempool_size = CONFIG_BRIDGE_MEMPOOL_SIZE; ++ pr_info("%s: %x bytes @ %x\n", __func__, ++ pdata->phys_mempool_size, pdata->phys_mempool_base); ++ } ++ ++ pdev = platform_device_alloc("C6410", -1); ++ if (!pdev) ++ goto err_out; ++ ++ err = platform_device_add_data(pdev, pdata, sizeof(*pdata)); ++ if (err) ++ goto err_out; ++ ++ err = platform_device_add(pdev); ++ if (err) ++ goto err_out; ++ ++ dspbridge_pdev = pdev; ++ return 0; ++ ++err_out: ++ platform_device_put(pdev); ++ return err; ++} ++module_init(dspbridge_init); ++ ++static void __exit dspbridge_exit(void) ++{ ++ platform_device_unregister(dspbridge_pdev); ++} ++module_exit(dspbridge_exit); ++ ++MODULE_AUTHOR("Hiroshi DOYU"); ++MODULE_DESCRIPTION("TI's dspbridge platform device registration"); ++MODULE_LICENSE("GPL v2"); +diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c +index a04e3ee..40488ae 100644 +--- a/arch/arm/mach-omap2/io.c ++++ b/arch/arm/mach-omap2/io.c +@@ -38,6 +38,8 @@ + #include + #include "clockdomains.h" + ++#include ++ + /* + * The machine specific code may provide the extra mapping besides the + * default mapping provided here. +@@ -192,6 +194,7 @@ void __init omap2_map_common_io(void) + omap2_check_revision(); + omap_sram_init(); + omapfb_reserve_sdram(); ++ dspbridge_reserve_sdram(); + } + + void __init omap2_init_common_hw(struct omap_sdrc_params *sp) +diff --git a/arch/arm/plat-omap/devices.c b/arch/arm/plat-omap/devices.c +index 6b742a8..e66dd8a 100644 +--- a/arch/arm/plat-omap/devices.c ++++ b/arch/arm/plat-omap/devices.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -89,6 +90,33 @@ EXPORT_SYMBOL(dsp_kfunc_device_register); + static inline void omap_init_dsp(void) { } + #endif /* CONFIG_OMAP_DSP */ + ++#if defined(CONFIG_MPU_BRIDGE) || defined(CONFIG_MPU_BRIDGE_MODULE) ++ ++static unsigned long dspbridge_phys_mempool_base; ++ ++void dspbridge_reserve_sdram(void) ++{ ++ void *va; ++ unsigned long size = CONFIG_BRIDGE_MEMPOOL_SIZE; ++ ++ if (!size) ++ return; ++ ++ va = __alloc_bootmem_nopanic(size, SZ_1M, 0); ++ if (!va) { ++ pr_err("%s: Failed to bootmem allocation(%lu bytes)\n", ++ __func__, size); ++ return; ++ } ++ dspbridge_phys_mempool_base = virt_to_phys(va); ++} ++ ++unsigned long dspbridge_get_mempool_base(void) ++{ ++ return dspbridge_phys_mempool_base; ++} ++EXPORT_SYMBOL(dspbridge_get_mempool_base); ++#endif + /*-------------------------------------------------------------------------*/ + #if defined(CONFIG_KEYBOARD_OMAP) || defined(CONFIG_KEYBOARD_OMAP_MODULE) + +diff --git a/arch/arm/plat-omap/include/dspbridge/_chnl_sm.h b/arch/arm/plat-omap/include/dspbridge/_chnl_sm.h +new file mode 100644 +index 0000000..28af799 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/_chnl_sm.h +@@ -0,0 +1,212 @@ ++/* ++ * _chnl_sm.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== _chnl_sm.h ======== ++ * Description: ++ * Private header file defining channel manager and channel objects for ++ * a shared memory channel driver. ++ * ++ * Public Functions: ++ * None. ++ * ++ * Notes: ++ * Shared between the modules implementing the shared memory channel class ++ * library. ++ * ++ *! Revision History: ++ *! ================ ++ *! 15-Oct-2002 kc Removed legacy PERF code. ++ *! 12-Jan-2002 ag Removed unused gppReqIO & ddmaChnlId DDMA fields. ++ *! Added zero-copy chnl descriptor array: zchnldesc. ++ *! 21-Dec-2001 ag Moved descPaGpp to private chnl obj from chnl descriptor. ++ *! 20-May-2001 ag/jeh Removed fShmSyms field from CHNL_MGR. ++ *! 04-Feb-2001 ag DSP-DMA support added. ++ *! 26-Oct-2000 jeh Added arg and resvd to SHM control structure. Added dwArg ++ *! to CHNL_IRP. ++ *! 16-Oct-2000 jeh Removed #ifdef DEBUG from around channel object's cIOCs ++ *! field, added cIOReqs. ++ *! 20-Jan-2000 ag: Incorporated code review comments. ++ *! 05-Jan-2000 ag: Text format cleanup. ++ *! 03-Nov-1999 ag: Added szEventName[] to CHNL object for name event support. ++ *! 02-Nov-1999 ag: _SHM_BEG & _END Syms from COFF now used for IO and SM CLASS. ++ *! 27-Oct-1999 jeh Define SHM structure to work for 16-bit targets. ++ *! 25-May-1999 jg: Added target side symbol names for share memory buffer ++ *! 03-Jan-1997 gp: Added fSharedIRQ field. ++ *! 22-Oct-1996 gp: Made dwProcessID a handle. ++ *! 09-Sep-1996 gp: Added dwProcessID field to CHNL_OBJECT. ++ *! 13-Aug-1996 gp: Created. ++ */ ++ ++#ifndef _CHNL_SM_ ++#define _CHNL_SM_ ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* ++ * These target side symbols define the beginning and ending addresses ++ * of shared memory buffer. They are defined in the *cfg.cmd file by ++ * cdb code. ++ */ ++#define CHNL_SHARED_BUFFER_BASE_SYM "_SHM_BEG" ++#define CHNL_SHARED_BUFFER_LIMIT_SYM "_SHM_END" ++#define BRIDGEINIT_BIOSGPTIMER "_BRIDGEINIT_BIOSGPTIMER" ++#define BRIDGEINIT_LOADMON_GPTIMER "_BRIDGEINIT_LOADMON_GPTIMER" ++ ++#ifndef _CHNL_WORDSIZE ++#define _CHNL_WORDSIZE 4 /* default _CHNL_WORDSIZE is 2 bytes/word */ ++#endif ++ ++#define MAXOPPS 16 ++ ++struct oppTableEntry { ++ u32 voltage; ++ u32 frequency; ++ u32 minFreq; ++ u32 maxFreq; ++} ; ++ ++struct oppStruct { ++ u32 currOppPt; ++ u32 numOppPts; ++ struct oppTableEntry oppPoint[MAXOPPS]; ++} ; ++ ++/* Request to MPU */ ++struct oppRqstStruct { ++ u32 rqstDspFreq; ++ u32 rqstOppPt; ++}; ++ ++/* Info to MPU */ ++struct loadMonStruct { ++ u32 currDspLoad; ++ u32 currDspFreq; ++ u32 predDspLoad; ++ u32 predDspFreq; ++}; ++ ++ enum SHM_DESCTYPE { ++ SHM_CURROPP = 0, ++ SHM_OPPINFO = 1, ++ SHM_GETOPP = 2, /* Get DSP requested OPP info */ ++ } ; ++ ++/* Structure in shared between DSP and PC for communication.*/ ++ struct SHM { ++ u32 dspFreeMask; /* Written by DSP, read by PC. */ ++ u32 hostFreeMask; /* Written by PC, read by DSP */ ++ ++ u32 inputFull; /* Input channel has unread data. */ ++ u32 inputId; /* Channel for which input is available. */ ++ u32 inputSize; /* Size of data block (in DSP words). */ ++ ++ u32 outputFull; /* Output channel has unread data. */ ++ u32 outputId; /* Channel for which output is available. */ ++ u32 outputSize; /* Size of data block (in DSP words). */ ++ ++ u32 arg; /* Arg for Issue/Reclaim (23 bits for 55x). */ ++ u32 resvd; /* Keep structure size even for 32-bit DSPs */ ++ ++ /* Operating Point structure */ ++ struct oppStruct oppTableStruct; ++ /* Operating Point Request structure */ ++ struct oppRqstStruct oppRequest; ++ /* load monitor information structure*/ ++ struct loadMonStruct loadMonInfo; ++ char dummy[184]; /* padding to 256 byte boundary */ ++ u32 shm_dbg_var[64]; /* shared memory debug variables */ ++ } ; ++ ++ /* Channel Manager: only one created per board: */ ++ struct CHNL_MGR { ++ u32 dwSignature; /* Used for object validation */ ++ /* Function interface to WMD */ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ struct IO_MGR *hIOMgr; /* IO manager */ ++ /* Device this board represents */ ++ struct DEV_OBJECT *hDevObject; ++ ++ /* These fields initialized in WMD_CHNL_Create(): */ ++ u32 dwOutputMask; /* Host output channels w/ full buffers */ ++ u32 dwLastOutput; /* Last output channel fired from DPC */ ++ /* Critical section object handle */ ++ struct SYNC_CSOBJECT *hCSObj; ++ u32 uWordSize; /* Size in bytes of DSP word */ ++ u32 cChannels; /* Total number of channels */ ++ u32 cOpenChannels; /* Total number of open channels */ ++ struct CHNL_OBJECT **apChannel; /* Array of channels */ ++ u32 dwType; /* Type of channel class library */ ++ /* If no SHM syms, return for CHNL_Open */ ++ DSP_STATUS chnlOpenStatus; ++ } ; ++ ++/* ++ * Channel: up to CHNL_MAXCHANNELS per board or if DSP-DMA supported then ++ * up to CHNL_MAXCHANNELS + CHNL_MAXDDMACHNLS per board. ++ */ ++ struct CHNL_OBJECT { ++ u32 dwSignature; /* Used for object validation */ ++ /* Pointer back to channel manager */ ++ struct CHNL_MGR *pChnlMgr; ++ u32 uId; /* Channel id */ ++ u32 dwState; /* Current channel state */ ++ u32 uMode; /* Chnl mode and attributes */ ++ /* Chnl I/O completion event (user mode) */ ++ HANDLE hUserEvent; ++ /* Abstract syncronization object */ ++ struct SYNC_OBJECT *hSyncEvent; ++ /* Name of Sync event */ ++ char szEventName[SYNC_MAXNAMELENGTH + 1]; ++ u32 hProcess; /* Process which created this channel */ ++ u32 pCBArg; /* Argument to use with callback */ ++ struct LST_LIST *pIORequests; /* List of IOR's to driver */ ++ s32 cIOCs; /* Number of IOC's in queue */ ++ s32 cIOReqs; /* Number of IORequests in queue */ ++ s32 cChirps; /* Initial number of free Irps */ ++ /* List of IOC's from driver */ ++ struct LST_LIST *pIOCompletions; ++ struct LST_LIST *pFreeList; /* List of free Irps */ ++ struct NTFY_OBJECT *hNtfy; ++ u32 cBytesMoved; /* Total number of bytes transfered */ ++ ++ /* For DSP-DMA */ ++ ++ /* Type of chnl transport:CHNL_[PCPY][DDMA] */ ++ u32 uChnlType; ++ } ; ++ ++/* I/O Request/completion packet: */ ++ struct CHNL_IRP { ++ struct LST_ELEM link; /* Link to next CHIRP in queue. */ ++ /* Buffer to be filled/emptied. (User) */ ++ u8 *pHostUserBuf; ++ /* Buffer to be filled/emptied. (System) */ ++ u8 *pHostSysBuf; ++ u32 dwArg; /* Issue/Reclaim argument. */ ++ u32 uDspAddr; /* Transfer address on DSP side. */ ++ u32 cBytes; /* Bytes transferred. */ ++ u32 cBufSize; /* Actual buffer size when allocated. */ ++ u32 status; /* Status of IO completion. */ ++ } ; ++ ++#endif /* _CHNL_SM_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/_dcd.h b/arch/arm/plat-omap/include/dspbridge/_dcd.h +new file mode 100644 +index 0000000..b6a8d9e +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/_dcd.h +@@ -0,0 +1,187 @@ ++/* ++ * _dcd.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== _dcd.h ======== ++ * Description: ++ * Includes the wrapper functions called directly by the ++ * DeviceIOControl interface. ++ * ++ * Public Functions: ++ * WCD_CallDevIOCtl ++ * WCD_Init ++ * WCD_InitComplete2 ++ * WCD_Exit ++ * WRAP_* ++ * ++ * Notes: ++ * Compiled with CDECL calling convention. ++ * ++ *! Revision History: ++ *! ================ ++ *! 19-Apr-2004 sb Aligned DMM definitions with Symbian ++ *! 08-Mar-2004 sb Added the Dynamic Memory Mapping feature ++ *! 30-Jan-2002 ag Renamed CMMWRAP_AllocBuf to CMMWRAP_CallocBuf. ++ *! 22-Nov-2000 kc: Added MGRWRAP_GetPerf_Data to acquire PERF stats. ++ *! 27-Oct-2000 jeh Added NODEWRAP_AllocMsgBuf, NODEWRAP_FreeMsgBuf. Removed ++ *! NODEWRAP_GetMessageStream. ++ *! 10-Oct-2000 ag: Added user CMM wrappers. ++ *! 04-Aug-2000 rr: MEMWRAP and UTIL_Wrap added. ++ *! 27-Jul-2000 rr: NODEWRAP, STRMWRAP added. ++ *! 27-Jun-2000 rr: MGRWRAP fxns added.IFDEF to build for PM or DSP/BIOS Bridge ++ *! 03-Dec-1999 rr: WCD_InitComplete2 enabled for BRD_AutoStart. ++ *! 09-Nov-1999 kc: Added MEMRY. ++ *! 02-Nov-1999 ag: Added CHNL. ++ *! 08-Oct-1999 rr: Utilwrap_Testdll fxn added ++ *! 24-Sep-1999 rr: header changed from _wcd.h to _dcd.h ++ *! 09-Sep-1997 gp: Created. ++ */ ++ ++#ifndef _WCD_ ++#define _WCD_ ++ ++#include ++ ++/* ++ * ======== WCD_CallDevIOCtl ======== ++ * Purpose: ++ * Call the (wrapper) function for the corresponding WCD IOCTL. ++ * Parameters: ++ * cmd: IOCTL id, base 0. ++ * args: Argument structure. ++ * pResult: ++ * Returns: ++ * DSP_SOK if command called; DSP_EINVALIDARG if command not in IOCTL ++ * table. ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS WCD_CallDevIOCtl(unsigned int cmd, ++ union Trapped_Args *args, ++ u32 *pResult); ++ ++/* ++ * ======== WCD_Init ======== ++ * Purpose: ++ * Initialize WCD modules, and export WCD services to WMD's. ++ * This procedure is called when the class driver is loaded. ++ * Parameters: ++ * Returns: ++ * TRUE if success; FALSE otherwise. ++ * Requires: ++ * Ensures: ++ */ ++ extern bool WCD_Init(void); ++ ++/* ++ * ======== WCD_InitComplete2 ======== ++ * Purpose: ++ * Perform any required WCD, and WMD initialization which ++ * cannot not be performed in WCD_Init(void) or DEV_StartDevice() due ++ * to the fact that some services are not yet ++ * completely initialized. ++ * Parameters: ++ * Returns: ++ * DSP_SOK: Allow this device to load ++ * DSP_EFAIL: Failure. ++ * Requires: ++ * WCD initialized. ++ * Ensures: ++ */ ++ extern DSP_STATUS WCD_InitComplete2(void); ++ ++/* ++ * ======== WCD_Exit ======== ++ * Purpose: ++ * Exit all modules initialized in WCD_Init(void). ++ * This procedure is called when the class driver is unloaded. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * WCD_Init(void) was previously called. ++ * Ensures: ++ * Resources acquired in WCD_Init(void) are freed. ++ */ ++ extern void WCD_Exit(void); ++ ++/* MGR wrapper functions */ ++ extern u32 MGRWRAP_EnumNode_Info(union Trapped_Args *args); ++ extern u32 MGRWRAP_EnumProc_Info(union Trapped_Args *args); ++ extern u32 MGRWRAP_RegisterObject(union Trapped_Args *args); ++ extern u32 MGRWRAP_UnregisterObject(union Trapped_Args *args); ++ extern u32 MGRWRAP_WaitForBridgeEvents(union Trapped_Args *args); ++ ++#ifndef RES_CLEANUP_DISABLE ++ extern u32 MGRWRAP_GetProcessResourcesInfo(union Trapped_Args *args); ++#endif ++ ++ ++/* CPRC (Processor) wrapper Functions */ ++ extern u32 PROCWRAP_Attach(union Trapped_Args *args); ++ extern u32 PROCWRAP_Ctrl(union Trapped_Args *args); ++ extern u32 PROCWRAP_Detach(union Trapped_Args *args); ++ extern u32 PROCWRAP_EnumNode_Info(union Trapped_Args *args); ++ extern u32 PROCWRAP_EnumResources(union Trapped_Args *args); ++ extern u32 PROCWRAP_GetState(union Trapped_Args *args); ++ extern u32 PROCWRAP_GetTrace(union Trapped_Args *args); ++ extern u32 PROCWRAP_Load(union Trapped_Args *args); ++ extern u32 PROCWRAP_RegisterNotify(union Trapped_Args *args); ++ extern u32 PROCWRAP_Start(union Trapped_Args *args); ++ extern u32 PROCWRAP_ReserveMemory(union Trapped_Args *args); ++ extern u32 PROCWRAP_UnReserveMemory(union Trapped_Args *args); ++ extern u32 PROCWRAP_Map(union Trapped_Args *args); ++ extern u32 PROCWRAP_UnMap(union Trapped_Args *args); ++ extern u32 PROCWRAP_FlushMemory(union Trapped_Args *args); ++ extern u32 PROCWRAP_Stop(union Trapped_Args *args); ++ extern u32 PROCWRAP_InvalidateMemory(union Trapped_Args *args); ++ ++/* NODE wrapper functions */ ++ extern u32 NODEWRAP_Allocate(union Trapped_Args *args); ++ extern u32 NODEWRAP_AllocMsgBuf(union Trapped_Args *args); ++ extern u32 NODEWRAP_ChangePriority(union Trapped_Args *args); ++ extern u32 NODEWRAP_Connect(union Trapped_Args *args); ++ extern u32 NODEWRAP_Create(union Trapped_Args *args); ++ extern u32 NODEWRAP_Delete(union Trapped_Args *args); ++ extern u32 NODEWRAP_FreeMsgBuf(union Trapped_Args *args); ++ extern u32 NODEWRAP_GetAttr(union Trapped_Args *args); ++ extern u32 NODEWRAP_GetMessage(union Trapped_Args *args); ++ extern u32 NODEWRAP_Pause(union Trapped_Args *args); ++ extern u32 NODEWRAP_PutMessage(union Trapped_Args *args); ++ extern u32 NODEWRAP_RegisterNotify(union Trapped_Args *args); ++ extern u32 NODEWRAP_Run(union Trapped_Args *args); ++ extern u32 NODEWRAP_Terminate(union Trapped_Args *args); ++ extern u32 NODEWRAP_GetUUIDProps(union Trapped_Args *args); ++ ++/* STRM wrapper functions */ ++ extern u32 STRMWRAP_AllocateBuffer(union Trapped_Args *args); ++ extern u32 STRMWRAP_Close(union Trapped_Args *args); ++ extern u32 STRMWRAP_FreeBuffer(union Trapped_Args *args); ++ extern u32 STRMWRAP_GetEventHandle(union Trapped_Args *args); ++ extern u32 STRMWRAP_GetInfo(union Trapped_Args *args); ++ extern u32 STRMWRAP_Idle(union Trapped_Args *args); ++ extern u32 STRMWRAP_Issue(union Trapped_Args *args); ++ extern u32 STRMWRAP_Open(union Trapped_Args *args); ++ extern u32 STRMWRAP_Reclaim(union Trapped_Args *args); ++ extern u32 STRMWRAP_RegisterNotify(union Trapped_Args *args); ++ extern u32 STRMWRAP_Select(union Trapped_Args *args); ++ ++ extern u32 CMMWRAP_CallocBuf(union Trapped_Args *args); ++ extern u32 CMMWRAP_FreeBuf(union Trapped_Args *args); ++ extern u32 CMMWRAP_GetHandle(union Trapped_Args *args); ++ extern u32 CMMWRAP_GetInfo(union Trapped_Args *args); ++ ++#endif /* _WCD_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/brddefs.h b/arch/arm/plat-omap/include/dspbridge/brddefs.h +new file mode 100644 +index 0000000..c62388c +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/brddefs.h +@@ -0,0 +1,54 @@ ++/* ++ * brddefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== brddefs.h ======== ++ * Description: ++ * Global BRD constants and types, shared between WSX, WCD, and WMD. ++ * ++ *! Revision History: ++ *! ================ ++ *! 31-Jan-2000 rr: Comment Exec changed to Monitor ++ *! 22-Jul-1999 jeh Added BRD_LOADED state. ++ *! 26-Mar-1997 gp: Added BRD_SYNCINIT state. ++ *! 11-Dec-1996 cr: Added BRD_LASTSTATE definition. ++ *! 11-Jul-1996 gp: Added missing u32 callback argument to BRD_CALLBACK. ++ *! 10-Jun-1996 gp: Created from board.h and brd.h. ++ */ ++ ++#ifndef BRDDEFS_ ++#define BRDDEFS_ ++ ++/* platform status values */ ++#define BRD_STOPPED 0x0 /* No Monitor Loaded, Not running. */ ++#define BRD_IDLE 0x1 /* Monitor Loaded, but suspended. */ ++#define BRD_RUNNING 0x2 /* Monitor loaded, and executing. */ ++#define BRD_UNKNOWN 0x3 /* Board state is indeterminate. */ ++#define BRD_SYNCINIT 0x4 ++#define BRD_LOADED 0x5 ++#define BRD_LASTSTATE BRD_LOADED /* Set to highest legal board state. */ ++#define BRD_SLEEP_TRANSITION 0x6 /* Sleep transition in progress */ ++#define BRD_HIBERNATION 0x7 /* MPU initiated hibernation */ ++#define BRD_RETENTION 0x8 /* Retention mode */ ++#define BRD_DSP_HIBERNATION 0x9 /* DSP initiated hibernation */ ++#define BRD_ERROR 0xA /* Board state is Error */ ++ typedef u32 BRD_STATUS; ++ ++/* BRD Object */ ++ struct BRD_OBJECT; ++ ++#endif /* BRDDEFS_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/cfg.h b/arch/arm/plat-omap/include/dspbridge/cfg.h +new file mode 100644 +index 0000000..68db842 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/cfg.h +@@ -0,0 +1,339 @@ ++/* ++ * cfg.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== cfg.h ======== ++ * Purpose: ++ * PM Configuration module. ++ * ++ * Private Functions: ++ * CFG_Exit ++ * CFG_GetAutoStart ++ * CFG_GetCDVersion ++ * CFG_GetDevObject ++ * CFG_GetDSPResources ++ * CFG_GetExecFile ++ * CFG_GetHostResources ++ * CFG_GetObject ++ * CFG_GetPerfValue ++ * CFG_GetWMDFileName ++ * CFG_GetZLFile ++ * CFG_Init ++ * CFG_SetDevObject ++ * CFG_SetObject ++ * ++ *! Revision History: ++ *! ================= ++ *! 26-Feb-2003 kc Removed unused CFG fxns. ++ *! 28-Aug-2001 jeh Added CFG_GetLoaderName. ++ *! 26-Jul-2000 rr: Added CFG_GetDCDName to retrieve the DCD Dll name. ++ *! 13-Jul-2000 rr: Added CFG_GetObject & CFG_SetObject. ++ *! 13-Jan-2000 rr: CFG_Get/SetPrivateDword renamed to CFG_Get/SetDevObject. ++ *! CFG_GetWinBRIDGEDir/Directory,CFG_GetSearchPath removed. ++ *! 15-Jan-1998 cr: Code review cleanup. ++ *! 16-Aug-1997 cr: Added explicit cdecl identifiers. ++ *! 12-Dec-1996 gp: Moved CFG_FindInSearchPath to CSP module. ++ *! 13-Sep-1996 gp: Added CFG_GetBoardName(). ++ *! 22-Jul-1996 gp: Added CFG_GetTraceStr, to retrieve an initial GT trace. ++ *! 26-Jun-1996 cr: Added CFG_FindInSearchPath. ++ *! 25-Jun-1996 cr: Added CFG_GetWinSPOXDir. ++ *! 17-Jun-1996 cr: Added CFG_GetDevNode. ++ *! 11-Jun-1996 cr: Cleaned up for code review. ++ *! 07-Jun-1996 cr: Added CFG_GetExecFile and CFG_GetZLFileName functions. ++ *! 04-Jun-1996 gp: Added AutoStart regkey and accessor function. Placed ++ *! OUT parameters in accessor function param. lists at end. ++ *! 29-May-1996 gp: Moved DEV_HDEVNODE to here and renamed CFG_HDEVNODE. ++ *! 22-May-1996 cr: Added GetHostResources, GetDSPResources, and ++ *! GetWMDFileName services. ++ *! 18-May-1996 gp: Created. ++ */ ++ ++#ifndef CFG_ ++#define CFG_ ++#include ++#include ++ ++/* ++ * ======== CFG_Exit ======== ++ * Purpose: ++ * Discontinue usage of the CFG module. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * CFG_Init(void) was previously called. ++ * Ensures: ++ * Resources acquired in CFG_Init(void) are freed. ++ */ ++ extern void CFG_Exit(void); ++ ++/* ++ * ======== CFG_GetAutoStart ======== ++ * Purpose: ++ * Retreive the autostart mask, if any, for this board. ++ * Parameters: ++ * hDevNode: Handle to the DevNode who's WMD we are querying. ++ * pdwAutoStart: Ptr to location for 32 bit autostart mask. ++ * Returns: ++ * DSP_SOK: Success. ++ * CFG_E_INVALIDHDEVNODE: hDevNode is invalid. ++ * CFG_E_RESOURCENOTAVAIL: Unable to retreive resource. ++ * Requires: ++ * CFG initialized. ++ * Ensures: ++ * DSP_SOK: *pdwAutoStart contains autostart mask for this devnode. ++ */ ++ extern DSP_STATUS CFG_GetAutoStart(IN struct CFG_DEVNODE *hDevNode, ++ OUT u32 *pdwAutoStart); ++ ++/* ++ * ======== CFG_GetCDVersion ======== ++ * Purpose: ++ * Retrieves the version of the PM Class Driver. ++ * Parameters: ++ * pdwVersion: Ptr to u32 to contain version number upon return. ++ * Returns: ++ * DSP_SOK: Success. pdwVersion contains Class Driver version in ++ * the form: 0xAABBCCDD where AABB is Major version and ++ * CCDD is Minor. ++ * DSP_EFAIL: Failure. ++ * Requires: ++ * CFG initialized. ++ * Ensures: ++ * DSP_SOK: Success. ++ * else: *pdwVersion is NULL. ++ */ ++ extern DSP_STATUS CFG_GetCDVersion(OUT u32 *pdwVersion); ++ ++/* ++ * ======== CFG_GetDevObject ======== ++ * Purpose: ++ * Retrieve the Device Object handle for a given devnode. ++ * Parameters: ++ * hDevNode: Platform's DevNode handle from which to retrieve value. ++ * pdwValue: Ptr to location to store the value. ++ * Returns: ++ * DSP_SOK: Success. ++ * CFG_E_INVALIDHDEVNODE: hDevNode is invalid. ++ * CFG_E_INVALIDPOINTER: phDevObject is invalid. ++ * CFG_E_RESOURCENOTAVAIL: The resource is not available. ++ * Requires: ++ * CFG initialized. ++ * Ensures: ++ * DSP_SOK: *pdwValue is set to the retrieved u32. ++ * else: *pdwValue is set to 0L. ++ */ ++ extern DSP_STATUS CFG_GetDevObject(IN struct CFG_DEVNODE *hDevNode, ++ OUT u32 *pdwValue); ++ ++/* ++ * ======== CFG_GetDSPResources ======== ++ * Purpose: ++ * Get the DSP resources available to a given device. ++ * Parameters: ++ * hDevNode: Handle to the DEVNODE who's resources we are querying. ++ * pDSPResTable: Ptr to a location to store the DSP resource table. ++ * Returns: ++ * DSP_SOK: On success. ++ * CFG_E_INVALIDHDEVNODE: hDevNode is invalid. ++ * CFG_E_RESOURCENOTAVAIL: The DSP Resource information is not ++ * available ++ * Requires: ++ * CFG initialized. ++ * Ensures: ++ * DSP_SOK: pDSPResTable points to a filled table of resources allocated ++ * for the specified WMD. ++ */ ++ extern DSP_STATUS CFG_GetDSPResources(IN struct CFG_DEVNODE *hDevNode, ++ OUT struct CFG_DSPRES *pDSPResTable); ++ ++ ++/* ++ * ======== CFG_GetExecFile ======== ++ * Purpose: ++ * Retreive the default executable, if any, for this board. ++ * Parameters: ++ * hDevNode: Handle to the DevNode who's WMD we are querying. ++ * cBufSize: Size of buffer. ++ * pstrExecFile: Ptr to character buf to hold ExecFile. ++ * Returns: ++ * DSP_SOK: Success. ++ * CFG_E_INVALIDHDEVNODE: hDevNode is invalid. ++ * CFG_E_INVALIDPOINTER: pstrExecFile is invalid. ++ * CFG_E_RESOURCENOTAVAIL: The resource is not available. ++ * Requires: ++ * CFG initialized. ++ * Ensures: ++ * DSP_SOK: Not more than cBufSize bytes were copied into pstrExecFile, ++ * and *pstrExecFile contains default executable for this ++ * devnode. ++ */ ++ extern DSP_STATUS CFG_GetExecFile(IN struct CFG_DEVNODE *hDevNode, ++ IN u32 cBufSize, ++ OUT char *pstrExecFile); ++ ++/* ++ * ======== CFG_GetHostResources ======== ++ * Purpose: ++ * Get the Host PC allocated resources assigned to a given device. ++ * Parameters: ++ * hDevNode: Handle to the DEVNODE who's resources we are querying. ++ * pHostResTable: Ptr to a location to store the host resource table. ++ * Returns: ++ * DSP_SOK: On success. ++ * CFG_E_INVALIDPOINTER: pHostResTable is invalid. ++ * CFG_E_INVALIDHDEVNODE: hDevNode is invalid. ++ * CFG_E_RESOURCENOTAVAIL: The resource is not available. ++ * Requires: ++ * CFG initialized. ++ * Ensures: ++ * DSP_SOK: pHostResTable points to a filled table of resources ++ * allocated for the specified WMD. ++ * ++ */ ++ extern DSP_STATUS CFG_GetHostResources(IN struct CFG_DEVNODE *hDevNode, ++ OUT struct CFG_HOSTRES *pHostResTable); ++ ++/* ++ * ======== CFG_GetObject ======== ++ * Purpose: ++ * Retrieve the Driver Object handle From the Registry ++ * Parameters: ++ * pdwValue: Ptr to location to store the value. ++ * dwType Type of Object to Get ++ * Returns: ++ * DSP_SOK: Success. ++ * Requires: ++ * CFG initialized. ++ * Ensures: ++ * DSP_SOK: *pdwValue is set to the retrieved u32(non-Zero). ++ * else: *pdwValue is set to 0L. ++ */ ++ extern DSP_STATUS CFG_GetObject(OUT u32 *pdwValue, u32 dwType); ++ ++/* ++ * ======== CFG_GetPerfValue ======== ++ * Purpose: ++ * Retrieve a flag indicating whether PERF should log statistics for the ++ * PM class driver. ++ * Parameters: ++ * pfEnablePerf: Location to store flag. 0 indicates the key was ++ * not found, or had a zero value. A nonzero value ++ * means the key was found and had a nonzero value. ++ * Returns: ++ * Requires: ++ * pfEnablePerf != NULL; ++ * Ensures: ++ */ ++ extern void CFG_GetPerfValue(OUT bool *pfEnablePerf); ++ ++/* ++ * ======== CFG_GetWMDFileName ======== ++ * Purpose: ++ * Get the mini-driver file name for a given device. ++ * Parameters: ++ * hDevNode: Handle to the DevNode who's WMD we are querying. ++ * cBufSize: Size of buffer. ++ * pWMDFileName: Ptr to a character buffer to hold the WMD filename. ++ * Returns: ++ * DSP_SOK: On success. ++ * CFG_E_INVALIDHDEVNODE: hDevNode is invalid. ++ * CFG_E_RESOURCENOTAVAIL: The filename is not available. ++ * Requires: ++ * CFG initialized. ++ * Ensures: ++ * DSP_SOK: Not more than cBufSize bytes were copied ++ * into pWMDFileName. ++ * ++ */ ++ extern DSP_STATUS CFG_GetWMDFileName(IN struct CFG_DEVNODE *hDevNode, ++ IN u32 cBufSize, ++ OUT char *pWMDFileName); ++ ++/* ++ * ======== CFG_GetZLFile ======== ++ * Purpose: ++ * Retreive the ZLFile, if any, for this board. ++ * Parameters: ++ * hDevNode: Handle to the DevNode who's WMD we are querying. ++ * cBufSize: Size of buffer. ++ * pstrZLFileName: Ptr to character buf to hold ZLFileName. ++ * Returns: ++ * DSP_SOK: Success. ++ * CFG_E_INVALIDPOINTER: pstrZLFileName is invalid. ++ * CFG_E_INVALIDHDEVNODE: hDevNode is invalid. ++ * CFG_E_RESOURCENOTAVAIL: couldn't find the ZLFileName. ++ * Requires: ++ * CFG initialized. ++ * Ensures: ++ * DSP_SOK: Not more than cBufSize bytes were copied into ++ * pstrZLFileName, and *pstrZLFileName contains ZLFileName ++ * for this devnode. ++ */ ++ extern DSP_STATUS CFG_GetZLFile(IN struct CFG_DEVNODE *hDevNode, ++ IN u32 cBufSize, ++ OUT char *pstrZLFileName); ++ ++/* ++ * ======== CFG_Init ======== ++ * Purpose: ++ * Initialize the CFG module's private state. ++ * Parameters: ++ * Returns: ++ * TRUE if initialized; FALSE if error occured. ++ * Requires: ++ * Ensures: ++ * A requirement for each of the other public CFG functions. ++ */ ++ extern bool CFG_Init(void); ++ ++/* ++ * ======== CFG_SetDevObject ======== ++ * Purpose: ++ * Store the Device Object handle for a given devnode. ++ * Parameters: ++ * hDevNode: Platform's DevNode handle we are storing value with. ++ * dwValue: Arbitrary value to store. ++ * Returns: ++ * DSP_SOK: Success. ++ * CFG_E_INVALIDHDEVNODE: hDevNode is invalid. ++ * DSP_EFAIL: Internal Error. ++ * Requires: ++ * CFG initialized. ++ * Ensures: ++ * DSP_SOK: The Private u32 was successfully set. ++ */ ++ extern DSP_STATUS CFG_SetDevObject(IN struct CFG_DEVNODE *hDevNode, ++ IN u32 dwValue); ++ ++/* ++ * ======== CFG_SetDrvObject ======== ++ * Purpose: ++ * Store the Driver Object handle. ++ * Parameters: ++ * dwValue: Arbitrary value to store. ++ * dwType Type of Object to Store ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Internal Error. ++ * Requires: ++ * CFG initialized. ++ * Ensures: ++ * DSP_SOK: The Private u32 was successfully set. ++ */ ++ extern DSP_STATUS CFG_SetObject(IN u32 dwValue, IN u32 dwType); ++ ++#endif /* CFG_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/cfgdefs.h b/arch/arm/plat-omap/include/dspbridge/cfgdefs.h +new file mode 100644 +index 0000000..4f78d82 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/cfgdefs.h +@@ -0,0 +1,126 @@ ++/* ++ * cfgdefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== cfgdefs.h ======== ++ * Purpose: ++ * Global CFG constants and types, shared between class and mini driver. ++ * ++ *! Revision History: ++ *! ================ ++ *! 24-Feb-2003 kc Removed wIOPort* in CFG_HOSTRES. ++ *! 06-Sep-2000 jeh Added channel info to CFG_HOSTRES. ++ *! 09-May-2000 rr: CFG_HOSTRES now support multiple windows for PCI support. ++ *! 31-Jan-2000 rr: Comments changed after code review. ++ *! 06-Jan-2000 rr: Bus Type included in CFG_HOSTRES. ++ *! 12-Nov-1999 rr: CFG_HOSTRES member names changed. ++ *! 25-Oct-1999 rr: Modified the CFG_HOSTRES Structure ++ *! PCMCIA ISR Register/Unregister fxn removed.. ++ *! New flag PCCARD introduced during compile time. ++ *! 10-Sep-1999 ww: Added PCMCIA ISR Register/Unregister fxn. ++ *! 01-Sep-1999 ag: Removed NT/95 specific fields in CFG_HOSTRES ++ *! 27-Oct-1997 cr: Updated CFG_HOSTRES struct to support 1+ IRQs per board. ++ *! 17-Sep-1997 gp: Tacked some NT config info to end of CFG_HOSTRES structure. ++ *! 12-Dec-1996 cr: Cleaned up after code review. ++ *! 14-Nov-1996 gp: Renamed from wsxcfg.h ++ *! 19-Jun-1996 cr: Created. ++ */ ++ ++#ifndef CFGDEFS_ ++#define CFGDEFS_ ++ ++/* Maximum length of module search path. */ ++#define CFG_MAXSEARCHPATHLEN 255 ++ ++/* Maximum length of general paths. */ ++#define CFG_MAXPATH 255 ++ ++/* Host Resources: */ ++#define CFG_MAXMEMREGISTERS 9 ++#define CFG_MAXIOPORTS 20 ++#define CFG_MAXIRQS 7 ++#define CFG_MAXDMACHANNELS 7 ++ ++/* IRQ flag */ ++#define CFG_IRQSHARED 0x01 /* IRQ can be shared */ ++ ++/* DSP Resources: */ ++#define CFG_DSPMAXMEMTYPES 10 ++#define CFG_DEFAULT_NUM_WINDOWS 1 /* We support only one window. */ ++ ++/* A platform-related device handle: */ ++ struct CFG_DEVNODE; ++ ++/* ++ * Host resource structure. ++ */ ++ struct CFG_HOSTRES { ++ u32 wNumMemWindows; /* Set to default */ ++ /* This is the base.memory */ ++ u32 dwMemBase[CFG_MAXMEMREGISTERS]; /* SHM virtual address */ ++ u32 dwMemLength[CFG_MAXMEMREGISTERS]; /* Length of the Base */ ++ u32 dwMemPhys[CFG_MAXMEMREGISTERS]; /* SHM Physical address */ ++ u8 bIRQRegisters; /* IRQ Number */ ++ u8 bIRQAttrib; /* IRQ Attribute */ ++ u32 dwOffsetForMonitor; /* The Shared memory starts from ++ * dwMemBase + this offset */ ++ u32 dwBusType; /* Bus type for this device */ ++ u32 dwProgBase; /* DSP ProgBase */ ++ u32 dwProgLength; /* DSP ProgBase Length */ ++ u32 dwRegBase; /* DSP memory mapped register base */ ++ u32 dwRegLength; /* DSP Register Base Length */ ++ u32 ClientHandle; /* Client Handle */ ++ u32 SocketHandle; /* Socket and Function Pair */ ++ u32 CardInfo; /* This will be used as a context data in ++ * in the CardRequestIRQ */ ++ /* ++ * Info needed by NODE for allocating channels to communicate with RMS: ++ * dwChnlOffset: Offset of RMS channels. Lower channels are ++ * reserved. ++ * dwChnlBufSize: Size of channel buffer to send to RMS ++ * dwNumChnls: Total number of channels (including reserved). ++ */ ++ u32 dwChnlOffset; ++ u32 dwChnlBufSize; ++ u32 dwNumChnls; ++ u32 dwPrmBase; ++ u32 dwCmBase; ++ u32 dwPerBase; ++ u32 dwPerPmBase; ++ u32 dwCorePmBase; ++ u32 dwWdTimerDspBase; ++ u32 dwMboxBase; ++ u32 dwDmmuBase; ++ u32 dwDipiBase; ++ u32 dwSysCtrlBase; ++ } ; ++ ++ struct CFG_DSPMEMDESC { ++ u32 uMemType; /* Type of memory. */ ++ u32 ulMin; /* Minimum amount of memory of this type. */ ++ u32 ulMax; /* Maximum amount of memory of this type. */ ++ } ; ++ ++ struct CFG_DSPRES { ++ u32 uChipType; /* DSP chip type. */ ++ u32 uWordSize; /* Number of bytes in a word */ ++ u32 cChips; /* Number of chips. */ ++ u32 cMemTypes; /* Types of memory. */ ++ struct CFG_DSPMEMDESC aMemDesc[CFG_DSPMAXMEMTYPES]; ++ /* DSP Memory types */ ++ } ; ++ ++#endif /* CFGDEFS_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/chnl.h b/arch/arm/plat-omap/include/dspbridge/chnl.h +new file mode 100644 +index 0000000..f39e3f4 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/chnl.h +@@ -0,0 +1,170 @@ ++/* ++ * chnl.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== chnl.h ======== ++ * Description: ++ * WCD channel interface: multiplexes data streams through the single ++ * physical link managed by a mini-driver. ++ * ++ * Public Functions: ++ * CHNL_AddIOReq ++ * CHNL_AllocBuffer ++ * CHNL_CancelIO ++ * CHNL_Close ++ * CHNL_CloseOrphans ++ * CHNL_Create ++ * CHNL_Destroy ++ * CHNL_Exit ++ * CHNL_FlushIO ++ * CHNL_FreeBuffer ++ * CHNL_GetEventHandle ++ * CHNL_GetHandle ++ * CHNL_GetIOCompletion ++ * CHNL_GetId ++ * CHNL_GetMgr ++ * CHNL_GetMode ++ * CHNL_GetPosition ++ * CHNL_GetProcessHandle ++ * CHNL_Init ++ * CHNL_Open ++ * ++ * Notes: ++ * See DSP API chnl.h for more details. ++ * ++ *! Revision History: ++ *! ================ ++ *! 14-Jan-1997 gp: Updated based on code review feedback. ++ *! 24-Oct-1996 gp: Move CloseOrphans into here from dspsys. ++ *! 09-Sep-1996 gp: Added CHNL_GetProcessID() and CHNL_GetHandle(). ++ *! 10-Jul-1996 gp: Created. ++ */ ++ ++#ifndef CHNL_ ++#define CHNL_ ++ ++#include ++ ++/* ++ * ======== CHNL_Close ======== ++ * Purpose: ++ * Ensures all pending I/O on this channel is cancelled, discards all ++ * queued I/O completion notifications, then frees the resources allocated ++ * for this channel, and makes the corresponding logical channel id ++ * available for subsequent use. ++ * Parameters: ++ * hChnl: Channel object handle. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EHANDLE: Invalid hChnl. ++ * Requires: ++ * CHNL_Init(void) called. ++ * No thread must be blocked on this channel's I/O completion event. ++ * Ensures: ++ * DSP_SOK: The I/O completion event for this channel is freed. ++ * hChnl is no longer valid. ++ */ ++ extern DSP_STATUS CHNL_Close(struct CHNL_OBJECT *hChnl); ++ ++ ++/* ++ * ======== CHNL_Create ======== ++ * Purpose: ++ * Create a channel manager object, responsible for opening new channels ++ * and closing old ones for a given board. ++ * Parameters: ++ * phChnlMgr: Location to store a channel manager object on output. ++ * hDevObject: Handle to a device object. ++ * pMgrAttrs: Channel manager attributes. ++ * pMgrAttrs->cChannels: Max channels ++ * pMgrAttrs->bIRQ: Channel's I/O IRQ number. ++ * pMgrAttrs->fShared: TRUE if the IRQ is shareable. ++ * pMgrAttrs->uWordSize: DSP Word size in equivalent PC bytes.. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EHANDLE: hDevObject is invalid. ++ * DSP_EINVALIDARG: cChannels is 0. ++ * DSP_EMEMORY: Insufficient memory for requested resources. ++ * CHNL_E_ISR: Unable to plug channel ISR for configured IRQ. ++ * CHNL_E_MAXCHANNELS: This manager cannot handle this many channels. ++ * CHNL_E_INVALIDIRQ: Invalid IRQ number. Must be 0 <= bIRQ <= 15. ++ * CHNL_E_INVALIDWORDSIZE: Invalid DSP word size. Must be > 0. ++ * CHNL_E_INVALIDMEMBASE: Invalid base address for DSP communications. ++ * CHNL_E_MGREXISTS: Channel manager already exists for this device. ++ * Requires: ++ * CHNL_Init(void) called. ++ * phChnlMgr != NULL. ++ * pMgrAttrs != NULL. ++ * Ensures: ++ * DSP_SOK: Subsequent calls to CHNL_Create() for the same ++ * board without an intervening call to ++ * CHNL_Destroy() will fail. ++ */ ++ extern DSP_STATUS CHNL_Create(OUT struct CHNL_MGR **phChnlMgr, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct CHNL_MGRATTRS *pMgrAttrs); ++ ++/* ++ * ======== CHNL_Destroy ======== ++ * Purpose: ++ * Close all open channels, and destroy the channel manager. ++ * Parameters: ++ * hChnlMgr: Channel manager object. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: hChnlMgr was invalid. ++ * Requires: ++ * CHNL_Init(void) called. ++ * Ensures: ++ * DSP_SOK: Cancels I/O on each open channel. ++ * Closes each open channel. ++ * CHNL_Create may subsequently be called for the ++ * same board. ++ */ ++ extern DSP_STATUS CHNL_Destroy(struct CHNL_MGR *hChnlMgr); ++ ++/* ++ * ======== CHNL_Exit ======== ++ * Purpose: ++ * Discontinue usage of the CHNL module. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * CHNL_Init(void) previously called. ++ * Ensures: ++ * Resources, if any acquired in CHNL_Init(void), are freed when the last ++ * client of CHNL calls CHNL_Exit(void). ++ */ ++ extern void CHNL_Exit(void); ++ ++ ++/* ++ * ======== CHNL_Init ======== ++ * Purpose: ++ * Initialize the CHNL module's private state. ++ * Parameters: ++ * Returns: ++ * TRUE if initialized; FALSE if error occurred. ++ * Requires: ++ * Ensures: ++ * A requirement for each of the other public CHNL functions. ++ */ ++ extern bool CHNL_Init(void); ++ ++ ++ ++#endif /* CHNL_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/chnl_sm.h b/arch/arm/plat-omap/include/dspbridge/chnl_sm.h +new file mode 100644 +index 0000000..789b9bd +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/chnl_sm.h +@@ -0,0 +1,168 @@ ++/* ++ * chnl_sm.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== chnl_sm.h ======== ++ * Description: ++ * Prototypes for channel lower edge functions for a WinBRIDGE mini driver ++ * implementing data transfer via shared memory. ++ * ++ * Public Functions: ++ * CHNLSM_DisableInterrupt; ++ * CHNLSM_EnableInterrupt; ++ * CHNLSM_ISR; ++ * CHNLSM_Read; ++ * CHNLSM_UpdateSHMLength; ++ * CHNLSM_Write; ++ * ++ * Notes: ++ * These lower edge functions must be implemented by the WMD writer. ++ * Currently, CHNLSM_Read() and CHNLSM_Write() are not called, but must ++ * be defined to link. ++ * ++ */ ++ ++#ifndef CHNLSM_ ++#define CHNLSM_ ++ ++#include ++ ++/* ++ * ======== CHNLSM_DisableInterrupt ======== ++ * Purpose: ++ * Disable interrupts from the DSP board to the PC. ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device info. ++ * Returns: ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS CHNLSM_DisableInterrupt(struct WMD_DEV_CONTEXT* ++ hDevContext); ++ ++/* ++ * ======== CHNLSM_EnableInterrupt ======== ++ * Purpose: ++ * Enable interrupts from the DSP board to the PC. ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device info. ++ * Returns: ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS CHNLSM_EnableInterrupt(struct WMD_DEV_CONTEXT* ++ hDevContext); ++ ++/* ++ * ======== CHNLSM_InterruptDSP2 ======== ++ * Purpose: ++ * Set interrupt value & send an interrupt to the DSP processor(s). ++ * This is typicaly used when mailbox interrupt mechanisms allow data ++ * to be associated with interrupt such as for OMAP's CMD/DATA regs. ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device info. ++ * wMbVal: Value associated with interrupt(e.g. mailbox value). ++ * Returns: ++ * DSP_SOK: Interrupt sent; ++ * else: Unable to send interrupt. ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS CHNLSM_InterruptDSP2(struct WMD_DEV_CONTEXT* ++ hDevContext, u16 wMbVal); ++ ++/* ++ * ======== CHNLSM_ISR ======== ++ * Purpose: ++ * Mini-driver's ISR, called by WCD when the board interrupts the host. ++ * Parameters: ++ * hDevContext: Handle to the mini-driver defined device info. ++ * pfSchedDPC: Set to TRUE to schedule a deferred procedure call ++ * to advance the channel protocol. The channel class ++ * library will call the WMD's CHNLSM_DPC routine during ++ * its own DPC, before dispatching I/O. ++ * The channel class library should ignore *pfSchedDPC when ++ * CHNLSM_ISR returns FALSE. ++ * pwMBRegVal: Value of mailbox register. ++ * Returns: ++ * TRUE if this interrupt is was generated by the DSP board. ++ * FALSE otherwise. ++ * Requires: ++ * Interrupts to the host processor are disabled on entry. ++ * Must only call functions which are in page locked memory. ++ * Must only call asynchronous OS services. ++ * The EOI for this interrupt has already been sent to the PIC. ++ * Ensures: ++ * If the interrupt is *not* shared, this routine must return TRUE. ++ */ ++ extern bool CHNLSM_ISR(struct WMD_DEV_CONTEXT *hDevContext, ++ OUT bool *pfSchedDPC, ++ OUT u16 *pwIntrVal); ++ ++/* ++ * ======== CHNLSM_Read ======== ++ * Purpose: ++ * Read data from DSP board memory into a Host buffer. ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device info. ++ * pHostBuf: Pointer to host buffer (Destination). ++ * dwDSPAddr: Address on DSP board (Source). ++ * ulNumBytes: Number of bytes to transfer. ++ * Returns: ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS CHNLSM_Read(struct WMD_DEV_CONTEXT *hDevContext, ++ OUT u8 *pHostBuf, ++ u32 dwDSPAddr, u32 ulNumBytes); ++ ++/* ++ * ======== CHNLSM_UpdateSHMLength ======== ++ * Purpose: ++ * Allow the minidriver a chance to override the SHM length as reported ++ * to the mini driver (chnl_sm.lib) by Windows Plug and Play. ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device info. ++ * pSHMLength: Pointer to size of SHM window (in DSP words). ++ * Returns: ++ * TRUE if pSHMLength updated; FALSE otherwise. ++ * Requires: ++ * pSHMLength != NULL. ++ * Ensures: ++ * No more than sizeof(u32) bytes written to *pSHMLength ++ */ ++ extern bool CHNLSM_UpdateSHMLength(struct WMD_DEV_CONTEXT *hDevContext, ++ IN OUT u32 *pSHMLength); ++ ++/* ++ * ======== CHNLSM_Write ======== ++ * Purpose: ++ * Write data from a Host buffer to DSP board memory. ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device info. ++ * pHostBuf: Pointer to host buffer (Source). ++ * dwDSPAddr: Address on DSP board (Destination). ++ * ulNumBytes: Number of bytes to transfer. ++ * Returns: ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS CHNLSM_Write(struct WMD_DEV_CONTEXT *hDevContext, ++ IN u8 *pHostBuf, ++ u32 dwDSPAddr, u32 ulNumBytes); ++ ++#endif /* CHNLSM_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/chnldefs.h b/arch/arm/plat-omap/include/dspbridge/chnldefs.h +new file mode 100644 +index 0000000..9f59229 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/chnldefs.h +@@ -0,0 +1,92 @@ ++/* ++ * chnldefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== chnldefs.h ======== ++ * Purpose: ++ * System-wide channel objects and constants. ++ * ++ *! Revision History: ++ *! ================ ++ *! 19-Jan-2002 ag Added cBufSize to IOC. ++ *! 05-Jan-2000 ag: Text format cleanup. ++ *! 02-Dec-1999 ag: Added new chnl attribute pstrEventName. ++ *! 12-Nov-1999 kc: Enabled hEvent attribute for tests. ++ *! 01-Nov-1999 ag: hEvent attribute not supported(yet). ++ *! 16-Jan-1997 gp: Moved private stuff into chnlpriv.h ++ *! 14-Jan-1997 gp: Updated based on code review feedback: ++ *! Removed CHNL_MODENOWAIT, CHNL_MODEDIRECT, ++ *! 03-Jan-1997 gp: Added channel class library types. ++ *! 14-Dec-1996 gp: Moved uChnlId field from CHNL_ATTRS to CHNL_Open(). ++ *! 10-Dec-1996 gp: Added CHNL_IsTimedOut() macro. ++ *! 14-Nov-1996 gp: Renamed from wsxchnl.h. ++ *! 09-Sep-1996 gp: Added hReserved2 field to CHNL_ATTRS. Updated CHNL_INFO. ++ *! 10-Jul-1996 gp: Created from channel.h. ++ */ ++ ++#ifndef CHNLDEFS_ ++#define CHNLDEFS_ ++ ++/* Channel id option. */ ++#define CHNL_PICKFREE (~0UL) /* Let manager pick a free channel. */ ++ ++/* Channel manager limits: */ ++#define CHNL_INITIOREQS 4 /* Default # of I/O requests. */ ++ ++/* Channel modes */ ++#define CHNL_MODETODSP 0x0000 /* Data streaming to the DSP. */ ++#define CHNL_MODEFROMDSP 0x0001 /* Data streaming from the DSP. */ ++ ++/* GetIOCompletion flags */ ++#define CHNL_IOCINFINITE 0xffffffff /* Wait forever for IO completion. */ ++#define CHNL_IOCNOWAIT 0x0 /* Dequeue an IOC, if available. */ ++ ++/* IO Completion Record status: */ ++#define CHNL_IOCSTATCOMPLETE 0x0000 /* IO Completed. */ ++#define CHNL_IOCSTATCANCEL 0x0002 /* IO was cancelled */ ++#define CHNL_IOCSTATTIMEOUT 0x0008 /* Wait for IOC timed out. */ ++#define CHNL_IOCSTATEOS 0x8000 /* End Of Stream reached. */ ++ ++/* Macros for checking I/O Completion status: */ ++#define CHNL_IsEOS(ioc) (ioc.status & CHNL_IOCSTATEOS) ++#define CHNL_IsIOComplete(ioc) (!(ioc.status & ~CHNL_IOCSTATEOS)) ++#define CHNL_IsIOCancelled(ioc) (ioc.status & CHNL_IOCSTATCANCEL) ++#define CHNL_IsTimedOut(ioc) (ioc.status & CHNL_IOCSTATTIMEOUT) ++ ++/* CHNL types: */ ++ typedef u32 CHNL_MODE; /* Channel transfer mode. */ ++ ++/* Channel attributes: */ ++ struct CHNL_ATTRS { ++ u32 uIOReqs; /* Max # of preallocated I/O requests. */ ++ HANDLE hEvent; /* User supplied auto-reset event object. */ ++ char *pstrEventName; /* Ptr to name of user event object. */ ++ HANDLE hReserved1; /* Reserved for future use. */ ++ u32 hReserved2; /* Reserved for future use. */ ++ ++ }; ++ ++/* I/O completion record: */ ++ struct CHNL_IOC { ++ void *pBuf; /* Buffer to be filled/emptied. */ ++ u32 cBytes; /* Bytes transferred. */ ++ u32 cBufSize; /* Actual buffer size in bytes */ ++ u32 status; /* Status of IO completion. */ ++ u32 dwArg; /* User argument associated with pBuf. */ ++ } ; ++ ++#endif /* CHNLDEFS_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/chnlpriv.h b/arch/arm/plat-omap/include/dspbridge/chnlpriv.h +new file mode 100644 +index 0000000..fdcda24 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/chnlpriv.h +@@ -0,0 +1,136 @@ ++/* ++ * chnlpriv.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== chnlpriv.h ======== ++ * Description: ++ * Private channel header shared between DSPSYS, WCD and WMD modules. ++ * ++ * Public Functions: ++ * None. ++ * ++ * Notes: ++ * ++ *! Revision History: ++ *! ================ ++ *! 05-Jan-2002 ag Added cChannels(total # of chnls) to CHNL_MGRINFO struct. ++ *! Added private CHNL_[PCPY][ZCPY][DDMA]. ++ *! 17-Nov-2000 jeh Removed IRQ, shared memory from CHNL_MGRATTRS, since these ++ *! now belong to IO_ATTRS. ++ *! 21-Jan-2000 ag: Code review comments added. ++ *! 05-Jan-2000 ag: Text format cleanup. ++ *! 11-Dec-1999 ag: Added CHNL_MAXLOCKPAGES for CHNL_PrepareBuffer(). ++ *! 04-Dec-1999 ag: Added CHNL_MAXEVTNAMELEN for i/o compl named event support. ++ *! 01-Nov-1999 ag: CHNL_MAXCHANNELS set to 16 for 16-bit DSPs. ++ *! 27-Oct-1997 cr: Expanded CHNL_MAXIRQ from 0x0f to 0xff. ++ *! 16-Jan-1997 gp: Moved symbols into here from chnldefs.h. ++ *! 03-Jan-1997 gp: Added CHNL_MAXIRQ define. ++ *! 09-Dec-1996 gp: Removed CHNL_STATEIDLE. ++ *! 15-Jul-1996 gp: Created. ++ */ ++ ++#ifndef CHNLPRIV_ ++#define CHNLPRIV_ ++ ++#include ++#include ++#include ++ ++/* CHNL Object validation signatures: */ ++#define CHNL_MGRSIGNATURE 0x52474D43 /* "CMGR" (in reverse). */ ++#define CHNL_SIGNATURE 0x4C4E4843 /* "CHNL" (in reverse). */ ++ ++/* Channel manager limits: */ ++#define CHNL_MAXCHANNELS 32 /* Max channels available per transport */ ++ ++ ++/* ++ * Trans port channel Id definitions:(must match dsp-side). ++ * ++ * For CHNL_MAXCHANNELS = 16: ++ * ++ * ChnlIds: ++ * 0-15 (PCPY) - transport 0) ++ * 16-31 (DDMA) - transport 1) ++ * 32-47 (ZCPY) - transport 2) ++ */ ++#define CHNL_PCPY 0 /* Proc-copy transport 0 */ ++ ++#define CHNL_MAXIRQ 0xff /* Arbitrarily large number. */ ++ ++/* The following modes are private: */ ++#define CHNL_MODEUSEREVENT 0x1000 /* User provided the channel event. */ ++#define CHNL_MODEMASK 0x1001 ++ ++/* Higher level channel states: */ ++#define CHNL_STATEREADY 0x0000 /* Channel ready for I/O. */ ++#define CHNL_STATECANCEL 0x0001 /* I/O was cancelled. */ ++#define CHNL_STATEEOS 0x0002 /* End Of Stream reached. */ ++ ++/* Determine if user supplied an event for this channel: */ ++#define CHNL_IsUserEvent(mode) (mode & CHNL_MODEUSEREVENT) ++ ++/* Macros for checking mode: */ ++#define CHNL_IsInput(mode) (mode & CHNL_MODEFROMDSP) ++#define CHNL_IsOutput(mode) (!CHNL_IsInput(mode)) ++ ++/* Types of channel class libraries: */ ++#define CHNL_TYPESM 1 /* Shared memory driver. */ ++#define CHNL_TYPEBM 2 /* Bus Mastering driver. */ ++ ++/* Max string length of channel I/O completion event name - change if needed */ ++#define CHNL_MAXEVTNAMELEN 32 ++ ++/* Max memory pages lockable in CHNL_PrepareBuffer() - change if needed */ ++#define CHNL_MAXLOCKPAGES 64 ++ ++/* Channel info. */ ++ struct CHNL_INFO { ++ struct CHNL_MGR *hChnlMgr; /* Owning channel manager. */ ++ u32 dwID; /* Channel ID. */ ++ HANDLE hEvent; /* Channel I/O completion event. */ ++ /*Abstraction of I/O completion event.*/ ++ struct SYNC_OBJECT *hSyncEvent; ++ u32 dwMode; /* Channel mode. */ ++ u32 dwState; /* Current channel state. */ ++ u32 cPosition; /* Total bytes transferred. */ ++ u32 cIOCs; /* Number of IOCs in queue. */ ++ u32 cIOReqs; /* Number of IO Requests in queue. */ ++ u32 hProcess; /* Process owning this channel. */ ++ /* ++ * Name of channel I/O completion event. Not required in Linux ++ */ ++ char szEventName[CHNL_MAXEVTNAMELEN + 1]; ++ } ; ++ ++/* Channel manager info: */ ++ struct CHNL_MGRINFO { ++ u32 dwType; /* Type of channel class library. */ ++ /* Channel handle, given the channel id. */ ++ struct CHNL_OBJECT *hChnl; ++ u32 cOpenChannels; /* Number of open channels. */ ++ u32 cChannels; /* total # of chnls supported */ ++ } ; ++ ++/* Channel Manager Attrs: */ ++ struct CHNL_MGRATTRS { ++ /* Max number of channels this manager can use. */ ++ u32 cChannels; ++ u32 uWordSize; /* DSP Word size. */ ++ } ; ++ ++#endif /* CHNLPRIV_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/clk.h b/arch/arm/plat-omap/include/dspbridge/clk.h +new file mode 100644 +index 0000000..4a23dab +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/clk.h +@@ -0,0 +1,155 @@ ++/* ++ * clk.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== clk.h ======== ++ * Purpose: Provides Clock functions. ++ * ++ *! Revision History: ++ *! ================ ++ *! 08-May-2007 rg: Moved all clock functions from sync module. ++ */ ++ ++#ifndef _CLK_H ++#define _CLK_H ++ ++ /* Generic TIMER object: */ ++ struct TIMER_OBJECT; ++ enum SERVICES_ClkId { ++ SERVICESCLK_iva2_ck = 0, ++ SERVICESCLK_mailbox_ick, ++ SERVICESCLK_gpt5_fck, ++ SERVICESCLK_gpt5_ick, ++ SERVICESCLK_gpt6_fck, ++ SERVICESCLK_gpt6_ick, ++ SERVICESCLK_gpt7_fck, ++ SERVICESCLK_gpt7_ick, ++ SERVICESCLK_gpt8_fck, ++ SERVICESCLK_gpt8_ick, ++ SERVICESCLK_wdt3_fck, ++ SERVICESCLK_wdt3_ick, ++ SERVICESCLK_mcbsp1_fck, ++ SERVICESCLK_mcbsp1_ick, ++ SERVICESCLK_mcbsp2_fck, ++ SERVICESCLK_mcbsp2_ick, ++ SERVICESCLK_mcbsp3_fck, ++ SERVICESCLK_mcbsp3_ick, ++ SERVICESCLK_mcbsp4_fck, ++ SERVICESCLK_mcbsp4_ick, ++ SERVICESCLK_mcbsp5_fck, ++ SERVICESCLK_mcbsp5_ick, ++ SERVICESCLK_ssi_fck, ++ SERVICESCLK_ssi_ick, ++ SERVICESCLK_sys_32k_ck, ++ SERVICESCLK_sys_ck, ++ SERVICESCLK_NOT_DEFINED ++ } ; ++ ++/* ++ * ======== CLK_Exit ======== ++ * Purpose: ++ * Discontinue usage of module; free resources when reference count ++ * reaches 0. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * CLK initialized. ++ * Ensures: ++ * Resources used by module are freed when cRef reaches zero. ++ */ ++ extern void CLK_Exit(void); ++ ++/* ++ * ======== CLK_Init ======== ++ * Purpose: ++ * Initializes private state of CLK module. ++ * Parameters: ++ * Returns: ++ * TRUE if initialized; FALSE if error occured. ++ * Requires: ++ * Ensures: ++ * CLK initialized. ++ */ ++ extern bool CLK_Init(void); ++ ++ ++/* ++ * ======== CLK_Enable ======== ++ * Purpose: ++ * Enables the clock requested. ++ * Parameters: ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Error occured while enabling the clock. ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS CLK_Enable(IN enum SERVICES_ClkId clk_id); ++ ++/* ++ * ======== CLK_Disable ======== ++ * Purpose: ++ * Disables the clock requested. ++ * Parameters: ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Error occured while disabling the clock. ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS CLK_Disable(IN enum SERVICES_ClkId clk_id); ++ ++/* ++ * ======== CLK_GetRate ======== ++ * Purpose: ++ * Get the clock rate of requested clock. ++ * Parameters: ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Error occured while Getting the clock rate. ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS CLK_GetRate(IN enum SERVICES_ClkId clk_id, ++ u32 *speedMhz); ++/* ++ * ======== CLK_Set_32KHz ======== ++ * Purpose: ++ * Set the requested clock to 32KHz. ++ * Parameters: ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Error occured while setting the clock parent to 32KHz. ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS CLK_Set_32KHz(IN enum SERVICES_ClkId clk_id); ++ extern void SSI_Clk_Prepare(bool FLAG); ++ ++/* ++ * ======== CLK_Get_RefCnt ======== ++ * Purpose: ++ * get the reference count for the clock. ++ * Parameters: ++ * Returns: ++ * s32: Reference Count for the clock. ++ * DSP_EFAIL: Error occured while getting the reference count of a clock. ++ * Requires: ++ * Ensures: ++ */ ++ extern s32 CLK_Get_UseCnt(IN enum SERVICES_ClkId clk_id); ++ ++#endif /* _SYNC_H */ +diff --git a/arch/arm/plat-omap/include/dspbridge/cmm.h b/arch/arm/plat-omap/include/dspbridge/cmm.h +new file mode 100644 +index 0000000..0df8b83 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/cmm.h +@@ -0,0 +1,420 @@ ++/* ++ * cmm.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== cmm.h ======== ++ * Purpose: ++ * The Communication Memory Management(CMM) module provides shared memory ++ * management services for DSP/BIOS Bridge data streaming and messaging. ++ * Multiple shared memory segments can be registered with CMM. Memory is ++ * coelesced back to the appropriate pool when a buffer is freed. ++ * ++ * The CMM_Xlator[xxx] functions are used for node messaging and data ++ * streaming address translation to perform zero-copy inter-processor ++ * data transfer(GPP<->DSP). A "translator" object is created for a node or ++ * stream object that contains per thread virtual address information. This ++ * translator info is used at runtime to perform SM address translation ++ * to/from the DSP address space. ++ * ++ * ++ * Public Functions: ++ * CMM_CallocBuf ++ * CMM_Create ++ * CMM_Destroy ++ * CMM_Exit ++ * CMM_FreeBuf ++ * CMM_GetHandle ++ * CMM_GetInfo ++ * CMM_Init ++ * CMM_RegisterGPPSMSeg ++ * CMM_UnRegisterGPPSMSeg ++ * CMM_XlatorAllocBuf (Note #1 below) ++ * CMM_XlatorCreate " ++ * CMM_XlatorDelete " ++ * CMM_XlatorFreeBuf " ++ * CMM_XlatorTranslate " ++ * ++ * ++ * Notes: ++ * #1: Used by Node and Stream modules for SM address translation. ++ * ++ *! Revision History: ++ *! ================ ++ *! 30-Jan-2002 ag Removed unused CMM_Alloc[Free]Desc & CMM_XlatorRegisterPa. ++ *! Renamed CMM_AllocBuf() to CMM_CallocBuf(). ++ *! 29-Aug-2001 ag: Added dsp virt base and size to CMM_RegisterGPPSMSeg(). ++ *! 12-Aug-2001 ag: Added CMM_UnRegisterGPP[DSP}SMSeg[s](). ++ *! 05-Dec-2000 ag: Added param to CMM_XlatorDelete() to force buf cleanup. ++ *! 30-Oct-2000 ag: Added conversion factor to CMM_RegisterDSP[GPP]SMSeg(). ++ *! 12-Oct-2000 ag: Added CMM_Xlator[xxx] functions. ++ *! 10-Aug-2000 ag: Created. ++ *! ++ */ ++ ++#ifndef CMM_ ++#define CMM_ ++ ++#include ++ ++#include ++#include ++ ++/* ++ * ======== CMM_CallocBuf ======== ++ * Purpose: ++ * Allocate memory buffers that can be used for data streaming or ++ * messaging. ++ * Parameters: ++ * hCmmMgr: Cmm Mgr handle. ++ * uSize: Number of bytes to allocate. ++ * pAttr: Attributes of memory to allocate. ++ * ppBufVA: Address of where to place VA. ++ * Returns: ++ * Pointer to a zero'd block of SM memory; ++ * NULL if memory couldn't be allocated, ++ * or if cBytes == 0, ++ * Requires: ++ * Valid hCmmMgr. ++ * CMM initialized. ++ * Ensures: ++ * The returned pointer, if not NULL, points to a valid memory block of ++ * the size requested. ++ * ++ */ ++ extern void *CMM_CallocBuf(struct CMM_OBJECT *hCmmMgr, ++ u32 uSize, struct CMM_ATTRS *pAttrs, ++ OUT void **ppBufVA); ++ ++/* ++ * ======== CMM_Create ======== ++ * Purpose: ++ * Create a communication memory manager object. ++ * Parameters: ++ * phCmmMgr: Location to store a communication manager handle on output. ++ * hDevObject: Handle to a device object. ++ * pMgrAttrs: Comm mem manager attributes. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EMEMORY: Insufficient memory for requested resources. ++ * DSP_EFAIL: Failed to initialize critical sect sync object. ++ * ++ * Requires: ++ * CMM_Init(void) called. ++ * phCmmMgr != NULL. ++ * pMgrAttrs->ulMinBlockSize >= 4 bytes. ++ * Ensures: ++ * ++ */ ++ extern DSP_STATUS CMM_Create(OUT struct CMM_OBJECT **phCmmMgr, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct CMM_MGRATTRS *pMgrAttrs); ++ ++/* ++ * ======== CMM_Destroy ======== ++ * Purpose: ++ * Destroy the communication memory manager object. ++ * Parameters: ++ * hCmmMgr: Cmm Mgr handle. ++ * bForce: Force deallocation of all cmm memory immediately if set TRUE. ++ * If FALSE, and outstanding allocations will return DSP_EFAIL ++ * status. ++ * Returns: ++ * DSP_SOK: CMM object & resources deleted. ++ * DSP_EFAIL: Unable to free CMM object due to outstanding allocation. ++ * DSP_EHANDLE: Unable to free CMM due to bad handle. ++ * Requires: ++ * CMM is initialized. ++ * hCmmMgr != NULL. ++ * Ensures: ++ * Memory resources used by Cmm Mgr are freed. ++ */ ++ extern DSP_STATUS CMM_Destroy(struct CMM_OBJECT *hCmmMgr, bool bForce); ++ ++/* ++ * ======== CMM_Exit ======== ++ * Purpose: ++ * Discontinue usage of module. Cleanup CMM module if CMM cRef reaches zero. ++ * Parameters: ++ * n/a ++ * Returns: ++ * n/a ++ * Requires: ++ * CMM is initialized. ++ * Ensures: ++ */ ++ extern void CMM_Exit(void); ++ ++/* ++ * ======== CMM_FreeBuf ======== ++ * Purpose: ++ * Free the given buffer. ++ * Parameters: ++ * hCmmMgr: Cmm Mgr handle. ++ * pBuf: Pointer to memory allocated by CMM_CallocBuf(). ++ * ulSegId: SM segment Id used in CMM_Calloc() attrs. ++ * Set to 0 to use default segment. ++ * Returns: ++ * DSP_SOK ++ * DSP_EFAIL ++ * Requires: ++ * CMM initialized. ++ * pBufPA != NULL ++ * Ensures: ++ * ++ */ ++ extern DSP_STATUS CMM_FreeBuf(struct CMM_OBJECT *hCmmMgr, ++ void *pBufPA, u32 ulSegId); ++ ++/* ++ * ======== CMM_GetHandle ======== ++ * Purpose: ++ * Return the handle to the cmm mgr for the given device obj. ++ * Parameters: ++ * hProcessor: Handle to a Processor. ++ * phCmmMgr: Location to store the shared memory mgr handle on output. ++ * ++ * Returns: ++ * DSP_SOK: Cmm Mgr opaque handle returned. ++ * DSP_EHANDLE: Invalid handle. ++ * Requires: ++ * phCmmMgr != NULL ++ * hDevObject != NULL ++ * Ensures: ++ */ ++ extern DSP_STATUS CMM_GetHandle(DSP_HPROCESSOR hProcessor, ++ OUT struct CMM_OBJECT **phCmmMgr); ++ ++/* ++ * ======== CMM_GetInfo ======== ++ * Purpose: ++ * Return the current SM and VM utilization information. ++ * Parameters: ++ * hCmmMgr: Handle to a Cmm Mgr. ++ * pCmmInfo: Location to store the Cmm information on output. ++ * ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid handle. ++ * DSP_EINVALIDARG Invalid input argument. ++ * Requires: ++ * Ensures: ++ * ++ */ ++ extern DSP_STATUS CMM_GetInfo(struct CMM_OBJECT *hCmmMgr, ++ OUT struct CMM_INFO *pCmmInfo); ++ ++/* ++ * ======== CMM_Init ======== ++ * Purpose: ++ * Initializes private state of CMM module. ++ * Parameters: ++ * Returns: ++ * TRUE if initialized; FALSE if error occured. ++ * Requires: ++ * Ensures: ++ * CMM initialized. ++ */ ++ extern bool CMM_Init(void); ++ ++/* ++ * ======== CMM_RegisterGPPSMSeg ======== ++ * Purpose: ++ * Register a block of SM with the CMM. ++ * Parameters: ++ * hCmmMgr: Handle to a Cmm Mgr. ++ * lpGPPBasePA: GPP Base Physical address. ++ * ulSize: Size in GPP bytes. ++ * dwDSPAddrOffset GPP PA to DSP PA Offset. ++ * cFactor: Add offset if CMM_ADDTODSPPA, sub if CMM_SUBFROMDSPPA. ++ * dwDSPBase: DSP virtual base byte address. ++ * ulDSPSize: Size of DSP segment in bytes. ++ * pulSegId: Address to store segment Id. ++ * ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hCmmMgr handle. ++ * DSP_EINVALIDARG: Invalid input argument. ++ * DSP_EFAIL: Unable to register. ++ * - On success *pulSegId is a valid SM segment ID. ++ * Requires: ++ * ulSize > 0 ++ * pulSegId != NULL ++ * dwGPPBasePA != 0 ++ * cFactor = CMM_ADDTODSPPA || cFactor = CMM_SUBFROMDSPPA ++ * Ensures: ++ * ++ */ ++ extern DSP_STATUS CMM_RegisterGPPSMSeg(struct CMM_OBJECT *hCmmMgr, ++ unsigned int dwGPPBasePA, ++ u32 ulSize, ++ u32 dwDSPAddrOffset, ++ enum CMM_CNVTTYPE cFactor, ++ unsigned int dwDSPBase, ++ u32 ulDSPSize, ++ u32 *pulSegId, ++ u32 dwGPPBaseBA); ++ ++/* ++ * ======== CMM_UnRegisterGPPSMSeg ======== ++ * Purpose: ++ * Unregister the given memory segment that was previously registered ++ * by CMM_RegisterGPPSMSeg. ++ * Parameters: ++ * hCmmMgr: Handle to a Cmm Mgr. ++ * ulSegId Segment identifier returned by CMM_RegisterGPPSMSeg. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid handle. ++ * DSP_EINVALIDARG: Invalid ulSegId. ++ * DSP_EFAIL: Unable to unregister for unknown reason. ++ * Requires: ++ * Ensures: ++ * ++ */ ++ extern DSP_STATUS CMM_UnRegisterGPPSMSeg(struct CMM_OBJECT *hCmmMgr, ++ u32 ulSegId); ++ ++/* ++ * ======== CMM_XlatorAllocBuf ======== ++ * Purpose: ++ * Allocate the specified SM buffer and create a local memory descriptor. ++ * Place on the descriptor on the translator's HaQ (Host Alloc'd Queue). ++ * Parameters: ++ * hXlator: Handle to a Xlator object. ++ * pVaBuf: Virtual address ptr(client context) ++ * uPaSize: Size of SM memory to allocate. ++ * Returns: ++ * Ptr to valid physical address(Pa) of uPaSize bytes, NULL if failed. ++ * Requires: ++ * pVaBuf != 0. ++ * uPaSize != 0. ++ * Ensures: ++ * ++ */ ++ extern void *CMM_XlatorAllocBuf(struct CMM_XLATOROBJECT *hXlator, ++ void *pVaBuf, u32 uPaSize); ++ ++/* ++ * ======== CMM_XlatorCreate ======== ++ * Purpose: ++ * Create a translator(xlator) object used for process specific Va<->Pa ++ * address translation. Node messaging and streams use this to perform ++ * inter-processor(GPP<->DSP) zero-copy data transfer. ++ * Parameters: ++ * phXlator: Address to place handle to a new Xlator handle. ++ * hCmmMgr: Handle to Cmm Mgr associated with this translator. ++ * pXlatorAttrs: Translator attributes used for the client NODE or STREAM. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EINVALIDARG: Bad input Attrs. ++ * DSP_EMEMORY: Insufficient memory(local) for requested resources. ++ * Requires: ++ * phXlator != NULL ++ * hCmmMgr != NULL ++ * pXlatorAttrs != NULL ++ * Ensures: ++ * ++ */ ++ extern DSP_STATUS CMM_XlatorCreate(OUT struct CMM_XLATOROBJECT **phXlator, ++ struct CMM_OBJECT *hCmmMgr, ++ struct CMM_XLATORATTRS *pXlatorAttrs); ++ ++/* ++ * ======== CMM_XlatorDelete ======== ++ * Purpose: ++ * Delete translator resources ++ * Parameters: ++ * hXlator: handle to translator. ++ * bForce: bForce = TRUE will free XLators SM buffers/dscriptrs. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Bad translator handle. ++ * DSP_EFAIL: Unable to free translator resources. ++ * Requires: ++ * cRefs > 0 ++ * Ensures: ++ * ++ */ ++ extern DSP_STATUS CMM_XlatorDelete(struct CMM_XLATOROBJECT *hXlator, ++ bool bForce); ++ ++/* ++ * ======== CMM_XlatorFreeBuf ======== ++ * Purpose: ++ * Free SM buffer and descriptor. ++ * Does not free client process VM. ++ * Parameters: ++ * hXlator: handle to translator. ++ * pBufVa Virtual address of PA to free. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Bad translator handle. ++ * Requires: ++ * Ensures: ++ * ++ */ ++ extern DSP_STATUS CMM_XlatorFreeBuf(struct CMM_XLATOROBJECT *hXlator, ++ void *pBufVa); ++ ++/* ++ * ======== CMM_XlatorInfo ======== ++ * Purpose: ++ * Set/Get process specific "translator" address info. ++ * This is used to perform fast virtaul address translation ++ * for shared memory buffers between the GPP and DSP. ++ * Parameters: ++ * hXlator: handle to translator. ++ * pAddr: Virtual base address of segment. ++ * ulSize: Size in bytes. ++ * uSegId: Segment identifier of SM segment(s) ++ * bSetInfo Set xlator fields if TRUE, else return base addr ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Bad translator handle. ++ * Requires: ++ * (cRefs > 0) ++ * (pAddr != NULL) ++ * (ulSize > 0) ++ * Ensures: ++ * ++ */ ++ extern DSP_STATUS CMM_XlatorInfo(struct CMM_XLATOROBJECT *hXlator, ++ IN OUT u8 **pAddr, ++ u32 ulSize, u32 uSegId, ++ bool bSetInfo); ++ ++/* ++ * ======== CMM_XlatorTranslate ======== ++ * Purpose: ++ * Perform address translation VA<->PA for the specified stream or ++ * message shared memory buffer. ++ * Parameters: ++ * hXlator: handle to translator. ++ * pAddr address of buffer to translate. ++ * xType Type of address xlation. CMM_PA2VA or CMM_VA2PA. ++ * Returns: ++ * Valid address on success, else NULL. ++ * Requires: ++ * cRefs > 0 ++ * pAddr != NULL ++ * xType >= CMM_VA2PA) && (xType <= CMM_DSPPA2PA) ++ * Ensures: ++ * ++ */ ++ extern void *CMM_XlatorTranslate(struct CMM_XLATOROBJECT *hXlator, ++ void *pAddr, enum CMM_XLATETYPE xType); ++ ++#endif /* CMM_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/cmmdefs.h b/arch/arm/plat-omap/include/dspbridge/cmmdefs.h +new file mode 100644 +index 0000000..a779377 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/cmmdefs.h +@@ -0,0 +1,135 @@ ++/* ++ * cmmdefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== cmmdefs.h ======== ++ * Purpose: ++ * Global MEM constants and types. ++ * ++ *! Revision History: ++ *! ================ ++ *! 12-Nov-2001 ag CMM_KERNMAPTYPE added for dsp<->device process addr map'n. ++ *! This allows addr conversion from drvr process <-> DSP addr. ++ *! 29-Aug-2001 ag Added CMM_ALLSEGMENTS. ++ *! 08-Dec-2000 ag Added bus address conversion type CMM_POMAPEMIF2DSPBUS. ++ *! 05-Dec-2000 ag Added default CMM_DEFLTCONVFACTOR & CMM_DEFLTDSPADDROFFSET. ++ *! 29-Oct-2000 ag Added converstion factor for GPP DSP Pa translation. ++ *! 15-Oct-2000 ag Added address translator attributes and defaults. ++ *! 12-Jul-2000 ag Created. ++ */ ++ ++#ifndef CMMDEFS_ ++#define CMMDEFS_ ++ ++#include ++ ++/* Cmm attributes used in CMM_Create() */ ++ struct CMM_MGRATTRS { ++ /* Minimum SM allocation; default 32 bytes. */ ++ u32 ulMinBlockSize; ++ } ; ++ ++/* Attributes for CMM_AllocBuf() & CMM_AllocDesc() */ ++ struct CMM_ATTRS { ++ u32 ulSegId; /* 1,2... are SM segments. 0 is not. */ ++ u32 ulAlignment; /* 0,1,2,4....ulMinBlockSize */ ++ } ; ++ ++/* ++ * DSPPa to GPPPa Conversion Factor. ++ * ++ * For typical platforms: ++ * converted Address = PaDSP + ( cFactor * addressToConvert). ++ */ ++ enum CMM_CNVTTYPE { ++ CMM_SUBFROMDSPPA = -1, ++ /* PreOMAP is special case: not simple offset */ ++ CMM_POMAPEMIF2DSPBUS = 0, ++ CMM_ADDTODSPPA = 1 ++ } ; ++ ++#define CMM_DEFLTDSPADDROFFSET 0 ++#define CMM_DEFLTCONVFACTOR CMM_POMAPEMIF2DSPBUS /* PreOMAP DSPBUS<->EMIF */ ++#define CMM_ALLSEGMENTS 0xFFFFFF /* All SegIds */ ++#define CMM_MAXGPPSEGS 1 /* Maximum # of SM segs */ ++ ++/* ++ * SMSEGs are SM segments the DSP allocates from. ++ * ++ * This info is used by the GPP to xlate DSP allocated PAs. ++ */ ++ ++ struct CMM_SEGINFO { ++ u32 dwSegBasePa; /* Start Phys address of SM segment */ ++ /* Total size in bytes of segment: DSP+GPP */ ++ u32 ulTotalSegSize; ++ u32 dwGPPBasePA; /* Start Phys addr of Gpp SM seg */ ++ u32 ulGPPSize; /* Size of Gpp SM seg in bytes */ ++ u32 dwDSPBaseVA; /* DSP virt base byte address */ ++ u32 ulDSPSize; /* DSP seg size in bytes */ ++ /* # of current GPP allocations from this segment */ ++ u32 ulInUseCnt; ++ u32 dwSegBaseVa; /* Start Virt address of SM seg */ ++ ++ } ; ++ ++/* CMM useful information */ ++ struct CMM_INFO { ++ /* # of SM segments registered with this Cmm. */ ++ u32 ulNumGPPSMSegs; ++ /* Total # of allocations outstanding for CMM */ ++ u32 ulTotalInUseCnt; ++ /* Min SM block size allocation from CMM_Create() */ ++ u32 ulMinBlockSize; ++ /* Info per registered SM segment. */ ++ struct CMM_SEGINFO segInfo[CMM_MAXGPPSEGS]; ++ } ; ++ ++/* XlatorCreate attributes */ ++ struct CMM_XLATORATTRS { ++ u32 ulSegId; /* segment Id used for SM allocations */ ++ u32 dwDSPBufs; /* # of DSP-side bufs */ ++ u32 dwDSPBufSize; /* size of DSP-side bufs in GPP bytes */ ++ /* Vm base address alloc'd in client process context */ ++ void *pVmBase; ++ /* dwVmSize must be >= (dwMaxNumBufs * dwMaxSize) */ ++ u32 dwVmSize; ++ } ; ++ ++/* ++ * Cmm translation types. Use to map SM addresses to process context. ++ */ ++ enum CMM_XLATETYPE { ++ CMM_VA2PA = 0, /* Virtual to GPP physical address xlation */ ++ CMM_PA2VA = 1, /* GPP Physical to virtual */ ++ CMM_VA2DSPPA = 2, /* Va to DSP Pa */ ++ CMM_PA2DSPPA = 3, /* GPP Pa to DSP Pa */ ++ CMM_DSPPA2PA = 4, /* DSP Pa to GPP Pa */ ++ } ; ++ ++/* ++ * Used to "map" between device process virt addr and dsp addr. ++ */ ++ enum CMM_KERNMAPTYPE { ++ CMM_KERNVA2DSP = 0, /* Device process context to dsp address. */ ++ CMM_DSP2KERNVA = 1, /* Dsp address to device process context. */ ++ } ; ++ ++ struct CMM_OBJECT; ++ struct CMM_XLATOROBJECT; ++ ++#endif /* CMMDEFS_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/cod.h b/arch/arm/plat-omap/include/dspbridge/cod.h +new file mode 100644 +index 0000000..a8a12c6 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/cod.h +@@ -0,0 +1,433 @@ ++/* ++ * cod.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== cod.h ======== ++ * Description: ++ * Code management module for DSPs. This module provides an interface ++ * interface for loading both static and dynamic code objects onto DSP ++ * systems. ++ * ++ * Public Functions: ++ * COD_Close ++ * COD_Create ++ * COD_Delete ++ * COD_Exit ++ * COD_GetBaseLib ++ * COD_GetBaseName ++ * COD_GetLoader ++ * COD_GetSection ++ * COD_GetSymValue ++ * COD_Init ++ * COD_LoadBase ++ * COD_Open ++ * COD_OpenBase ++ * COD_ReadSection ++ * COD_UnloadSection ++ * ++ * Note: ++ * Currently, only static loading is supported. ++ * ++ *! Revision History ++ *! ================ ++ *! 08-Apr-2003 map: Changed DBL to DBLL ++ *! 07-Aug-2002 jeh: Added COD_GetBaseName(). ++ *! 17-Jul-2002 jeh: Added COD_Open(), COD_Close(). ++ *! 15-Mar-2002 jeh: Added DBL_Flags param to COD_OpenBase(). ++ *! 19-Oct-2001 jeh: Added COD_GetBaseLib, COD_GetLoader, (left in ++ *! COD_LoadSection(), COD_UnloadSection(), since they ++ *! may be needed for BridgeLite). ++ *! 07-Sep-2001 jeh: Added COD_LoadSection(), COD_UnloadSection(). ++ *! 11-Jan-2001 jeh: Added COD_OpenBase. ++ *! 29-Sep-2000 kc: Added size param to COD_ReadSection for input buffer ++ *! validation. ++ *! 02-Aug-2000 kc: Added COD_ReadSection. ++ *! 04-Sep-1997 gp: Added CDECL identifier to COD_WRITEFXN (for NT).. ++ *! 18-Aug-1997 cr: Added explicit CDECL identifier. ++ *! 28-Oct-1996 gp: Added COD_GetSection. ++ *! 30-Jul-1996 gp: Added envp[] argument to COD_LoadBase(). ++ *! 12-Jun-1996 gp: Moved OUT param first in _Create(). Updated _Create() ++ *! call to take a ZLFileName. Moved COD_ processor types ++ *! to CFG. ++ *! 29-May-1996 gp: Changed WCD_STATUS to DSP_STATUS. Removed include's. ++ *! 07-May-1996 mg: Created. ++ * ++ */ ++ ++#ifndef COD_ ++#define COD_ ++ ++#include ++ ++#define COD_MAXPATHLENGTH 255 ++#define COD_TRACEBEG "SYS_PUTCBEG" ++#define COD_TRACEEND "SYS_PUTCEND" ++#define COD_TRACESECT "trace" ++#define COD_TRACEBEGOLD "PUTCBEG" ++#define COD_TRACEENDOLD "PUTCEND" ++ ++#define COD_NOLOAD DBLL_NOLOAD ++#define COD_SYMB DBLL_SYMB ++ ++/* Flags passed to COD_Open */ ++ typedef DBLL_Flags COD_FLAGS; ++ ++/* COD code manager handle */ ++ struct COD_MANAGER; ++ ++/* COD library handle */ ++ struct COD_LIBRARYOBJ; ++ ++/* COD attributes */ ++ struct COD_ATTRS { ++ u32 ulReserved; ++ } ; ++ ++/* ++ * Function prototypes for writing memory to a DSP system, allocating ++ * and freeing DSP memory. ++ */ ++ typedef u32(*COD_WRITEFXN) (void *pPrivRef, u32 ulDspAddr, ++ void *pBuf, u32 ulNumBytes, ++ u32 nMemSpace); ++ ++ ++/* ++ * ======== COD_Close ======== ++ * Purpose: ++ * Close a library opened with COD_Open(). ++ * Parameters: ++ * lib - Library handle returned by COD_Open(). ++ * Returns: ++ * None. ++ * Requires: ++ * COD module initialized. ++ * valid lib. ++ * Ensures: ++ * ++ */ ++ extern void COD_Close(struct COD_LIBRARYOBJ *lib); ++ ++/* ++ * ======== COD_Create ======== ++ * Purpose: ++ * Create an object to manage code on a DSP system. This object can be ++ * used to load an initial program image with arguments that can later ++ * be expanded with dynamically loaded object files. ++ * Symbol table information is managed by this object and can be retrieved ++ * using the COD_GetSymValue() function. ++ * Parameters: ++ * phManager: created manager object ++ * pstrZLFile: ZL DLL filename, of length < COD_MAXPATHLENGTH. ++ * attrs: attributes to be used by this object. A NULL value ++ * will cause default attrs to be used. ++ * Returns: ++ * DSP_SOK: Success. ++ * COD_E_NOZLFUNCTIONS: Could not initialize ZL functions. ++ * COD_E_ZLCREATEFAILED: ZL_Create failed. ++ * DSP_ENOTIMPL: attrs was not NULL. We don't yet support ++ * non default values of attrs. ++ * Requires: ++ * COD module initialized. ++ * pstrZLFile != NULL ++ * Ensures: ++ */ ++ extern DSP_STATUS COD_Create(OUT struct COD_MANAGER **phManager, ++ char *pstrZLFile, ++ IN OPTIONAL CONST struct COD_ATTRS *attrs); ++ ++/* ++ * ======== COD_Delete ======== ++ * Purpose: ++ * Delete a code manager object. ++ * Parameters: ++ * hManager: handle of manager to be deleted ++ * Returns: ++ * None. ++ * Requires: ++ * COD module initialized. ++ * valid hManager. ++ * Ensures: ++ */ ++ extern void COD_Delete(struct COD_MANAGER *hManager); ++ ++/* ++ * ======== COD_Exit ======== ++ * Purpose: ++ * Discontinue usage of the COD module. ++ * Parameters: ++ * None. ++ * Returns: ++ * None. ++ * Requires: ++ * COD initialized. ++ * Ensures: ++ * Resources acquired in COD_Init(void) are freed. ++ */ ++ extern void COD_Exit(void); ++ ++/* ++ * ======== COD_GetBaseLib ======== ++ * Purpose: ++ * Get handle to the base image DBL library. ++ * Parameters: ++ * hManager: handle of manager to be deleted ++ * plib: location to store library handle on output. ++ * Returns: ++ * DSP_SOK: Success. ++ * Requires: ++ * COD module initialized. ++ * valid hManager. ++ * plib != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS COD_GetBaseLib(struct COD_MANAGER *hManager, ++ struct DBLL_LibraryObj **plib); ++ ++/* ++ * ======== COD_GetBaseName ======== ++ * Purpose: ++ * Get the name of the base image DBL library. ++ * Parameters: ++ * hManager: handle of manager to be deleted ++ * pszName: location to store library name on output. ++ * uSize: size of name buffer. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Buffer too small. ++ * Requires: ++ * COD module initialized. ++ * valid hManager. ++ * pszName != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS COD_GetBaseName(struct COD_MANAGER *hManager, ++ char *pszName, u32 uSize); ++ ++/* ++ * ======== COD_GetEntry ======== ++ * Purpose: ++ * Retrieve the entry point of a loaded DSP program image ++ * Parameters: ++ * hManager: handle of manager to be deleted ++ * pulEntry: pointer to location for entry point ++ * Returns: ++ * DSP_SOK: Success. ++ * Requires: ++ * COD module initialized. ++ * valid hManager. ++ * pulEntry != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS COD_GetEntry(struct COD_MANAGER *hManager, ++ u32 *pulEntry); ++ ++/* ++ * ======== COD_GetLoader ======== ++ * Purpose: ++ * Get handle to the DBL loader. ++ * Parameters: ++ * hManager: handle of manager to be deleted ++ * phLoader: location to store loader handle on output. ++ * Returns: ++ * DSP_SOK: Success. ++ * Requires: ++ * COD module initialized. ++ * valid hManager. ++ * phLoader != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS COD_GetLoader(struct COD_MANAGER *hManager, ++ struct DBLL_TarObj **phLoader); ++ ++/* ++ * ======== COD_GetSection ======== ++ * Purpose: ++ * Retrieve the starting address and length of a section in the COFF file ++ * given the section name. ++ * Parameters: ++ * lib Library handle returned from COD_Open(). ++ * pstrSect: name of the section, with or without leading "." ++ * puAddr: Location to store address. ++ * puLen: Location to store length. ++ * Returns: ++ * DSP_SOK: Success ++ * COD_E_NOSYMBOLSLOADED: Symbols have not been loaded onto the board. ++ * COD_E_SYMBOLNOTFOUND: The symbol could not be found. ++ * Requires: ++ * COD module initialized. ++ * valid hManager. ++ * pstrSect != NULL; ++ * puAddr != NULL; ++ * puLen != NULL; ++ * Ensures: ++ * DSP_SOK: *puAddr and *puLen contain the address and length of the ++ * section. ++ * else: *puAddr == 0 and *puLen == 0; ++ * ++ */ ++ extern DSP_STATUS COD_GetSection(struct COD_LIBRARYOBJ *lib, ++ IN char *pstrSect, ++ OUT u32 *puAddr, ++ OUT u32 *puLen); ++ ++/* ++ * ======== COD_GetSymValue ======== ++ * Purpose: ++ * Retrieve the value for the specified symbol. The symbol is first ++ * searched for literally and then, if not found, searched for as a ++ * C symbol. ++ * Parameters: ++ * lib: library handle returned from COD_Open(). ++ * pstrSymbol: name of the symbol ++ * value: value of the symbol ++ * Returns: ++ * DSP_SOK: Success. ++ * COD_E_NOSYMBOLSLOADED: Symbols have not been loaded onto the board. ++ * COD_E_SYMBOLNOTFOUND: The symbol could not be found. ++ * Requires: ++ * COD module initialized. ++ * Valid hManager. ++ * pstrSym != NULL. ++ * pulValue != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS COD_GetSymValue(struct COD_MANAGER *hManager, ++ IN char *pstrSym, ++ OUT u32 *pulValue); ++ ++/* ++ * ======== COD_Init ======== ++ * Purpose: ++ * Initialize the COD module's private state. ++ * Parameters: ++ * None. ++ * Returns: ++ * TRUE if initialized; FALSE if error occured. ++ * Requires: ++ * Ensures: ++ * A requirement for each of the other public COD functions. ++ */ ++ extern bool COD_Init(void); ++ ++/* ++ * ======== COD_LoadBase ======== ++ * Purpose: ++ * Load the initial program image, optionally with command-line arguments, ++ * on the DSP system managed by the supplied handle. The program to be ++ * loaded must be the first element of the args array and must be a fully ++ * qualified pathname. ++ * Parameters: ++ * hMgr: manager to load the code with ++ * nArgc: number of arguments in the args array ++ * args: array of strings for arguments to DSP program ++ * writeFxn: board-specific function to write data to DSP system ++ * pArb: arbitrary pointer to be passed as first arg to writeFxn ++ * envp: array of environment strings for DSP exec. ++ * Returns: ++ * DSP_SOK: Success. ++ * COD_E_OPENFAILED: Failed to open target code. ++ * COD_E_LOADFAILED: Failed to load code onto target. ++ * Requires: ++ * COD module initialized. ++ * hMgr is valid. ++ * nArgc > 0. ++ * aArgs != NULL. ++ * aArgs[0] != NULL. ++ * pfnWrite != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS COD_LoadBase(struct COD_MANAGER *hManager, ++ u32 nArgc, char *aArgs[], ++ COD_WRITEFXN pfnWrite, void *pArb, ++ char *envp[]); ++ ++ ++/* ++ * ======== COD_Open ======== ++ * Purpose: ++ * Open a library for reading sections. Does not load or set the base. ++ * Parameters: ++ * hMgr: manager to load the code with ++ * pszCoffPath: Coff file to open. ++ * flags: COD_NOLOAD (don't load symbols) or COD_SYMB (load ++ * symbols). ++ * pLib: Handle returned that can be used in calls to COD_Close ++ * and COD_GetSection. ++ * Returns: ++ * S_OK: Success. ++ * COD_E_OPENFAILED: Failed to open target code. ++ * Requires: ++ * COD module initialized. ++ * hMgr is valid. ++ * flags == COD_NOLOAD || flags == COD_SYMB. ++ * pszCoffPath != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS COD_Open(struct COD_MANAGER *hMgr, ++ IN char *pszCoffPath, ++ COD_FLAGS flags, ++ OUT struct COD_LIBRARYOBJ **pLib); ++ ++/* ++ * ======== COD_OpenBase ======== ++ * Purpose: ++ * Open base image for reading sections. Does not load the base. ++ * Parameters: ++ * hMgr: manager to load the code with ++ * pszCoffPath: Coff file to open. ++ * flags: Specifies whether to load symbols. ++ * Returns: ++ * DSP_SOK: Success. ++ * COD_E_OPENFAILED: Failed to open target code. ++ * Requires: ++ * COD module initialized. ++ * hMgr is valid. ++ * pszCoffPath != NULL. ++ * Ensures: ++ */ ++extern DSP_STATUS COD_OpenBase(struct COD_MANAGER *hMgr, IN char *pszCoffPath, ++ DBLL_Flags flags); ++ ++/* ++ * ======== COD_ReadSection ======== ++ * Purpose: ++ * Retrieve the content of a code section given the section name. ++ * Parameters: ++ * hManager - manager in which to search for the symbol ++ * pstrSect - name of the section, with or without leading "." ++ * pstrContent - buffer to store content of the section. ++ * Returns: ++ * DSP_SOK: on success, error code on failure ++ * COD_E_NOSYMBOLSLOADED: Symbols have not been loaded onto the board. ++ * COD_E_READFAILED: Failed to read content of code section. ++ * Requires: ++ * COD module initialized. ++ * valid hManager. ++ * pstrSect != NULL; ++ * pstrContent != NULL; ++ * Ensures: ++ * DSP_SOK: *pstrContent stores the content of the named section. ++ */ ++ extern DSP_STATUS COD_ReadSection(struct COD_LIBRARYOBJ *lib, ++ IN char *pstrSect, ++ OUT char *pstrContent, ++ IN u32 cContentSize); ++ ++ ++ ++#endif /* COD_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/csl.h b/arch/arm/plat-omap/include/dspbridge/csl.h +new file mode 100644 +index 0000000..b90d6ff +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/csl.h +@@ -0,0 +1,135 @@ ++/* ++ * csl.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== csl.h ======== ++ * Purpose: ++ * Platform independent C Standard library functions. ++ * ++ * Public Functions: ++ * CSL_AnsiToWchar ++ * CSL_ByteSwap ++ * CSL_Exit ++ * CSL_Init ++ * CSL_NumToAscii ++ * CSL_Strtok ++ * CSL_Strtokr ++ * CSL_WcharToAnsi ++ * ++ *! Revision History: ++ *! ================ ++ *! 07-Aug-2002 jeh: Added CSL_Strtokr(). ++ *! 21-Sep-2001 jeh: Added CSL_Strncmp. ++ *! 22-Nov-2000 map: Added CSL_Atoi and CSL_Strtok ++ *! 19-Nov-2000 kc: Added CSL_ByteSwap(). ++ *! 09-Nov-2000 kc: Added CSL_Strncat. ++ *! 29-Oct-1999 kc: Added CSL_Wstrlen(). ++ *! 20-Sep-1999 ag: Added CSL_Wchar2Ansi(). ++ *! 19-Jan-1998 cr: Code review cleanup (mostly documentation fixes). ++ *! 29-Dec-1997 cr: Changed CSL_lowercase to CSL_Uppercase, added ++ *! CSL_AnsiToWchar. ++ *! 30-Sep-1997 cr: Added explicit cdecl descriptors to fxn definitions. ++ *! 25-Jun-1997 cr: Added CSL_strcmp. ++ *! 12-Jun-1996 gp: Created. ++ */ ++ ++#ifndef CSL_ ++#define CSL_ ++ ++#include ++ ++/* ++ * ======== CSL_Exit ======== ++ * Purpose: ++ * Discontinue usage of the CSL module. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * CSL initialized. ++ * Ensures: ++ * Resources acquired in CSL_Init(void) are freed. ++ */ ++ extern void CSL_Exit(void); ++ ++/* ++ * ======== CSL_Init ======== ++ * Purpose: ++ * Initialize the CSL module's private state. ++ * Parameters: ++ * Returns: ++ * TRUE if initialized; FALSE if error occured. ++ * Requires: ++ * Ensures: ++ * A requirement for each of the other public CSL functions. ++ */ ++ extern bool CSL_Init(void); ++ ++/* ++ * ======== CSL_NumToAscii ======== ++ * Purpose: ++ * Convert a 1 or 2 digit number to a 2 digit string. ++ * Parameters: ++ * pstrNumber: Buffer to store converted string. ++ * dwNum: Number to convert. ++ * Returns: ++ * Requires: ++ * pstrNumber must be able to hold at least three characters. ++ * Ensures: ++ * pstrNumber will be null terminated. ++ */ ++ extern void CSL_NumToAscii(OUT char *pstrNumber, IN u32 dwNum); ++ ++ ++/* ++ * ======== CSL_Strtok ======== ++ * Purpose: ++ * Tokenize a NULL terminated string ++ * Parameters: ++ * ptstrSrc: pointer to string. ++ * szSeparators: pointer to a string of seperators ++ * Returns: ++ * char * ++ * Requires: ++ * CSL initialized. ++ * ptstrSrc is a valid string pointer. ++ * szSeparators is a valid string pointer. ++ * Ensures: ++ */ ++ extern char *CSL_Strtok(IN char *ptstrSrc, ++ IN CONST char *szSeparators); ++ ++/* ++ * ======== CSL_Strtokr ======== ++ * Purpose: ++ * Re-entrant version of strtok. ++ * Parameters: ++ * pstrSrc: Pointer to string. May be NULL on subsequent calls. ++ * szSeparators: Pointer to a string of seperators ++ * ppstrCur: Location to store start of string for next call to ++ * to CSL_Strtokr. ++ * Returns: ++ * char * (the token) ++ * Requires: ++ * CSL initialized. ++ * szSeparators != NULL ++ * ppstrCur != NULL ++ * Ensures: ++ */ ++ extern char *CSL_Strtokr(IN char *pstrSrc, ++ IN CONST char *szSeparators, ++ OUT char **ppstrCur); ++ ++#endif /* CSL_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/dbc.h b/arch/arm/plat-omap/include/dspbridge/dbc.h +new file mode 100644 +index 0000000..0e6a67d +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dbc.h +@@ -0,0 +1,66 @@ ++/* ++ * dbc.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== dbc.h ======== ++ * Purpose: ++ * "Design by Contract" programming macros. ++ * ++ * Public Functions: ++ * DBC_Assert ++ * DBC_Require ++ * DBC_Ensure ++ * ++ * Notes: ++ * Requires that the GT->ERROR function has been defaulted to a valid ++ * error handler for the given execution environment. ++ * ++ * Does not require that GT_init() be called. ++ * ++ *! Revision History: ++ *! ================ ++ *! 11-Aug-2000 ag: Removed include ++ *! 22-Apr-1996 gp: Created. ++ */ ++ ++#ifndef DBC_ ++#define DBC_ ++ ++#ifndef GT_TRACE ++#define GT_TRACE 0 /* 0 = "trace compiled out"; 1 = "trace active" */ ++#endif ++ ++/* Assertion Macros: */ ++#if GT_TRACE ++ ++#include ++ ++#define DBC_Assert(exp) \ ++ if (!(exp)) \ ++ printk("%s, line %d: Assertion (" #exp ") failed.\n", \ ++ __FILE__, __LINE__) ++#define DBC_Require DBC_Assert /* Function Precondition. */ ++#define DBC_Ensure DBC_Assert /* Function Postcondition. */ ++ ++#else ++ ++#define DBC_Assert(exp) ++#define DBC_Require(exp) ++#define DBC_Ensure(exp) ++ ++#endif /* DEBUG */ ++ ++#endif /* DBC_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/dbdcd.h b/arch/arm/plat-omap/include/dspbridge/dbdcd.h +new file mode 100644 +index 0000000..fbc3870 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dbdcd.h +@@ -0,0 +1,388 @@ ++/* ++ * dbdcd.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== dbdcd.h ======== ++ * Description: ++ * Defines the DSP/BIOS Bridge Configuration Database (DCD) API. ++ * ++ *! Revision History ++ *! ================ ++ *! 03-Dec-2003 map Changed DCD_OBJTYPE to DSP_DCDOBJTYPE ++ *! 24-Feb-2003 kc Updated DCD_AutoUnregister and DCD_GetObjects to simplify ++ *! DCD implementation. ++ *! 05-Aug-2002 jeh Added DCD_GetObjects(). ++ *! 11-Jul-2002 jeh Added DCD_GetDepLibs(), DCD_GetNumDepLibs(). ++ *! 22-Apr-2002 jeh Added DCD_GetLibraryName(). ++ *! 03-Apr-2001 sg Changed error names to have DCD_E* format. ++ *! 13-Feb-2001 kc Name changed from dcdbs.h to dbdcd.h. ++ *! 12-Dec-2000 kc Added DCD_AutoUnregister. ++ *! 09-Nov-2000 kc Updated usage of DCD_EnumerateObject. ++ *! 30-Oct-2000 kc Added DCD_AutoRegister. Updated error DCD error codes. ++ *! 29-Sep-2000 kc Incorporated code review comments. See ++ *! /src/reviews/dcd_review.txt. ++ *! 26-Jul-2000 kc Created. ++ *! ++ */ ++ ++#ifndef DBDCD_ ++#define DBDCD_ ++ ++#include ++#include ++#include ++ ++/* ++ * ======== DCD_AutoRegister ======== ++ * Purpose: ++ * This function automatically registers DCD objects specified in a ++ * special COFF section called ".dcd_register" ++ * Parameters: ++ * hDcdMgr: A DCD manager handle. ++ * pszCoffPath: Pointer to name of COFF file containing DCD ++ * objects to be registered. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EDCDNOAUTOREGISTER: Unable to find auto-registration section. ++ * DSP_EDCDREADSECT: Unable to read object code section. ++ * DSP_EDCDLOADBASE: Unable to load code base. ++ * DSP_EHANDLE: Invalid DCD_HMANAGER handle.. ++ * Requires: ++ * DCD initialized. ++ * Ensures: ++ * Note: ++ * Due to the DCD database construction, it is essential for a DCD-enabled ++ * COFF file to contain the right COFF sections, especially ++ * ".dcd_register", which is used for auto registration. ++ */ ++ extern DSP_STATUS DCD_AutoRegister(IN struct DCD_MANAGER *hDcdMgr, ++ IN char *pszCoffPath); ++ ++/* ++ * ======== DCD_AutoUnregister ======== ++ * Purpose: ++ * This function automatically unregisters DCD objects specified in a ++ * special COFF section called ".dcd_register" ++ * Parameters: ++ * hDcdMgr: A DCD manager handle. ++ * pszCoffPath: Pointer to name of COFF file containing ++ * DCD objects to be unregistered. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EDCDNOAUTOREGISTER: Unable to find auto-registration section. ++ * DSP_EDCDREADSECT: Unable to read object code section. ++ * DSP_EDCDLOADBASE: Unable to load code base. ++ * DSP_EHANDLE: Invalid DCD_HMANAGER handle.. ++ * Requires: ++ * DCD initialized. ++ * Ensures: ++ * Note: ++ * Due to the DCD database construction, it is essential for a DCD-enabled ++ * COFF file to contain the right COFF sections, especially ++ * ".dcd_register", which is used for auto unregistration. ++ */ ++ extern DSP_STATUS DCD_AutoUnregister(IN struct DCD_MANAGER *hDcdMgr, ++ IN char *pszCoffPath); ++ ++/* ++ * ======== DCD_CreateManager ======== ++ * Purpose: ++ * This function creates a DCD module manager. ++ * Parameters: ++ * pszZlDllName: Pointer to a DLL name string. ++ * phDcdMgr: A pointer to a DCD manager handle. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Unable to allocate memory for DCD manager handle. ++ * DSP_EFAIL: General failure. ++ * Requires: ++ * DCD initialized. ++ * pszZlDllName is non-NULL. ++ * phDcdMgr is non-NULL. ++ * Ensures: ++ * A DCD manager handle is created. ++ */ ++ extern DSP_STATUS DCD_CreateManager(IN char *pszZlDllName, ++ OUT struct DCD_MANAGER **phDcdMgr); ++ ++/* ++ * ======== DCD_DestroyManager ======== ++ * Purpose: ++ * This function destroys a DCD module manager. ++ * Parameters: ++ * hDcdMgr: A DCD manager handle. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid DCD manager handle. ++ * Requires: ++ * DCD initialized. ++ * Ensures: ++ */ ++ extern DSP_STATUS DCD_DestroyManager(IN struct DCD_MANAGER *hDcdMgr); ++ ++/* ++ * ======== DCD_EnumerateObject ======== ++ * Purpose: ++ * This function enumerates currently visible DSP/BIOS Bridge objects ++ * and returns the UUID and type of each enumerated object. ++ * Parameters: ++ * cIndex: The object enumeration index. ++ * objType: Type of object to enumerate. ++ * pUuid: Pointer to a DSP_UUID object. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Unable to enumerate through the DCD database. ++ * DSP_SENUMCOMPLETE: Enumeration completed. This is not an error code. ++ * Requires: ++ * DCD initialized. ++ * pUuid is a valid pointer. ++ * Ensures: ++ * Details: ++ * This function can be used in conjunction with DCD_GetObjectDef to ++ * retrieve object properties. ++ */ ++ extern DSP_STATUS DCD_EnumerateObject(IN s32 cIndex, ++ IN enum DSP_DCDOBJTYPE objType, ++ OUT struct DSP_UUID *pUuid); ++ ++/* ++ * ======== DCD_Exit ======== ++ * Purpose: ++ * This function cleans up the DCD module. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * DCD initialized. ++ * Ensures: ++ */ ++ extern void DCD_Exit(void); ++ ++/* ++ * ======== DCD_GetDepLibs ======== ++ * Purpose: ++ * Given the uuid of a library and size of array of uuids, this function ++ * fills the array with the uuids of all dependent libraries of the input ++ * library. ++ * Parameters: ++ * hDcdMgr: A DCD manager handle. ++ * pUuid: Pointer to a DSP_UUID for a library. ++ * numLibs: Size of uuid array (number of library uuids). ++ * pDepLibUuids: Array of dependent library uuids to be filled in. ++ * pPersistentDepLibs: Array indicating if corresponding lib is persistent. ++ * phase: phase to obtain correct input library ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Memory allocation failure. ++ * DSP_EDCDREADSECT: Failure to read section containing library info. ++ * DSP_EFAIL: General failure. ++ * Requires: ++ * DCD initialized. ++ * Valid hDcdMgr. ++ * pUuid != NULL ++ * pDepLibUuids != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS DCD_GetDepLibs(IN struct DCD_MANAGER *hDcdMgr, ++ IN struct DSP_UUID *pUuid, ++ u16 numLibs, ++ OUT struct DSP_UUID *pDepLibUuids, ++ OUT bool *pPersistentDepLibs, ++ IN enum NLDR_PHASE phase); ++ ++/* ++ * ======== DCD_GetNumDepLibs ======== ++ * Purpose: ++ * Given the uuid of a library, determine its number of dependent ++ * libraries. ++ * Parameters: ++ * hDcdMgr: A DCD manager handle. ++ * pUuid: Pointer to a DSP_UUID for a library. ++ * pNumLibs: Size of uuid array (number of library uuids). ++ * pNumPersLibs: number of persistent dependent library. ++ * phase: Phase to obtain correct input library ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Memory allocation failure. ++ * DSP_EDCDREADSECT: Failure to read section containing library info. ++ * DSP_EFAIL: General failure. ++ * Requires: ++ * DCD initialized. ++ * Valid hDcdMgr. ++ * pUuid != NULL ++ * pNumLibs != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS DCD_GetNumDepLibs(IN struct DCD_MANAGER *hDcdMgr, ++ IN struct DSP_UUID *pUuid, ++ OUT u16 *pNumLibs, ++ OUT u16 *pNumPersLibs, ++ IN enum NLDR_PHASE phase); ++ ++/* ++ * ======== DCD_GetLibraryName ======== ++ * Purpose: ++ * This function returns the name of a (dynamic) library for a given ++ * UUID. ++ * Parameters: ++ * hDcdMgr: A DCD manager handle. ++ * pUuid: Pointer to a DSP_UUID that represents a unique DSP/BIOS ++ * Bridge object. ++ * pstrLibName: Buffer to hold library name. ++ * pdwSize: Contains buffer size. Set to string size on output. ++ * phase: Which phase to load ++ * fPhaseSplit: Are phases in multiple libraries ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: General failure. ++ * Requires: ++ * DCD initialized. ++ * Valid hDcdMgr. ++ * pstrLibName != NULL. ++ * pUuid != NULL ++ * pdwSize != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS DCD_GetLibraryName(IN struct DCD_MANAGER *hDcdMgr, ++ IN struct DSP_UUID *pUuid, ++ IN OUT char *pstrLibName, ++ IN OUT u32 *pdwSize, ++ IN enum NLDR_PHASE phase, ++ OUT bool *fPhaseSplit); ++ ++/* ++ * ======== DCD_GetObjectDef ======== ++ * Purpose: ++ * This function returns the properties/attributes of a DSP/BIOS Bridge ++ * object. ++ * Parameters: ++ * hDcdMgr: A DCD manager handle. ++ * pUuid: Pointer to a DSP_UUID that represents a unique ++ * DSP/BIOS Bridge object. ++ * objType: The type of DSP/BIOS Bridge object to be ++ * referenced (node, processor, etc). ++ * pObjDef: Pointer to an object definition structure. A ++ * union of various possible DCD object types. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EDCDPARSESECT: Unable to parse content of object code section. ++ * DSP_EDCDREADSECT: Unable to read object code section. ++ * DSP_EDCDGETSECT: Unable to access object code section. ++ * DSP_EDCDLOADBASE: Unable to load code base. ++ * DSP_EFAIL: General failure. ++ * DSP_EHANDLE: Invalid DCD_HMANAGER handle. ++ * Requires: ++ * DCD initialized. ++ * pObjUuid is non-NULL. ++ * pObjDef is non-NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS DCD_GetObjectDef(IN struct DCD_MANAGER *hDcdMgr, ++ IN struct DSP_UUID *pObjUuid, ++ IN enum DSP_DCDOBJTYPE objType, ++ OUT struct DCD_GENERICOBJ *pObjDef); ++ ++/* ++ * ======== DCD_GetObjects ======== ++ * Purpose: ++ * This function finds all DCD objects specified in a special ++ * COFF section called ".dcd_register", and for each object, ++ * call a "register" function. The "register" function may perform ++ * various actions, such as 1) register nodes in the node database, 2) ++ * unregister nodes from the node database, and 3) add overlay nodes. ++ * Parameters: ++ * hDcdMgr: A DCD manager handle. ++ * pszCoffPath: Pointer to name of COFF file containing DCD ++ * objects. ++ * registerFxn: Callback fxn to be applied on each located ++ * DCD object. ++ * handle: Handle to pass to callback. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EDCDNOAUTOREGISTER: Unable to find .dcd_register section. ++ * DSP_EDCDREADSECT: Unable to read object code section. ++ * DSP_EDCDLOADBASE: Unable to load code base. ++ * DSP_EHANDLE: Invalid DCD_HMANAGER handle.. ++ * Requires: ++ * DCD initialized. ++ * Ensures: ++ * Note: ++ * Due to the DCD database construction, it is essential for a DCD-enabled ++ * COFF file to contain the right COFF sections, especially ++ * ".dcd_register", which is used for auto registration. ++ */ ++ extern DSP_STATUS DCD_GetObjects(IN struct DCD_MANAGER *hDcdMgr, ++ IN char *pszCoffPath, ++ DCD_REGISTERFXN registerFxn, ++ void *handle); ++ ++/* ++ * ======== DCD_Init ======== ++ * Purpose: ++ * This function initializes DCD. ++ * Parameters: ++ * Returns: ++ * FALSE: Initialization failed. ++ * TRUE: Initialization succeeded. ++ * Requires: ++ * Ensures: ++ * DCD initialized. ++ */ ++ extern bool DCD_Init(void); ++ ++/* ++ * ======== DCD_RegisterObject ======== ++ * Purpose: ++ * This function registers a DSP/BIOS Bridge object in the DCD database. ++ * Parameters: ++ * pUuid: Pointer to a DSP_UUID that identifies a DSP/BIOS ++ * Bridge object. ++ * objType: Type of object. ++ * pszPathName: Path to the object's COFF file. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Failed to register object. ++ * Requires: ++ * DCD initialized. ++ * pUuid and szPathName are non-NULL values. ++ * objType is a valid type value. ++ * Ensures: ++ */ ++ extern DSP_STATUS DCD_RegisterObject(IN struct DSP_UUID *pUuid, ++ IN enum DSP_DCDOBJTYPE objType, ++ IN char *pszPathName); ++ ++/* ++ * ======== DCD_UnregisterObject ======== ++ * Purpose: ++ * This function de-registers a valid DSP/BIOS Bridge object from the DCD ++ * database. ++ * Parameters: ++ * pUuid: Pointer to a DSP_UUID that identifies a DSP/BIOS Bridge ++ * object. ++ * objType: Type of object. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Unable to de-register the specified object. ++ * Requires: ++ * DCD initialized. ++ * pUuid is a non-NULL value. ++ * objType is a valid type value. ++ * Ensures: ++ */ ++ extern DSP_STATUS DCD_UnregisterObject(IN struct DSP_UUID *pUuid, ++ IN enum DSP_DCDOBJTYPE objType); ++ ++#endif /* _DBDCD_H */ +diff --git a/arch/arm/plat-omap/include/dspbridge/dbdcddef.h b/arch/arm/plat-omap/include/dspbridge/dbdcddef.h +new file mode 100644 +index 0000000..91b1c45 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dbdcddef.h +@@ -0,0 +1,94 @@ ++/* ++ * dbdcddef.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dbdcddef.h ======== ++ * Description: ++ * DCD (DSP/BIOS Bridge Configuration Database) constants and types. ++ * ++ *! Revision History: ++ *! ================ ++ *! 03-Dec-2003 map Moved and renamed DCD_OBJTYPE to DSP_DCDOBJTYPE in dbdefs.h ++ *! 05-Dec-2002 map Added DCD_CREATELIBTYPE, DCD_EXECUTELIBTYPE, ++ * DCD_DELETELIBTYPE ++ *! 24-Feb-2003 kc Updated REG entry names to DspBridge. ++ *! 22-Nov-2002 gp Cleaned up comments, formatting. ++ *! 05-Aug-2002 jeh Added DCD_REGISTERFXN. ++ *! 19-Apr-2002 jeh Added DCD_LIBRARYTYPE to DCD_OBJTYPE, dynamic load ++ *! properties to DCD_NODEPROPS. ++ *! 29-Jul-2001 ag Added extended procObj. ++ *! 13-Feb-2001 kc: Named changed from dcdbsdef.h dbdcddef.h. ++ *! 12-Dec-2000 jeh Added DAIS iAlg name to DCD_NODEPROPS. ++ *! 30-Oct-2000 kc: Added #defines for DCD_AutoRegister function. ++ *! 05-Sep-2000 jeh Added DCD_NODEPROPS. ++ *! 12-Aug-2000 kc: Incoroporated the use of types defined in . ++ *! 29-Jul-2000 kc: Created. ++ */ ++ ++#ifndef DBDCDDEF_ ++#define DBDCDDEF_ ++ ++#include ++#include /* for MGR_PROCESSOREXTINFO */ ++ ++/* ++ * The following defines are critical elements for the DCD module: ++ * ++ * - DCD_REGKEY enables DCD functions to locate registered DCD objects. ++ * - DCD_REGISTER_SECTION identifies the COFF section where the UUID of ++ * registered DCD objects are stored. ++ */ ++#define DCD_REGKEY "Software\\TexasInstruments\\DspBridge\\DCD" ++#define DCD_REGISTER_SECTION ".dcd_register" ++ ++/* DCD Manager Object */ ++ struct DCD_MANAGER; ++ ++/* DCD Node Properties */ ++ struct DCD_NODEPROPS { ++ struct DSP_NDBPROPS ndbProps; ++ u32 uMsgSegid; ++ u32 uMsgNotifyType; ++ char *pstrCreatePhaseFxn; ++ char *pstrDeletePhaseFxn; ++ char *pstrExecutePhaseFxn; ++ char *pstrIAlgName; ++ ++ /* Dynamic load properties */ ++ u16 usLoadType; /* Static, dynamic, overlay */ ++ u32 ulDataMemSegMask; /* Data memory requirements */ ++ u32 ulCodeMemSegMask; /* Code memory requirements */ ++ } ; ++ ++/* DCD Generic Object Type */ ++ struct DCD_GENERICOBJ { ++ union dcdObjUnion { ++ struct DCD_NODEPROPS nodeObj; /* node object. */ ++ /* processor object. */ ++ struct DSP_PROCESSORINFO procObj; ++ /* extended proc object (private) */ ++ struct MGR_PROCESSOREXTINFO extProcObj; ++ } objData; ++ } ; ++ ++/* DCD Internal Callback Type */ ++ typedef DSP_STATUS(*DCD_REGISTERFXN) (IN struct DSP_UUID *pUuid, ++ IN enum DSP_DCDOBJTYPE objType, ++ IN void *handle); ++ ++#endif /* DBDCDDEF_ */ ++ +diff --git a/arch/arm/plat-omap/include/dspbridge/dbdefs.h b/arch/arm/plat-omap/include/dspbridge/dbdefs.h +new file mode 100644 +index 0000000..9782693 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dbdefs.h +@@ -0,0 +1,580 @@ ++/* ++ * dbdefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== dbdefs.h ======== ++ * Description: ++ * Global definitions and constants for DSP/BIOS Bridge. ++ * ++ *! Revision History: ++ *! ================ ++ *! 19-Apr-2004 sb Aligned DMM definitions with Symbian ++ *! 08-Mar-2004 sb Added MAPATTR & ELEM_SIZE for Dynamic Memory Mapping feature ++ *! 09-Feb-2004 vp Added processor ID numbers for DSP and IVA ++ *! 06-Feb-2003 kc Removed DSP_POSTMESSAGE. Updated IsValid*Event macros. ++ *! 22-Nov-2002 gp Cleaned up comments, formatting. ++ *! Removed unused DSP_ENUMLASTNODE define. ++ *! 13-Feb-2002 jeh Added uSysStackSize to DSP_NDBPROPS. ++ *! 23-Jan-2002 ag Added #define DSP_SHMSEG0. ++ *! 12-Dec-2001 ag Added DSP_ESTRMMODE error code. ++ *! 04-Dec-2001 jeh Added DSP_ENOTCONNECTED error code. ++ *! 10-Dec-2001 kc: Modified macros and definitions to disable DSP_POSTMESSAGE. ++ *! 01-Nov-2001 jeh Added DSP_EOVERLAYMEMORY. ++ *! 18-Oct-2001 ag Added DSP_STRMMODE type. ++ *! Added DSP_ENOTSHAREDMEM. ++ *! 21-Sep-2001 ag Added additional error codes. ++ *! 07-Jun-2001 sg Made DSPStream_AllocateBuffer fxn name plural. ++ *! 11-May-2001 jeh Changed DSP_NODE_MIN_PRIORITY from 0 to 1. Removed hNode ++ *! from DSP_NODEINFO. ++ *! 02-Apr-2001 sg Added missing error codes, rearranged codes, switched to ++ *! hex offsets, renamed some codes to match API spec. ++ *! 16-Jan-2001 jeh Added DSP_ESYMBOL, DSP_EUUID. ++ *! 13-Feb-2001 kc: DSP/BIOS Bridge name updates. ++ *! 05-Dec-2000 ag: Added DSP_RMSxxx user available message command codes. ++ *! 09-Nov-2000 rr: Added DSP_PROCEESORRESTART define; Removed DSP_PBUFFER. ++ *! Added DSP_DCD_ENOAUTOREGISTER, DSP_EUSER1-16, DSP_ESTRMFUL ++ *! Removed DSP_EDONE. Macros's modified. ++ *! 23-Oct-2000 jeh Replaced DSP_STREAMSTATECHANGE with DSP_STREAMDONE. ++ *! 09-Oct-2000 jeh Updated to version 0.9 DSP Bridge API spec. ++ *! 29-Sep-2000 kc Added error codes for DCD and REG to simplify use of ++ *! these codes within the RM module. ++ *! 27-Sep-2000 jeh Added segid, alignment, uNumBufs to DSP_STREAMATTRIN. ++ *! 29-Aug-2000 jeh Added DSP_NODETYPE enum, changed DSP_EALREADYATTACHED to ++ *! DSP_EALREADYCONNECTED. Changed scStreamConnection[1] ++ *! to scStreamConnection[16] in DSP_NODEINFO structure. ++ *! Added DSP_NOTIFICATION, DSP_STRMATTR. PSTRING changed ++ *! back to TCHAR * and moved to dbtype.h. ++ *! 11-Aug-2000 rr: Macros to check valid events and notify masks added. ++ *! 09-Aug-2000 rr: Changed PSTRING to *s8 ++ *! 07-Aug-2000 rr: PROC_IDLE/SYNCINIT/UNKNOWN state removed. ++ *! 20-Jul-2000 rr: Updated to version 0.8 ++ *! 17-Jul-2000 rr: New PROC states added to the DSP_PROCSTATE. ++ *! 27-Jun-2000 rr: Created from dspapi.h ++ */ ++ ++#ifndef DBDEFS_ ++#define DBDEFS_ ++ ++#include ++ ++#include /* GPP side type definitions */ ++#include /* DSP/BIOS type definitions */ ++#include /* Types shared between GPP and DSP */ ++ ++#define PG_SIZE_4K 4096 ++#define PG_MASK(pg_size) (~((pg_size)-1)) ++#define PG_ALIGN_LOW(addr, pg_size) ((addr) & PG_MASK(pg_size)) ++#define PG_ALIGN_HIGH(addr, pg_size) (((addr)+(pg_size)-1) & PG_MASK(pg_size)) ++ ++/* API return value and calling convention */ ++#define DBAPI DSP_STATUS ++ ++/* Infinite time value for the uTimeout parameter to DSPStream_Select() */ ++#define DSP_FOREVER (-1) ++ ++/* Maximum length of node name, used in DSP_NDBPROPS */ ++#define DSP_MAXNAMELEN 32 ++ ++/* uNotifyType values for the RegisterNotify() functions. */ ++#define DSP_SIGNALEVENT 0x00000001 ++ ++/* Types of events for processors */ ++#define DSP_PROCESSORSTATECHANGE 0x00000001 ++#define DSP_PROCESSORATTACH 0x00000002 ++#define DSP_PROCESSORDETACH 0x00000004 ++#define DSP_PROCESSORRESTART 0x00000008 ++ ++/* DSP exception events (DSP/BIOS and DSP MMU fault) */ ++#define DSP_MMUFAULT 0x00000010 ++#define DSP_SYSERROR 0x00000020 ++#define DSP_EXCEPTIONABORT 0x00000300 ++ ++/* IVA exception events (IVA MMU fault) */ ++#define IVA_MMUFAULT 0x00000040 ++/* Types of events for nodes */ ++#define DSP_NODESTATECHANGE 0x00000100 ++#define DSP_NODEMESSAGEREADY 0x00000200 ++ ++/* Types of events for streams */ ++#define DSP_STREAMDONE 0x00001000 ++#define DSP_STREAMIOCOMPLETION 0x00002000 ++ ++/* Handle definition representing the GPP node in DSPNode_Connect() calls */ ++#define DSP_HGPPNODE 0xFFFFFFFF ++ ++/* Node directions used in DSPNode_Connect() */ ++#define DSP_TONODE 1 ++#define DSP_FROMNODE 2 ++ ++/* Define Node Minimum and Maximum Priorities */ ++#define DSP_NODE_MIN_PRIORITY 1 ++#define DSP_NODE_MAX_PRIORITY 15 ++ ++/* Pre-Defined Message Command Codes available to user: */ ++#define DSP_RMSUSERCODESTART RMS_USER /* Start of RMS user cmd codes */ ++/* end of user codes */ ++#define DSP_RMSUSERCODEEND (RMS_USER + RMS_MAXUSERCODES); ++#define DSP_RMSBUFDESC RMS_BUFDESC /* MSG contains SM buffer description */ ++ ++/* Shared memory identifier for MEM segment named "SHMSEG0" */ ++#define DSP_SHMSEG0 (u32)(-1) ++ ++/* Processor ID numbers */ ++#define DSP_UNIT 0 ++#define IVA_UNIT 1 ++ ++#define DSPWORD unsigned char ++#define DSPWORDSIZE sizeof(DSPWORD) ++ ++/* Success & Failure macros */ ++#define DSP_SUCCEEDED(Status) likely((s32)(Status) >= 0) ++#define DSP_FAILED(Status) unlikely((s32)(Status) < 0) ++ ++/* Power control enumerations */ ++#define PROC_PWRCONTROL 0x8070 ++ ++#define PROC_PWRMGT_ENABLE (PROC_PWRCONTROL + 0x3) ++#define PROC_PWRMGT_DISABLE (PROC_PWRCONTROL + 0x4) ++ ++/* Bridge Code Version */ ++#define BRIDGE_VERSION_CODE 333 ++ ++#define MAX_PROFILES 16 ++ ++/* Types defined for 'Bridge API */ ++ typedef u32 DSP_STATUS; /* API return code type */ ++ ++ typedef HANDLE DSP_HNODE; /* Handle to a DSP Node object */ ++ typedef HANDLE DSP_HPROCESSOR; /* Handle to a Processor object */ ++ typedef HANDLE DSP_HSTREAM; /* Handle to a Stream object */ ++ ++ typedef u32 DSP_PROCFAMILY; /* Processor family */ ++ typedef u32 DSP_PROCTYPE; /* Processor type (w/in family) */ ++ typedef u32 DSP_RTOSTYPE; /* Type of DSP RTOS */ ++ ++/* Handy Macros */ ++#define IsValidProcEvent(x) (((x) == 0) || (((x) & (DSP_PROCESSORSTATECHANGE | \ ++ DSP_PROCESSORATTACH | \ ++ DSP_PROCESSORDETACH | \ ++ DSP_PROCESSORRESTART | \ ++ DSP_NODESTATECHANGE | \ ++ DSP_STREAMDONE | \ ++ DSP_STREAMIOCOMPLETION | \ ++ DSP_MMUFAULT | \ ++ DSP_SYSERROR)) && \ ++ !((x) & ~(DSP_PROCESSORSTATECHANGE | \ ++ DSP_PROCESSORATTACH | \ ++ DSP_PROCESSORDETACH | \ ++ DSP_PROCESSORRESTART | \ ++ DSP_NODESTATECHANGE | \ ++ DSP_STREAMDONE | \ ++ DSP_STREAMIOCOMPLETION | \ ++ DSP_MMUFAULT | \ ++ DSP_SYSERROR)))) ++ ++#define IsValidNodeEvent(x) (((x) == 0) || (((x) & (DSP_NODESTATECHANGE | \ ++ DSP_NODEMESSAGEREADY)) && \ ++ !((x) & ~(DSP_NODESTATECHANGE | \ ++ DSP_NODEMESSAGEREADY)))) ++ ++#define IsValidStrmEvent(x) (((x) == 0) || (((x) & (DSP_STREAMDONE | \ ++ DSP_STREAMIOCOMPLETION)) && \ ++ !((x) & ~(DSP_STREAMDONE | \ ++ DSP_STREAMIOCOMPLETION)))) ++ ++#define IsValidNotifyMask(x) ((x) & DSP_SIGNALEVENT) ++ ++/* The Node UUID structure */ ++ struct DSP_UUID { ++ u32 ulData1; ++ u16 usData2; ++ u16 usData3; ++ u8 ucData4; ++ u8 ucData5; ++ u8 ucData6[6]; ++ }; ++ ++/* DCD types */ ++ enum DSP_DCDOBJTYPE { ++ DSP_DCDNODETYPE, ++ DSP_DCDPROCESSORTYPE, ++ DSP_DCDLIBRARYTYPE, ++ DSP_DCDCREATELIBTYPE, ++ DSP_DCDEXECUTELIBTYPE, ++ DSP_DCDDELETELIBTYPE ++ } ; ++ ++/* Processor states */ ++ enum DSP_PROCSTATE { ++ PROC_STOPPED, ++ PROC_LOADED, ++ PROC_RUNNING, ++ PROC_ERROR ++ } ; ++ ++/* ++ * Node types: Message node, task node, xDAIS socket node, and ++ * device node. _NODE_GPP is used when defining a stream connection ++ * between a task or socket node and the GPP. ++ * ++ */ ++ enum NODE_TYPE { ++ NODE_DEVICE, ++ NODE_TASK, ++ NODE_DAISSOCKET, ++ NODE_MESSAGE, ++ NODE_GPP ++ } ; ++ ++/* ++ * ======== NODE_STATE ======== ++ * Internal node states. ++ */ ++ enum NODE_STATE { ++ NODE_ALLOCATED, ++ NODE_CREATED, ++ NODE_RUNNING, ++ NODE_PAUSED, ++ NODE_DONE, ++ NODE_CREATING, ++ NODE_STARTING, ++ NODE_PAUSING, ++ NODE_TERMINATING, ++ NODE_DELETING, ++ } ; ++ ++/* Stream states */ ++ enum DSP_STREAMSTATE { ++ STREAM_IDLE, ++ STREAM_READY, ++ STREAM_PENDING, ++ STREAM_DONE ++ } ; ++ ++/* Stream connect types */ ++ enum DSP_CONNECTTYPE { ++ CONNECTTYPE_NODEOUTPUT, ++ CONNECTTYPE_GPPOUTPUT, ++ CONNECTTYPE_NODEINPUT, ++ CONNECTTYPE_GPPINPUT ++ } ; ++ ++/* Stream mode types */ ++ enum DSP_STRMMODE { ++ STRMMODE_PROCCOPY, /* Processor(s) copy stream data payloads */ ++ STRMMODE_ZEROCOPY, /* Strm buffer ptrs swapped no data copied */ ++ STRMMODE_LDMA, /* Local DMA : OMAP's System-DMA device */ ++ STRMMODE_RDMA /* Remote DMA: OMAP's DSP-DMA device */ ++ } ; ++ ++/* Resource Types */ ++ enum DSP_RESOURCEINFOTYPE { ++ DSP_RESOURCE_DYNDARAM = 0, ++ DSP_RESOURCE_DYNSARAM, ++ DSP_RESOURCE_DYNEXTERNAL, ++ DSP_RESOURCE_DYNSRAM, ++ DSP_RESOURCE_PROCLOAD ++ } ; ++ ++/* Memory Segment Types */ ++ enum DSP_MEMTYPE { ++ DSP_DYNDARAM = 0, ++ DSP_DYNSARAM, ++ DSP_DYNEXTERNAL, ++ DSP_DYNSRAM ++ } ; ++ ++/* Memory Flush Types */ ++ enum DSP_FLUSHTYPE { ++ PROC_INVALIDATE_MEM = 0, ++ PROC_WRITEBACK_MEM, ++ PROC_WRITEBACK_INVALIDATE_MEM, ++ } ; ++ ++/* Memory Segment Status Values */ ++ struct DSP_MEMSTAT { ++ u32 ulSize; ++ u32 ulTotalFreeSize; ++ u32 ulLenMaxFreeBlock; ++ u32 ulNumFreeBlocks; ++ u32 ulNumAllocBlocks; ++ } ; ++ ++/* Processor Load information Values */ ++ struct DSP_PROCLOADSTAT { ++ u32 uCurrLoad; ++ u32 uPredictedLoad; ++ u32 uCurrDspFreq; ++ u32 uPredictedFreq; ++ } ; ++ ++/* Attributes for STRM connections between nodes */ ++ struct DSP_STRMATTR { ++ u32 uSegid; /* Memory segment on DSP to allocate buffers */ ++ u32 uBufsize; /* Buffer size (DSP words) */ ++ u32 uNumBufs; /* Number of buffers */ ++ u32 uAlignment; /* Buffer alignment */ ++ u32 uTimeout; /* Timeout for blocking STRM calls */ ++ enum DSP_STRMMODE lMode; /* mode of stream when opened */ ++ /* DMA chnl id if DSP_STRMMODE is LDMA or RDMA */ ++ u32 uDMAChnlId; ++ u32 uDMAPriority; /* DMA channel priority 0=lowest, >0=high */ ++ } ; ++ ++/* The DSP_CBDATA structure */ ++ struct DSP_CBDATA { ++ u32 cbData; ++ u8 cData[1]; ++ } ; ++ ++/* The DSP_MSG structure */ ++ struct DSP_MSG { ++ u32 dwCmd; ++ u32 dwArg1; ++ u32 dwArg2; ++ } ; ++ ++/* The DSP_RESOURCEREQMTS structure for node's resource requirements */ ++ struct DSP_RESOURCEREQMTS { ++ u32 cbStruct; ++ u32 uStaticDataSize; ++ u32 uGlobalDataSize; ++ u32 uProgramMemSize; ++ u32 uWCExecutionTime; ++ u32 uWCPeriod; ++ u32 uWCDeadline; ++ u32 uAvgExectionTime; ++ u32 uMinimumPeriod; ++ } ; ++ ++/* ++ * The DSP_STREAMCONNECT structure describes a stream connection ++ * between two nodes, or between a node and the GPP ++ */ ++ struct DSP_STREAMCONNECT { ++ u32 cbStruct; ++ enum DSP_CONNECTTYPE lType; ++ u32 uThisNodeStreamIndex; ++ DSP_HNODE hConnectedNode; ++ struct DSP_UUID uiConnectedNodeID; ++ u32 uConnectedNodeStreamIndex; ++ } ; ++ ++ struct DSP_NODEPROFS { ++ u32 ulHeapSize; ++ } ; ++ ++/* The DSP_NDBPROPS structure reports the attributes of a node */ ++ struct DSP_NDBPROPS { ++ u32 cbStruct; ++ struct DSP_UUID uiNodeID; ++ char acName[DSP_MAXNAMELEN]; ++ enum NODE_TYPE uNodeType; ++ u32 bCacheOnGPP; ++ struct DSP_RESOURCEREQMTS dspResourceReqmts; ++ s32 iPriority; ++ u32 uStackSize; ++ u32 uSysStackSize; ++ u32 uStackSeg; ++ u32 uMessageDepth; ++ u32 uNumInputStreams; ++ u32 uNumOutputStreams; ++ u32 uTimeout; ++ u32 uCountProfiles; /* Number of supported profiles */ ++ /* Array of profiles */ ++ struct DSP_NODEPROFS aProfiles[MAX_PROFILES]; ++ u32 uStackSegName; /* Stack Segment Name */ ++ } ; ++ ++ /* The DSP_NODEATTRIN structure describes the attributes of a ++ * node client */ ++ struct DSP_NODEATTRIN { ++ u32 cbStruct; ++ s32 iPriority; ++ u32 uTimeout; ++ u32 uProfileID; ++ /* Reserved, for Bridge Internal use only */ ++ u32 uHeapSize; ++ void *pGPPVirtAddr; /* Reserved, for Bridge Internal use only */ ++ } ; ++ ++ /* The DSP_NODEINFO structure is used to retrieve information ++ * about a node */ ++ struct DSP_NODEINFO { ++ u32 cbStruct; ++ struct DSP_NDBPROPS nbNodeDatabaseProps; ++ u32 uExecutionPriority; ++ enum NODE_STATE nsExecutionState; ++ DSP_HNODE hDeviceOwner; ++ u32 uNumberStreams; ++ struct DSP_STREAMCONNECT scStreamConnection[16]; ++ u32 uNodeEnv; ++ } ; ++ ++ /* The DSP_NODEATTR structure describes the attributes of a node */ ++ struct DSP_NODEATTR { ++ u32 cbStruct; ++ struct DSP_NODEATTRIN inNodeAttrIn; ++ u32 uInputs; ++ u32 uOutputs; ++ struct DSP_NODEINFO iNodeInfo; ++ } ; ++ ++/* ++ * Notification type: either the name of an opened event, or an event or ++ * window handle. ++ */ ++ struct DSP_NOTIFICATION { ++ char *psName; ++ HANDLE handle; ++ } ; ++ ++/* The DSP_PROCESSORATTRIN structure describes the attributes of a processor */ ++ struct DSP_PROCESSORATTRIN{ ++ u32 cbStruct; ++ u32 uTimeout; ++ } ; ++ ++ enum chipTypes { ++ DSPTYPE_55 = 6, ++ IVA_ARM7 = 0x97, ++ DSPTYPE_64 = 0x99 ++ }; ++ ++/* ++ * The DSP_PROCESSORINFO structure describes basic capabilities of a ++ * DSP processor ++ */ ++ struct DSP_PROCESSORINFO { ++ u32 cbStruct; ++ DSP_PROCFAMILY uProcessorFamily; ++ DSP_PROCTYPE uProcessorType; ++ u32 uClockRate; ++ u32 ulInternalMemSize; ++ u32 ulExternalMemSize; ++ u32 uProcessorID; ++ DSP_RTOSTYPE tyRunningRTOS; ++ s32 nNodeMinPriority; ++ s32 nNodeMaxPriority; ++ } ; ++ ++/* Error information of last DSP exception signalled to the GPP */ ++ struct DSP_ERRORINFO { ++ u32 dwErrMask; ++ u32 dwVal1; ++ u32 dwVal2; ++ u32 dwVal3; ++ } ; ++ ++/* The DSP_PROCESSORSTATE structure describes the state of a DSP processor */ ++ struct DSP_PROCESSORSTATE { ++ u32 cbStruct; ++ enum DSP_PROCSTATE iState; ++ struct DSP_ERRORINFO errInfo; ++ } ; ++ ++/* ++ * The DSP_RESOURCEINFO structure is used to retrieve information about a ++ * processor's resources ++ */ ++ struct DSP_RESOURCEINFO { ++ u32 cbStruct; ++ enum DSP_RESOURCEINFOTYPE uResourceType; ++ union { ++ u32 ulResource; ++ struct DSP_MEMSTAT memStat; ++ struct DSP_PROCLOADSTAT procLoadStat; ++ } result; ++ } ; ++ ++/* ++ * The DSP_STREAMATTRIN structure describes the attributes of a stream, ++ * including segment and alignment of data buffers allocated with ++ * DSPStream_AllocateBuffers(), if applicable ++ */ ++ struct DSP_STREAMATTRIN { ++ u32 cbStruct; ++ u32 uTimeout; ++ u32 uSegment; ++ u32 uAlignment; ++ u32 uNumBufs; ++ enum DSP_STRMMODE lMode; ++ u32 uDMAChnlId; ++ u32 uDMAPriority; ++ } ; ++ ++/* The DSP_BUFFERATTR structure describes the attributes of a data buffer */ ++ struct DSP_BUFFERATTR { ++ u32 cbStruct; ++ u32 uSegment; ++ u32 uAlignment; ++ } ; ++ ++/* ++ * The DSP_STREAMINFO structure is used to retrieve information ++ * about a stream. ++ */ ++ struct DSP_STREAMINFO { ++ u32 cbStruct; ++ u32 uNumberBufsAllowed; ++ u32 uNumberBufsInStream; ++ u32 ulNumberBytes; ++ HANDLE hSyncObjectHandle; ++ enum DSP_STREAMSTATE ssStreamState; ++ } ; ++ ++/* DMM MAP attributes ++It is a bit mask with each bit value indicating a specific attribute ++bit 0 - GPP address type (user virtual=0, physical=1) ++bit 1 - MMU Endianism (Big Endian=1, Little Endian=0) ++bit 2 - MMU mixed page attribute (Mixed/ CPUES=1, TLBES =0) ++bit 3 - MMU element size = 8bit (valid only for non mixed page entries) ++bit 4 - MMU element size = 16bit (valid only for non mixed page entries) ++bit 5 - MMU element size = 32bit (valid only for non mixed page entries) ++bit 6 - MMU element size = 64bit (valid only for non mixed page entries) ++*/ ++ ++/* Types of mapping attributes */ ++ ++/* MPU address is virtual and needs to be translated to physical addr */ ++#define DSP_MAPVIRTUALADDR 0x00000000 ++#define DSP_MAPPHYSICALADDR 0x00000001 ++ ++/* Mapped data is big endian */ ++#define DSP_MAPBIGENDIAN 0x00000002 ++#define DSP_MAPLITTLEENDIAN 0x00000000 ++ ++/* Element size is based on DSP r/w access size */ ++#define DSP_MAPMIXEDELEMSIZE 0x00000004 ++ ++/* ++ * Element size for MMU mapping (8, 16, 32, or 64 bit) ++ * Ignored if DSP_MAPMIXEDELEMSIZE enabled ++ */ ++#define DSP_MAPELEMSIZE8 0x00000008 ++#define DSP_MAPELEMSIZE16 0x00000010 ++#define DSP_MAPELEMSIZE32 0x00000020 ++#define DSP_MAPELEMSIZE64 0x00000040 ++ ++#define DSP_MAPVMALLOCADDR 0x00000080 ++ ++#define DSP_MAPDONOTLOCK 0x00000100 ++ ++ ++#define GEM_CACHE_LINE_SIZE 128 ++#define GEM_L1P_PREFETCH_SIZE 128 ++ ++#endif /* DBDEFS_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/dbg.h b/arch/arm/plat-omap/include/dspbridge/dbg.h +new file mode 100644 +index 0000000..7f44ff9 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dbg.h +@@ -0,0 +1,110 @@ ++/* ++ * dbg.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== dbg.h ======== ++ * Purpose: ++ * Provide debugging services for 'Bridge Mini Drivers. ++ * ++ * Public Functions: ++ * DBG_Exit ++ * DBG_Init ++ * DBG_Printf ++ * DBG_Trace ++ * ++ * Notes: ++ * WMD's must not call DBG_Init or DBG_Exit. ++ * ++ *! Revision History: ++ *! ================ ++ *! 03-Feb-2000 rr: DBG Levels redefined. ++ *! 29-Oct-1999 kc: Cleaned up for code review. ++ *! 10-Oct-1997 cr: Added DBG_Printf service. ++ *! 29-May-1996 gp: Removed WCD_ prefix. ++ *! 15-May-1996 gp: Created. ++ */ ++ ++#ifndef DBG_ ++#define DBG_ ++#include ++#include ++ ++/* Levels of trace debug messages: */ ++#define DBG_ENTER (u8)(0x01) /* Function entry point. */ ++#define DBG_LEVEL1 (u8)(0x02) /* Display debugging state/varibles */ ++#define DBG_LEVEL2 (u8)(0x04) /* Display debugging state/varibles */ ++#define DBG_LEVEL3 (u8)(0x08) /* Display debugging state/varibles */ ++#define DBG_LEVEL4 (u8)(0x10) /* Display debugging state/varibles */ ++#define DBG_LEVEL5 (u8)(0x20) /* Module Init, Exit */ ++#define DBG_LEVEL6 (u8)(0x40) /* Warn SERVICES Failures */ ++#define DBG_LEVEL7 (u8)(0x80) /* Warn Critical Errors */ ++ ++#if (defined(DEBUG) || defined(DDSP_DEBUG_PRODUCT)) && GT_TRACE ++ ++/* ++ * ======== DBG_Exit ======== ++ * Purpose: ++ * Discontinue usage of module; free resources when reference count ++ * reaches 0. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * DBG initialized. ++ * Ensures: ++ * Resources used by module are freed when cRef reaches zero. ++ */ ++ extern void DBG_Exit(void); ++ ++/* ++ * ======== DBG_Init ======== ++ * Purpose: ++ * Initializes private state of DBG module. ++ * Parameters: ++ * Returns: ++ * TRUE if initialized; FALSE if error occured. ++ * Requires: ++ * Ensures: ++ */ ++ extern bool DBG_Init(void); ++ ++/* ++ * ======== DBG_Trace ======== ++ * Purpose: ++ * Output a trace message to the debugger, if the given trace level ++ * is unmasked. ++ * Parameters: ++ * bLevel: Trace level. ++ * pstrFormat: sprintf-style format string. ++ * ...: Arguments for format string. ++ * Returns: ++ * DSP_SOK: Success, or trace level masked. ++ * DSP_EFAIL: On Error. ++ * Requires: ++ * DBG initialized. ++ * Ensures: ++ * Debug message is printed to debugger output window, if trace level ++ * is unmasked. ++ */ ++ extern DSP_STATUS DBG_Trace(IN u8 bLevel, IN char *pstrFormat, ...); ++#else ++ ++#define DBG_Exit(void) ++#define DBG_Init(void) true ++#define DBG_Trace(bLevel, pstrFormat, args...) ++ ++#endif /* (defined(DEBUG) || defined(DDSP_DEBUG_PRODUCT)) && GT_TRACE */ ++ ++#endif /* DBG_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/dbl.h b/arch/arm/plat-omap/include/dspbridge/dbl.h +new file mode 100644 +index 0000000..19847f9 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dbl.h +@@ -0,0 +1,354 @@ ++/* ++ * dbl.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dbl.h ======== ++ * ++ *! Revision History ++ *! ================ ++ *! 19-Mar-2002 jeh Pass DBL_Symbol pointer to DBL_getAddr, DBL_getCAddr ++ *! to accomodate dynamic loader library. ++ *! 20-Nov-2001 jeh Removed DBL_loadArgs(). ++ *! 24-Sep-2001 jeh Code review changes. ++ *! 07-Sep-2001 jeh Added DBL_LoadSect(), DBL_UnloadSect(). ++ *! 05-Jun-2001 jeh Created based on zl.h. ++ */ ++ ++#ifndef DBL_ ++#define DBL_ ++ ++#include ++#include ++ ++/* ++ * ======== DBL_close ======== ++ * Close library opened with DBL_open. ++ * Parameters: ++ * lib - Handle returned from DBL_open(). ++ * Returns: ++ * Requires: ++ * DBL initialized. ++ * Valid lib. ++ * Ensures: ++ */ ++ extern void DBL_close(struct DBL_LibraryObj *lib); ++ ++/* ++ * ======== DBL_create ======== ++ * Create a target object by specifying the alloc, free, and write ++ * functions for the target. ++ * Parameters: ++ * pTarget - Location to store target handle on output. ++ * pAttrs - Attributes. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Memory allocation failed. ++ * Requires: ++ * DBL initialized. ++ * pAttrs != NULL. ++ * pTarget != NULL; ++ * Ensures: ++ * Success: *pTarget != NULL. ++ * Failure: *pTarget == NULL. ++ */ ++ extern DSP_STATUS DBL_create(struct DBL_TargetObj **pTarget, ++ struct DBL_Attrs *pAttrs); ++ ++/* ++ * ======== DBL_delete ======== ++ * Delete target object and free resources for any loaded libraries. ++ * Parameters: ++ * target - Handle returned from DBL_Create(). ++ * Returns: ++ * Requires: ++ * DBL initialized. ++ * Valid target. ++ * Ensures: ++ */ ++ extern void DBL_delete(struct DBL_TargetObj *target); ++ ++/* ++ * ======== DBL_exit ======== ++ * Discontinue use of DBL module. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * cRefs > 0. ++ * Ensures: ++ * cRefs >= 0. ++ */ ++ extern void DBL_exit(void); ++ ++/* ++ * ======== DBL_getAddr ======== ++ * Get address of name in the specified library. ++ * Parameters: ++ * lib - Handle returned from DBL_open(). ++ * name - Name of symbol ++ * ppSym - Location to store symbol address on output. ++ * Returns: ++ * TRUE: Success. ++ * FALSE: Symbol not found. ++ * Requires: ++ * DBL initialized. ++ * Valid lib. ++ * name != NULL. ++ * pAddr != NULL. ++ * Ensures: ++ */ ++ extern bool DBL_getAddr(struct DBL_LibraryObj *lib, char *name, ++ struct DBL_Symbol **ppSym); ++ ++/* ++ * ======== DBL_getAttrs ======== ++ * Retrieve the attributes of the target. ++ * Parameters: ++ * target - Handle returned from DBL_Create(). ++ * pAttrs - Location to store attributes on output. ++ * Returns: ++ * Requires: ++ * DBL initialized. ++ * Valid target. ++ * pAttrs != NULL. ++ * Ensures: ++ */ ++ extern void DBL_getAttrs(struct DBL_TargetObj *target, ++ struct DBL_Attrs *pAttrs); ++ ++/* ++ * ======== DBL_getCAddr ======== ++ * Get address of "C" name in the specified library. ++ * Parameters: ++ * lib - Handle returned from DBL_open(). ++ * name - Name of symbol ++ * ppSym - Location to store symbol address on output. ++ * Returns: ++ * TRUE: Success. ++ * FALSE: Symbol not found. ++ * Requires: ++ * DBL initialized. ++ * Valid lib. ++ * name != NULL. ++ * pAddr != NULL. ++ * Ensures: ++ */ ++ extern bool DBL_getCAddr(struct DBL_LibraryObj *lib, char *name, ++ struct DBL_Symbol **ppSym); ++ ++/* ++ * ======== DBL_getEntry ======== ++ * Get program entry point. ++ * ++ * Parameters: ++ * lib - Handle returned from DBL_open(). ++ * pEntry - Location to store entry address on output. ++ * Returns: ++ * TRUE: Success. ++ * FALSE: Failure. ++ * Requires: ++ * DBL initialized. ++ * Valid lib. ++ * pEntry != NULL. ++ * Ensures: ++ */ ++ extern bool DBL_getEntry(struct DBL_LibraryObj *lib, u32 *pEntry); ++ ++/* ++ * ======== DBL_getSect ======== ++ * Get address and size of a named section. ++ * Parameters: ++ * lib - Library handle returned from DBL_open(). ++ * name - Name of section. ++ * pAddr - Location to store section address on output. ++ * pSize - Location to store section size on output. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ENOSECT: Section not found. ++ * Requires: ++ * DBL initialized. ++ * Valid lib. ++ * name != NULL. ++ * pAddr != NULL; ++ * pSize != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS DBL_getSect(struct DBL_LibraryObj *lib, char *name, ++ u32 *pAddr, u32 *pSize); ++ ++/* ++ * ======== DBL_init ======== ++ * Initialize DBL module. ++ * Parameters: ++ * Returns: ++ * TRUE: Success. ++ * FALSE: Failure. ++ * Requires: ++ * cRefs >= 0. ++ * Ensures: ++ * Success: cRefs > 0. ++ * Failure: cRefs >= 0. ++ */ ++ extern bool DBL_init(void); ++ ++/* ++ * ======== DBL_load ======== ++ * Add symbols/code/data defined in file to that already present on ++ * the target. ++ * ++ * Parameters: ++ * lib - Library handle returned from DBL_open(). ++ * flags - Specifies whether loading code, data, and/or symbols. ++ * attrs - May contain write, alloc, and free functions. ++ * pulEntry - Location to store program entry on output. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFREAD: File read failed. ++ * DSP_EFWRITE: Write to target failed. ++ * Requires: ++ * DBL initialized. ++ * Valid lib. ++ * pEntry != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS DBL_load(struct DBL_LibraryObj *lib, DBL_Flags flags, ++ struct DBL_Attrs *attrs, u32 *pEntry); ++ ++/* ++ * ======== DBL_loadSect ======== ++ * Load a named section from an library (for overlay support). ++ * Parameters: ++ * lib - Handle returned from DBL_open(). ++ * sectName - Name of section to load. ++ * attrs - Contains write function and handle to pass to it. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ENOSECT: Section not found. ++ * DSP_EFWRITE: Write function failed. ++ * Requires: ++ * Valid lib. ++ * sectName != NULL. ++ * attrs != NULL. ++ * attrs->write != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS DBL_loadSect(struct DBL_LibraryObj *lib, ++ char *sectName, ++ struct DBL_Attrs *attrs); ++ ++/* ++ * ======== DBL_open ======== ++ * DBL_open() returns a library handle that can be used to load/unload ++ * the symbols/code/data via DBL_load()/DBL_unload(). ++ * Parameters: ++ * target - Handle returned from DBL_create(). ++ * file - Name of file to open. ++ * flags - Specifies whether to load symbols now. ++ * pLib - Location to store library handle on output. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Memory allocation failure. ++ * DSP_EFOPEN: File open failure. ++ * DSP_EFREAD: File read failure. ++ * DSP_ECORRUPTFILE: Unable to determine target type. ++ * Requires: ++ * DBL initialized. ++ * Valid target. ++ * file != NULL. ++ * pLib != NULL. ++ * struct DBL_Attrs fopen function non-NULL. ++ * Ensures: ++ * Success: Valid *pLib. ++ * Failure: *pLib == NULL. ++ */ ++ extern DSP_STATUS DBL_open(struct DBL_TargetObj *target, char *file, ++ DBL_Flags flags, ++ struct DBL_LibraryObj **pLib); ++ ++/* ++ * ======== DBL_readSect ======== ++ * Read COFF section into a character buffer. ++ * Parameters: ++ * lib - Library handle returned from DBL_open(). ++ * name - Name of section. ++ * pBuf - Buffer to write section contents into. ++ * size - Buffer size ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ENOSECT: Named section does not exists. ++ * Requires: ++ * DBL initialized. ++ * Valid lib. ++ * name != NULL. ++ * pBuf != NULL. ++ * size != 0. ++ * Ensures: ++ */ ++ extern DSP_STATUS DBL_readSect(struct DBL_LibraryObj *lib, char *name, ++ char *pBuf, u32 size); ++ ++/* ++ * ======== DBL_setAttrs ======== ++ * Set the attributes of the target. ++ * Parameters: ++ * target - Handle returned from DBL_create(). ++ * pAttrs - New attributes. ++ * Returns: ++ * Requires: ++ * DBL initialized. ++ * Valid target. ++ * pAttrs != NULL. ++ * Ensures: ++ */ ++ extern void DBL_setAttrs(struct DBL_TargetObj *target, ++ struct DBL_Attrs *pAttrs); ++ ++/* ++ * ======== DBL_unload ======== ++ * Remove the symbols/code/data corresponding to the library lib. ++ * Parameters: ++ * lib - Handle returned from DBL_open(). ++ * attrs - Contains free() function and handle to pass to it. ++ * Returns: ++ * Requires: ++ * DBL initialized. ++ * Valid lib. ++ * Ensures: ++ */ ++ extern void DBL_unload(struct DBL_LibraryObj *lib, ++ struct DBL_Attrs *attrs); ++ ++/* ++ * ======== DBL_unloadSect ======== ++ * Unload a named section from an library (for overlay support). ++ * Parameters: ++ * lib - Handle returned from DBL_open(). ++ * sectName - Name of section to load. ++ * attrs - Contains free() function and handle to pass to it. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ENOSECT: Named section not found. ++ * Requires: ++ * DBL initialized. ++ * Valid lib. ++ * sectName != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS DBL_unloadSect(struct DBL_LibraryObj *lib, ++ char *sectName, ++ struct DBL_Attrs *attrs); ++ ++#endif /* DBL_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/dbldefs.h b/arch/arm/plat-omap/include/dspbridge/dbldefs.h +new file mode 100644 +index 0000000..79b9e54 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dbldefs.h +@@ -0,0 +1,155 @@ ++/* ++ * dbldefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dbldefs.h ======== ++ * ++ *! Revision History ++ *! ================ ++ *! 19-Mar-2002 jeh Added DBL_Fxns type (to make it easier to switch ++ *! between different loaders). ++ *! 28-Sep-2001 jeh Created from zl.h. ++ */ ++#ifndef DBLDEFS_ ++#define DBLDEFS_ ++ ++/* ++ * Bit masks for DBL_Flags. ++ */ ++#define DBL_NOLOAD 0x0 /* Don't load symbols, code, or data */ ++#define DBL_SYMB 0x1 /* load symbols */ ++#define DBL_CODE 0x2 /* load code */ ++#define DBL_DATA 0x4 /* load data */ ++#define DBL_DYNAMIC 0x8 /* dynamic load */ ++#define DBL_BSS 0x20 /* Unitialized section */ ++ ++#define DBL_MAXPATHLENGTH 255 ++ ++ ++ ++/* ++ * ======== DBL_Flags ======== ++ * Specifies whether to load code, data, or symbols ++ */ ++typedef s32 DBL_Flags; ++ ++/* ++ * ======== DBL_SectInfo ======== ++ * For collecting info on overlay sections ++ */ ++struct DBL_SectInfo { ++ const char *name; /* name of section */ ++ u32 runAddr; /* run address of section */ ++ u32 loadAddr; /* load address of section */ ++ u32 size; /* size of section (target MAUs) */ ++ DBL_Flags type; /* Code, data, or BSS */ ++} ; ++ ++/* ++ * ======== DBL_Symbol ======== ++ * (Needed for dynamic load library) ++ */ ++struct DBL_Symbol { ++ u32 value; ++}; ++ ++/* ++ * ======== DBL_AllocFxn ======== ++ * Allocate memory function. Allocate or reserve (if reserved == TRUE) ++ * "size" bytes of memory from segment "space" and return the address in ++ * *dspAddr (or starting at *dspAddr if reserve == TRUE). Returns 0 on ++ * success, or an error code on failure. ++ */ ++typedef s32(*DBL_AllocFxn) (void *hdl, s32 space, u32 size, u32 align, ++ u32 *dspAddr, s32 segId, s32 req, bool reserved); ++ ++ ++ ++/* ++ * ======== DBL_FreeFxn ======== ++ * Free memory function. Free, or unreserve (if reserved == TRUE) "size" ++ * bytes of memory from segment "space" ++ */ ++typedef bool(*DBL_FreeFxn) (void *hdl, u32 addr, s32 space, u32 size, ++ bool reserved); ++ ++/* ++ * ======== DBL_LogWriteFxn ======== ++ * Function to call when writing data from a section, to log the info. ++ * Can be NULL if no logging is required. ++ */ ++typedef DSP_STATUS(*DBL_LogWriteFxn) (void *handle, struct DBL_SectInfo *sect, ++ u32 addr, u32 nBytes); ++ ++ ++/* ++ * ======== DBL_SymLookup ======== ++ * Symbol lookup function - Find the symbol name and return its value. ++ * ++ * Parameters: ++ * handle - Opaque handle ++ * pArg - Opaque argument. ++ * name - Name of symbol to lookup. ++ * sym - Location to store address of symbol structure. ++ * ++ * Returns: ++ * TRUE: Success (symbol was found). ++ * FALSE: Failed to find symbol. ++ */ ++typedef bool(*DBL_SymLookup) (void *handle, void *pArg, void *rmmHandle, ++ const char *name, struct DBL_Symbol **sym); ++ ++ ++/* ++ * ======== DBL_WriteFxn ======== ++ * Write memory function. Write "n" HOST bytes of memory to segment "mtype" ++ * starting at address "dspAddr" from the buffer "buf". The buffer is ++ * formatted as an array of words appropriate for the DSP. ++ */ ++typedef s32(*DBL_WriteFxn) (void *hdl, u32 dspAddr, void *buf, ++ u32 n, s32 mtype); ++ ++/* ++ * ======== DBL_Attrs ======== ++ */ ++struct DBL_Attrs { ++ DBL_AllocFxn alloc; ++ DBL_FreeFxn free; ++ void *rmmHandle; /* Handle to pass to alloc, free functions */ ++ DBL_WriteFxn write; ++ void *wHandle; /* Handle to pass to write, cinit function */ ++ ++ DBL_LogWriteFxn logWrite; ++ void *logWriteHandle; ++ ++ /* Symbol matching function and handle to pass to it */ ++ DBL_SymLookup symLookup; ++ void *symHandle; ++ void *symArg; ++ ++ /* ++ * These file manipulation functions should be compatible with the ++ * "C" run time library functions of the same name. ++ */ ++ s32(*fread) (void *, size_t, size_t, void *); ++ s32(*fseek) (void *, long, int); ++ s32(*ftell) (void *); ++ s32(*fclose) (void *); ++ void *(*fopen) (const char *, const char *); ++} ; ++ ++#endif /* DBLDEFS_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/dbll.h b/arch/arm/plat-omap/include/dspbridge/dbll.h +new file mode 100644 +index 0000000..c3aa212 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dbll.h +@@ -0,0 +1,70 @@ ++/* ++ * dbll.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dbll.h ======== ++ * DSP/BIOS Bridge Dynamic load library module interface. Function header ++ * comments are in the file dblldefs.h. ++ * ++ *! Revision History ++ *! ================ ++ *! 31-Jul-2002 jeh Removed function comments (now in dblldefs.h). ++ *! 17-Apr-2002 jeh Created based on zl.h. ++ */ ++ ++#ifndef DBLL_ ++#define DBLL_ ++ ++#include ++#include ++ ++ extern void DBLL_close(struct DBLL_LibraryObj *lib); ++ extern DSP_STATUS DBLL_create(struct DBLL_TarObj **pTarget, ++ struct DBLL_Attrs *pAttrs); ++ extern void DBLL_delete(struct DBLL_TarObj *target); ++ extern void DBLL_exit(void); ++ extern bool DBLL_getAddr(struct DBLL_LibraryObj *lib, char *name, ++ struct DBLL_Symbol **ppSym); ++ extern void DBLL_getAttrs(struct DBLL_TarObj *target, ++ struct DBLL_Attrs *pAttrs); ++ extern bool DBLL_getCAddr(struct DBLL_LibraryObj *lib, char *name, ++ struct DBLL_Symbol **ppSym); ++ extern DSP_STATUS DBLL_getSect(struct DBLL_LibraryObj *lib, char *name, ++ u32 *pAddr, u32 *pSize); ++ extern bool DBLL_init(void); ++ extern DSP_STATUS DBLL_load(struct DBLL_LibraryObj *lib, ++ DBLL_Flags flags, ++ struct DBLL_Attrs *attrs, u32 *pEntry); ++ extern DSP_STATUS DBLL_loadSect(struct DBLL_LibraryObj *lib, ++ char *sectName, ++ struct DBLL_Attrs *attrs); ++ extern DSP_STATUS DBLL_open(struct DBLL_TarObj *target, char *file, ++ DBLL_Flags flags, ++ struct DBLL_LibraryObj **pLib); ++ extern DSP_STATUS DBLL_readSect(struct DBLL_LibraryObj *lib, ++ char *name, ++ char *pBuf, u32 size); ++ extern void DBLL_setAttrs(struct DBLL_TarObj *target, ++ struct DBLL_Attrs *pAttrs); ++ extern void DBLL_unload(struct DBLL_LibraryObj *lib, ++ struct DBLL_Attrs *attrs); ++ extern DSP_STATUS DBLL_unloadSect(struct DBLL_LibraryObj *lib, ++ char *sectName, ++ struct DBLL_Attrs *attrs); ++ ++#endif /* DBLL_ */ ++ +diff --git a/arch/arm/plat-omap/include/dspbridge/dblldefs.h b/arch/arm/plat-omap/include/dspbridge/dblldefs.h +new file mode 100644 +index 0000000..2361ce8 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dblldefs.h +@@ -0,0 +1,509 @@ ++/* ++ * dblldefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dblldefs.h ======== ++ * ++ *! Revision History ++ *! ================ ++ *! 08-Apr-2003 map Consolidated DBL into DBLL name ++ *! 19-Mar-2002 jeh Added DBL_Fxns type (to make it easier to switch ++ *! between different loaders). ++ *! 28-Sep-2001 jeh Created from zl.h. ++ */ ++#ifndef DBLLDEFS_ ++#define DBLLDEFS_ ++ ++/* ++ * Bit masks for DBL_Flags. ++ */ ++#define DBLL_NOLOAD 0x0 /* Don't load symbols, code, or data */ ++#define DBLL_SYMB 0x1 /* load symbols */ ++#define DBLL_CODE 0x2 /* load code */ ++#define DBLL_DATA 0x4 /* load data */ ++#define DBLL_DYNAMIC 0x8 /* dynamic load */ ++#define DBLL_BSS 0x20 /* Unitialized section */ ++ ++#define DBLL_MAXPATHLENGTH 255 ++ ++ ++/* ++ * ======== DBLL_Target ======== ++ * ++ */ ++struct DBLL_TarObj; ++ ++/* ++ * ======== DBLL_Flags ======== ++ * Specifies whether to load code, data, or symbols ++ */ ++typedef s32 DBLL_Flags; ++ ++/* ++ * ======== DBLL_Library ======== ++ * ++ */ ++struct DBLL_LibraryObj; ++ ++/* ++ * ======== DBLL_SectInfo ======== ++ * For collecting info on overlay sections ++ */ ++struct DBLL_SectInfo { ++ const char *name; /* name of section */ ++ u32 runAddr; /* run address of section */ ++ u32 loadAddr; /* load address of section */ ++ u32 size; /* size of section (target MAUs) */ ++ DBLL_Flags type; /* Code, data, or BSS */ ++} ; ++ ++/* ++ * ======== DBLL_Symbol ======== ++ * (Needed for dynamic load library) ++ */ ++struct DBLL_Symbol { ++ u32 value; ++}; ++ ++/* ++ * ======== DBLL_AllocFxn ======== ++ * Allocate memory function. Allocate or reserve (if reserved == TRUE) ++ * "size" bytes of memory from segment "space" and return the address in ++ * *dspAddr (or starting at *dspAddr if reserve == TRUE). Returns 0 on ++ * success, or an error code on failure. ++ */ ++typedef s32(*DBLL_AllocFxn) (void *hdl, s32 space, u32 size, u32 align, ++ u32 *dspAddr, s32 segId, s32 req, ++ bool reserved); ++ ++/* ++ * ======== DBLL_CloseFxn ======== ++ */ ++typedef s32(*DBLL_FCloseFxn) (void *); ++ ++/* ++ * ======== DBLL_FreeFxn ======== ++ * Free memory function. Free, or unreserve (if reserved == TRUE) "size" ++ * bytes of memory from segment "space" ++ */ ++typedef bool(*DBLL_FreeFxn) (void *hdl, u32 addr, s32 space, u32 size, ++ bool reserved); ++ ++/* ++ * ======== DBLL_FOpenFxn ======== ++ */ ++typedef void *(*DBLL_FOpenFxn) (const char *, const char *); ++ ++/* ++ * ======== DBLL_LogWriteFxn ======== ++ * Function to call when writing data from a section, to log the info. ++ * Can be NULL if no logging is required. ++ */ ++typedef DSP_STATUS(*DBLL_LogWriteFxn)(void *handle, struct DBLL_SectInfo *sect, ++ u32 addr, u32 nBytes); ++ ++/* ++ * ======== DBLL_ReadFxn ======== ++ */ ++typedef s32(*DBLL_ReadFxn) (void *, size_t, size_t, void *); ++ ++/* ++ * ======== DBLL_SeekFxn ======== ++ */ ++typedef s32(*DBLL_SeekFxn) (void *, long, int); ++ ++/* ++ * ======== DBLL_SymLookup ======== ++ * Symbol lookup function - Find the symbol name and return its value. ++ * ++ * Parameters: ++ * handle - Opaque handle ++ * pArg - Opaque argument. ++ * name - Name of symbol to lookup. ++ * sym - Location to store address of symbol structure. ++ * ++ * Returns: ++ * TRUE: Success (symbol was found). ++ * FALSE: Failed to find symbol. ++ */ ++typedef bool(*DBLL_SymLookup) (void *handle, void *pArg, void *rmmHandle, ++ const char *name, struct DBLL_Symbol **sym); ++ ++/* ++ * ======== DBLL_TellFxn ======== ++ */ ++typedef s32(*DBLL_TellFxn) (void *); ++ ++/* ++ * ======== DBLL_WriteFxn ======== ++ * Write memory function. Write "n" HOST bytes of memory to segment "mtype" ++ * starting at address "dspAddr" from the buffer "buf". The buffer is ++ * formatted as an array of words appropriate for the DSP. ++ */ ++typedef s32(*DBLL_WriteFxn) (void *hdl, u32 dspAddr, void *buf, ++ u32 n, s32 mtype); ++ ++/* ++ * ======== DBLL_Attrs ======== ++ */ ++struct DBLL_Attrs { ++ DBLL_AllocFxn alloc; ++ DBLL_FreeFxn free; ++ void *rmmHandle; /* Handle to pass to alloc, free functions */ ++ DBLL_WriteFxn write; ++ void *wHandle; /* Handle to pass to write, cinit function */ ++ bool baseImage; ++ DBLL_LogWriteFxn logWrite; ++ void *logWriteHandle; ++ ++ /* Symbol matching function and handle to pass to it */ ++ DBLL_SymLookup symLookup; ++ void *symHandle; ++ void *symArg; ++ ++ /* ++ * These file manipulation functions should be compatible with the ++ * "C" run time library functions of the same name. ++ */ ++ s32(*fread) (void *, size_t, size_t, void *); ++ s32(*fseek) (void *, long, int); ++ s32(*ftell) (void *); ++ s32(*fclose) (void *); ++ void *(*fopen) (const char *, const char *); ++} ; ++ ++/* ++ * ======== DBLL_close ======== ++ * Close library opened with DBLL_open. ++ * Parameters: ++ * lib - Handle returned from DBLL_open(). ++ * Returns: ++ * Requires: ++ * DBL initialized. ++ * Valid lib. ++ * Ensures: ++ */ ++typedef void(*DBLL_CloseFxn) (struct DBLL_LibraryObj *library); ++ ++/* ++ * ======== DBLL_create ======== ++ * Create a target object, specifying the alloc, free, and write functions. ++ * Parameters: ++ * pTarget - Location to store target handle on output. ++ * pAttrs - Attributes. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Memory allocation failed. ++ * Requires: ++ * DBL initialized. ++ * pAttrs != NULL. ++ * pTarget != NULL; ++ * Ensures: ++ * Success: *pTarget != NULL. ++ * Failure: *pTarget == NULL. ++ */ ++typedef DSP_STATUS(*DBLL_CreateFxn)(struct DBLL_TarObj **pTarget, ++ struct DBLL_Attrs *attrs); ++ ++/* ++ * ======== DBLL_delete ======== ++ * Delete target object and free resources for any loaded libraries. ++ * Parameters: ++ * target - Handle returned from DBLL_Create(). ++ * Returns: ++ * Requires: ++ * DBL initialized. ++ * Valid target. ++ * Ensures: ++ */ ++typedef void(*DBLL_DeleteFxn) (struct DBLL_TarObj *target); ++ ++/* ++ * ======== DBLL_exit ======== ++ * Discontinue use of DBL module. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * cRefs > 0. ++ * Ensures: ++ * cRefs >= 0. ++ */ ++typedef void(*DBLL_ExitFxn) (void); ++ ++/* ++ * ======== DBLL_getAddr ======== ++ * Get address of name in the specified library. ++ * Parameters: ++ * lib - Handle returned from DBLL_open(). ++ * name - Name of symbol ++ * ppSym - Location to store symbol address on output. ++ * Returns: ++ * TRUE: Success. ++ * FALSE: Symbol not found. ++ * Requires: ++ * DBL initialized. ++ * Valid library. ++ * name != NULL. ++ * ppSym != NULL. ++ * Ensures: ++ */ ++typedef bool(*DBLL_GetAddrFxn) (struct DBLL_LibraryObj *lib, char *name, ++ struct DBLL_Symbol **ppSym); ++ ++/* ++ * ======== DBLL_getAttrs ======== ++ * Retrieve the attributes of the target. ++ * Parameters: ++ * target - Handle returned from DBLL_Create(). ++ * pAttrs - Location to store attributes on output. ++ * Returns: ++ * Requires: ++ * DBL initialized. ++ * Valid target. ++ * pAttrs != NULL. ++ * Ensures: ++ */ ++typedef void(*DBLL_GetAttrsFxn) (struct DBLL_TarObj *target, ++ struct DBLL_Attrs *attrs); ++ ++/* ++ * ======== DBLL_getCAddr ======== ++ * Get address of "C" name on the specified library. ++ * Parameters: ++ * lib - Handle returned from DBLL_open(). ++ * name - Name of symbol ++ * ppSym - Location to store symbol address on output. ++ * Returns: ++ * TRUE: Success. ++ * FALSE: Symbol not found. ++ * Requires: ++ * DBL initialized. ++ * Valid target. ++ * name != NULL. ++ * ppSym != NULL. ++ * Ensures: ++ */ ++typedef bool(*DBLL_GetCAddrFxn) (struct DBLL_LibraryObj *lib, char *name, ++ struct DBLL_Symbol **ppSym); ++ ++/* ++ * ======== DBLL_getSect ======== ++ * Get address and size of a named section. ++ * Parameters: ++ * lib - Library handle returned from DBLL_open(). ++ * name - Name of section. ++ * pAddr - Location to store section address on output. ++ * pSize - Location to store section size on output. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ENOSECT: Section not found. ++ * Requires: ++ * DBL initialized. ++ * Valid lib. ++ * name != NULL. ++ * pAddr != NULL; ++ * pSize != NULL. ++ * Ensures: ++ */ ++typedef DSP_STATUS(*DBLL_GetSectFxn) (struct DBLL_LibraryObj *lib, char *name, ++ u32 *addr, u32 *size); ++ ++/* ++ * ======== DBLL_init ======== ++ * Initialize DBL module. ++ * Parameters: ++ * Returns: ++ * TRUE: Success. ++ * FALSE: Failure. ++ * Requires: ++ * cRefs >= 0. ++ * Ensures: ++ * Success: cRefs > 0. ++ * Failure: cRefs >= 0. ++ */ ++typedef bool(*DBLL_InitFxn) (void); ++ ++/* ++ * ======== DBLL_load ======== ++ * Load library onto the target. ++ * ++ * Parameters: ++ * lib - Library handle returned from DBLL_open(). ++ * flags - Load code, data and/or symbols. ++ * attrs - May contain alloc, free, and write function. ++ * pulEntry - Location to store program entry on output. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFREAD: File read failed. ++ * DSP_EFWRITE: Write to target failed. ++ * DSP_EDYNLOAD: Failure in dynamic loader library. ++ * Requires: ++ * DBL initialized. ++ * Valid lib. ++ * pEntry != NULL. ++ * Ensures: ++ */ ++typedef DSP_STATUS(*DBLL_LoadFxn) (struct DBLL_LibraryObj *lib, ++ DBLL_Flags flags, ++ struct DBLL_Attrs *attrs, u32 *entry); ++ ++/* ++ * ======== DBLL_loadSect ======== ++ * Load a named section from an library (for overlay support). ++ * Parameters: ++ * lib - Handle returned from DBLL_open(). ++ * sectName - Name of section to load. ++ * attrs - Contains write function and handle to pass to it. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ENOSECT: Section not found. ++ * DSP_EFWRITE: Write function failed. ++ * DSP_ENOTIMPL: Function not implemented. ++ * Requires: ++ * Valid lib. ++ * sectName != NULL. ++ * attrs != NULL. ++ * attrs->write != NULL. ++ * Ensures: ++ */ ++typedef DSP_STATUS(*DBLL_LoadSectFxn) (struct DBLL_LibraryObj *lib, ++ char *pszSectName, ++ struct DBLL_Attrs *attrs); ++ ++/* ++ * ======== DBLL_open ======== ++ * DBLL_open() returns a library handle that can be used to load/unload ++ * the symbols/code/data via DBLL_load()/DBLL_unload(). ++ * Parameters: ++ * target - Handle returned from DBLL_create(). ++ * file - Name of file to open. ++ * flags - If flags & DBLL_SYMB, load symbols. ++ * pLib - Location to store library handle on output. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Memory allocation failure. ++ * DSP_EFOPEN: File open failure. ++ * DSP_EFREAD: File read failure. ++ * DSP_ECORRUPTFILE: Unable to determine target type. ++ * Requires: ++ * DBL initialized. ++ * Valid target. ++ * file != NULL. ++ * pLib != NULL. ++ * DBLL_Attrs fopen function non-NULL. ++ * Ensures: ++ * Success: Valid *pLib. ++ * Failure: *pLib == NULL. ++ */ ++typedef DSP_STATUS(*DBLL_OpenFxn) (struct DBLL_TarObj *target, char *file, ++ DBLL_Flags flags, ++ struct DBLL_LibraryObj **pLib); ++ ++/* ++ * ======== DBLL_readSect ======== ++ * Read COFF section into a character buffer. ++ * Parameters: ++ * lib - Library handle returned from DBLL_open(). ++ * name - Name of section. ++ * pBuf - Buffer to write section contents into. ++ * size - Buffer size ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ENOSECT: Named section does not exists. ++ * Requires: ++ * DBL initialized. ++ * Valid lib. ++ * name != NULL. ++ * pBuf != NULL. ++ * size != 0. ++ * Ensures: ++ */ ++typedef DSP_STATUS(*DBLL_ReadSectFxn) (struct DBLL_LibraryObj *lib, char *name, ++ char *content, u32 uContentSize); ++ ++/* ++ * ======== DBLL_setAttrs ======== ++ * Set the attributes of the target. ++ * Parameters: ++ * target - Handle returned from DBLL_create(). ++ * pAttrs - New attributes. ++ * Returns: ++ * Requires: ++ * DBL initialized. ++ * Valid target. ++ * pAttrs != NULL. ++ * Ensures: ++ */ ++typedef void(*DBLL_SetAttrsFxn)(struct DBLL_TarObj *target, ++ struct DBLL_Attrs *attrs); ++ ++/* ++ * ======== DBLL_unload ======== ++ * Unload library loaded with DBLL_load(). ++ * Parameters: ++ * lib - Handle returned from DBLL_open(). ++ * attrs - Contains free() function and handle to pass to it. ++ * Returns: ++ * Requires: ++ * DBL initialized. ++ * Valid lib. ++ * Ensures: ++ */ ++typedef void(*DBLL_UnloadFxn) (struct DBLL_LibraryObj *library, ++ struct DBLL_Attrs *attrs); ++ ++/* ++ * ======== DBLL_unloadSect ======== ++ * Unload a named section from an library (for overlay support). ++ * Parameters: ++ * lib - Handle returned from DBLL_open(). ++ * sectName - Name of section to load. ++ * attrs - Contains free() function and handle to pass to it. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ENOSECT: Named section not found. ++ * DSP_ENOTIMPL ++ * Requires: ++ * DBL initialized. ++ * Valid lib. ++ * sectName != NULL. ++ * Ensures: ++ */ ++typedef DSP_STATUS(*DBLL_UnloadSectFxn) (struct DBLL_LibraryObj *lib, ++ char *pszSectName, ++ struct DBLL_Attrs *attrs); ++ ++struct DBLL_Fxns { ++ DBLL_CloseFxn closeFxn; ++ DBLL_CreateFxn createFxn; ++ DBLL_DeleteFxn deleteFxn; ++ DBLL_ExitFxn exitFxn; ++ DBLL_GetAttrsFxn getAttrsFxn; ++ DBLL_GetAddrFxn getAddrFxn; ++ DBLL_GetCAddrFxn getCAddrFxn; ++ DBLL_GetSectFxn getSectFxn; ++ DBLL_InitFxn initFxn; ++ DBLL_LoadFxn loadFxn; ++ DBLL_LoadSectFxn loadSectFxn; ++ DBLL_OpenFxn openFxn; ++ DBLL_ReadSectFxn readSectFxn; ++ DBLL_SetAttrsFxn setAttrsFxn; ++ DBLL_UnloadFxn unloadFxn; ++ DBLL_UnloadSectFxn unloadSectFxn; ++} ; ++ ++#endif /* DBLDEFS_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/dbof.h b/arch/arm/plat-omap/include/dspbridge/dbof.h +new file mode 100644 +index 0000000..54f4250 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dbof.h +@@ -0,0 +1,117 @@ ++/* ++ * dbof.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dbof.h ======== ++ * Description: ++ * Defines and typedefs for DSP/BIOS Bridge Object File Format (DBOF). ++ * ++ *! Revision History ++ *! ================ ++ *! 12-Jul-2002 jeh Added defines for DBOF_SectHdr page. ++ *! 12-Oct-2001 jeh Converted to std.h format. ++ *! 07-Sep-2001 jeh Added overlay support. ++ *! 06-Jul-2001 jeh Created. ++ */ ++ ++#ifndef DBOF_ ++#define DBOF_ ++ ++/* Enough to hold DCD section names: 32 digit ID + underscores */ ++#define DBOF_DCDSECTNAMELEN 40 ++ ++/* Values for DBOF_SectHdr page field. */ ++#define DBOF_PROGRAM 0 ++#define DBOF_DATA 1 ++#define DBOF_CINIT 2 ++ ++/* ++ * ======== DBOF_FileHdr ======== ++ */ ++ struct DBOF_FileHdr { ++ u32 magic; /* COFF magic number */ ++ u32 entry; /* Program entry point */ ++ u16 numSymbols; /* Number of bridge symbols */ ++ u16 numDCDSects; /* Number of DCD sections */ ++ u16 numSects; /* Number of sections to load */ ++ u16 numOvlySects; /* Number of overlay sections */ ++ u32 symOffset; /* Offset in file to symbols */ ++ u32 dcdSectOffset; /* Offset to DCD sections */ ++ u32 loadSectOffset; /* Offset to loadable sections */ ++ u32 ovlySectOffset; /* Offset to overlay data */ ++ u16 version; /* DBOF version number */ ++ u16 resvd; /* Reserved for future use */ ++ } ; ++ ++/* ++ * ======== DBOF_DCDSectHdr ======== ++ */ ++ struct DBOF_DCDSectHdr { ++ u32 size; /* Sect size (target MAUs) */ ++ char name[DBOF_DCDSECTNAMELEN]; /* DCD section name */ ++ } ; ++ ++/* ++ * ======== DBOF_OvlySectHdr ======== ++ */ ++ struct DBOF_OvlySectHdr { ++ u16 nameLen; /* Length of section name */ ++ u16 numCreateSects; /* # of sects loaded for create phase */ ++ u16 numDeleteSects; /* # of sects loaded for delete phase */ ++ u16 numExecuteSects; /* # of sects loaded for execute phase */ ++ ++ /* ++ * Number of sections where load/unload phase is not specified. ++ * These sections will be loaded when create phase sects are ++ * loaded, and unloaded when the delete phase is unloaded. ++ */ ++ u16 numOtherSects; ++ u16 resvd; /* Reserved for future use */ ++ }; ++ ++/* ++ * ======== DBOF_OvlySectData ======== ++ */ ++ struct DBOF_OvlySectData { ++ u32 loadAddr; /* Section load address */ ++ u32 runAddr; /* Section run address */ ++ u32 size; /* Section size (target MAUs) */ ++ u16 page; /* Memory page number */ ++ u16 resvd; /* Reserved */ ++ } ; ++ ++/* ++ * ======== DBOF_SectHdr ======== ++ */ ++ struct DBOF_SectHdr { ++ u32 addr; /* Section address */ ++ u32 size; /* Section size (target MAUs) */ ++ u16 page; /* Page number */ ++ u16 resvd; /* Reserved for future use */ ++ } ; ++ ++/* ++ * ======== DBOF_SymbolHdr ======== ++ */ ++ struct DBOF_SymbolHdr { ++ u32 value; /* Symbol value */ ++ u16 nameLen; /* Length of symbol name */ ++ u16 resvd; /* Reserved for future use */ ++ } ; ++ ++#endif /* DBOF_ */ ++ +diff --git a/arch/arm/plat-omap/include/dspbridge/dbreg.h b/arch/arm/plat-omap/include/dspbridge/dbreg.h +new file mode 100644 +index 0000000..d311b88 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dbreg.h +@@ -0,0 +1,113 @@ ++/* ++ * dbreg.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dbreg.h ======== ++ * Purpose: ++ * Registry keys for use in Linux. This is the clearinghouse for ++ * registry definitions, hopefully eliminating overlapping between ++ * modules. ++ * ++ *! Revision History: ++ *! ================ ++ *! 10-Apr-2003 vp: Added macro for subkey TCWORDSWAP. ++ *! 21-Mar-2003 sb: Added macro for subkey SHMSize ++ *! 27-Aug-2001 jeh Added WSXREG_LOADERFILENAME. ++ *! 13-Feb-2001 kc: DSP/BIOS Bridge name updates. ++ *! 29-Nov-2000 rr: Added WSXREG_DSPTYPE_55 as 6. ++ *! 06-Sep-2000 jeh: Added WSXREG_CHNLOFFSET, WSXREG_NUMCHNLS, ++ *! WSXREG_CHNLBUFSIZE. ++ *! 26-Aug-2000 rr: MEMBASE expanded to 9 entries. ++ *! 26-Jul-2000 rr: Added WSXREG_DCDNAME for the DCD Dll name. It will ++ *! live under WSXREG_WINSPOXCONFIG. ++ *! 17-Jul-2000 rr: REG_MGR_OBJECT and REG_DRV_OBJECT defined. They ++ *! are stored in the Registrty under WSXREG_WINSPOXCONFIG ++ *! when they are created in DSP_Init. WSXREG_DEVOBJECT ++ *! and WSXREG_MGROBJECT defined. ++ *! 11-Dec-1999 ag: Renamed Isa to IsaBus due to conflict with ceddk.h. ++ *! 12-Nov-1999 rr: New Registry Defnitions. ++ *! 15-Oct-1999 rr: New entry for DevObject created. WSXREG_DEVOBJECT ++ *! under WSXREG_DDSPDRIVERPATH ++ *! 10-Nov-1997 cr: Added WSXREG_INFPATH, WSXREG_WINDEVICEPATH, ++ *! WSXREG_WINCURVERSION ++ *! 21-Oct-1997 cr: Added WSXREG_BUSTYPE. ++ *! 08-Sep-1997 cr: Added WSXREG_SERVICES, WSXREG_SERVICENAME and ++ *! WSXREG_CLASSINDEX. ++ *! 30-Aug-1997 cr: Added WSXREG_SOFTWAREPATHNT & WSXREG_WBCLASSGUID. ++ *! 24-Mar-1997 gp: Added MAXCHIPINFOSUBKEY def. ++ *! 18-Feb-1997 cr: Changed Version1.1 -> Version1.0 ++ *! 12-Feb-1997 cr: Changed WinSPOX -> WinBRIDGE. ++ *! 11-Dec-1996 gp: Added Perf key name in WinSPOX Config. ++ *! 22-Jul-1996 gp: Added Trace key name. ++ *! 30-May-1996 cr: Created. ++ */ ++ ++#ifndef DBREG_ ++#define DBREG_ 1 /* Defined as "1" so InstallShield programs compile. */ ++ ++#define REG_MGR_OBJECT 1 ++#define REG_DRV_OBJECT 2 ++/* general registry definitions */ ++#define MAXREGPATHLENGTH 255 /* Max registry path length. Also the ++ max registry value length. */ ++#define DSPTYPE_55 6 /* This is the DSP Chip type for 55 */ ++#define DSPTYPE_64 0x99 ++#define IVA_ARM7 0x97 /* This is the DSP Chip type for IVA/ARM7 */ ++ ++#define DSPPROCTYPE_C55 5510 ++#define DSPPROCTYPE_C64 6410 ++#define IVAPROCTYPE_ARM7 470 ++/* registry */ ++#define DEVNODESTRING "DevNode" /* u32 devnode */ ++#define CONFIG "Software\\TexasInstruments\\DirectDSP\\Config" ++#define DRVOBJECT "DrvObject" ++#define MGROBJECT "MgrObject" ++#define CLASS "Device" /* device class */ ++#define TRACE "Trace" /* GT Trace settings. */ ++#define PERFR "Perf" /* Enable perf bool. */ ++#define ROOT "Root" /* root dir */ ++ ++/* MiniDriver related definitions */ ++/* The following definitions are under "Drivers\\DirectDSP\\Device\\XXX " ++ * Where XXX is the device or board name */ ++ ++#define WMDFILENAME "MiniDriver" /* WMD entry name */ ++#define CHIPTYPE "ChipType" /* Chip type */ ++#define CHIPNUM "NumChips" /* Number of chips */ ++#define NUMPROCS "NumOfProcessors" /* Number of processors */ ++#define DEFEXEC "DefaultExecutable" /* Default executable */ ++#define AUTOSTART "AutoStart" /* Statically load flag */ ++#define IVAAUTOSTART "IvaAutoStart" /* Statically load flag */ ++#define BOARDNAME "BoardName" /* Name of the Board */ ++#define UNITNUMBER "UnitNumber" /* Unit # of the Board */ ++#define BUSTYPE "BusType" /* Bus type board is on */ ++#define BUSNUMBER "BusNumber" /* Bus number board is on */ ++#define CURRENTCONFIG "CurrentConfig" /* Current resources */ ++#define PCIVENDEVID "VendorDeviceId" /* The board's id */ ++#define INFPATH "InfPath" /* wmd's inf filename */ ++#define DEVOBJECT "DevObject" ++#define ZLFILENAME "ZLFileName" /* Name of ZL file */ ++#define WORDSIZE "WordSize" /* NumBytes in DSP Word */ ++#define SHMSIZE "SHMSize" /* Size of SHM reservd on MPU */ ++#define IVAEXTMEMSIZE "IVAEXTMEMSize" /* IVA External Memeory size */ ++#define TCWORDSWAP "TCWordSwap" /* Traffic Contoller Word Swap */ ++#define DSPRESOURCES "DspTMSResources" /* C55 DSP resurces on OMAP */ ++#define IVA1RESOURCES "ARM7IvaResources" /* ARM7 IVA resurces on OMAP */ ++#define PHYSMEMPOOLBASE "PhysicalMemBase" /* Physical mem passed to driver */ ++#define PHYSMEMPOOLSIZE "PhysicalMemSize" /* Physical mem passed to driver */ ++ ++#endif /* DBREG_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/dbtype.h b/arch/arm/plat-omap/include/dspbridge/dbtype.h +new file mode 100644 +index 0000000..b4953a0 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dbtype.h +@@ -0,0 +1,103 @@ ++/* ++ * dbtype.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== dbtype.h ======== ++ * Description: ++ * This header defines data types for DSP/BIOS Bridge APIs and device ++ * driver modules. It also defines the Hungarian ++ * prefix to use for each base type. ++ * ++ * ++ *! Revision History: ++ *! ================= ++ *! 23-Nov-2002 gp: Purpose -> Description in file header. ++ *! 13-Feb-2001 kc: Name changed from ddsptype.h dbtype.h. ++ *! 09-Oct-2000 jeh Added CHARACTER. ++ *! 11-Aug-2000 ag: Added 'typedef void void'. ++ *! 08-Apr-2000 ww: Cloned. ++ */ ++ ++#ifndef DBTYPE_ ++#define DBTYPE_ ++ ++/*============================================================================*/ ++/* Argument specification syntax */ ++/*============================================================================*/ ++ ++#ifndef IN ++#define IN /* Following parameter is for input. */ ++#endif ++ ++#ifndef OUT ++#define OUT /* Following parameter is for output. */ ++#endif ++ ++#ifndef OPTIONAL ++#define OPTIONAL /* Function may optionally use previous parameter. */ ++#endif ++ ++#ifndef CONST ++#define CONST const ++#endif ++ ++/*============================================================================*/ ++/* Boolean constants */ ++/*============================================================================*/ ++ ++#ifndef FALSE ++#define FALSE 0 ++#endif ++#ifndef TRUE ++#define TRUE 1 ++#endif ++ ++/*============================================================================*/ ++/* NULL (Definition is language specific) */ ++/*============================================================================*/ ++ ++#ifndef NULL ++#define NULL ((void *)0) /* Null pointer. */ ++#endif ++ ++/*============================================================================*/ ++/* NULL character (normally used for string termination) */ ++/*============================================================================*/ ++ ++#ifndef NULL_CHAR ++#define NULL_CHAR '\0' /* Null character. */ ++#endif ++ ++/*============================================================================*/ ++/* Basic Type definitions (with Prefixes for Hungarian notation) */ ++/*============================================================================*/ ++ ++#ifndef OMAPBRIDGE_TYPES ++#define OMAPBRIDGE_TYPES ++typedef volatile unsigned short REG_UWORD16; ++#endif ++ ++typedef void *HANDLE; /* h */ ++ ++#define TEXT(x) x ++ ++#define DLLIMPORT ++#define DLLEXPORT ++ ++/* Define DSPAPIDLL correctly in dspapi.h */ ++#define _DSPSYSDLL32_ ++ ++#endif /* DBTYPE_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/dehdefs.h b/arch/arm/plat-omap/include/dspbridge/dehdefs.h +new file mode 100644 +index 0000000..06e5582 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dehdefs.h +@@ -0,0 +1,42 @@ ++/* ++ * dehdefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== dehdefs.h ======== ++ * Purpose: ++ * Definition for mini-driver module DEH. ++ * ++ *! Revision History: ++ *! ================ ++ *! 17-Dec-2001 ag: added #include for shared mailbox codes. ++ *! 10-Dec-2001 kc: added DEH error base value and error max value. ++ *! 11-Sep-2001 kc: created. ++ */ ++ ++#ifndef DEHDEFS_ ++#define DEHDEFS_ ++ ++#include /* shared mailbox codes */ ++ ++/* DEH object manager */ ++ struct DEH_MGR; ++ ++/* Magic code used to determine if DSP signaled exception. */ ++#define DEH_BASE MBX_DEH_BASE ++#define DEH_USERS_BASE MBX_DEH_USERS_BASE ++#define DEH_LIMIT MBX_DEH_LIMIT ++ ++#endif /* _DEHDEFS_H */ +diff --git a/arch/arm/plat-omap/include/dspbridge/dev.h b/arch/arm/plat-omap/include/dspbridge/dev.h +new file mode 100644 +index 0000000..5f468c9 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dev.h +@@ -0,0 +1,785 @@ ++/* ++ * dev.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dev.h ======== ++ * Description: ++ * 'Bridge Mini-driver device operations. ++ * ++ * Public Functions: ++ * DEV_BrdWriteFxn ++ * DEV_CreateDevice ++ * DEV_Create2 ++ * DEV_Destroy2 ++ * DEV_DestroyDevice ++ * DEV_GetChnlMgr ++ * DEV_GetCmmMgr ++ * DEV_GetCodMgr ++ * DEV_GetDehMgr ++ * DEV_GetDevNode ++ * DEV_GetDSPWordSize ++ * DEV_GetFirst ++ * DEV_GetIntfFxns ++ * DEV_GetIOMgr ++ * DEV_GetMsgMgr ++ * DEV_GetNext ++ * DEV_GetNodeManager ++ * DEV_GetSymbol ++ * DEV_GetWMDContext ++ * DEV_Exit ++ * DEV_Init ++ * DEV_InsertProcObject ++ * DEV_IsLocked ++ * DEV_NotifyClient ++ * DEV_RegisterNotify ++ * DEV_ReleaseCodMgr ++ * DEV_RemoveDevice ++ * DEV_RemoveProcObject ++ * DEV_SetChnlMgr ++ * DEV_SetMsgMgr ++ * DEV_SetLockOwner ++ * DEV_StartDevice ++ * ++ *! Revision History: ++ *! ================ ++ *! 08-Mar-2004 sb Added the Dynamic Memory Mapping feature - Dev_GetDmmMgr ++ *! 09-Feb-2004 vp Added functions required for IVA ++ *! 25-Feb-2003 swa PMGR Code Review changes incorporated ++ *! 05-Nov-2001 kc: Added DEV_GetDehMgr. ++ *! 05-Dec-2000 jeh Added DEV_SetMsgMgr. ++ *! 29-Nov-2000 rr: Incorporated code review changes. ++ *! 17-Nov-2000 jeh Added DEV_GetMsgMgr. ++ *! 05-Oct-2000 rr: DEV_Create2 & DEV_Destroy2 Added. ++ *! 02-Oct-2000 rr: Added DEV_GetNodeManager. ++ *! 11-Aug-2000 ag: Added DEV_GetCmmMgr() for shared memory management. ++ *! 10-Aug-2000 rr: DEV_InsertProcObject/RemoveProcObject added. ++ *! 06-Jun-2000 jeh Added DEV_GetSymbol(). ++ *! 05-Nov-1999 kc: Updated function prototypes. ++ *! 08-Oct-1997 cr: Added explicit CDECL function identifiers. ++ *! 07-Nov-1996 gp: Updated for code review. ++ *! 22-Oct-1996 gp: Added DEV_CleanupProcessState(). ++ *! 29-May-1996 gp: Changed DEV_HDEVNODE --> CFG_HDEVNODE. ++ *! 18-May-1996 gp: Created. ++ */ ++ ++#ifndef DEV_ ++#define DEV_ ++ ++/* ----------------------------------- Module Dependent Headers */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++ ++/* ++ * ======== DEV_BrdWriteFxn ======== ++ * Purpose: ++ * Exported function to be used as the COD write function. This function ++ * is passed a handle to a DEV_hObject by ZL in pArb, then calls the ++ * device's WMD_BRD_Write() function. ++ * Parameters: ++ * pArb: Handle to a Device Object. ++ * hDevContext: Handle to mini-driver defined device info. ++ * dwDSPAddr: Address on DSP board (Destination). ++ * pHostBuf: Pointer to host buffer (Source). ++ * ulNumBytes: Number of bytes to transfer. ++ * ulMemType: Memory space on DSP to which to transfer. ++ * Returns: ++ * Number of bytes written. Returns 0 if the DEV_hObject passed in via ++ * pArb is invalid. ++ * Requires: ++ * DEV Initialized. ++ * pHostBuf != NULL ++ * Ensures: ++ */ ++ extern u32 DEV_BrdWriteFxn(void *pArb, ++ u32 ulDspAddr, ++ void *pHostBuf, ++ u32 ulNumBytes, u32 nMemSpace); ++ ++/* ++ * ======== DEV_CreateDevice ======== ++ * Purpose: ++ * Called by the operating system to load the 'Bridge Mini Driver for a ++ * 'Bridge device. ++ * Parameters: ++ * phDevObject: Ptr to location to receive the device object handle. ++ * pstrWMDFileName: Name of WMD PE DLL file to load. If the absolute ++ * path is not provided, the file is loaded through ++ * 'Bridge's module search path. ++ * pHostConfig: Host configuration information, to be passed down ++ * to the WMD when WMD_DEV_Create() is called. ++ * pDspConfig: DSP resources, to be passed down to the WMD when ++ * WMD_DEV_Create() is called. ++ * hDevNode: Platform (Windows) specific device node. ++ * Returns: ++ * DSP_SOK: Module is loaded, device object has been created ++ * DSP_EMEMORY: Insufficient memory to create needed resources. ++ * DEV_E_NEWWMD: The WMD was compiled for a newer version of WCD. ++ * DEV_E_NULLWMDINTF: WMD passed back a NULL Fxn Interface Struct Ptr ++ * DEV_E_NOCODMODULE: No ZL file name was specified in the registry ++ * for this hDevNode. ++ * LDR_E_FILEUNABLETOOPEN: Unable to open the specified WMD. ++ * LDR_E_NOMEMORY: PELDR is out of resources. ++ * DSP_EFAIL: Unable to find WMD entry point function. ++ * COD_E_NOZLFUNCTIONS: One or more ZL functions exports not found. ++ * COD_E_ZLCREATEFAILED: Unable to load ZL DLL. ++ * Requires: ++ * DEV Initialized. ++ * phDevObject != NULL. ++ * pstrWMDFileName != NULL. ++ * pHostConfig != NULL. ++ * pDspConfig != NULL. ++ * Ensures: ++ * DSP_SOK: *phDevObject will contain handle to the new device object. ++ * Otherwise, does not create the device object, ensures the WMD module is ++ * unloaded, and sets *phDevObject to NULL. ++ */ ++ extern DSP_STATUS DEV_CreateDevice(OUT struct DEV_OBJECT ++ **phDevObject, ++ IN CONST char *pstrWMDFileName, ++ IN CONST struct CFG_HOSTRES ++ *pHostConfig, ++ IN CONST struct CFG_DSPRES ++ *pDspConfig, ++ struct CFG_DEVNODE *hDevNode); ++ ++/* ++ * ======== DEV_CreateIVADevice ======== ++ * Purpose: ++ * Called by the operating system to load the 'Bridge Mini Driver for IVA. ++ * Parameters: ++ * phDevObject: Ptr to location to receive the device object handle. ++ * pstrWMDFileName: Name of WMD PE DLL file to load. If the absolute ++ * path is not provided, the file is loaded through ++ * 'Bridge's module search path. ++ * pHostConfig: Host configuration information, to be passed down ++ * to the WMD when WMD_DEV_Create() is called. ++ * pDspConfig: DSP resources, to be passed down to the WMD when ++ * WMD_DEV_Create() is called. ++ * hDevNode: Platform (Windows) specific device node. ++ * Returns: ++ * DSP_SOK: Module is loaded, device object has been created ++ * DSP_EMEMORY: Insufficient memory to create needed resources. ++ * DEV_E_NEWWMD: The WMD was compiled for a newer version of WCD. ++ * DEV_E_NULLWMDINTF: WMD passed back a NULL Fxn Interface Struct Ptr ++ * DEV_E_NOCODMODULE: No ZL file name was specified in the registry ++ * for this hDevNode. ++ * LDR_E_FILEUNABLETOOPEN: Unable to open the specified WMD. ++ * LDR_E_NOMEMORY: PELDR is out of resources. ++ * DSP_EFAIL: Unable to find WMD entry point function. ++ * COD_E_NOZLFUNCTIONS: One or more ZL functions exports not found. ++ * COD_E_ZLCREATEFAILED: Unable to load ZL DLL. ++ * Requires: ++ * DEV Initialized. ++ * phDevObject != NULL. ++ * pstrWMDFileName != NULL. ++ * pHostConfig != NULL. ++ * pDspConfig != NULL. ++ * Ensures: ++ * DSP_SOK: *phDevObject will contain handle to the new device object. ++ * Otherwise, does not create the device object, ensures the WMD module is ++ * unloaded, and sets *phDevObject to NULL. ++ */ ++ extern DSP_STATUS DEV_CreateIVADevice(OUT struct DEV_OBJECT ++ **phDevObject, ++ IN CONST char *pstrWMDFileName, ++ IN CONST struct CFG_HOSTRES *pHostConfig, ++ IN CONST struct CFG_DSPRES *pDspConfig, ++ struct CFG_DEVNODE *hDevNode); ++ ++/* ++ * ======== DEV_Create2 ======== ++ * Purpose: ++ * After successful loading of the image from WCD_InitComplete2 ++ * (PROC Auto_Start) or PROC_Load this fxn is called. This creates ++ * the Node Manager and updates the DEV Object. ++ * Parameters: ++ * hDevObject: Handle to device object created with DEV_CreateDevice(). ++ * Returns: ++ * DSP_SOK: Successful Creation of Node Manager ++ * DSP_EFAIL: Some Error Occurred. ++ * Requires: ++ * DEV Initialized ++ * Valid hDevObject ++ * Ensures: ++ * DSP_SOK and hDevObject->hNodeMgr != NULL ++ * else hDevObject->hNodeMgr == NULL ++ */ ++ extern DSP_STATUS DEV_Create2(IN struct DEV_OBJECT *hDevObject); ++ ++/* ++ * ======== DEV_Destroy2 ======== ++ * Purpose: ++ * Destroys the Node manager for this device. ++ * Parameters: ++ * hDevObject: Handle to device object created with DEV_CreateDevice(). ++ * Returns: ++ * DSP_SOK: Successful Creation of Node Manager ++ * DSP_EFAIL: Some Error Occurred. ++ * Requires: ++ * DEV Initialized ++ * Valid hDevObject ++ * Ensures: ++ * DSP_SOK and hDevObject->hNodeMgr == NULL ++ * else DSP_EFAIL. ++ */ ++ extern DSP_STATUS DEV_Destroy2(IN struct DEV_OBJECT *hDevObject); ++ ++/* ++ * ======== DEV_DestroyDevice ======== ++ * Purpose: ++ * Destroys the channel manager for this device, if any, calls ++ * WMD_DEV_Destroy(), and then attempts to unload the WMD module. ++ * Parameters: ++ * hDevObject: Handle to device object created with ++ * DEV_CreateDevice(). ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hDevObject. ++ * DSP_EFAIL: The WMD failed it's WMD_DEV_Destroy() function. ++ * Requires: ++ * DEV Initialized. ++ * Ensures: ++ */ ++ extern DSP_STATUS DEV_DestroyDevice(struct DEV_OBJECT ++ *hDevObject); ++ ++/* ++ * ======== DEV_GetChnlMgr ======== ++ * Purpose: ++ * Retrieve the handle to the channel manager created for this device. ++ * Parameters: ++ * hDevObject: Handle to device object created with ++ * DEV_CreateDevice(). ++ * *phMgr: Ptr to location to store handle. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hDevObject. ++ * Requires: ++ * phMgr != NULL. ++ * DEV Initialized. ++ * Ensures: ++ * DSP_SOK: *phMgr contains a handle to a channel manager object, ++ * or NULL. ++ * else: *phMgr is NULL. ++ */ ++ extern DSP_STATUS DEV_GetChnlMgr(struct DEV_OBJECT *hDevObject, ++ OUT struct CHNL_MGR **phMgr); ++ ++/* ++ * ======== DEV_GetCmmMgr ======== ++ * Purpose: ++ * Retrieve the handle to the shared memory manager created for this ++ * device. ++ * Parameters: ++ * hDevObject: Handle to device object created with ++ * DEV_CreateDevice(). ++ * *phMgr: Ptr to location to store handle. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hDevObject. ++ * Requires: ++ * phMgr != NULL. ++ * DEV Initialized. ++ * Ensures: ++ * DSP_SOK: *phMgr contains a handle to a channel manager object, ++ * or NULL. ++ * else: *phMgr is NULL. ++ */ ++ extern DSP_STATUS DEV_GetCmmMgr(struct DEV_OBJECT *hDevObject, ++ OUT struct CMM_OBJECT **phMgr); ++ ++/* ++ * ======== DEV_GetDmmMgr ======== ++ * Purpose: ++ * Retrieve the handle to the dynamic memory manager created for this ++ * device. ++ * Parameters: ++ * hDevObject: Handle to device object created with ++ * DEV_CreateDevice(). ++ * *phMgr: Ptr to location to store handle. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hDevObject. ++ * Requires: ++ * phMgr != NULL. ++ * DEV Initialized. ++ * Ensures: ++ * DSP_SOK: *phMgr contains a handle to a channel manager object, ++ * or NULL. ++ * else: *phMgr is NULL. ++ */ ++ extern DSP_STATUS DEV_GetDmmMgr(struct DEV_OBJECT *hDevObject, ++ OUT struct DMM_OBJECT **phMgr); ++ ++/* ++ * ======== DEV_GetCodMgr ======== ++ * Purpose: ++ * Retrieve the COD manager create for this device. ++ * Parameters: ++ * hDevObject: Handle to device object created with ++ * DEV_CreateDevice(). ++ * *phCodMgr: Ptr to location to store handle. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hDevObject. ++ * Requires: ++ * phCodMgr != NULL. ++ * DEV Initialized. ++ * Ensures: ++ * DSP_SOK: *phCodMgr contains a handle to a COD manager object. ++ * else: *phCodMgr is NULL. ++ */ ++ extern DSP_STATUS DEV_GetCodMgr(struct DEV_OBJECT *hDevObject, ++ OUT struct COD_MANAGER **phCodMgr); ++ ++/* ++ * ======== DEV_GetDehMgr ======== ++ * Purpose: ++ * Retrieve the DEH manager created for this device. ++ * Parameters: ++ * hDevObject: Handle to device object created with DEV_CreateDevice(). ++ * *phDehMgr: Ptr to location to store handle. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hDevObject. ++ * Requires: ++ * phDehMgr != NULL. ++ * DEH Initialized. ++ * Ensures: ++ * DSP_SOK: *phDehMgr contains a handle to a DEH manager object. ++ * else: *phDehMgr is NULL. ++ */ ++ extern DSP_STATUS DEV_GetDehMgr(struct DEV_OBJECT *hDevObject, ++ OUT struct DEH_MGR **phDehMgr); ++ ++/* ++ * ======== DEV_GetDevNode ======== ++ * Purpose: ++ * Retrieve the platform specific device ID for this device. ++ * Parameters: ++ * hDevObject: Handle to device object created with ++ * DEV_CreateDevice(). ++ * phDevNode: Ptr to location to get the device node handle. ++ * Returns: ++ * DSP_SOK: In Win95, returns a DEVNODE in *hDevNode; In NT, ??? ++ * DSP_EHANDLE: Invalid hDevObject. ++ * Requires: ++ * phDevNode != NULL. ++ * DEV Initialized. ++ * Ensures: ++ * DSP_SOK: *phDevNode contains a platform specific device ID; ++ * else: *phDevNode is NULL. ++ */ ++ extern DSP_STATUS DEV_GetDevNode(struct DEV_OBJECT *hDevObject, ++ OUT struct CFG_DEVNODE **phDevNode); ++ ++/* ++ * ======== DEV_GetDevType ======== ++ * Purpose: ++ * Retrieve the platform specific device ID for this device. ++ * Parameters: ++ * hDevObject: Handle to device object created with ++ * DEV_CreateDevice(). ++ * phDevNode: Ptr to location to get the device node handle. ++ * Returns: ++ * DSP_SOK: Success ++ * DSP_EHANDLE: Invalid hDevObject. ++ * Requires: ++ * phDevNode != NULL. ++ * DEV Initialized. ++ * Ensures: ++ * DSP_SOK: *phDevNode contains a platform specific device ID; ++ * else: *phDevNode is NULL. ++ */ ++ extern DSP_STATUS DEV_GetDevType(struct DEV_OBJECT *hdevObject, ++ u32 *devType); ++ ++/* ++ * ======== DEV_GetFirst ======== ++ * Purpose: ++ * Retrieve the first Device Object handle from an internal linked list of ++ * of DEV_OBJECTs maintained by DEV. ++ * Parameters: ++ * Returns: ++ * NULL if there are no device objects stored; else ++ * a valid DEV_HOBJECT. ++ * Requires: ++ * No calls to DEV_CreateDevice or DEV_DestroyDevice (which my modify the ++ * internal device object list) may occur between calls to DEV_GetFirst ++ * and DEV_GetNext. ++ * Ensures: ++ * The DEV_HOBJECT returned is valid. ++ * A subsequent call to DEV_GetNext will return the next device object in ++ * the list. ++ */ ++ extern struct DEV_OBJECT *DEV_GetFirst(void); ++ ++/* ++ * ======== DEV_GetIntfFxns ======== ++ * Purpose: ++ * Retrieve the WMD interface function structure for the loaded WMD. ++ * Parameters: ++ * hDevObject: Handle to device object created with ++ * DEV_CreateDevice(). ++ * *ppIntfFxns: Ptr to location to store fxn interface. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hDevObject. ++ * Requires: ++ * ppIntfFxns != NULL. ++ * DEV Initialized. ++ * Ensures: ++ * DSP_SOK: *ppIntfFxns contains a pointer to the WMD interface; ++ * else: *ppIntfFxns is NULL. ++ */ ++ extern DSP_STATUS DEV_GetIntfFxns(struct DEV_OBJECT *hDevObject, ++ OUT struct WMD_DRV_INTERFACE **ppIntfFxns); ++ ++/* ++ * ======== DEV_GetIOMgr ======== ++ * Purpose: ++ * Retrieve the handle to the IO manager created for this device. ++ * Parameters: ++ * hDevObject: Handle to device object created with ++ * DEV_CreateDevice(). ++ * *phMgr: Ptr to location to store handle. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hDevObject. ++ * Requires: ++ * phMgr != NULL. ++ * DEV Initialized. ++ * Ensures: ++ * DSP_SOK: *phMgr contains a handle to an IO manager object. ++ * else: *phMgr is NULL. ++ */ ++ extern DSP_STATUS DEV_GetIOMgr(struct DEV_OBJECT *hDevObject, ++ OUT struct IO_MGR **phMgr); ++ ++/* ++ * ======== DEV_GetNext ======== ++ * Purpose: ++ * Retrieve the next Device Object handle from an internal linked list of ++ * of DEV_OBJECTs maintained by DEV, after having previously called ++ * DEV_GetFirst() and zero or more DEV_GetNext ++ * Parameters: ++ * hDevObject: Handle to the device object returned from a previous ++ * call to DEV_GetFirst() or DEV_GetNext(). ++ * Returns: ++ * NULL if there are no further device objects on the list or hDevObject ++ * was invalid; ++ * else the next valid DEV_HOBJECT in the list. ++ * Requires: ++ * No calls to DEV_CreateDevice or DEV_DestroyDevice (which my modify the ++ * internal device object list) may occur between calls to DEV_GetFirst ++ * and DEV_GetNext. ++ * Ensures: ++ * The DEV_HOBJECT returned is valid. ++ * A subsequent call to DEV_GetNext will return the next device object in ++ * the list. ++ */ ++ extern struct DEV_OBJECT *DEV_GetNext(struct DEV_OBJECT ++ *hDevObject); ++ ++/* ++ * ========= DEV_GetMsgMgr ======== ++ * Purpose: ++ * Retrieve the MSG Manager Handle from the DevObject. ++ * Parameters: ++ * hDevObject: Handle to the Dev Object ++ * phMsgMgr: Location where MSG Manager handle will be returned. ++ * Returns: ++ * Requires: ++ * DEV Initialized. ++ * Valid hDevObject. ++ * phNodeMgr != NULL. ++ * Ensures: ++ */ ++ extern void DEV_GetMsgMgr(struct DEV_OBJECT *hDevObject, ++ OUT struct MSG_MGR **phMsgMgr); ++ ++/* ++ * ========= DEV_GetNodeManager ======== ++ * Purpose: ++ * Retrieve the Node Manager Handle from the DevObject. It is an ++ * accessor function ++ * Parameters: ++ * hDevObject: Handle to the Dev Object ++ * phNodeMgr: Location where Handle to the Node Manager will be ++ * returned.. ++ * Returns: ++ * DSP_SOK: Success ++ * DSP_EHANDLE: Invalid Dev Object handle. ++ * Requires: ++ * DEV Initialized. ++ * phNodeMgr is not null ++ * Ensures: ++ * DSP_SOK: *phNodeMgr contains a handle to a Node manager object. ++ * else: *phNodeMgr is NULL. ++ */ ++ extern DSP_STATUS DEV_GetNodeManager(struct DEV_OBJECT ++ *hDevObject, ++ OUT struct NODE_MGR **phNodeMgr); ++ ++/* ++ * ======== DEV_GetSymbol ======== ++ * Purpose: ++ * Get the value of a symbol in the currently loaded program. ++ * Parameters: ++ * hDevObject: Handle to device object created with ++ * DEV_CreateDevice(). ++ * pstrSym: Name of symbol to look up. ++ * pulValue: Ptr to symbol value. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hDevObject. ++ * COD_E_NOSYMBOLSLOADED: Symbols have not been loaded onto the board. ++ * COD_E_SYMBOLNOTFOUND: The symbol could not be found. ++ * Requires: ++ * pstrSym != NULL. ++ * pulValue != NULL. ++ * DEV Initialized. ++ * Ensures: ++ * DSP_SOK: *pulValue contains the symbol value; ++ */ ++ extern DSP_STATUS DEV_GetSymbol(struct DEV_OBJECT *hDevObject, ++ IN CONST char *pstrSym, ++ OUT u32 *pulValue); ++ ++/* ++ * ======== DEV_GetWMDContext ======== ++ * Purpose: ++ * Retrieve the WMD Context handle, as returned by the WMD_Create fxn. ++ * Parameters: ++ * hDevObject: Handle to device object created with DEV_CreateDevice() ++ * *phWmdContext: Ptr to location to store context handle. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hDevObject. ++ * Requires: ++ * phWmdContext != NULL. ++ * DEV Initialized. ++ * Ensures: ++ * DSP_SOK: *phWmdContext contains context handle; ++ * else: *phWmdContext is NULL; ++ */ ++ extern DSP_STATUS DEV_GetWMDContext(struct DEV_OBJECT *hDevObject, ++ OUT struct WMD_DEV_CONTEXT **phWmdContext); ++ ++/* ++ * ======== DEV_Exit ======== ++ * Purpose: ++ * Decrement reference count, and free resources when reference count is ++ * 0. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * DEV is initialized. ++ * Ensures: ++ * When reference count == 0, DEV's private resources are freed. ++ */ ++ extern void DEV_Exit(void); ++ ++/* ++ * ======== DEV_Init ======== ++ * Purpose: ++ * Initialize DEV's private state, keeping a reference count on each call. ++ * Parameters: ++ * Returns: ++ * TRUE if initialized; FALSE if error occured. ++ * Requires: ++ * Ensures: ++ * TRUE: A requirement for the other public DEV functions. ++ */ ++ extern bool DEV_Init(void); ++ ++/* ++ * ======== DEV_IsLocked ======== ++ * Purpose: ++ * Predicate function to determine if the device has been ++ * locked by a client for exclusive access. ++ * Parameters: ++ * hDevObject: Handle to device object created with ++ * DEV_CreateDevice(). ++ * Returns: ++ * DSP_SOK: TRUE: device has been locked. ++ * DSP_SFALSE: FALSE: device not locked. ++ * DSP_EHANDLE: hDevObject was invalid. ++ * Requires: ++ * DEV Initialized. ++ * Ensures: ++ */ ++ extern DSP_STATUS DEV_IsLocked(IN struct DEV_OBJECT *hDevObject); ++ ++/* ++ * ======== DEV_InsertProcObject ======== ++ * Purpose: ++ * Inserts the Processor Object into the List of PROC Objects ++ * kept in the DEV Object ++ * Parameters: ++ * hProcObject: Handle to the Proc Object ++ * hDevObject Handle to the Dev Object ++ * bAttachedNew Specifies if there are already processors attached ++ * Returns: ++ * DSP_SOK: Successfully inserted into the list ++ * Requires: ++ * hProcObject is not NULL ++ * hDevObject is a valid handle to the DEV. ++ * DEV Initialized. ++ * List(of Proc object in Dev) Exists. ++ * Ensures: ++ * DSP_SOK & the PROC Object is inserted and the list is not empty ++ * Details: ++ * If the List of Proc Object is empty bAttachedNew is TRUE, it indicated ++ * this is the first Processor attaching. ++ * If it is False, there are already processors attached. ++ */ ++ extern DSP_STATUS DEV_InsertProcObject(IN struct DEV_OBJECT ++ *hDevObject, ++ IN u32 hProcObject, ++ OUT bool * ++ pbAlreadyAttached); ++ ++/* ++ * ======== DEV_RemoveProcObject ======== ++ * Purpose: ++ * Search for and remove a Proc object from the given list maintained ++ * by the DEV ++ * Parameters: ++ * pProcObject: Ptr to ProcObject to insert. ++ * pDevObject: Ptr to Dev Object where the list is. ++ * pbAlreadyAttached: Ptr to return the bool ++ * Returns: ++ * DSP_SOK: If successful. ++ * DSP_EFAIL Failure to Remove the PROC Object from the list ++ * Requires: ++ * DevObject is Valid ++ * hProcObject != 0 ++ * pDevObject->procList != NULL ++ * !LST_IsEmpty(pDevObject->procList) ++ * pbAlreadyAttached !=NULL ++ * Ensures: ++ * Details: ++ * List will be deleted when the DEV is destroyed. ++ * ++ */ ++ extern DSP_STATUS DEV_RemoveProcObject(struct DEV_OBJECT ++ *hDevObject, ++ u32 hProcObject); ++ ++/* ++ * ======== DEV_NotifyClients ======== ++ * Purpose: ++ * Notify all clients of this device of a change in device status. ++ * Clients may include multiple users of BRD, as well as CHNL. ++ * This function is asychronous, and may be called by a timer event ++ * set up by a watchdog timer. ++ * Parameters: ++ * hDevObject: Handle to device object created with DEV_CreateDevice(). ++ * ulStatus: A status word, most likely a BRD_STATUS. ++ * Returns: ++ * DSP_SOK: All registered clients were asynchronously notified. ++ * DSP_EINVALIDARG: Invalid hDevObject. ++ * Requires: ++ * DEV Initialized. ++ * Ensures: ++ * DSP_SOK: Notifications are queued by the operating system to be ++ * delivered to clients. This function does not ensure that ++ * the notifications will ever be delivered. ++ */ ++ extern DSP_STATUS DEV_NotifyClients(struct DEV_OBJECT *hDevObject, ++ u32 ulStatus); ++ ++ ++ ++/* ++ * ======== DEV_RemoveDevice ======== ++ * Purpose: ++ * Destroys the Device Object created by DEV_StartDevice. ++ * Parameters: ++ * hDevNode: Device node as it is know to OS. ++ * Returns: ++ * DSP_SOK: If success; ++ * Otherwise. ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS DEV_RemoveDevice(struct CFG_DEVNODE *hDevNode); ++ ++/* ++ * ======== DEV_SetChnlMgr ======== ++ * Purpose: ++ * Set the channel manager for this device. ++ * Parameters: ++ * hDevObject: Handle to device object created with ++ * DEV_CreateDevice(). ++ * hMgr: Handle to a channel manager, or NULL. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hDevObject. ++ * Requires: ++ * DEV Initialized. ++ * Ensures: ++ */ ++ extern DSP_STATUS DEV_SetChnlMgr(struct DEV_OBJECT *hDevObject, ++ struct CHNL_MGR *hMgr); ++ ++/* ++ * ======== DEV_SetMsgMgr ======== ++ * Purpose: ++ * Set the Message manager for this device. ++ * Parameters: ++ * hDevObject: Handle to device object created with DEV_CreateDevice(). ++ * hMgr: Handle to a message manager, or NULL. ++ * Returns: ++ * Requires: ++ * DEV Initialized. ++ * Ensures: ++ */ ++ extern void DEV_SetMsgMgr(struct DEV_OBJECT *hDevObject, ++ struct MSG_MGR *hMgr); ++ ++/* ++ * ======== DEV_StartDevice ======== ++ * Purpose: ++ * Initializes the new device with the WinBRIDGE environment. This ++ * involves querying CM for allocated resources, querying the registry ++ * for necessary dsp resources (requested in the INF file), and using ++ * this information to create a WinBRIDGE device object. ++ * Parameters: ++ * hDevNode: Device node as it is know to OS. ++ * Returns: ++ * DSP_SOK: If success; ++ * Otherwise. ++ * Requires: ++ * DEV initialized. ++ * Ensures: ++ */ ++ extern DSP_STATUS DEV_StartDevice(struct CFG_DEVNODE *hDevNode); ++ ++#endif /* DEV_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/devdefs.h b/arch/arm/plat-omap/include/dspbridge/devdefs.h +new file mode 100644 +index 0000000..e9ff725 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/devdefs.h +@@ -0,0 +1,35 @@ ++/* ++ * devdefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== devdefs.h ======== ++ * Purpose: ++ * Definition of common include typedef between wmd.h and dev.h. Required ++ * to break circular dependency between WMD and DEV include files. ++ * ++ *! Revision History: ++ *! ================ ++ *! 12-Nov-1996 gp: Renamed from dev1.h. ++ *! 30-May-1996 gp: Broke out from dev.h ++ */ ++ ++#ifndef DEVDEFS_ ++#define DEVDEFS_ ++ ++/* WCD Device Object */ ++ struct DEV_OBJECT; ++ ++#endif /* DEVDEFS_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/disp.h b/arch/arm/plat-omap/include/dspbridge/disp.h +new file mode 100644 +index 0000000..e116734 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/disp.h +@@ -0,0 +1,236 @@ ++/* ++ * disp.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== disp.h ======== ++ * ++ * Description: ++ * DSP/BIOS Bridge Node Dispatcher. ++ * ++ * Public Functions: ++ * DISP_Create ++ * DISP_Delete ++ * DISP_Exit ++ * DISP_Init ++ * DISP_NodeChangePriority ++ * DISP_NodeCreate ++ * DISP_NodeDelete ++ * DISP_NodeRun ++ * ++ *! Revision History: ++ *! ================= ++ *! 28-Jan-2003 map Removed DISP_DoCinit(). ++ *! 15-May-2002 jeh Added DISP_DoCinit(). ++ *! 24-Apr-2002 jeh Added DISP_MemWrite(). ++ *! 07-Sep-2001 jeh Added DISP_MemCopy(). ++ *! 10-May-2001 jeh Code review cleanup. ++ *! 08-Aug-2000 jeh Removed DISP_NodeTerminate since it no longer uses RMS. ++ *! 17-Jul-2000 jeh Updates to function headers. ++ *! 19-Jun-2000 jeh Created. ++ */ ++ ++#ifndef DISP_ ++#define DISP_ ++ ++#include ++#include ++#include ++#include ++ ++/* ++ * ======== DISP_Create ======== ++ * Create a NODE Dispatcher object. This object handles the creation, ++ * deletion, and execution of nodes on the DSP target, through communication ++ * with the Resource Manager Server running on the target. Each NODE ++ * Manager object should have exactly one NODE Dispatcher. ++ * ++ * Parameters: ++ * phDispObject: Location to store node dispatcher object on output. ++ * hDevObject: Device for this processor. ++ * pDispAttrs: Node dispatcher attributes. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EMEMORY: Insufficient memory for requested resources. ++ * DSP_EFAIL: Unable to create dispatcher. ++ * Requires: ++ * DISP_Init(void) called. ++ * pDispAttrs != NULL. ++ * hDevObject != NULL. ++ * phDispObject != NULL. ++ * Ensures: ++ * DSP_SOK: IsValid(*phDispObject). ++ * error: *phDispObject == NULL. ++ */ ++ extern DSP_STATUS DISP_Create(OUT struct DISP_OBJECT **phDispObject, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct DISP_ATTRS *pDispAttrs); ++ ++/* ++ * ======== DISP_Delete ======== ++ * Delete the NODE Dispatcher. ++ * ++ * Parameters: ++ * hDispObject: Node Dispatcher object. ++ * Returns: ++ * Requires: ++ * DISP_Init(void) called. ++ * Valid hDispObject. ++ * Ensures: ++ * hDispObject is invalid. ++ */ ++ extern void DISP_Delete(struct DISP_OBJECT *hDispObject); ++ ++/* ++ * ======== DISP_Exit ======== ++ * Discontinue usage of DISP module. ++ * ++ * Parameters: ++ * Returns: ++ * Requires: ++ * DISP_Init(void) previously called. ++ * Ensures: ++ * Any resources acquired in DISP_Init(void) will be freed when last DISP ++ * client calls DISP_Exit(void). ++ */ ++ extern void DISP_Exit(void); ++ ++/* ++ * ======== DISP_Init ======== ++ * Initialize the DISP module. ++ * ++ * Parameters: ++ * Returns: ++ * TRUE if initialization succeeded, FALSE otherwise. ++ * Ensures: ++ */ ++ extern bool DISP_Init(void); ++ ++/* ++ * ======== DISP_NodeChangePriority ======== ++ * Change the priority of a node currently running on the target. ++ * ++ * Parameters: ++ * hDispObject: Node Dispatcher object. ++ * hNode: Node object representing a node currently ++ * allocated or running on the DSP. ++ * ulFxnAddress: Address of RMS function for changing priority. ++ * nodeEnv: Address of node's environment structure. ++ * nPriority: New priority level to set node's priority to. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ETIMEOUT: A timeout occurred before the DSP responded. ++ * Requires: ++ * DISP_Init(void) called. ++ * Valid hDispObject. ++ * hNode != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS DISP_NodeChangePriority(struct DISP_OBJECT ++ *hDispObject, ++ struct NODE_OBJECT *hNode, ++ u32 ulFxnAddr, ++ NODE_ENV nodeEnv, ++ s32 nPriority); ++ ++/* ++ * ======== DISP_NodeCreate ======== ++ * Create a node on the DSP by remotely calling the node's create function. ++ * ++ * Parameters: ++ * hDispObject: Node Dispatcher object. ++ * hNode: Node handle obtained from NODE_Allocate(). ++ * ulFxnAddr: Address or RMS create node function. ++ * ulCreateFxn: Address of node's create function. ++ * pArgs: Arguments to pass to RMS node create function. ++ * pNodeEnv: Location to store node environment pointer on ++ * output. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ETASK: Unable to create the node's task or process on the DSP. ++ * DSP_ESTREAM: Stream creation failure on the DSP. ++ * DSP_ETIMEOUT: A timeout occurred before the DSP responded. ++ * DSP_EUSER: A user-defined failure occurred. ++ * DSP_EFAIL: A failure occurred, unable to create node. ++ * Requires: ++ * DISP_Init(void) called. ++ * Valid hDispObject. ++ * pArgs != NULL. ++ * hNode != NULL. ++ * pNodeEnv != NULL. ++ * NODE_GetType(hNode) != NODE_DEVICE. ++ * Ensures: ++ */ ++ extern DSP_STATUS DISP_NodeCreate(struct DISP_OBJECT *hDispObject, ++ struct NODE_OBJECT *hNode, ++ u32 ulFxnAddr, ++ u32 ulCreateFxn, ++ IN CONST struct NODE_CREATEARGS ++ *pArgs, ++ OUT NODE_ENV *pNodeEnv); ++ ++/* ++ * ======== DISP_NodeDelete ======== ++ * Delete a node on the DSP by remotely calling the node's delete function. ++ * ++ * Parameters: ++ * hDispObject: Node Dispatcher object. ++ * hNode: Node object representing a node currently ++ * loaded on the DSP. ++ * ulFxnAddr: Address or RMS delete node function. ++ * ulDeleteFxn: Address of node's delete function. ++ * nodeEnv: Address of node's environment structure. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ETIMEOUT: A timeout occurred before the DSP responded. ++ * Requires: ++ * DISP_Init(void) called. ++ * Valid hDispObject. ++ * hNode != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS DISP_NodeDelete(struct DISP_OBJECT *hDispObject, ++ struct NODE_OBJECT *hNode, ++ u32 ulFxnAddr, ++ u32 ulDeleteFxn, NODE_ENV nodeEnv); ++ ++/* ++ * ======== DISP_NodeRun ======== ++ * Start execution of a node's execute phase, or resume execution of a node ++ * that has been suspended (via DISP_NodePause()) on the DSP. ++ * ++ * Parameters: ++ * hDispObject: Node Dispatcher object. ++ * hNode: Node object representing a node to be executed ++ * on the DSP. ++ * ulFxnAddr: Address or RMS node execute function. ++ * ulExecuteFxn: Address of node's execute function. ++ * nodeEnv: Address of node's environment structure. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ETIMEOUT: A timeout occurred before the DSP responded. ++ * Requires: ++ * DISP_Init(void) called. ++ * Valid hDispObject. ++ * hNode != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS DISP_NodeRun(struct DISP_OBJECT *hDispObject, ++ struct NODE_OBJECT *hNode, ++ u32 ulFxnAddr, ++ u32 ulExecuteFxn, NODE_ENV nodeEnv); ++ ++#endif /* DISP_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/dispdefs.h b/arch/arm/plat-omap/include/dspbridge/dispdefs.h +new file mode 100644 +index 0000000..401ad4f +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dispdefs.h +@@ -0,0 +1,45 @@ ++/* ++ * dispdefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dispdefs.h ======== ++ * Description: ++ * Global DISP constants and types, shared by PROCESSOR, NODE, and DISP. ++ * ++ *! Revision History ++ *! ================ ++ *! 08-Aug-2000 jeh Added fields to DISP_ATTRS. ++ *! 06-Jul-2000 jeh Created. ++ */ ++ ++#ifndef DISPDEFS_ ++#define DISPDEFS_ ++ ++ struct DISP_OBJECT; ++ ++/* Node Dispatcher attributes */ ++ struct DISP_ATTRS { ++ u32 ulChnlOffset; /* Offset of channel ids reserved for RMS */ ++ /* Size of buffer for sending data to RMS */ ++ u32 ulChnlBufSize; ++ DSP_PROCFAMILY procFamily; /* eg, 5000 */ ++ DSP_PROCTYPE procType; /* eg, 5510 */ ++ HANDLE hReserved1; /* Reserved for future use. */ ++ u32 hReserved2; /* Reserved for future use. */ ++ } ; ++ ++#endif /* DISPDEFS_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/dmm.h b/arch/arm/plat-omap/include/dspbridge/dmm.h +new file mode 100644 +index 0000000..ef37668 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dmm.h +@@ -0,0 +1,85 @@ ++/* ++ * dmm.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dmm.h ======== ++ * Purpose: ++ * The Dynamic Memory Mapping(DMM) module manages the DSP Virtual address ++ * space that can be directly mapped to any MPU buffer or memory region ++ * ++ * Public Functions: ++ * ++ *! Revision History: ++ *! ================ ++ *! 20-Feb-2004 sb: Created. ++ *! ++ */ ++ ++#ifndef DMM_ ++#define DMM_ ++ ++#include ++ ++ struct DMM_OBJECT; ++ ++/* DMM attributes used in DMM_Create() */ ++ struct DMM_MGRATTRS { ++ u32 reserved; ++ } ; ++ ++#define DMMPOOLSIZE 0x4000000 ++ ++/* ++ * ======== DMM_GetHandle ======== ++ * Purpose: ++ * Return the dynamic memory manager object for this device. ++ * This is typically called from the client process. ++ */ ++ ++ extern DSP_STATUS DMM_GetHandle(DSP_HPROCESSOR hProcessor, ++ OUT struct DMM_OBJECT **phDmmMgr); ++ ++ extern DSP_STATUS DMM_ReserveMemory(struct DMM_OBJECT *hDmmMgr, ++ u32 size, ++ u32 *pRsvAddr); ++ ++ extern DSP_STATUS DMM_UnReserveMemory(struct DMM_OBJECT *hDmmMgr, ++ u32 rsvAddr); ++ ++ extern DSP_STATUS DMM_MapMemory(struct DMM_OBJECT *hDmmMgr, u32 addr, ++ u32 size); ++ ++ extern DSP_STATUS DMM_UnMapMemory(struct DMM_OBJECT *hDmmMgr, ++ u32 addr, ++ u32 *pSize); ++ ++ extern DSP_STATUS DMM_Destroy(struct DMM_OBJECT *hDmmMgr); ++ ++ extern DSP_STATUS DMM_DeleteTables(struct DMM_OBJECT *hDmmMgr); ++ ++ extern DSP_STATUS DMM_Create(OUT struct DMM_OBJECT **phDmmMgr, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct DMM_MGRATTRS *pMgrAttrs); ++ ++ extern bool DMM_Init(void); ++ ++ extern void DMM_Exit(void); ++ ++ extern DSP_STATUS DMM_CreateTables(struct DMM_OBJECT *hDmmMgr, ++ u32 addr, u32 size); ++ extern u32 *DMM_GetPhysicalAddrTable(void); ++#endif /* DMM_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/dpc.h b/arch/arm/plat-omap/include/dspbridge/dpc.h +new file mode 100644 +index 0000000..8c20506 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dpc.h +@@ -0,0 +1,167 @@ ++/* ++ * dpc.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dpc.h ======== ++ * Purpose: ++ * Deferred Procedure Call(DPC) Services. ++ * ++ * Public Functions: ++ * DPC_Cancel ++ * DPC_Create ++ * DPC_Destroy ++ * DPC_Exit ++ * DPC_Init ++ * DPC_Schedule ++ * ++ *! Revision History: ++ *! ================ ++ *! 31-Jan-2000 rr: DPC_Destroy ensures Suceess and DPC Object is NULL. ++ *! 21-Jan-2000 ag: Updated comments per code review. ++ *! 06-Jan-2000 ag: Removed DPC_[Lower|Raise]IRQL[From|To]DispatchLevel. ++ *! 14-Jan-1998 gp: Added DPC_[Lower|Raise]IRQL[From|To]DispatchLevel. ++ *! 18-Aug-1997 cr: Added explicit CDECL identifiers. ++ *! 28-Jul-1996 gp: Created. ++ */ ++ ++#ifndef DPC_ ++#define DPC_ ++ ++ struct DPC_OBJECT; ++ ++/* ++ * ======== DPC_PROC ======== ++ * Purpose: ++ * Deferred processing routine. Typically scheduled from an ISR to ++ * complete I/O processing. ++ * Parameters: ++ * pRefData: Ptr to user data: passed in via ISR_ScheduleDPC. ++ * Returns: ++ * Requires: ++ * The DPC should not block, or otherwise acquire resources. ++ * Interrupts to the processor are enabled. ++ * DPC_PROC executes in a critical section. ++ * Ensures: ++ * This DPC will not be reenterred on the same thread. ++ * However, the DPC may take hardware interrupts during execution. ++ * Interrupts to the processor are enabled. ++ */ ++ typedef void(*DPC_PROC) (void *pRefData); ++ ++/* ++ * ======== DPC_Cancel ======== ++ * Purpose: ++ * Cancel a DPC previously scheduled by DPC_Schedule. ++ * Parameters: ++ * hDPC: A DPC object handle created in DPC_Create(). ++ * Returns: ++ * DSP_SOK: Scheduled DPC, if any, is cancelled. ++ * DSP_SFALSE: No DPC is currently scheduled for execution. ++ * DSP_EHANDLE: Invalid hDPC. ++ * Requires: ++ * Ensures: ++ * If the DPC has already executed, is executing, or was not yet ++ * scheduled, this function will have no effect. ++ */ ++ extern DSP_STATUS DPC_Cancel(IN struct DPC_OBJECT *hDPC); ++ ++/* ++ * ======== DPC_Create ======== ++ * Purpose: ++ * Create a DPC object, allowing a client's own DPC procedure to be ++ * scheduled for a call with client reference data. ++ * Parameters: ++ * phDPC: Pointer to location to store DPC object. ++ * pfnDPC: Client's DPC procedure. ++ * pRefData: Pointer to user-defined reference data. ++ * Returns: ++ * DSP_SOK: DPC object created. ++ * DSP_EPOINTER: phDPC == NULL or pfnDPC == NULL. ++ * DSP_EMEMORY: Insufficient memory. ++ * Requires: ++ * Must not be called at interrupt time. ++ * Ensures: ++ * DSP_SOK: DPC object is created; ++ * else: *phDPC is set to NULL. ++ */ ++ extern DSP_STATUS DPC_Create(OUT struct DPC_OBJECT **phDPC, ++ IN DPC_PROC pfnDPC, ++ IN void *pRefData); ++ ++/* ++ * ======== DPC_Destroy ======== ++ * Purpose: ++ * Cancel the last scheduled DPC, and deallocate a DPC object previously ++ * allocated with DPC_Create().Frees the Object only if the thread and ++ * the events are terminated successfuly. ++ * Parameters: ++ * hDPC: A DPC object handle created in DPC_Create(). ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hDPC. ++ * Requires: ++ * All DPC's scheduled for the DPC object must have completed their ++ * processing. ++ * Ensures: ++ * (SUCCESS && hDPC is NULL) or DSP_EFAILED status ++ */ ++ extern DSP_STATUS DPC_Destroy(IN struct DPC_OBJECT *hDPC); ++ ++/* ++ * ======== DPC_Exit ======== ++ * Purpose: ++ * Discontinue usage of the DPC module. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * DPC_Init(void) was previously called. ++ * Ensures: ++ * Resources acquired in DPC_Init(void) are freed. ++ */ ++ extern void DPC_Exit(void); ++ ++/* ++ * ======== DPC_Init ======== ++ * Purpose: ++ * Initialize the DPC module's private state. ++ * Parameters: ++ * Returns: ++ * TRUE if initialized; FALSE if error occured. ++ * Requires: ++ * Ensures: ++ * A requirement for each of the other public DPC functions. ++ */ ++ extern bool DPC_Init(void); ++ ++/* ++ * ======== DPC_Schedule ======== ++ * Purpose: ++ * Schedule a deferred procedure call to be executed at a later time. ++ * Latency and order of DPC execution is platform specific. ++ * Parameters: ++ * hDPC: A DPC object handle created in DPC_Create(). ++ * Returns: ++ * DSP_SOK: An event is scheduled for deferred processing. ++ * DSP_EHANDLE: Invalid hDPC. ++ * Requires: ++ * See requirements for DPC_PROC. ++ * Ensures: ++ * DSP_SOK: The DPC will not be called before this function returns. ++ */ ++ extern DSP_STATUS DPC_Schedule(IN struct DPC_OBJECT *hDPC); ++ ++#endif /* DPC_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/drv.h b/arch/arm/plat-omap/include/dspbridge/drv.h +new file mode 100644 +index 0000000..c468461 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/drv.h +@@ -0,0 +1,449 @@ ++/* ++ * drv.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== drv.h ======== ++ * Purpose: ++ * DRV Resource allocation module. Driver Object gets Created ++ * at the time of Loading. It holds the List of Device Objects ++ * in the Syste, ++ * ++ * Public Functions: ++ * DRV_Create ++ * DRV_Destroy ++ * DRV_Exit ++ * DRV_GetDevObject ++ * DRV_GetDevExtension ++ * DRV_GetFirstDevObject ++ * DRV_GetNextDevObject ++ * DRV_GetNextDevExtension ++ * DRV_Init ++ * DRV_InsertDevObject ++ * DRV_RemoveDevObject ++ * DRV_RequestResources ++ * DRV_ReleaseResources ++ * ++ *! Revision History ++ *! ================ ++ *! 10-Feb-2004 vp: Added OMAP24xx specific definitions. ++ *! 14-Aug-2000 rr: Cleaned up. ++ *! 27-Jul-2000 rr: DRV_RequestResources split into two(Request and Release) ++ *! Device extension created to hold the DevNodeString. ++ *! 17-Jul-2000 rr: Driver Object holds the list of Device Objects. ++ *! Added DRV_Create, DRV_Destroy, DRV_GetDevObject, ++ *! DRV_GetFirst/NextDevObject, DRV_Insert/RemoveDevObject. ++ *! 12-Nov-1999 rr: New Flag defines for DRV_ASSIGN and DRV_RELEASE ++ *! 25-Oct-1999 rr: Resource Structure removed. ++ *! 15-Oct-1999 rr: New Resource structure created. ++ *! 05-Oct-1999 rr: Added DRV_RequestResources ++ *! Removed fxn'sDRV_RegisterMiniDriver(), ++ *! DRV_UnRegisterMiniDriver() ++ *! Removed Structures DSP_DRIVER & DRV_EXTENSION. ++ *! ++ *! 24-Sep-1999 rr: Added DRV_EXTENSION and DSP_DRIVER structures. ++ *! ++ */ ++ ++#ifndef DRV_ ++#define DRV_ ++ ++#include ++ ++#include ++ ++#define DRV_ASSIGN 1 ++#define DRV_RELEASE 0 ++ ++/* Provide the DSP Internal memory windows that can be accessed from L3 address ++ * space */ ++ ++#define OMAP_GEM_BASE 0x107F8000 ++#define OMAP_DSP_SIZE 0x00720000 ++ ++/* MEM1 is L2 RAM + L2 Cache space */ ++#define OMAP_DSP_MEM1_BASE 0x5C7F8000 ++#define OMAP_DSP_MEM1_SIZE 0x18000 ++#define OMAP_DSP_GEM1_BASE 0x107F8000 ++ ++ ++/* MEM2 is L1P RAM/CACHE space */ ++#define OMAP_DSP_MEM2_BASE 0x5CE00000 ++#define OMAP_DSP_MEM2_SIZE 0x8000 ++#define OMAP_DSP_GEM2_BASE 0x10E00000 ++ ++/* MEM3 is L1D RAM/CACHE space */ ++#define OMAP_DSP_MEM3_BASE 0x5CF04000 ++#define OMAP_DSP_MEM3_SIZE 0x14000 ++#define OMAP_DSP_GEM3_BASE 0x10F04000 ++ ++ ++#define OMAP_IVA2_PRM_BASE 0x48306000 ++#define OMAP_IVA2_PRM_SIZE 0x1000 ++ ++#define OMAP_IVA2_CM_BASE 0x48004000 ++#define OMAP_IVA2_CM_SIZE 0x1000 ++ ++#define OMAP_PER_CM_BASE 0x48005000 ++#define OMAP_PER_CM_SIZE 0x1000 ++ ++#define OMAP_PER_PRM_BASE 0x48307000 ++#define OMAP_PER_PRM_SIZE 0x1000 ++ ++#define OMAP_CORE_PRM_BASE 0x48306A00 ++#define OMAP_CORE_PRM_SIZE 0x1000 ++ ++#define OMAP_SYSC_BASE 0x48002000 ++#define OMAP_SYSC_SIZE 0x1000 ++ ++#define OMAP_MBOX_BASE 0x48094000 ++#define OMAP_MBOX_SIZE 0x1000 ++ ++#define OMAP_DMMU_BASE 0x5D000000 ++#define OMAP_DMMU_SIZE 0x1000 ++ ++#define OMAP_PRCM_VDD1_DOMAIN 1 ++#define OMAP_PRCM_VDD2_DOMAIN 2 ++ ++#ifndef RES_CLEANUP_DISABLE ++ ++/* GPP PROCESS CLEANUP Data structures */ ++ ++/* New structure (member of process context) abstracts NODE resource info */ ++struct NODE_RES_OBJECT { ++ DSP_HNODE hNode; ++ s32 nodeAllocated; /* Node status */ ++ s32 heapAllocated; /* Heap status */ ++ s32 streamsAllocated; /* Streams status */ ++ struct NODE_RES_OBJECT *next; ++} ; ++ ++/* New structure (member of process context) abstracts DMM resource info */ ++struct DMM_RES_OBJECT { ++ s32 dmmAllocated; /* DMM status */ ++ u32 ulMpuAddr; ++ u32 ulDSPAddr; ++ u32 ulDSPResAddr; ++ u32 dmmSize; ++ HANDLE hProcessor; ++ struct DMM_RES_OBJECT *next; ++} ; ++ ++/* New structure (member of process context) abstracts DMM resource info */ ++struct DSPHEAP_RES_OBJECT { ++ s32 heapAllocated; /* DMM status */ ++ u32 ulMpuAddr; ++ u32 ulDSPAddr; ++ u32 ulDSPResAddr; ++ u32 heapSize; ++ HANDLE hProcessor; ++ struct DSPHEAP_RES_OBJECT *next; ++} ; ++ ++/* New structure (member of process context) abstracts stream resource info */ ++struct STRM_RES_OBJECT { ++ s32 streamAllocated; /* Stream status */ ++ DSP_HSTREAM hStream; ++ u32 uNumBufs; ++ u32 uDir; ++ struct STRM_RES_OBJECT *next; ++} ; ++ ++/* Overall Bridge process resource usage state */ ++enum GPP_PROC_RES_STATE { ++ PROC_RES_ALLOCATED, ++ PROC_RES_FREED ++} ; ++ ++/* Process Context */ ++struct PROCESS_CONTEXT{ ++ /* Process State */ ++ enum GPP_PROC_RES_STATE resState; ++ ++ /* Process ID (Same as UNIX process ID) */ ++ u32 pid; ++ ++ /* Pointer to next process context ++ * (To maintain a linked list of process contexts) */ ++ struct PROCESS_CONTEXT *next; ++ ++ /* Processor info to which the process is related */ ++ DSP_HPROCESSOR hProcessor; ++ ++ /* DSP Node resources */ ++ struct NODE_RES_OBJECT *pNodeList; ++ ++ /* DMM resources */ ++ struct DMM_RES_OBJECT *pDMMList; ++ ++ /* DSP Heap resources */ ++ struct DSPHEAP_RES_OBJECT *pDSPHEAPList; ++ ++ /* Stream resources */ ++ struct STRM_RES_OBJECT *pSTRMList; ++} ; ++#endif ++ ++/* ++ * ======== DRV_Create ======== ++ * Purpose: ++ * Creates the Driver Object. This is done during the driver loading. ++ * There is only one Driver Object in the DSP/BIOS Bridge. ++ * Parameters: ++ * phDrvObject: Location to store created DRV Object handle. ++ * Returns: ++ * DSP_SOK: Sucess ++ * DSP_EMEMORY: Failed in Memory allocation ++ * DSP_EFAIL: General Failure ++ * Requires: ++ * DRV Initialized (cRefs > 0 ) ++ * phDrvObject != NULL. ++ * Ensures: ++ * DSP_SOK: - *phDrvObject is a valid DRV interface to the device. ++ * - List of DevObject Created and Initialized. ++ * - List of DevNode String created and intialized. ++ * - Registry is updated with the DRV Object. ++ * !DSP_SOK: DRV Object not created ++ * Details: ++ * There is one Driver Object for the Driver representing ++ * the driver itself. It contains the list of device ++ * Objects and the list of Device Extensions in the system. ++ * Also it can hold other neccessary ++ * information in its storage area. ++ */ ++ extern DSP_STATUS DRV_Create(struct DRV_OBJECT **phDrvObject); ++ ++/* ++ * ======== DRV_Destroy ======== ++ * Purpose: ++ * destroys the Dev Object list, DrvExt list ++ * and destroy the DRV object ++ * Called upon driver unLoading.or unsuccesful loading of the driver. ++ * Parameters: ++ * hDrvObject: Handle to Driver object . ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Failed to destroy DRV Object ++ * Requires: ++ * DRV Initialized (cRegs > 0 ) ++ * hDrvObject is not NULL and a valid DRV handle . ++ * List of DevObject is Empty. ++ * List of DrvExt is Empty ++ * Ensures: ++ * DSP_SOK: - DRV Object destroyed and hDrvObject is not a valid ++ * DRV handle. ++ * - Registry is updated with "0" as the DRV Object. ++ */ ++ extern DSP_STATUS DRV_Destroy(struct DRV_OBJECT *hDrvObject); ++ ++/* ++ * ======== DRV_Exit ======== ++ * Purpose: ++ * Exit the DRV module, freeing any modules initialized in DRV_Init. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * Ensures: ++ */ ++ extern void DRV_Exit(void); ++ ++/* ++ * ======== DRV_GetFirstDevObject ======== ++ * Purpose: ++ * Returns the Ptr to the FirstDev Object in the List ++ * Parameters: ++ * Requires: ++ * DRV Initialized ++ * Returns: ++ * dwDevObject: Ptr to the First Dev Object as a u32 ++ * 0 if it fails to retrieve the First Dev Object ++ * Ensures: ++ */ ++ extern u32 DRV_GetFirstDevObject(void); ++ ++/* ++ * ======== DRV_GetFirstDevExtension ======== ++ * Purpose: ++ * Returns the Ptr to the First Device Extension in the List ++ * Parameters: ++ * Requires: ++ * DRV Initialized ++ * Returns: ++ * dwDevExtension: Ptr to the First Device Extension as a u32 ++ * 0: Failed to Get the Device Extension ++ * Ensures: ++ */ ++ extern u32 DRV_GetFirstDevExtension(void); ++ ++/* ++ * ======== DRV_GetDevObject ======== ++ * Purpose: ++ * Given a index, returns a handle to DevObject from the list ++ * Parameters: ++ * hDrvObject: Handle to the Manager ++ * phDevObject: Location to store the Dev Handle ++ * Requires: ++ * DRV Initialized ++ * uIndex >= 0 ++ * hDrvObject is not NULL and Valid DRV Object ++ * phDevObject is not NULL ++ * Device Object List not Empty ++ * Returns: ++ * DSP_SOK: Success ++ * DSP_EFAIL: Failed to Get the Dev Object ++ * Ensures: ++ * DSP_SOK: *phDevObject != NULL ++ * DSP_EFAIL: *phDevObject = NULL ++ */ ++ extern DSP_STATUS DRV_GetDevObject(u32 uIndex, ++ struct DRV_OBJECT *hDrvObject, ++ struct DEV_OBJECT **phDevObject); ++ ++/* ++ * ======== DRV_GetNextDevObject ======== ++ * Purpose: ++ * Returns the Ptr to the Next Device Object from the the List ++ * Parameters: ++ * hDevObject: Handle to the Device Object ++ * Requires: ++ * DRV Initialized ++ * hDevObject != 0 ++ * Returns: ++ * dwDevObject: Ptr to the Next Dev Object as a u32 ++ * 0: If it fail to get the next Dev Object. ++ * Ensures: ++ */ ++ extern u32 DRV_GetNextDevObject(u32 hDevObject); ++ ++/* ++ * ======== DRV_GetNextDevExtension ======== ++ * Purpose: ++ * Returns the Ptr to the Next Device Extension from the the List ++ * Parameters: ++ * hDevExtension: Handle to the Device Extension ++ * Requires: ++ * DRV Initialized ++ * hDevExtension != 0. ++ * Returns: ++ * dwDevExtension: Ptr to the Next Dev Extension ++ * 0: If it fail to Get the next Dev Extension ++ * Ensures: ++ */ ++ extern u32 DRV_GetNextDevExtension(u32 hDevExtension); ++ ++/* ++ * ======== DRV_Init ======== ++ * Purpose: ++ * Initialize the DRV module. ++ * Parameters: ++ * Returns: ++ * TRUE if success; FALSE otherwise. ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS DRV_Init(void); ++ ++/* ++ * ======== DRV_InsertDevObject ======== ++ * Purpose: ++ * Insert a DeviceObject into the list of Driver object. ++ * Parameters: ++ * hDrvObject: Handle to DrvObject ++ * hDevObject: Handle to DeviceObject to insert. ++ * Returns: ++ * DSP_SOK: If successful. ++ * DSP_EFAIL: General Failure: ++ * Requires: ++ * hDrvObject != NULL and Valid DRV Handle. ++ * hDevObject != NULL. ++ * Ensures: ++ * DSP_SOK: Device Object is inserted and the List is not empty. ++ */ ++ extern DSP_STATUS DRV_InsertDevObject(struct DRV_OBJECT *hDrvObject, ++ struct DEV_OBJECT *hDevObject); ++ ++/* ++ * ======== DRV_RemoveDevObject ======== ++ * Purpose: ++ * Search for and remove a Device object from the given list of Device Obj ++ * objects. ++ * Parameters: ++ * hDrvObject: Handle to DrvObject ++ * hDevObject: Handle to DevObject to Remove ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Unable to find pDevObject. ++ * Requires: ++ * hDrvObject != NULL and a Valid DRV Handle. ++ * hDevObject != NULL. ++ * List exists and is not empty. ++ * Ensures: ++ * List either does not exist (NULL), or is not empty if it does exist. ++*/ ++ extern DSP_STATUS DRV_RemoveDevObject(struct DRV_OBJECT *hDrvObject, ++ struct DEV_OBJECT *hDevObject); ++ ++/* ++ * ======== DRV_RequestResources ======== ++ * Purpose: ++ * Assigns the Resources or Releases them. ++ * Parameters: ++ * dwContext: Path to the driver Registry Key. ++ * pDevNodeString: Ptr to DevNode String stored in the Device Ext. ++ * Returns: ++ * TRUE if success; FALSE otherwise. ++ * Requires: ++ * Ensures: ++ * The Resources are assigned based on Bus type. ++ * The hardware is initialized. Resource information is ++ * gathered from the Registry(ISA, PCMCIA)or scanned(PCI) ++ * Resource structure is stored in the registry which will be ++ * later used by the CFG module. ++ */ ++ extern DSP_STATUS DRV_RequestResources(IN u32 dwContext, ++ OUT u32 *pDevNodeString); ++ ++/* ++ * ======== DRV_ReleaseResources ======== ++ * Purpose: ++ * Assigns the Resources or Releases them. ++ * Parameters: ++ * dwContext: Path to the driver Registry Key. ++ * hDrvObject: Handle to the Driver Object. ++ * Returns: ++ * TRUE if success; FALSE otherwise. ++ * Requires: ++ * Ensures: ++ * The Resources are released based on Bus type. ++ * Resource structure is deleted from the registry ++ */ ++ extern DSP_STATUS DRV_ReleaseResources(IN u32 dwContext, ++ struct DRV_OBJECT *hDrvObject); ++ ++/* ++ * ======== DRV_ProcFreeDMMRes ======== ++ * Purpose: ++ * Actual DMM De-Allocation. ++ * Parameters: ++ * hPCtxt: Path to the driver Registry Key. ++ * Returns: ++ * DSP_SOK if success; ++ */ ++ ++ ++ extern DSP_STATUS DRV_ProcFreeDMMRes(HANDLE hPCtxt); ++ ++#endif /* DRV_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/drvdefs.h b/arch/arm/plat-omap/include/dspbridge/drvdefs.h +new file mode 100644 +index 0000000..ed86010 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/drvdefs.h +@@ -0,0 +1,34 @@ ++/* ++ * drvdefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== drvdefs.h ======== ++ * Purpose: ++ * Definition of common include typedef between wmd.h and drv.h. ++ * ++ *! Revision History: ++ *! ================ ++ *! 17-Jul-2000 rr: Created ++ */ ++ ++#ifndef DRVDEFS_ ++#define DRVDEFS_ ++ ++/* WCD Driver Object */ ++ struct DRV_OBJECT; ++ ++#endif /* DRVDEFS_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/dspdrv.h b/arch/arm/plat-omap/include/dspbridge/dspdrv.h +new file mode 100644 +index 0000000..f500ffb +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dspdrv.h +@@ -0,0 +1,106 @@ ++/* ++ * dspdrv.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dspdrv.h ======== ++ * Purpose: ++ * This is the Stream Interface for the DDSP Class driver. ++ * All Device operations are performed via DeviceIOControl. ++ * Read, Seek and Write not used. ++ * ++ * Public Functions ++ * DSP_Close ++ * DSP_Deinit ++ * DSP_Init ++ * DSP_IOControl ++ * DSP_Open ++ * DSP_PowerUp ++ * DSP_PowerDown ++ * ++ *! Revision History ++ *! ================ ++ *! 28-Jan-2000 rr: Type void changed to Void. ++ *! 02-Dec-1999 rr: MAX_DEV define moved from wcdce.c file.Code cleaned up. ++ *! 12-Nov-1999 rr: "#include removed. ++ *! 05-Oct-1999 rr Renamed the file name to wcdce.h Removed Bus Specific ++ *! code and #defines to PCCARD.h. ++ *! 24-Sep-1999 rr Changed the DSP_COMMON_WINDOW_SIZE to 0x4000(16k) for the ++ *! Memory windows. ++ *! 16-Jul-1999 ag Adapted from rkw's CAC Bullet driver. ++ *! ++ */ ++ ++#if !defined __DSPDRV_h__ ++#define __DSPDRV_h__ ++ ++#define MAX_DEV 10 /* Max support of 10 devices */ ++ ++/* ++ * ======== DSP_Close ======== ++ * Purpose: ++ * Called when the client application/driver unloads the DDSP DLL. Upon ++ * unloading, the DDSP DLL will call CloseFile(). ++ * Parameters: ++ * dwDeviceContext: Handle returned by XXX_Open used to identify ++ * the open context of the device ++ * Returns: ++ * TRUE indicates the device is successfully closed. FALSE indicates ++ * otherwise. ++ * Requires: ++ * dwOpenContext!= NULL. ++ * Ensures:The Application instance owned objects are cleaned up. ++ */ ++extern bool DSP_Close(u32 dwDeviceContext); ++ ++/* ++ * ======== DSP_Deinit ======== ++ * Purpose: ++ * This function is called by Device Manager to de-initialize a device. ++ * This function is not called by applications. ++ * Parameters: ++ * dwDeviceContext:Handle to the device context. The XXX_Init function ++ * creates and returns this identifier. ++ * Returns: ++ * TRUE indicates the device successfully de-initialized. Otherwise it ++ * returns FALSE. ++ * Requires: ++ * dwDeviceContext!= NULL. For a built in device this should never ++ * get called. ++ * Ensures: ++ */ ++extern bool DSP_Deinit(u32 dwDeviceContext); ++ ++/* ++ * ======== DSP_Init ======== ++ * Purpose: ++ * This function is called by Device Manager to initialize a device. ++ * This function is not called by applications ++ * Parameters: ++ * dwContext: Specifies a pointer to a string containing the registry ++ * path to the active key for the stream interface driver. ++ * HKEY_LOCAL_MACHINE\Drivers\Active ++ * Returns: ++ * Returns a handle to the device context created. This is the our actual ++ * Device Object representing the DSP Device instance. ++ * Requires: ++ * Ensures: ++ * Succeeded: device context > 0 ++ * Failed: device Context = 0 ++ */ ++extern u32 DSP_Init(OUT u32 *initStatus); ++ ++#endif +diff --git a/arch/arm/plat-omap/include/dspbridge/dynamic_loader.h b/arch/arm/plat-omap/include/dspbridge/dynamic_loader.h +new file mode 100644 +index 0000000..ea5f77f +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/dynamic_loader.h +@@ -0,0 +1,505 @@ ++/* ++ * dynamic_loader.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++ ++#ifndef _DYNAMIC_LOADER_H_ ++#define _DYNAMIC_LOADER_H_ ++#include ++#include ++ ++/* ++ * Dynamic Loader ++ * ++ * The function of the dynamic loader is to load a "module" containing ++ * instructions for a "target" processor into that processor. In the process ++ * it assigns memory for the module, resolves symbol references made by the ++ * module, and remembers symbols defined by the module. ++ * ++ * The dynamic loader is parameterized for a particular system by 4 classes ++ * that supply the module and system specific functions it requires ++ */ ++ /* The read functions for the module image to be loaded */ ++ struct Dynamic_Loader_Stream; ++ ++ /* This class defines "host" symbol and support functions */ ++ struct Dynamic_Loader_Sym; ++ ++ /* This class defines the allocator for "target" memory */ ++ struct Dynamic_Loader_Allocate; ++ ++ /* This class defines the copy-into-target-memory functions */ ++ struct Dynamic_Loader_Initialize; ++ ++/* ++ * Option flags to modify the behavior of module loading ++ */ ++#define DLOAD_INITBSS 0x1 /* initialize BSS sections to zero */ ++#define DLOAD_BIGEND 0x2 /* require big-endian load module */ ++#define DLOAD_LITTLE 0x4 /* require little-endian load module */ ++ ++ typedef void *DLOAD_mhandle; /* module handle for loaded modules */ ++ ++/***************************************************************************** ++ * Procedure Dynamic_Load_Module ++ * ++ * Parameters: ++ * module The input stream that supplies the module image ++ * syms Host-side symbol table and malloc/free functions ++ * alloc Target-side memory allocation ++ * init Target-side memory initialization, or NULL for symbol read only ++ * options Option flags DLOAD_* ++ * mhandle A module handle for use with Dynamic_Unload ++ * ++ * Effect: ++ * The module image is read using *module. Target storage for the new image is ++ * obtained from *alloc. Symbols defined and referenced by the module are ++ * managed using *syms. The image is then relocated and references resolved ++ * as necessary, and the resulting executable bits are placed into target memory ++ * using *init. ++ * ++ * Returns: ++ * On a successful load, a module handle is placed in *mhandle, and zero is ++ * returned. On error, the number of errors detected is returned. Individual ++ * errors are reported during the load process using syms->Error_Report(). ++ *****************************************************************************/ ++ extern int Dynamic_Load_Module( ++ /* the source for the module image*/ ++ struct Dynamic_Loader_Stream *module, ++ /* host support for symbols and storage*/ ++ struct Dynamic_Loader_Sym *syms, ++ /* the target memory allocator*/ ++ struct Dynamic_Loader_Allocate *alloc, ++ /* the target memory initializer*/ ++ struct Dynamic_Loader_Initialize *init, ++ unsigned options, /* option flags*/ ++ /* the returned module handle*/ ++ DLOAD_mhandle *mhandle ++ ); ++ ++/***************************************************************************** ++ * Procedure Dynamic_Open_Module ++ * ++ * Parameters: ++ * module The input stream that supplies the module image ++ * syms Host-side symbol table and malloc/free functions ++ * alloc Target-side memory allocation ++ * init Target-side memory initialization, or NULL for symbol read only ++ * options Option flags DLOAD_* ++ * mhandle A module handle for use with Dynamic_Unload ++ * ++ * Effect: ++ * The module image is read using *module. Target storage for the new image is ++ * obtained from *alloc. Symbols defined and referenced by the module are ++ * managed using *syms. The image is then relocated and references resolved ++ * as necessary, and the resulting executable bits are placed into target memory ++ * using *init. ++ * ++ * Returns: ++ * On a successful load, a module handle is placed in *mhandle, and zero is ++ * returned. On error, the number of errors detected is returned. Individual ++ * errors are reported during the load process using syms->Error_Report(). ++ *****************************************************************************/ ++ extern int Dynamic_Open_Module( ++ /* the source for the module image */ ++ struct Dynamic_Loader_Stream *module, ++ /* host support for symbols and storage */ ++ struct Dynamic_Loader_Sym *syms, ++ /* the target memory allocator */ ++ struct Dynamic_Loader_Allocate *alloc, ++ /* the target memory initializer */ ++ struct Dynamic_Loader_Initialize *init, ++ unsigned options, /* option flags */ ++ /* the returned module handle */ ++ DLOAD_mhandle *mhandle ++ ); ++ ++/***************************************************************************** ++ * Procedure Dynamic_Unload_Module ++ * ++ * Parameters: ++ * mhandle A module handle from Dynamic_Load_Module ++ * syms Host-side symbol table and malloc/free functions ++ * alloc Target-side memory allocation ++ * ++ * Effect: ++ * The module specified by mhandle is unloaded. Unloading causes all ++ * target memory to be deallocated, all symbols defined by the module to ++ * be purged, and any host-side storage used by the dynamic loader for ++ * this module to be released. ++ * ++ * Returns: ++ * Zero for success. On error, the number of errors detected is returned. ++ * Individual errors are reported using syms->Error_Report(). ++ *****************************************************************************/ ++ extern int Dynamic_Unload_Module(DLOAD_mhandle mhandle, /* the module ++ * handle*/ ++ /* host support for symbols and ++ * storage */ ++ struct Dynamic_Loader_Sym *syms, ++ /* the target memory allocator*/ ++ struct Dynamic_Loader_Allocate *alloc, ++ /* the target memory initializer*/ ++ struct Dynamic_Loader_Initialize *init ++ ); ++ ++/***************************************************************************** ++ ***************************************************************************** ++ * A class used by the dynamic loader for input of the module image ++ ***************************************************************************** ++ *****************************************************************************/ ++ struct Dynamic_Loader_Stream { ++/* public: */ ++ /************************************************************************* ++ * read_buffer ++ * ++ * PARAMETERS : ++ * buffer Pointer to the buffer to fill ++ * bufsiz Amount of data desired in sizeof() units ++ * ++ * EFFECT : ++ * Reads the specified amount of data from the module input stream ++ * into the specified buffer. Returns the amount of data read in sizeof() ++ * units (which if less than the specification, represents an error). ++ * ++ * NOTES: ++ * In release 1 increments the file position by the number of bytes read ++ * ++ *************************************************************************/ ++ int (*read_buffer) (struct Dynamic_Loader_Stream *thisptr, ++ void *buffer, unsigned bufsiz); ++ ++ /************************************************************************* ++ * set_file_posn (release 1 only) ++ * ++ * PARAMETERS : ++ * posn Desired file position relative to start of file in sizeof() units. ++ * ++ * EFFECT : ++ * Adjusts the internal state of the stream object so that the next ++ * read_buffer call will begin to read at the specified offset from ++ * the beginning of the input module. Returns 0 for success, non-zero ++ * for failure. ++ * ++ *************************************************************************/ ++ int (*set_file_posn) (struct Dynamic_Loader_Stream *thisptr, ++ /* to be eliminated in release 2*/ ++ unsigned int posn); ++ ++ }; ++ ++/***************************************************************************** ++ ***************************************************************************** ++ * A class used by the dynamic loader for symbol table support and ++ * miscellaneous host-side functions ++ ***************************************************************************** ++ *****************************************************************************/ ++ ++ typedef u32 LDR_ADDR; ++ ++/* ++ * the structure of a symbol known to the dynamic loader ++ */ ++ struct dynload_symbol { ++ LDR_ADDR value; ++ } ; ++ ++ struct Dynamic_Loader_Sym { ++/* public: */ ++ /************************************************************************* ++ * Find_Matching_Symbol ++ * ++ * PARAMETERS : ++ * name The name of the desired symbol ++ * ++ * EFFECT : ++ * Locates a symbol matching the name specified. A pointer to the ++ * symbol is returned if it exists; 0 is returned if no such symbol is ++ * found. ++ * ++ *************************************************************************/ ++ struct dynload_symbol *(*Find_Matching_Symbol) ++ (struct Dynamic_Loader_Sym * ++ thisptr, ++ const char *name); ++ ++ /************************************************************************* ++ * Add_To_Symbol_Table ++ * ++ * PARAMETERS : ++ * nname Pointer to the name of the new symbol ++ * moduleid An opaque module id assigned by the dynamic loader ++ * ++ * EFFECT : ++ * The new symbol is added to the table. A pointer to the symbol is ++ * returned, or NULL is returned for failure. ++ * ++ * NOTES: ++ * It is permissible for this function to return NULL; the effect is that ++ * the named symbol will not be available to resolve references in ++ * subsequent loads. Returning NULL will not cause the current load ++ * to fail. ++ *************************************************************************/ ++ struct dynload_symbol *(*Add_To_Symbol_Table) ++ (struct Dynamic_Loader_Sym * ++ thisptr, ++ const char *nname, ++ unsigned moduleid); ++ ++ /************************************************************************* ++ * Purge_Symbol_Table ++ * ++ * PARAMETERS : ++ * moduleid An opaque module id assigned by the dynamic loader ++ * ++ * EFFECT : ++ * Each symbol in the symbol table whose moduleid matches the argument ++ * is removed from the table. ++ *************************************************************************/ ++ void (*Purge_Symbol_Table) (struct Dynamic_Loader_Sym *thisptr, ++ unsigned moduleid); ++ ++ /************************************************************************* ++ * Allocate ++ * ++ * PARAMETERS : ++ * memsiz size of desired memory in sizeof() units ++ * ++ * EFFECT : ++ * Returns a pointer to some "host" memory for use by the dynamic ++ * loader, or NULL for failure. ++ * This function is serves as a replaceable form of "malloc" to ++ * allow the user to configure the memory usage of the dynamic loader. ++ *************************************************************************/ ++ void *(*Allocate) (struct Dynamic_Loader_Sym *thisptr, ++ unsigned memsiz); ++ ++ /************************************************************************* ++ * Deallocate ++ * ++ * PARAMETERS : ++ * memptr pointer to previously allocated memory ++ * ++ * EFFECT : ++ * Releases the previously allocated "host" memory. ++ *************************************************************************/ ++ void (*Deallocate) (struct Dynamic_Loader_Sym *thisptr, ++ void *memptr); ++ ++ /************************************************************************* ++ * Error_Report ++ * ++ * PARAMETERS : ++ * errstr pointer to an error string ++ * args additional arguments ++ * ++ * EFFECT : ++ * This function provides an error reporting interface for the dynamic ++ * loader. The error string and arguments are designed as for the ++ * library function vprintf. ++ *************************************************************************/ ++ void (*Error_Report) (struct Dynamic_Loader_Sym *thisptr, ++ const char *errstr, va_list args); ++ ++ }; /* class Dynamic_Loader_Sym */ ++ ++/***************************************************************************** ++ ***************************************************************************** ++ * A class used by the dynamic loader to allocate and deallocate target memory. ++ ***************************************************************************** ++ *****************************************************************************/ ++ ++ struct LDR_SECTION_INFO { ++ /* Name of the memory section assigned at build time */ ++ const char *name; ++ LDR_ADDR run_addr; /* execution address of the section */ ++ LDR_ADDR load_addr; /* load address of the section */ ++ LDR_ADDR size; /* size of the section in addressable units */ ++#ifndef _BIG_ENDIAN ++ u16 page; /* memory page or view */ ++ u16 type; /* one of the section types below */ ++#else ++ u16 type; /* one of the section types below */ ++ u16 page; /* memory page or view */ ++#endif ++ /* a context field for use by Dynamic_Loader_Allocate; ++ * ignored but maintained by the dynamic loader */ ++ u32 context; ++ } ; ++ ++/* use this macro to extract type of section from LDR_SECTION_INFO.type field */ ++#define DLOAD_SECTION_TYPE(typeinfo) (typeinfo & 0xF) ++ ++/* type of section to be allocated */ ++#define DLOAD_TEXT 0 ++#define DLOAD_DATA 1 ++#define DLOAD_BSS 2 ++ /* internal use only, run-time cinit will be of type DLOAD_DATA */ ++#define DLOAD_CINIT 3 ++ ++ struct Dynamic_Loader_Allocate { ++/* public: */ ++ ++ /************************************************************************* ++ * Function allocate ++ * ++ * Parameters: ++ * info A pointer to an information block for the section ++ * align The alignment of the storage in target AUs ++ * ++ * Effect: ++ * Allocates target memory for the specified section and fills in the ++ * load_addr and run_addr fields of the section info structure. Returns TRUE ++ * for success, FALSE for failure. ++ * ++ * Notes: ++ * Frequently load_addr and run_addr are the same, but if they are not ++ * load_addr is used with Dynamic_Loader_Initialize, and run_addr is ++ * used for almost all relocations. This function should always initialize ++ * both fields. ++ *************************************************************************/ ++ int (*Allocate) (struct Dynamic_Loader_Allocate *thisptr, ++ struct LDR_SECTION_INFO *info, unsigned align); ++ ++ /************************************************************************* ++ * Function deallocate ++ * ++ * Parameters: ++ * info A pointer to an information block for the section ++ * ++ * Effect: ++ * Releases the target memory previously allocated. ++ * ++ * Notes: ++ * The content of the info->name field is undefined on call to this function. ++ *************************************************************************/ ++ void (*Deallocate) (struct Dynamic_Loader_Allocate *thisptr, ++ struct LDR_SECTION_INFO *info); ++ ++ }; /* class Dynamic_Loader_Allocate */ ++ ++/***************************************************************************** ++ ***************************************************************************** ++ * A class used by the dynamic loader to load data into a target. This class ++ * provides the interface-specific functions needed to load data. ++ ***************************************************************************** ++ *****************************************************************************/ ++ ++ struct Dynamic_Loader_Initialize { ++/* public: */ ++ /************************************************************************* ++ * Function connect ++ * ++ * Parameters: ++ * none ++ * ++ * Effect: ++ * Connect to the initialization interface. Returns TRUE for success, ++ * FALSE for failure. ++ * ++ * Notes: ++ * This function is called prior to use of any other functions in ++ * this interface. ++ *************************************************************************/ ++ int (*connect) (struct Dynamic_Loader_Initialize *thisptr); ++ ++ /************************************************************************* ++ * Function readmem ++ * ++ * Parameters: ++ * bufr Pointer to a word-aligned buffer for the result ++ * locn Target address of first data element ++ * info Section info for the section in which the address resides ++ * bytsiz Size of the data to be read in sizeof() units ++ * ++ * Effect: ++ * Fills the specified buffer with data from the target. Returns TRUE for ++ * success, FALSE for failure. ++ *************************************************************************/ ++ int (*readmem) (struct Dynamic_Loader_Initialize *thisptr, ++ void *bufr, ++ LDR_ADDR locn, ++ struct LDR_SECTION_INFO *info, ++ unsigned bytsiz); ++ ++ /************************************************************************* ++ * Function writemem ++ * ++ * Parameters: ++ * bufr Pointer to a word-aligned buffer of data ++ * locn Target address of first data element to be written ++ * info Section info for the section in which the address resides ++ * bytsiz Size of the data to be written in sizeof() units ++ * ++ * Effect: ++ * Writes the specified buffer to the target. Returns TRUE for success, ++ * FALSE for failure. ++ *************************************************************************/ ++ int (*writemem) (struct Dynamic_Loader_Initialize *thisptr, ++ void *bufr, ++ LDR_ADDR locn, ++ struct LDR_SECTION_INFO *info, ++ unsigned bytsiz); ++ ++ /************************************************************************* ++ * Function fillmem ++ * ++ * Parameters: ++ * locn Target address of first data element to be written ++ * info Section info for the section in which the address resides ++ * bytsiz Size of the data to be written in sizeof() units ++ * val Value to be written in each byte ++ * Effect: ++ * Fills the specified area of target memory. Returns TRUE for success, ++ * FALSE for failure. ++ *************************************************************************/ ++ int (*fillmem) (struct Dynamic_Loader_Initialize *thisptr, ++ LDR_ADDR locn, struct LDR_SECTION_INFO *info, ++ unsigned bytsiz, unsigned val); ++ ++ /************************************************************************* ++ * Function execute ++ * ++ * Parameters: ++ * start Starting address ++ * ++ * Effect: ++ * The target code at the specified starting address is executed. ++ * ++ * Notes: ++ * This function is called at the end of the dynamic load process ++ * if the input module has specified a starting address. ++ *************************************************************************/ ++ int (*execute) (struct Dynamic_Loader_Initialize *thisptr, ++ LDR_ADDR start); ++ ++ /************************************************************************* ++ * Function release ++ * ++ * Parameters: ++ * none ++ * ++ * Effect: ++ * Releases the connection to the load interface. ++ * ++ * Notes: ++ * This function is called at the end of the dynamic load process. ++ *************************************************************************/ ++ void (*release) (struct Dynamic_Loader_Initialize *thisptr); ++ ++ }; /* class Dynamic_Loader_Initialize */ ++ ++#endif /* _DYNAMIC_LOADER_H_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/errbase.h b/arch/arm/plat-omap/include/dspbridge/errbase.h +new file mode 100644 +index 0000000..f04c005 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/errbase.h +@@ -0,0 +1,509 @@ ++/* ++ * errbase.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== errbase.h ======== ++ * Description: ++ * Central repository for DSP/BIOS Bridge error and status code. ++ * ++ * Error codes are of the form: ++ * []_E ++ * ++ * Success codes are of the form: ++ * []_S ++ * ++ *! Revision History: ++ *! ================ ++ *! 24-Jan-2003 map Added DSP_SALREADYLOADED for persistent library checking ++ *! 23-Nov-2002 gp: Minor comment cleanup. ++ *! 13-May-2002 sg Added DSP_SALREADYASLEEP and DSP_SALREADYWAKE. ++ *! 18-Feb-2002 mk: Added DSP_EOVERLAYMEMORY, EFWRITE, ENOSECT. ++ *! 31-Jan-2002 mk: Added definitions of DSP_STRUE and DSP_SFALSE. ++ *! 29-Jan-2002 mk: Added definition of CFG_E_INSUFFICIENTBUFSIZE. ++ *! 24-Oct-2001 sp: Consolidated all the error codes into this file. ++ *! 24-Jul-2001 mk: Type-casted all definitions of WSX_STATUS types for ++ *! removal of compile warnings. ++ *! 22-Nov-1999 kc: Changes from code review. ++ *! 18-Aug-1999 rr: Ported From WSX. ++ *! 29-May-1996 gp: Removed WCD_ and WMD_ error ranges. Redefined format of ++ *! error codes. ++ *! 10-May-1996 gp: Created. ++ */ ++ ++#ifndef ERRBASE_ ++#define ERRBASE_ ++ ++/* Base of generic errors and component errors */ ++#define DSP_SBASE (DSP_STATUS)0x00008000 ++#define DSP_EBASE (DSP_STATUS)0x80008000 ++ ++#define DSP_COMP_EBASE (DSP_STATUS)0x80040200 ++#define DSP_COMP_ELAST (DSP_STATUS)0x80047fff ++ ++/* SUCCESS Codes */ ++ ++/* Generic success code */ ++#define DSP_SOK (DSP_SBASE + 0) ++ ++/* GPP is already attached to this DSP processor */ ++#define DSP_SALREADYATTACHED (DSP_SBASE + 1) ++ ++/* This is the last object available for enumeration. */ ++#define DSP_SENUMCOMPLETE (DSP_SBASE + 2) ++ ++/* The DSP is already asleep. */ ++#define DSP_SALREADYASLEEP (DSP_SBASE + 3) ++ ++/* The DSP is already awake. */ ++#define DSP_SALREADYAWAKE (DSP_SBASE + 4) ++ ++/* TRUE */ ++#define DSP_STRUE (DSP_SBASE + 5) ++ ++/* FALSE */ ++#define DSP_SFALSE (DSP_SBASE + 6) ++ ++/* A library contains no dependent library references */ ++#define DSP_SNODEPENDENTLIBS (DSP_SBASE + 7) ++ ++/* A persistent library is already loaded by the dynamic loader */ ++#define DSP_SALREADYLOADED (DSP_SBASE + 8) ++ ++/* Some error occured, but it is OK to continue */ ++#define DSP_OKTO_CONTINUE (DSP_SBASE + 9) ++ ++/* FAILURE Codes */ ++ ++/* The caller does not have access privileges to call this function */ ++#define DSP_EACCESSDENIED (DSP_EBASE + 0) ++ ++/* The Specified Connection already exists */ ++#define DSP_EALREADYCONNECTED (DSP_EBASE + 1) ++ ++/* The GPP must be detached from the DSP before this function is called */ ++#define DSP_EATTACHED (DSP_EBASE + 2) ++ ++/* During enumeration a change in the number or properties of the objects ++ * has occurred. */ ++#define DSP_ECHANGEDURINGENUM (DSP_EBASE + 3) ++ ++/* An error occurred while parsing the DSP executable file */ ++#define DSP_ECORRUPTFILE (DSP_EBASE + 4) ++ ++/* A failure occurred during a delete operation */ ++#define DSP_EDELETE (DSP_EBASE + 5) ++ ++/* The specified direction is invalid */ ++#define DSP_EDIRECTION (DSP_EBASE + 6) ++ ++/* A stream has been issued the maximum number of buffers allowed in the ++ * stream at once ; buffers must be reclaimed from the stream before any ++ * more can be issued. */ ++#define DSP_ESTREAMFULL (DSP_EBASE + 7) ++ ++/* A general failure occurred */ ++#define DSP_EFAIL (DSP_EBASE + 8) ++ ++/* The specified executable file could not be found. */ ++#define DSP_EFILE (DSP_EBASE + 9) ++ ++/* The specified handle is invalid. */ ++#define DSP_EHANDLE (DSP_EBASE + 0xa) ++ ++/* An invalid argument was specified. */ ++#define DSP_EINVALIDARG (DSP_EBASE + 0xb) ++ ++/* A memory allocation failure occurred. */ ++#define DSP_EMEMORY (DSP_EBASE + 0xc) ++ ++/* The requested operation is invalid for this node type. */ ++#define DSP_ENODETYPE (DSP_EBASE + 0xd) ++ ++/* No error text was found for the specified error code. */ ++#define DSP_ENOERRTEXT (DSP_EBASE + 0xe) ++ ++/* No more connections can be made for this node. */ ++#define DSP_ENOMORECONNECTIONS (DSP_EBASE + 0xf) ++ ++/* The indicated operation is not supported. */ ++#define DSP_ENOTIMPL (DSP_EBASE + 0x10) ++ ++/* I/O is currently pending. */ ++#define DSP_EPENDING (DSP_EBASE + 0x11) ++ ++/* An invalid pointer was specified. */ ++#define DSP_EPOINTER (DSP_EBASE + 0x12) ++ ++/* A parameter is specified outside its valid range. */ ++#define DSP_ERANGE (DSP_EBASE + 0x13) ++ ++/* An invalid size parameter was specified. */ ++#define DSP_ESIZE (DSP_EBASE + 0x14) ++ ++/* A stream creation failure occurred on the DSP. */ ++#define DSP_ESTREAM (DSP_EBASE + 0x15) ++ ++/* A task creation failure occurred on the DSP. */ ++#define DSP_ETASK (DSP_EBASE + 0x16) ++ ++/* A timeout occurred before the requested operation could complete. */ ++ ++#define DSP_ETIMEOUT (DSP_EBASE + 0x17) ++ ++/* A data truncation occurred, e.g., when requesting a descriptive error ++ * string, not enough space was allocated for the complete error message. */ ++ ++#define DSP_ETRUNCATED (DSP_EBASE + 0x18) ++ ++/* A parameter is invalid. */ ++#define DSP_EVALUE (DSP_EBASE + 0x1a) ++ ++/* The state of the specified object is incorrect for the requested ++ * operation. */ ++#define DSP_EWRONGSTATE (DSP_EBASE + 0x1b) ++ ++/* Symbol not found in the COFF file. DSPNode_Create will return this if ++ * the iAlg function table for an xDAIS socket is not found in the COFF file. ++ * In this case, force the symbol to be linked into the COFF file. ++ * DSPNode_Create, DSPNode_Execute, and DSPNode_Delete will return this if ++ * the create, execute, or delete phase function, respectively, could not be ++ * found in the COFF file. */ ++#define DSP_ESYMBOL (DSP_EBASE + 0x1c) ++ ++/* UUID not found in registry. */ ++#define DSP_EUUID (DSP_EBASE + 0x1d) ++ ++/* Unable to read content of DCD data section ; this is typically caused by ++ * improperly configured nodes. */ ++#define DSP_EDCDREADSECT (DSP_EBASE + 0x1e) ++ ++/* Unable to decode DCD data section content ; this is typically caused by ++ * changes to DSP/BIOS Bridge data structures. */ ++#define DSP_EDCDPARSESECT (DSP_EBASE + 0x1f) ++ ++/* Unable to get pointer to DCD data section ; this is typically caused by ++ * improperly configured UUIDs. */ ++#define DSP_EDCDGETSECT (DSP_EBASE + 0x20) ++ ++/* Unable to load file containing DCD data section ; this is typically ++ * caused by a missing COFF file. */ ++#define DSP_EDCDLOADBASE (DSP_EBASE + 0x21) ++ ++/* The specified COFF file does not contain a valid node registration ++ * section. */ ++#define DSP_EDCDNOAUTOREGISTER (DSP_EBASE + 0x22) ++ ++/* A requested resource is not available. */ ++#define DSP_ERESOURCE (DSP_EBASE + 0x28) ++ ++/* A critical error has occurred, and the DSP is being re-started. */ ++#define DSP_ERESTART (DSP_EBASE + 0x29) ++ ++/* A DSP memory free operation failed. */ ++#define DSP_EFREE (DSP_EBASE + 0x2a) ++ ++/* A DSP I/O free operation failed. */ ++#define DSP_EIOFREE (DSP_EBASE + 0x2b) ++ ++/* Multiple instances are not allowed. */ ++#define DSP_EMULINST (DSP_EBASE + 0x2c) ++ ++/* A specified entity was not found. */ ++#define DSP_ENOTFOUND (DSP_EBASE + 0x2d) ++ ++/* A DSP I/O resource is not available. */ ++#define DSP_EOUTOFIO (DSP_EBASE + 0x2e) ++ ++/* A shared memory buffer contained in a message or stream could not be ++ * mapped to the GPP client process's virtual space. */ ++#define DSP_ETRANSLATE (DSP_EBASE + 0x2f) ++ ++/* File or section load write function failed to write to DSP */ ++#define DSP_EFWRITE (DSP_EBASE + 0x31) ++ ++/* Unable to find a named section in DSP executable */ ++#define DSP_ENOSECT (DSP_EBASE + 0x32) ++ ++/* Unable to open file */ ++#define DSP_EFOPEN (DSP_EBASE + 0x33) ++ ++/* Unable to read file */ ++#define DSP_EFREAD (DSP_EBASE + 0x34) ++ ++/* A non-existent memory segment identifier was specified */ ++#define DSP_EOVERLAYMEMORY (DSP_EBASE + 0x37) ++ ++/* Invalid segment ID */ ++#define DSP_EBADSEGID (DSP_EBASE + 0x38) ++ ++/* Invalid alignment */ ++#define DSP_EALIGNMENT (DSP_EBASE + 0x39) ++ ++/* Invalid stream mode */ ++#define DSP_ESTRMMODE (DSP_EBASE + 0x3a) ++ ++/* Nodes not connected */ ++#define DSP_ENOTCONNECTED (DSP_EBASE + 0x3b) ++ ++/* Not shared memory */ ++#define DSP_ENOTSHAREDMEM (DSP_EBASE + 0x3c) ++ ++/* Error occurred in a dynamic loader library function */ ++#define DSP_EDYNLOAD (DSP_EBASE + 0x3d) ++ ++/* Device in 'sleep/suspend' mode due to DPM */ ++#define DSP_EDPMSUSPEND (DSP_EBASE + 0x3e) ++ ++/* A node-specific error has occurred. */ ++#define DSP_EUSER1 (DSP_EBASE + 0x40) ++#define DSP_EUSER2 (DSP_EBASE + 0x41) ++#define DSP_EUSER3 (DSP_EBASE + 0x42) ++#define DSP_EUSER4 (DSP_EBASE + 0x43) ++#define DSP_EUSER5 (DSP_EBASE + 0x44) ++#define DSP_EUSER6 (DSP_EBASE + 0x45) ++#define DSP_EUSER7 (DSP_EBASE + 0x46) ++#define DSP_EUSER8 (DSP_EBASE + 0x47) ++#define DSP_EUSER9 (DSP_EBASE + 0x48) ++#define DSP_EUSER10 (DSP_EBASE + 0x49) ++#define DSP_EUSER11 (DSP_EBASE + 0x4a) ++#define DSP_EUSER12 (DSP_EBASE + 0x4b) ++#define DSP_EUSER13 (DSP_EBASE + 0x4c) ++#define DSP_EUSER14 (DSP_EBASE + 0x4d) ++#define DSP_EUSER15 (DSP_EBASE + 0x4e) ++#define DSP_EUSER16 (DSP_EBASE + 0x4f) ++ ++/* FAILURE Codes : DEV */ ++#define DEV_EBASE (DSP_COMP_EBASE + 0x000) ++ ++/* The mini-driver expected a newer version of the class driver. */ ++#define DEV_E_NEWWMD (DEV_EBASE + 0x00) ++ ++/* WMD_DRV_Entry function returned a NULL function interface table. */ ++#define DEV_E_NULLWMDINTF (DEV_EBASE + 0x01) ++ ++/* FAILURE Codes : LDR */ ++#define LDR_EBASE (DSP_COMP_EBASE + 0x100) ++ ++/* Insufficient memory to export class driver services. */ ++#define LDR_E_NOMEMORY (LDR_EBASE + 0x00) ++ ++/* Unable to find WMD file in system directory. */ ++#define LDR_E_FILEUNABLETOOPEN (LDR_EBASE + 0x01) ++ ++/* FAILURE Codes : CFG */ ++#define CFG_EBASE (DSP_COMP_EBASE + 0x200) ++ ++/* Invalid pointer passed into a configuration module function */ ++#define CFG_E_INVALIDPOINTER (CFG_EBASE + 0x00) ++ ++/* Invalid device node handle passed into a configuration module function. */ ++#define CFG_E_INVALIDHDEVNODE (CFG_EBASE + 0x01) ++ ++/* Unable to retrieve resource information from the registry. */ ++#define CFG_E_RESOURCENOTAVAIL (CFG_EBASE + 0x02) ++ ++/* Unable to find board name key in registry. */ ++#define CFG_E_INVALIDBOARDNAME (CFG_EBASE + 0x03) ++ ++/* Unable to find a device node in registry with given unit number. */ ++#define CFG_E_INVALIDUNITNUM (CFG_EBASE + 0x04) ++ ++/* Insufficient buffer size */ ++#define CFG_E_INSUFFICIENTBUFSIZE (CFG_EBASE + 0x05) ++ ++/* FAILURE Codes : BRD */ ++#define BRD_EBASE (DSP_COMP_EBASE + 0x300) ++ ++/* Board client does not have sufficient access rights for this operation. */ ++#define BRD_E_ACCESSDENIED (BRD_EBASE + 0x00) ++ ++/* Unable to find trace buffer symbols in the DSP executable COFF file. */ ++#define BRD_E_NOTRACEBUFFER (BRD_EBASE + 0x01) ++ ++/* Attempted to auto-start board, but no default DSP executable configured. */ ++#define BRD_E_NOEXEC (BRD_EBASE + 0x02) ++ ++/* The operation failed because it was started from a wrong state */ ++#define BRD_E_WRONGSTATE (BRD_EBASE + 0x03) ++ ++/* FAILURE Codes : COD */ ++#define COD_EBASE (DSP_COMP_EBASE + 0x400) ++ ++/* No symbol table is loaded for this board. */ ++#define COD_E_NOSYMBOLSLOADED (COD_EBASE + 0x00) ++ ++/* Symbol not found in for this board. */ ++#define COD_E_SYMBOLNOTFOUND (COD_EBASE + 0x01) ++ ++/* ZL DLL module is not exporting the correct function interface. */ ++#define COD_E_NOZLFUNCTIONS (COD_EBASE + 0x02) ++ ++/* Unable to initialize the ZL COFF parsing module. */ ++#define COD_E_ZLCREATEFAILED (COD_EBASE + 0x03) ++ ++/* Unable to open DSP executable COFF file. */ ++#define COD_E_OPENFAILED (COD_EBASE + 0x04) ++ ++/* Unable to parse DSP executable COFF file. */ ++#define COD_E_LOADFAILED (COD_EBASE + 0x05) ++ ++/* Unable to read DSP executable COFF file. */ ++#define COD_E_READFAILED (COD_EBASE + 0x06) ++ ++/* FAILURE Codes : CHNL */ ++#define CHNL_EBASE (DSP_COMP_EBASE + 0x500) ++ ++/* Attempt to created channel manager with too many channels. */ ++#define CHNL_E_MAXCHANNELS (CHNL_EBASE + 0x00) ++ ++/* No channel manager exists for this mini-driver. */ ++#define CHNL_E_NOMGR (CHNL_EBASE + 0x01) ++ ++/* No free channels are available. */ ++#define CHNL_E_OUTOFSTREAMS (CHNL_EBASE + 0x02) ++ ++/* Channel ID is out of range. */ ++#define CHNL_E_BADCHANID (CHNL_EBASE + 0x03) ++ ++/* Channel is already in use. */ ++#define CHNL_E_CHANBUSY (CHNL_EBASE + 0x04) ++ ++/* Invalid channel mode argument. */ ++#define CHNL_E_BADMODE (CHNL_EBASE + 0x05) ++ ++/* dwTimeOut parameter was CHNL_IOCNOWAIT, yet no I/O completions were ++ * queued. */ ++#define CHNL_E_NOIOC (CHNL_EBASE + 0x06) ++ ++/* I/O has been cancelled on this channel. */ ++#define CHNL_E_CANCELLED (CHNL_EBASE + 0x07) ++ ++/* End of stream was already requested on this output channel. */ ++#define CHNL_E_EOS (CHNL_EBASE + 0x09) ++ ++/* Unable to create the channel event object. */ ++#define CHNL_E_CREATEEVENT (CHNL_EBASE + 0x0A) ++ ++/* Board name and unit number do not identify a valid board name. */ ++#define CHNL_E_BRDID (CHNL_EBASE + 0x0B) ++ ++/* Invalid IRQ configured for this WMD for this system. */ ++#define CHNL_E_INVALIDIRQ (CHNL_EBASE + 0x0C) ++ ++/* DSP word size of zero configured for this device. */ ++#define CHNL_E_INVALIDWORDSIZE (CHNL_EBASE + 0x0D) ++ ++/* A zero length memory base was specified for a shared memory class driver. */ ++#define CHNL_E_INVALIDMEMBASE (CHNL_EBASE + 0x0E) ++ ++/* Memory map is not configured, or unable to map physical to linear ++ * address. */ ++#define CHNL_E_NOMEMMAP (CHNL_EBASE + 0x0F) ++ ++/* Attempted to create a channel manager when one already exists. */ ++#define CHNL_E_MGREXISTS (CHNL_EBASE + 0x10) ++ ++/* Unable to plug channel ISR for configured IRQ. */ ++#define CHNL_E_ISR (CHNL_EBASE + 0x11) ++ ++/* No free I/O request packets are available. */ ++#define CHNL_E_NOIORPS (CHNL_EBASE + 0x12) ++ ++/* Buffer size is larger than the size of physical channel. */ ++#define CHNL_E_BUFSIZE (CHNL_EBASE + 0x13) ++ ++/* User cannot mark end of stream on an input channel. */ ++#define CHNL_E_NOEOS (CHNL_EBASE + 0x14) ++ ++/* Wait for flush operation on an output channel timed out. */ ++#define CHNL_E_WAITTIMEOUT (CHNL_EBASE + 0x15) ++ ++/* User supplied hEvent must be specified with pstrEventName attribute */ ++#define CHNL_E_BADUSEREVENT (CHNL_EBASE + 0x16) ++ ++/* Illegal user event name specified */ ++#define CHNL_E_USEREVENTNAME (CHNL_EBASE + 0x17) ++ ++/* Unable to prepare buffer specified */ ++#define CHNL_E_PREPFAILED (CHNL_EBASE + 0x18) ++ ++/* Unable to Unprepare buffer specified */ ++#define CHNL_E_UNPREPFAILED (CHNL_EBASE + 0x19) ++ ++/* FAILURE Codes : SYNC */ ++#define SYNC_EBASE (DSP_COMP_EBASE + 0x600) ++ ++/* Wait on a kernel event failed. */ ++#define SYNC_E_FAIL (SYNC_EBASE + 0x00) ++ ++/* Timeout expired while waiting for event to be signalled. */ ++#define SYNC_E_TIMEOUT (SYNC_EBASE + 0x01) ++ ++/* FAILURE Codes : WMD */ ++#define WMD_EBASE (DSP_COMP_EBASE + 0x700) ++ ++/* A test of hardware assumptions or integrity failed. */ ++#define WMD_E_HARDWARE (WMD_EBASE + 0x00) ++ ++/* One or more configuration parameters violated WMD hardware assumptions. */ ++#define WMD_E_BADCONFIG (WMD_EBASE + 0x01) ++ ++/* Timeout occurred waiting for a response from the hardware. */ ++#define WMD_E_TIMEOUT (WMD_EBASE + 0x02) ++ ++/* FAILURE Codes : REG */ ++#define REG_EBASE (DSP_COMP_EBASE + 0x800) ++ ++/* Invalid subkey parameter. */ ++#define REG_E_INVALIDSUBKEY (REG_EBASE + 0x00) ++ ++/* Invalid entry parameter. */ ++#define REG_E_INVALIDENTRY (REG_EBASE + 0x01) ++ ++/* No more registry values. */ ++#define REG_E_NOMOREITEMS (REG_EBASE + 0x02) ++ ++/* Insufficient space to hold data in registry value. */ ++#define REG_E_MOREDATA (REG_EBASE + 0x03) ++ ++/* FAILURE Codes : KFILE */ ++#define KFILE_EBASE (DSP_COMP_EBASE + 0x900) ++ ++/* Invalid file handle. */ ++#define E_KFILE_INVALIDHANDLE (KFILE_EBASE + 0x01) ++ ++/* Bad file name. */ ++#define E_KFILE_BADFILENAME (KFILE_EBASE + 0x02) ++ ++/* Invalid file mode. */ ++#define E_KFILE_INVALIDMODE (KFILE_EBASE + 0x03) ++ ++/* No resources available. */ ++#define E_KFILE_NORESOURCES (KFILE_EBASE + 0x04) ++ ++/* Invalid file buffer . */ ++#define E_KFILE_INVALIDBUFFER (KFILE_EBASE + 0x05) ++ ++/* Bad origin argument. */ ++#define E_KFILE_BADORIGINFLAG (KFILE_EBASE + 0x06) ++ ++/* Invalid file offset value. */ ++#define E_KFILE_INVALIDOFFSET (KFILE_EBASE + 0x07) ++ ++/* General KFILE error condition */ ++#define E_KFILE_ERROR (KFILE_EBASE + 0x08) ++ ++#endif /* ERRBASE_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/gb.h b/arch/arm/plat-omap/include/dspbridge/gb.h +new file mode 100644 +index 0000000..f147751 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/gb.h +@@ -0,0 +1,85 @@ ++/* ++ * gb.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== gb.h ======== ++ * Generic bitmap manager. ++ * ++ *! Revision History ++ *! ================ ++ */ ++ ++#ifndef GB_ ++#define GB_ ++ ++#define GB_NOBITS (~0) ++#include ++typedef s32 GB_BitNum; ++struct GB_TMap; ++ ++/* ++ * ======== GB_clear ======== ++ * Clear the bit in position bitn in the bitmap map. Bit positions are ++ * zero based. ++ */ ++ ++extern void GB_clear(struct GB_TMap *map, GB_BitNum bitn); ++ ++/* ++ * ======== GB_create ======== ++ * Create a bit map with len bits. Initially all bits are cleared. ++ */ ++ ++extern struct GB_TMap *GB_create(GB_BitNum len); ++ ++/* ++ * ======== GB_delete ======== ++ * Delete previously created bit map ++ */ ++ ++extern void GB_delete(struct GB_TMap *map); ++ ++/* ++ * ======== GB_findandset ======== ++ * Finds a clear bit, sets it, and returns the position ++ */ ++ ++extern GB_BitNum GB_findandset(struct GB_TMap *map); ++ ++/* ++ * ======== GB_minclear ======== ++ * GB_minclear returns the minimum clear bit position. If no bit is ++ * clear, GB_minclear returns -1. ++ */ ++extern GB_BitNum GB_minclear(struct GB_TMap *map); ++ ++/* ++ * ======== GB_set ======== ++ * Set the bit in position bitn in the bitmap map. Bit positions are ++ * zero based. ++ */ ++ ++extern void GB_set(struct GB_TMap *map, GB_BitNum bitn); ++ ++/* ++ * ======== GB_test ======== ++ * Returns TRUE if the bit in position bitn is set in map; otherwise ++ * GB_test returns FALSE. Bit positions are zero based. ++ */ ++ ++extern bool GB_test(struct GB_TMap *map, GB_BitNum bitn); ++ ++#endif /*GB_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/getsection.h b/arch/arm/plat-omap/include/dspbridge/getsection.h +new file mode 100644 +index 0000000..33ff50d +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/getsection.h +@@ -0,0 +1,118 @@ ++/* ++ * getsection.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++#ifndef _GETSECTION_H_ ++#define _GETSECTION_H_ ++ ++ ++#include "dynamic_loader.h" ++ ++/* ++ * Get Section Information ++ * ++ * This file provides an API add-on to the dynamic loader that allows the user ++ * to query section information and extract section data from dynamic load ++ * modules. ++ * ++ * NOTE: ++ * Functions in this API assume that the supplied Dynamic_Loader_Stream object ++ * supports the set_file_posn method. ++ */ ++ ++ /* opaque handle for module information */ ++ typedef void *DLOAD_module_info; ++ ++/* ++ * Procedure DLOAD_module_open ++ * ++ * Parameters: ++ * module The input stream that supplies the module image ++ * syms Host-side malloc/free and error reporting functions. ++ * Other methods are unused. ++ * ++ * Effect: ++ * Reads header information from a dynamic loader module using the specified ++ * stream object, and returns a handle for the module information. This ++ * handle may be used in subsequent query calls to obtain information ++ * contained in the module. ++ * ++ * Returns: ++ * NULL if an error is encountered, otherwise a module handle for use ++ * in subsequent operations. ++ */ ++ extern DLOAD_module_info DLOAD_module_open(struct Dynamic_Loader_Stream ++ *module, ++ struct Dynamic_Loader_Sym ++ *syms); ++ ++/* ++ * Procedure DLOAD_GetSectionInfo ++ * ++ * Parameters: ++ * minfo Handle from DLOAD_module_open for this module ++ * sectionName Pointer to the string name of the section desired ++ * sectionInfo Address of a section info structure pointer to be initialized ++ * ++ * Effect: ++ * Finds the specified section in the module information, and fills in ++ * the provided LDR_SECTION_INFO structure. ++ * ++ * Returns: ++ * TRUE for success, FALSE for section not found ++ */ ++ extern int DLOAD_GetSectionInfo(DLOAD_module_info minfo, ++ const char *sectionName, ++ const struct LDR_SECTION_INFO ++ ** const sectionInfo); ++ ++/* ++ * Procedure DLOAD_GetSection ++ * ++ * Parameters: ++ * minfo Handle from DLOAD_module_open for this module ++ * sectionInfo Pointer to a section info structure for the desired section ++ * sectionData Buffer to contain the section initialized data ++ * ++ * Effect: ++ * Copies the initialized data for the specified section into the ++ * supplied buffer. ++ * ++ * Returns: ++ * TRUE for success, FALSE for section not found ++ */ ++ extern int DLOAD_GetSection(DLOAD_module_info minfo, ++ const struct LDR_SECTION_INFO *sectionInfo, ++ void *sectionData); ++ ++/* ++ * Procedure DLOAD_module_close ++ * ++ * Parameters: ++ * minfo Handle from DLOAD_module_open for this module ++ * ++ * Effect: ++ * Releases any storage associated with the module handle. On return, ++ * the module handle is invalid. ++ * ++ * Returns: ++ * Zero for success. On error, the number of errors detected is returned. ++ * Individual errors are reported using syms->Error_Report(), where syms was ++ * an argument to DLOAD_module_open ++ */ ++ extern void DLOAD_module_close(DLOAD_module_info minfo); ++ ++#endif /* _GETSECTION_H_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/gh.h b/arch/arm/plat-omap/include/dspbridge/gh.h +new file mode 100644 +index 0000000..089a042 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/gh.h +@@ -0,0 +1,37 @@ ++/* ++ * gh.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== gh.h ======== ++ * ++ *! Revision History ++ *! ================ ++ */ ++ ++#ifndef GH_ ++#define GH_ ++#include ++ ++extern struct GH_THashTab *GH_create(u16 maxBucket, u16 valSize, ++ u16(*hash) (void *, u16), bool(*match) (void *, void *), ++ void(*delete) (void *)); ++extern void GH_delete(struct GH_THashTab *hashTab); ++extern void GH_exit(void); ++extern void *GH_find(struct GH_THashTab *hashTab, void *key); ++extern void GH_init(void); ++extern void *GH_insert(struct GH_THashTab *hashTab, void *key, void *value); ++#endif /* GH_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/gs.h b/arch/arm/plat-omap/include/dspbridge/gs.h +new file mode 100644 +index 0000000..fd5ef27 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/gs.h +@@ -0,0 +1,64 @@ ++/* ++ * gs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== gs.h ======== ++ * Memory allocation/release wrappers. This module allows clients to ++ * avoid OS spacific issues related to memory allocation. It also provides ++ * simple diagnostic capabilities to assist in the detection of memory ++ * leaks. ++ *! Revision History ++ *! ================ ++ */ ++ ++#ifndef GS_ ++#define GS_ ++ ++/* ++ * ======== GS_alloc ======== ++ * Alloc size bytes of space. Returns pointer to space ++ * allocated, otherwise NULL. ++ */ ++extern void *GS_alloc(u32 size); ++ ++/* ++ * ======== GS_exit ======== ++ * Module exit. Do not change to "#define GS_init()"; in ++ * some environments this operation must actually do some work! ++ */ ++extern void GS_exit(void); ++ ++/* ++ * ======== GS_free ======== ++ * Free space allocated by GS_alloc() or GS_calloc(). ++ */ ++extern void GS_free(void *ptr); ++ ++/* ++ * ======== GS_frees ======== ++ * Free space allocated by GS_alloc() or GS_calloc() and assert that ++ * the size of the allocation is size bytes. ++ */ ++extern void GS_frees(void *ptr, u32 size); ++ ++/* ++ * ======== GS_init ======== ++ * Module initialization. Do not change to "#define GS_init()"; in ++ * some environments this operation must actually do some work! ++ */ ++extern void GS_init(void); ++ ++#endif /*GS_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/gt.h b/arch/arm/plat-omap/include/dspbridge/gt.h +new file mode 100644 +index 0000000..456c866 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/gt.h +@@ -0,0 +1,315 @@ ++/* ++ * gt.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== gt.h ======== ++ * Purpose: ++ * There are two definitions that affect which portions of trace ++ * are acutally compiled into the client: GT_TRACE and GT_ASSERT. If ++ * GT_TRACE is set to 0 then all trace statements (except for assertions) ++ * will be compiled out of the client. If GT_ASSERT is set to 0 then ++ * assertions will be compiled out of the client. GT_ASSERT can not be ++ * set to 0 unless GT_TRACE is also set to 0 (i.e. GT_TRACE == 1 implies ++ * GT_ASSERT == 1). ++ * ++ *! Revision History ++ *! ================ ++ *! 02-Feb-2000 rr: Renamed this file to gtce.h. GT CLASS and trace definitions ++ *! are WinCE Specific. ++ *! 03-Jan-1997 ge Replaced "GT_" prefix to GT_Config structure members ++ *! to eliminate preprocessor confusion with other macros. ++ */ ++#include ++#ifndef GT_ ++#define GT_ ++ ++#ifndef GT_TRACE ++#define GT_TRACE 0 /* 0 = "trace compiled out"; 1 = "trace active" */ ++#endif ++ ++#include ++ ++#if !defined(GT_ASSERT) || GT_TRACE ++#define GT_ASSERT 1 ++#endif ++ ++struct GT_Config { ++ Fxn PRINTFXN; ++ Fxn PIDFXN; ++ Fxn TIDFXN; ++ Fxn ERRORFXN; ++}; ++ ++extern struct GT_Config *GT; ++ ++struct GT_Mask { ++ char *modName; ++ u8 *flags; ++} ; ++ ++/* ++ * New GT Class defenitions. ++ * ++ * The following are the explanations and how it could be used in the code ++ * ++ * - GT_ENTER On Entry to Functions ++ * ++ * - GT_1CLASS Display level of debugging status- Object/Automatic ++ * variables ++ * - GT_2CLASS ---- do ---- ++ * ++ * - GT_3CLASS ---- do ---- + It can be used(recommended) for debug ++ * status in the ISR, IST ++ * - GT_4CLASS ---- do ---- ++ * ++ * - GT_5CLASS Display entry for module init/exit functions ++ * ++ * - GT_6CLASS Warn whenever SERVICES function fails ++ * ++ * - GT_7CLASS Warn failure of Critical failures ++ * ++ */ ++ ++#define GT_ENTER ((u8)0x01) ++#define GT_1CLASS ((u8)0x02) ++#define GT_2CLASS ((u8)0x04) ++#define GT_3CLASS ((u8)0x08) ++#define GT_4CLASS ((u8)0x10) ++#define GT_5CLASS ((u8)0x20) ++#define GT_6CLASS ((u8)0x40) ++#define GT_7CLASS ((u8)0x80) ++ ++#ifdef _LINT_ ++ ++/* LINTLIBRARY */ ++ ++/* ++ * ======== GT_assert ======== ++ */ ++/* ARGSUSED */ ++void GT_assert(struct GT_Mask mask, s32 expr) ++{ ++} ++ ++/* ++ * ======== GT_config ======== ++ */ ++/* ARGSUSED */ ++void GT_config(struct GT_Config config) ++{ ++} ++ ++/* ++ * ======== GT_create ======== ++ */ ++/* ARGSUSED */ ++void GT_create(struct GT_Mask *mask, char *modName) ++{ ++} ++ ++/* ++ * ======== GT_curLine ======== ++ * Purpose: ++ * Returns the current source code line number. Is useful for performing ++ * branch testing using trace. For example, ++ * ++ * GT_1trace(curTrace, GT_1CLASS, ++ * "in module XX_mod, executing line %u\n", GT_curLine()); ++ */ ++/* ARGSUSED */ ++u16 GT_curLine(void) ++{ ++ return (u16)NULL; ++} ++ ++/* ++ * ======== GT_exit ======== ++ */ ++/* ARGSUSED */ ++void GT_exit(void) ++{ ++} ++ ++/* ++ * ======== GT_init ======== ++ */ ++/* ARGSUSED */ ++void GT_init(void) ++{ ++} ++ ++/* ++ * ======== GT_query ======== ++ */ ++/* ARGSUSED */ ++bool GT_query(struct GT_Mask mask, u8 class) ++{ ++ return false; ++} ++ ++/* ++ * ======== GT_set ======== ++ * sets trace mask according to settings ++ */ ++ ++/* ARGSUSED */ ++void GT_set(char *settings) ++{ ++} ++ ++/* ++ * ======== GT_setprintf ======== ++ * sets printf function ++ */ ++ ++/* ARGSUSED */ ++void GT_setprintf(Fxn fxn) ++{ ++} ++ ++/* ARGSUSED */ ++void GT_0trace(struct GT_Mask mask, u8 class, char *format) ++{ ++} ++ ++/* ARGSUSED */ ++void GT_1trace(struct GT_Mask mask, u8 class, char *format, ...) ++{ ++} ++ ++/* ARGSUSED */ ++void GT_2trace(struct GT_Mask mask, u8 class, char *format, ...) ++{ ++} ++ ++/* ARGSUSED */ ++void GT_3trace(struct GT_Mask mask, u8 class, char *format, ...) ++{ ++} ++ ++/* ARGSUSED */ ++void GT_4trace(struct GT_Mask mask, u8 class, char *format, ...) ++{ ++} ++ ++/* ARGSUSED */ ++void GT_5trace(struct GT_Mask mask, u8 class, char *format, ...) ++{ ++} ++ ++/* ARGSUSED */ ++void GT_6trace(struct GT_Mask mask, u8 class, char *format, ...) ++{ ++} ++ ++#else ++ ++#define GT_BOUND 26 /* 26 letters in alphabet */ ++ ++extern void _GT_create(struct GT_Mask *mask, char *modName); ++ ++#define GT_exit() ++ ++extern void GT_init(void); ++extern void _GT_set(char *str); ++extern s32 _GT_trace(struct GT_Mask *mask, char *format, ...); ++ ++#if GT_ASSERT == 0 ++ ++#define GT_assert(mask, expr) ++#define GT_config(config) ++#define GT_configInit(config) ++#define GT_seterror(fxn) ++ ++#else ++ ++extern struct GT_Config _GT_params; ++ ++#define GT_assert(mask, expr) \ ++ (!(expr) ? \ ++ printk("assertion violation: %s, line %d\n", \ ++ __FILE__, __LINE__), NULL : NULL) ++ ++#define GT_config(config) (_GT_params = *(config)) ++#define GT_configInit(config) (*(config) = _GT_params) ++#define GT_seterror(fxn) (_GT_params.ERRORFXN = (Fxn)(fxn)) ++ ++#endif ++ ++#if GT_TRACE == 0 ++ ++#define GT_curLine() ((u16)__LINE__) ++#define GT_create(mask, modName) ++#define GT_exit() ++#define GT_init() ++#define GT_set(settings) ++#define GT_setprintf(fxn) ++ ++#define GT_query(mask, class) false ++ ++#define GT_0trace(mask, class, format) ++#define GT_1trace(mask, class, format, arg1) ++#define GT_2trace(mask, class, format, arg1, arg2) ++#define GT_3trace(mask, class, format, arg1, arg2, arg3) ++#define GT_4trace(mask, class, format, arg1, arg2, arg3, arg4) ++#define GT_5trace(mask, class, format, arg1, arg2, arg3, arg4, arg5) ++#define GT_6trace(mask, class, format, arg1, arg2, arg3, arg4, arg5, arg6) ++ ++#else /* GT_TRACE == 1 */ ++ ++ ++#define GT_create(mask, modName) _GT_create((mask), (modName)) ++#define GT_curLine() ((u16)__LINE__) ++#define GT_set(settings) _GT_set(settings) ++#define GT_setprintf(fxn) (_GT_params.PRINTFXN = (Fxn)(fxn)) ++ ++#define GT_query(mask, class) ((*(mask).flags & (class))) ++ ++#define GT_0trace(mask, class, format) \ ++ ((*(mask).flags & (class)) ? \ ++ _GT_trace(&(mask), (format)) : 0) ++ ++#define GT_1trace(mask, class, format, arg1) \ ++ ((*(mask).flags & (class)) ? \ ++ _GT_trace(&(mask), (format), (arg1)) : 0) ++ ++#define GT_2trace(mask, class, format, arg1, arg2) \ ++ ((*(mask).flags & (class)) ? \ ++ _GT_trace(&(mask), (format), (arg1), (arg2)) : 0) ++ ++#define GT_3trace(mask, class, format, arg1, arg2, arg3) \ ++ ((*(mask).flags & (class)) ? \ ++ _GT_trace(&(mask), (format), (arg1), (arg2), (arg3)) : 0) ++ ++#define GT_4trace(mask, class, format, arg1, arg2, arg3, arg4) \ ++ ((*(mask).flags & (class)) ? \ ++ _GT_trace(&(mask), (format), (arg1), (arg2), (arg3), (arg4)) : 0) ++ ++#define GT_5trace(mask, class, format, arg1, arg2, arg3, arg4, arg5) \ ++ ((*(mask).flags & (class)) ? \ ++ _GT_trace(&(mask), (format), (arg1), (arg2), (arg3), (arg4), (arg5)) : 0) ++ ++#define GT_6trace(mask, class, format, arg1, arg2, arg3, arg4, arg5, arg6) \ ++ ((*(mask).flags & (class)) ? \ ++ _GT_trace(&(mask), (format), (arg1), (arg2), (arg3), (arg4), (arg5), \ ++ (arg6)) : 0) ++ ++#endif /* GT_TRACE */ ++ ++#endif /* _LINT_ */ ++ ++#endif /* GTCE_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/host_os.h b/arch/arm/plat-omap/include/dspbridge/host_os.h +new file mode 100644 +index 0000000..f539bd0 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/host_os.h +@@ -0,0 +1,96 @@ ++/* ++ * host_os.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== windows.h ======== ++ * ++ *! Revision History ++ *! ================ ++ *! 08-Mar-2004 sb Added cacheflush.h to support Dynamic Memory Mapping feature ++ *! 16-Feb-2004 sb Added headers required for consistent_alloc ++ */ ++ ++#ifndef _HOST_OS_H_ ++#define _HOST_OS_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Macros */ ++ ++#define SEEK_SET 0 /* Seek from beginning of file. */ ++#define SEEK_CUR 1 /* Seek from current position. */ ++#define SEEK_END 2 /* Seek from end of file. */ ++ ++/* TODO -- Remove, once BP defines them */ ++#define INT_MAIL_MPU_IRQ 26 ++#define INT_DSP_MMU_IRQ 28 ++ ++struct dspbridge_platform_data { ++ void (*dsp_set_min_opp)(u8 opp_id); ++ u8 (*dsp_get_opp)(void); ++ void (*cpu_set_freq)(unsigned long f); ++ unsigned long (*cpu_get_freq)(void); ++ unsigned long mpu_speed[6]; ++ ++ u32 phys_mempool_base; ++ u32 phys_mempool_size; ++}; ++ ++#define PRCM_VDD1 1 ++ ++extern struct platform_device *omap_dspbridge_dev; ++ ++#if defined(CONFIG_MPU_BRIDGE) || defined(CONFIG_MPU_BRIDGE_MODULE) ++extern void dspbridge_reserve_sdram(void); ++#else ++static inline void dspbridge_reserve_sdram(void) {} ++#endif ++ ++extern unsigned long dspbridge_get_mempool_base(void); ++#endif ++ +diff --git a/arch/arm/plat-omap/include/dspbridge/io.h b/arch/arm/plat-omap/include/dspbridge/io.h +new file mode 100644 +index 0000000..6dc63f2 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/io.h +@@ -0,0 +1,132 @@ ++/* ++ * io.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== io.h ======== ++ * Description: ++ * The io module manages IO between CHNL and MSG. ++ * ++ * Public Functions: ++ * IO_Create ++ * IO_Destroy ++ * IO_Exit ++ * IO_Init ++ * IO_OnLoaded ++ * ++ * ++ *! Revision History: ++ *! ================ ++ *! 07-Nov-2000 jeh Created. ++ */ ++ ++#ifndef IO_ ++#define IO_ ++ ++#include ++#include ++ ++#include ++ ++/* ++ * ======== IO_Create ======== ++ * Purpose: ++ * Create an IO manager object, responsible for managing IO between ++ * CHNL and MSG. ++ * Parameters: ++ * phChnlMgr: Location to store a channel manager object on ++ * output. ++ * hDevObject: Handle to a device object. ++ * pMgrAttrs: IO manager attributes. ++ * pMgrAttrs->bIRQ: I/O IRQ number. ++ * pMgrAttrs->fShared: TRUE if the IRQ is shareable. ++ * pMgrAttrs->uWordSize: DSP Word size in equivalent PC bytes.. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EMEMORY: Insufficient memory for requested resources. ++ * CHNL_E_ISR: Unable to plug channel ISR for configured IRQ. ++ * CHNL_E_INVALIDIRQ: Invalid IRQ number. Must be 0 <= bIRQ <= 15. ++ * CHNL_E_INVALIDWORDSIZE: Invalid DSP word size. Must be > 0. ++ * CHNL_E_INVALIDMEMBASE: Invalid base address for DSP communications. ++ * Requires: ++ * IO_Init(void) called. ++ * phIOMgr != NULL. ++ * pMgrAttrs != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS IO_Create(OUT struct IO_MGR **phIOMgr, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct IO_ATTRS *pMgrAttrs); ++ ++/* ++ * ======== IO_Destroy ======== ++ * Purpose: ++ * Destroy the IO manager. ++ * Parameters: ++ * hIOMgr: IOmanager object. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: hIOMgr was invalid. ++ * Requires: ++ * IO_Init(void) called. ++ * Ensures: ++ */ ++ extern DSP_STATUS IO_Destroy(struct IO_MGR *hIOMgr); ++ ++/* ++ * ======== IO_Exit ======== ++ * Purpose: ++ * Discontinue usage of the IO module. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * IO_Init(void) previously called. ++ * Ensures: ++ * Resources, if any acquired in IO_Init(void), are freed when the last ++ * client of IO calls IO_Exit(void). ++ */ ++ extern void IO_Exit(void); ++ ++/* ++ * ======== IO_Init ======== ++ * Purpose: ++ * Initialize the IO module's private state. ++ * Parameters: ++ * Returns: ++ * TRUE if initialized; FALSE if error occurred. ++ * Requires: ++ * Ensures: ++ * A requirement for each of the other public CHNL functions. ++ */ ++ extern bool IO_Init(void); ++ ++/* ++ * ======== IO_OnLoaded ======== ++ * Purpose: ++ * Called when a program is loaded so IO manager can update its ++ * internal state. ++ * Parameters: ++ * hIOMgr: IOmanager object. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: hIOMgr was invalid. ++ * Requires: ++ * IO_Init(void) called. ++ * Ensures: ++ */ ++ extern DSP_STATUS IO_OnLoaded(struct IO_MGR *hIOMgr); ++ ++#endif /* CHNL_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/io_sm.h b/arch/arm/plat-omap/include/dspbridge/io_sm.h +new file mode 100644 +index 0000000..3dcbf74 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/io_sm.h +@@ -0,0 +1,335 @@ ++/* ++ * io_sm.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== io_sm.h ======== ++ * Description: ++ * IO dispatcher for a shared memory channel driver. ++ * Also, includes macros to simulate SHM via port io calls. ++ * ++ * Public Functions: ++ * IO_Dispatch ++ * IO_DPC ++ * IO_ISR ++ * IO_RequestChnl ++ * ++ * Notes: ++ * ++ *! Revision History: ++ *! ================ ++ *! 01-Mar-2004 vp: Added IVA releated functions. ++ *! 23-Apr-2003 sb: Fixed mailbox deadlock ++ *! 06-Feb-2003 kc Added IO_DDMAClearChnlDesc and IO_DDZCClearChnlDesc. ++ *! 21-Dec-2001 ag Removed unused param in IO_DDMAInitChnlDesc(). ++ * Updated comments. Removed #ifdef CHNL_NOIPCINTR. ++ *! 05-Nov-2001 kc Updated IO_CALLISR macro. ++ *! 01-May-2001 jeh Removed IO_RequestMsg. ++ *! 29-Mar-2001 ag Added #ifdef CHNL_NOIPCINTR. ++ *! 04-Dec-2000 jeh Added IO_RequestMsg. ++ *! 26-Oct-2000 jeh Added IO_GetLong, IO_SetLong, IO_ReadValueLong, and ++ *! IO_WriteValueLong, for passing arg in SHM structure. ++ *! 20-Jan-2000 ag: Updated header comments per code review. ++ *! 05-Jan-2000 ag: Text format clean-up. ++ *! 02-Nov-1999 ag: Updated header descriptions. ++ *! 25-May-1999 jg: Removed assumption of 4 bytes / word. ++ *! 12-Aug-1996 gp: Created. ++ */ ++ ++#ifndef IOSM_ ++#define IOSM_ ++ ++#include ++#include ++ ++#include ++ ++#define IO_INPUT 0 ++#define IO_OUTPUT 1 ++#define IO_SERVICE 2 ++#define IO_MAXSERVICE IO_SERVICE ++ ++#define IO_MGRSIGNATURE 0x494f4D43 /* "IOGR" */ ++ ++#define DSPFieldAddr(type, field, base, wordsize) \ ++ ((((s32)&(((type *)0)->field)) / wordsize) + (u32)base) ++ ++/* Access can be different SM access word size (e.g. 16/32 bit words) */ ++#define IO_SetValue(pContext, type, base, field, value) (base->field = value) ++#define IO_GetValue(pContext, type, base, field) (base->field) ++#define IO_OrValue(pContext, type, base, field, value) (base->field |= value) ++#define IO_AndValue(pContext, type, base, field, value) (base->field &= value) ++#define IO_SetLong(pContext, type, base, field, value) (base->field = value) ++#define IO_GetLong(pContext, type, base, field) (base->field) ++ ++#define IO_DisableInterrupt(h) CHNLSM_DisableInterrupt(h) ++#define IO_EnableInterrupt(h) CHNLSM_EnableInterrupt(h) ++#define IO_CALLISR(h, pFlag, pwMBRegVal) CHNLSM_ISR(h, pFlag, pwMBRegVal) ++ ++/* ++ * ======== IO_CancelChnl ======== ++ * Purpose: ++ * Cancel IO on a given channel. ++ * Parameters: ++ * hIOMgr: IO Manager. ++ * ulChnl: Index of channel to cancel IO on. ++ * Returns: ++ * Requires: ++ * Valid hIOMgr. ++ * Ensures: ++ */ ++ extern void IO_CancelChnl(struct IO_MGR *hIOMgr, u32 ulChnl); ++ ++/* ++ * ======== IO_DPC ======== ++ * Purpose: ++ * Deferred procedure call for shared memory channel driver ISR. Carries ++ * out the dispatch of I/O. ++ * Parameters: ++ * pRefData: Pointer to reference data registered via a call to ++ * DPC_Create(). ++ * Returns: ++ * Requires: ++ * Must not block. ++ * Must not acquire resources. ++ * All data touched must be locked in memory if running in kernel mode. ++ * Ensures: ++ * Non-preemptible (but interruptible). ++ */ ++ extern void IO_DPC(IN OUT void *pRefData); ++ ++/* ++ * ======== IO_ISR ======== ++ * Purpose: ++ * Main interrupt handler for the shared memory WMD channel manager. ++ * Calls the WMD's CHNLSM_ISR to determine if this interrupt is ours, then ++ * schedules a DPC to dispatch I/O.. ++ * Parameters: ++ * pRefData: Pointer to the channel manager object for this board. ++ * Set in an initial call to ISR_Install(). ++ * Returns: ++ * TRUE if interrupt handled; FALSE otherwise. ++ * Requires: ++ * Must be in locked memory if executing in kernel mode. ++ * Must only call functions which are in locked memory if Kernel mode. ++ * Must only call asynchronous services. ++ * Interrupts are disabled and EOI for this interrupt has been sent. ++ * Ensures: ++ */ ++ irqreturn_t IO_ISR(int irq, IN void *pRefData); ++/* ++ * ======== IO_RequestChnl ======== ++ * Purpose: ++ * Request I/O from the DSP. Sets flags in shared memory, then interrupts ++ * the DSP. ++ * Parameters: ++ * hIOMgr: IO manager handle. ++ * pChnl: Ptr to the channel requesting I/O. ++ * iMode: Mode of channel: {IO_INPUT | IO_OUTPUT}. ++ * Returns: ++ * Requires: ++ * pChnl != NULL ++ * Ensures: ++ */ ++ extern void IO_RequestChnl(struct IO_MGR *hIOMgr, ++ struct CHNL_OBJECT *pChnl, ++ u32 iMode, OUT u16 *pwMbVal); ++ ++/* ++ * ======== IO_Schedule ======== ++ * Purpose: ++ * Schedule DPC for IO. ++ * Parameters: ++ * pIOMgr: Ptr to a I/O manager. ++ * Returns: ++ * Requires: ++ * pChnl != NULL ++ * Ensures: ++ */ ++ extern void IO_Schedule(struct IO_MGR *hIOMgr); ++ ++/* ++ * DSP-DMA IO functions ++ */ ++ ++/* ++ * ======== IO_DDMAInitChnlDesc ======== ++ * Purpose: ++ * Initialize DSP DMA channel descriptor. ++ * Parameters: ++ * hIOMgr: Handle to a I/O manager. ++ * uDDMAChnlId: DDMA channel identifier. ++ * uNumDesc: Number of buffer descriptors(equals # of IOReqs & ++ * Chirps) ++ * pDsp: Dsp address; ++ * Returns: ++ * Requires: ++ * uDDMAChnlId < DDMA_MAXDDMACHNLS ++ * uNumDesc > 0 ++ * pVa != NULL ++ * pDspPa != NULL ++ * ++ * Ensures: ++ */ ++ extern void IO_DDMAInitChnlDesc(struct IO_MGR *hIOMgr, u32 uDDMAChnlId, ++ u32 uNumDesc, void *pDsp); ++ ++/* ++ * ======== IO_DDMAClearChnlDesc ======== ++ * Purpose: ++ * Clear DSP DMA channel descriptor. ++ * Parameters: ++ * hIOMgr: Handle to a I/O manager. ++ * uDDMAChnlId: DDMA channel identifier. ++ * Returns: ++ * Requires: ++ * uDDMAChnlId < DDMA_MAXDDMACHNLS ++ * Ensures: ++ */ ++ extern void IO_DDMAClearChnlDesc(struct IO_MGR *hIOMgr, ++ u32 uDDMAChnlId); ++ ++/* ++ * ======== IO_DDMARequestChnl ======== ++ * Purpose: ++ * Request channel DSP-DMA from the DSP. Sets up SM descriptors and ++ * control fields in shared memory. ++ * Parameters: ++ * hIOMgr: Handle to a I/O manager. ++ * pChnl: Ptr to channel object ++ * pChirp: Ptr to channel i/o request packet. ++ * Returns: ++ * Requires: ++ * pChnl != NULL ++ * pChnl->cIOReqs > 0 ++ * pChirp != NULL ++ * Ensures: ++ */ ++ extern void IO_DDMARequestChnl(struct IO_MGR *hIOMgr, ++ struct CHNL_OBJECT *pChnl, ++ struct CHNL_IRP *pChirp, ++ OUT u16 *pwMbVal); ++ ++/* ++ * Zero-copy IO functions ++ */ ++ ++/* ++ * ======== IO_DDZCInitChnlDesc ======== ++ * Purpose: ++ * Initialize ZCPY channel descriptor. ++ * Parameters: ++ * hIOMgr: Handle to a I/O manager. ++ * uZId: zero-copy channel identifier. ++ * Returns: ++ * Requires: ++ * uDDMAChnlId < DDMA_MAXZCPYCHNLS ++ * hIOMgr != Null ++ * Ensures: ++ */ ++ extern void IO_DDZCInitChnlDesc(struct IO_MGR *hIOMgr, u32 uZId); ++ ++/* ++ * ======== IO_DDZCClearChnlDesc ======== ++ * Purpose: ++ * Clear DSP ZC channel descriptor. ++ * Parameters: ++ * hIOMgr: Handle to a I/O manager. ++ * uChnlId: ZC channel identifier. ++ * Returns: ++ * Requires: ++ * hIOMgr is valid ++ * uChnlId < DDMA_MAXZCPYCHNLS ++ * Ensures: ++ */ ++ extern void IO_DDZCClearChnlDesc(struct IO_MGR *hIOMgr, u32 uChnlId); ++ ++/* ++ * ======== IO_DDZCRequestChnl ======== ++ * Purpose: ++ * Request zero-copy channel transfer. Sets up SM descriptors and ++ * control fields in shared memory. ++ * Parameters: ++ * hIOMgr: Handle to a I/O manager. ++ * pChnl: Ptr to channel object ++ * pChirp: Ptr to channel i/o request packet. ++ * Returns: ++ * Requires: ++ * pChnl != NULL ++ * pChnl->cIOReqs > 0 ++ * pChirp != NULL ++ * Ensures: ++ */ ++ extern void IO_DDZCRequestChnl(struct IO_MGR *hIOMgr, ++ struct CHNL_OBJECT *pChnl, ++ struct CHNL_IRP *pChirp, ++ OUT u16 *pwMbVal); ++ ++/* ++ * ======== IO_SHMsetting ======== ++ * Purpose: ++ * Sets the shared memory setting ++ * Parameters: ++ * hIOMgr: Handle to a I/O manager. ++ * desc: Shared memory type ++ * pArgs: Ptr to SHM setting ++ * Returns: ++ * Requires: ++ * hIOMgr != NULL ++ * pArgs != NULL ++ * Ensures: ++ */ ++ extern DSP_STATUS IO_SHMsetting(IN struct IO_MGR *hIOMgr, ++ IN enum SHM_DESCTYPE desc, ++ IN void *pArgs); ++ ++/* ++ * Misc functions for the CHNL_IO shared memory library: ++ */ ++ ++/* Maximum channel bufsize that can be used. */ ++ extern u32 IO_BufSize(struct IO_MGR *hIOMgr); ++ ++ extern u32 IO_ReadValue(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 dwDSPAddr); ++ ++ extern void IO_WriteValue(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 dwDSPAddr, u32 dwValue); ++ ++ extern u32 IO_ReadValueLong(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 dwDSPAddr); ++ ++ extern void IO_WriteValueLong(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 dwDSPAddr, u32 dwValue); ++ ++ extern void IO_OrSetValue(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 dwDSPAddr, u32 dwValue); ++ ++ extern void IO_AndSetValue(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 dwDSPAddr, u32 dwValue); ++ ++ extern void IO_IntrDSP2(IN struct IO_MGR *pIOMgr, IN u16 wMbVal); ++ ++ extern void IO_SM_init(void); ++ ++/* ++ * ========PrintDspTraceBuffer ======== ++ * Print DSP tracebuffer. ++ */ ++ extern DSP_STATUS PrintDspTraceBuffer(struct WMD_DEV_CONTEXT ++ *hWmdContext); ++ ++#endif /* IOSM_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/iodefs.h b/arch/arm/plat-omap/include/dspbridge/iodefs.h +new file mode 100644 +index 0000000..f45890a +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/iodefs.h +@@ -0,0 +1,45 @@ ++/* ++ * iodefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== iodefs.h ======== ++ * Description: ++ * System-wide channel objects and constants. ++ * ++ *! Revision History: ++ *! ================ ++ *! 07-Nov-2000 jeh Created. ++ */ ++ ++#ifndef IODEFS_ ++#define IODEFS_ ++ ++#define IO_MAXIRQ 0xff /* Arbitrarily large number. */ ++ ++/* IO Objects: */ ++ struct IO_MGR; ++ ++/* IO manager attributes: */ ++ struct IO_ATTRS { ++ u8 bIRQ; /* Channel's I/O IRQ number. */ ++ bool fShared; /* TRUE if the IRQ is shareable. */ ++ u32 uWordSize; /* DSP Word size. */ ++ u32 dwSMBase; /* Physical base address of shared memory. */ ++ u32 uSMLength; /* Size (in bytes) of shared memory. */ ++ } ; ++ ++#endif /* IODEFS_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/kfile.h b/arch/arm/plat-omap/include/dspbridge/kfile.h +new file mode 100644 +index 0000000..23c89b0 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/kfile.h +@@ -0,0 +1,216 @@ ++/* ++ * kfile.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== kfile.h ======== ++ * Purpose: ++ * Provide file I/O management capabilities. ++ * ++ * Public Functions: ++ * KFILE_Close ++ * KFILE_Exit ++ * KFILE_Init ++ * KFILE_Open ++ * KFILE_Read ++ * KFILE_Seek ++ * KFILE_Tell ++ * KFILE_Write ++ * ++ * Notes: ++ * The KFILE module is not like most of the other DSP/BIOS Bridge modules ++ * in that it doesn't return WSX_STATUS type values. Rather, it's ++ * prototypes are meant to match the stdio file prototypes ++ * (ie, fopen, fclose, etc.). ++ * ++ *! Revision History ++ *! ================ ++ *! 29-Oct-1999 kc: Clean up for code review. ++ *! 07-Jan-1998 cr: Clean up for code review. ++ *! 15-Aug-1997 cr: Added E_KFILE_ERROR for general error condition. ++ *! 04-Aug-1997 cr: Added explicit CDECL descriptions. ++ *! 11-Nov-1996 cr: Implemented changes based on code review. ++ *! 05-Nov-1996 cr: Cleaned up for code review. ++ *! 29-May-1996 gp: Added requirement that size != 0 in _Write() and _Read(). ++ *! 28-May-1996 mg: Changed return values for Read/Write. ++ *! 14-Dec-1995 cr: Created. ++ */ ++ ++#ifndef KFILE_ ++#define KFILE_ ++ ++/* ++ * Constants for KFILE_Seek. Note that these MUST be the same definitions as ++ * those defined for fseek. ++ */ ++#define KFILE_SEEK_SET 0x00 /* seek from beginning of file */ ++#define KFILE_SEEK_CUR 0x01 /* seek from current position */ ++#define KFILE_SEEK_END 0x02 /* seek from end of file */ ++ ++ struct KFILE_FileObj; ++ ++/* ++ * ======== KFILE_Close ======== ++ * Purpose: ++ * This function closes a file's stream. ++ * Parameters: ++ * hFile: Handle of the file stream returned by KFILE_Open. ++ * Returns: ++ * E_KFILE_INVALIDHANDLE: bad handle. ++ * 0: success. ++ * E_KFILE_ERROR: unable to close specified handle. ++ * Requires: ++ * KFILE initialized. ++ * Ensures: ++ */ ++ extern s32 KFILE_Close(IN struct KFILE_FileObj *hFile); ++ ++/* ++ * ======== KFILE_Exit ======== ++ * Purpose: ++ * Discontinue usage of module; free resources when reference count ++ * reaches 0. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * KFILE initialized. ++ * Ensures: ++ * Resources used by module are freed when cRef reaches zero. ++ */ ++ extern void KFILE_Exit(void); ++ ++/* ++ * ======== KFILE_Init ======== ++ * Purpose: ++ * Initializes private state of KFILE module. ++ * Parameters: ++ * Returns: ++ * TRUE if success, else FALSE. ++ * Requires: ++ * Ensures: ++ * KFILE initialized. ++ */ ++ extern bool KFILE_Init(void); ++ ++/* ++ * ======== KFILE_Open ======== ++ * Purpose: ++ * Opens a file for use. ++ * Parameters: ++ * pszFileName: Full path to name of the file to open. ++ * pszMode: String containing open status. Only the first ++ * character of the string is examined, for either ++ * "r" (read) or "w" (write) mode. ++ * Returns: ++ * A valid file handle if success, else NULL. ++ * Requires: ++ * - KFILE initialized. ++ * - pszMode != NULL. ++ * - pszFileName != NULL. ++ * Ensures: ++ */ ++ extern struct KFILE_FileObj *KFILE_Open(IN CONST char *filename, ++ IN CONST char *mode); ++ ++/* ++ * ======== KFILE_Read ======== ++ * Purpose: ++ * This function reads a specified number of bytes into a buffer. ++ * Parameters: ++ * pBuffer: Array to which the file data is copied. ++ * cSize: Number of characters in each object. ++ * cCount: Number of objects to read in. ++ * hFile: Handle of the file stream returned by KFILE_Open. ++ * Returns: ++ * E_KFILE_INVALIDHANDLE: bad file handle. ++ * E_KFILE_ERROR: general failure. ++ * > 0: success; # of objects read from file. ++ * Requires: ++ * KFILE initialized. ++ * pBuffer is a valid pointer. ++ * Ensures: ++ */ ++ extern s32 KFILE_Read(OUT void __user*buffer, ++ IN s32 size, IN s32 count, ++ IN struct KFILE_FileObj *hFile); ++ ++/* ++ * ======== KFILE_Seek ======== ++ * Purpose: ++ * This function sets the file position indicator. NOTE: we don't ++ * support seeking beyond the boundaries of a file. ++ * Parameters: ++ * hFile: Handle of the file stream returned by KFILE_Open. ++ * offset: Number of bytes from the origin to move. ++ * origin: File reference point, one of the following values: ++ * KFILE_SEEK_SET: Seek from beginning of file. ++ * KFILE_SEEK_CUR: Seek from current position. ++ * KFILE_SEEK_END: Seek from end of file. ++ * Returns: ++ * 0: success. ++ * E_KFILE_INVALIDHANDLE: bad handle. ++ * E_KFILE_BADORIGIN: invalid origin paramater. ++ * E_KFILE_ERROR: general failure. ++ * Requires: ++ * KFILE initialized. ++ * Ensures: ++ */ ++ extern s32 KFILE_Seek(IN struct KFILE_FileObj *hFile, ++ IN s32 offset, IN s32 origin); ++ ++/* ++ * ======== KFILE_Tell ======== ++ * Purpose: ++ * This function reports the current value of the position indicator. ++ * Parameters: ++ * hFile: Handle of the file stream returned by KFILE_Open. ++ * Return value: ++ * > 0: success; returns # of bytes the position indicator is from ++ * beginning of file. ++ * E_KFILE_ERROR: general failure. ++ * E_KFILE_INVALIDHANDLE: bad file handle. ++ * Requires: ++ * KFILE initialized. ++ * Ensures: ++ */ ++ extern s32 KFILE_Tell(IN struct KFILE_FileObj *hFile); ++ ++/* ++ * ======== KFILE_Write ======== ++ * Purpose: ++ * This function writes a number of objects to the stream. ++ * Parameters: ++ * pBuffer: Array from which the file data is written. ++ * cSize: Number of characters in each object. ++ * cCount: Number of objects to write out. ++ * hFile: Handle of the file stream returned by KFILE_Open. ++ * Returns: ++ * E_KFILE_INVALIDHANDLE: bad file handle. ++ * E_KFILE_ERROR: general failure. ++ * > 0: success; # of objects written to file. ++ * Requires: ++ * KFILE initialized. ++ * pBuffer != NULL. ++ * Postcondition: ++ * The file position indicator is advanced by the number of ++ * characters written. ++ */ ++ extern s32 KFILE_Write(OUT void *buffer, ++ IN s32 size, ++ IN s32 count, ++ IN struct KFILE_FileObj *hFile); ++ ++#endif /* KFILE_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/ldr.h b/arch/arm/plat-omap/include/dspbridge/ldr.h +new file mode 100644 +index 0000000..7e13c93 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/ldr.h +@@ -0,0 +1,51 @@ ++/* ++ * ldr.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== ldr.h ======== ++ * Purpose: ++ * Provide module loading services and symbol export services. ++ * ++ * Public Functions: ++ * LDR_Exit ++ * LDR_FreeModule ++ * LDR_GetProcAddress ++ * LDR_Init ++ * LDR_LoadModule ++ * ++ * Notes: ++ * This service is meant to be used by modules of the DSP/BIOS Bridge ++ * class driver. ++ * ++ *! Revision History: ++ *! ================ ++ *! 22-Nov-1999 kc: Changes from code review. ++ *! 12-Nov-1999 kc: Removed declaration of unused loader object. ++ *! 29-Oct-1999 kc: Cleaned up for code review. ++ *! 12-Jan-1998 cr: Cleaned up for code review. ++ *! 04-Aug-1997 cr: Added explicit CDECL identifiers. ++ *! 11-Nov-1996 cr: Cleaned up for code review. ++ *! 16-May-1996 gp: Created. ++ */ ++ ++#ifndef LDR_ ++#define LDR_ ++ ++/* Loader objects: */ ++ struct LDR_MODULE; ++ ++#endif /* LDR_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/list.h b/arch/arm/plat-omap/include/dspbridge/list.h +new file mode 100644 +index 0000000..2e3f995 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/list.h +@@ -0,0 +1,296 @@ ++/* ++ * list.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== list.h ======== ++ * Purpose: ++ * Declarations of list management control structures and definitions ++ * of inline list management functions. ++ * ++ * Public Functions: ++ * LST_Create ++ * LST_Delete ++ * LST_Exit ++ * LST_First ++ * LST_GetHead ++ * LST_InitElem ++ * LST_Init ++ * LST_InsertBefore ++ * LST_IsEmpty ++ * LST_Next ++ * LST_PutTail ++ * LST_RemoveElem ++ * ++ * Notes: ++ * ++ *! Revision History ++ *! ================ ++ *! 10-Aug-2000 ag: Added LST_InsertBefore(). ++ *! 29-Oct-1999 kc: Cleaned up for code review. ++ *! 16-Aug-1997 cr: added explicit identifiers. ++ *! 10-Aug-1996 gp: Acquired from SMM for WinSPOX v.1.1; renamed identifiers. ++ *! 21-Oct-1994 dh4: Cleaned / commented for code review. ++ *! 08-Jun-1994 dh4: Converted to SPM (added extern "C"). ++ */ ++ ++#ifndef LIST_ ++#define LIST_ ++ ++#include ++ ++#define LST_IsEmpty(l) (((l)->head.next == &(l)->head)) ++ ++ struct LST_ELEM { ++ struct LST_ELEM *next; ++ struct LST_ELEM *prev; ++ struct LST_ELEM *self; ++ } ; ++ ++ struct LST_LIST { ++ struct LST_ELEM head; ++ } ; ++ ++/* ++ * ======== LST_Create ======== ++ * Purpose: ++ * Allocates and initializes a circular list. ++ * Details: ++ * Uses portable MEM_Calloc() function to allocate a list containing ++ * a single element and initializes that element to indicate that it ++ * is the "end of the list" (i.e., the list is empty). ++ * An empty list is indicated by the "next" pointer in the element ++ * at the head of the list pointing to the head of the list, itself. ++ * Parameters: ++ * Returns: ++ * Pointer to beginning of created list (success) ++ * NULL --> Allocation failed ++ * Requires: ++ * LST initialized. ++ * Ensures: ++ * Notes: ++ * The created list contains a single element. This element is the ++ * "empty" element, because its "next" and "prev" pointers point at ++ * the same location (the element itself). ++ */ ++ extern struct LST_LIST *LST_Create(void); ++ ++/* ++ * ======== LST_Delete ======== ++ * Purpose: ++ * Removes a list by freeing its control structure's memory space. ++ * Details: ++ * Uses portable MEM_Free() function to deallocate the memory ++ * block pointed at by the input parameter. ++ * Parameters: ++ * pList: Pointer to list control structure of list to be deleted ++ * Returns: ++ * Void ++ * Requires: ++ * - LST initialized. ++ * - pList != NULL. ++ * Ensures: ++ * Notes: ++ * Must ONLY be used for empty lists, because it does not walk the ++ * chain of list elements. Calling this function on a non-empty list ++ * will cause a memory leak. ++ */ ++ extern void LST_Delete(IN struct LST_LIST *pList); ++ ++/* ++ * ======== LST_Exit ======== ++ * Purpose: ++ * Discontinue usage of module; free resources when reference count ++ * reaches 0. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * LST initialized. ++ * Ensures: ++ * Resources used by module are freed when cRef reaches zero. ++ */ ++ extern void LST_Exit(void); ++ ++/* ++ * ======== LST_First ======== ++ * Purpose: ++ * Returns a pointer to the first element of the list, or NULL if the list ++ * is empty. ++ * Parameters: ++ * pList: Pointer to list control structure. ++ * Returns: ++ * Pointer to first list element, or NULL. ++ * Requires: ++ * - LST initialized. ++ * - pList != NULL. ++ * Ensures: ++ */ ++ extern struct LST_ELEM *LST_First(IN struct LST_LIST *pList); ++ ++/* ++ * ======== LST_GetHead ======== ++ * Purpose: ++ * Pops the head off the list and returns a pointer to it. ++ * Details: ++ * If the list is empty, returns NULL. ++ * Else, removes the element at the head of the list, making the next ++ * element the head of the list. ++ * The head is removed by making the tail element of the list point its ++ * "next" pointer at the next element after the head, and by making the ++ * "prev" pointer of the next element after the head point at the tail ++ * element. So the next element after the head becomes the new head of ++ * the list. ++ * Parameters: ++ * pList: Pointer to list control structure of list whose head ++ * element is to be removed ++ * Returns: ++ * Pointer to element that was at the head of the list (success) ++ * NULL No elements in list ++ * Requires: ++ * - head.self must be correctly set to &head. ++ * - LST initialized. ++ * - pList != NULL. ++ * Ensures: ++ * Notes: ++ * Because the tail of the list points forward (its "next" pointer) to ++ * the head of the list, and the head of the list points backward (its ++ * "prev" pointer) to the tail of the list, this list is circular. ++ */ ++ extern struct LST_ELEM *LST_GetHead(IN struct LST_LIST *pList); ++ ++/* ++ * ======== LST_Init ======== ++ * Purpose: ++ * Initializes private state of LST module. ++ * Parameters: ++ * Returns: ++ * TRUE if initialized; FALSE otherwise. ++ * Requires: ++ * Ensures: ++ * LST initialized. ++ */ ++ extern bool LST_Init(void); ++ ++/* ++ * ======== LST_InitElem ======== ++ * Purpose: ++ * Initializes a list element to default (cleared) values ++ * Details: ++ * Parameters: ++ * pElem: Pointer to list element to be reset ++ * Returns: ++ * Requires: ++ * LST initialized. ++ * Ensures: ++ * Notes: ++ * This function must not be called to "reset" an element in the middle ++ * of a list chain -- that would break the chain. ++ * ++ */ ++ extern void LST_InitElem(IN struct LST_ELEM *pListElem); ++ ++/* ++ * ======== LST_InsertBefore ======== ++ * Purpose: ++ * Insert the element before the existing element. ++ * Parameters: ++ * pList: Pointer to list control structure. ++ * pElem: Pointer to element in list to insert. ++ * pElemExisting: Pointer to existing list element. ++ * Returns: ++ * Requires: ++ * - LST initialized. ++ * - pList != NULL. ++ * - pElem != NULL. ++ * - pElemExisting != NULL. ++ * Ensures: ++ */ ++ extern void LST_InsertBefore(IN struct LST_LIST *pList, ++ IN struct LST_ELEM *pElem, ++ IN struct LST_ELEM *pElemExisting); ++ ++/* ++ * ======== LST_Next ======== ++ * Purpose: ++ * Returns a pointer to the next element of the list, or NULL if the next ++ * element is the head of the list or the list is empty. ++ * Parameters: ++ * pList: Pointer to list control structure. ++ * pCurElem: Pointer to element in list to remove. ++ * Returns: ++ * Pointer to list element, or NULL. ++ * Requires: ++ * - LST initialized. ++ * - pList != NULL. ++ * - pCurElem != NULL. ++ * Ensures: ++ */ ++ extern struct LST_ELEM *LST_Next(IN struct LST_LIST *pList, ++ IN struct LST_ELEM *pCurElem); ++ ++/* ++ * ======== LST_PutTail ======== ++ * Purpose: ++ * Adds the specified element to the tail of the list ++ * Details: ++ * Sets new element's "prev" pointer to the address previously held by ++ * the head element's prev pointer. This is the previous tail member of ++ * the list. ++ * Sets the new head's prev pointer to the address of the element. ++ * Sets next pointer of the previous tail member of the list to point to ++ * the new element (rather than the head, which it had been pointing at). ++ * Sets new element's next pointer to the address of the head element. ++ * Sets head's prev pointer to the address of the new element. ++ * Parameters: ++ * pList: Pointer to list control structure to which *pElem will be ++ * added ++ * pElem: Pointer to list element to be added ++ * Returns: ++ * Void ++ * Requires: ++ * *pElem and *pList must both exist. ++ * pElem->self = pElem before pElem is passed to this function. ++ * LST initialized. ++ * Ensures: ++ * Notes: ++ * Because the tail is always "just before" the head of the list (the ++ * tail's "next" pointer points at the head of the list, and the head's ++ * "prev" pointer points at the tail of the list), the list is circular. ++ * Warning: if pElem->self is not set beforehand, LST_GetHead() will ++ * return an erroneous pointer when it is called for this element. ++ */ ++ extern void LST_PutTail(IN struct LST_LIST *pList, ++ IN struct LST_ELEM *pListElem); ++ ++/* ++ * ======== LST_RemoveElem ======== ++ * Purpose: ++ * Removes (unlinks) the given element from the list, if the list is not ++ * empty. Does not free the list element. ++ * Parameters: ++ * pList: Pointer to list control structure. ++ * pCurElem: Pointer to element in list to remove. ++ * Returns: ++ * Requires: ++ * - LST initialized. ++ * - pList != NULL. ++ * - pCurElem != NULL. ++ * Ensures: ++ */ ++extern void LST_RemoveElem(IN struct LST_LIST *pList, ++ IN struct LST_ELEM *pCurElem); ++ ++#endif /* LIST_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/mbx_sh.h b/arch/arm/plat-omap/include/dspbridge/mbx_sh.h +new file mode 100644 +index 0000000..be0909e +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/mbx_sh.h +@@ -0,0 +1,213 @@ ++/* ++ * mbx_sh.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== mbx_sh.h ======== ++ * Definitions for shared mailbox cmd/data values.(used on both ++ * the GPP and DSP sides). ++ * ++ * Bridge usage of OMAP mailbox 1 is determined by the "class" of the ++ * mailbox interrupt's cmd value received. The class value are defined ++ * as a bit (10 thru 15) being set. ++ * ++ * Note: Only 16 bits of each is used. Other 16 bit data reg available. ++ * ++ * 16 bit Mbx bit defns: ++ * ++ * A). Exception/Error handling (Module DEH) : class = 0. ++ * ++ * 15 10 0 ++ * --------------------------------- ++ * |0|0|0|0|0|0|x|x|x|x|x|x|x|x|x|x| ++ * --------------------------------- ++ * | (class) | (module specific) | ++ * ++ * ++ * ++ * B: DSP-DMA link driver channels (DDMA) : class = 1. ++ * ++ * 15 10 0 ++ * --------------------------------- ++ * |0|0|0|0|0|1|b|b|b|b|b|c|c|c|c|c| ++ * --------------------------------- ++ * | (class) | (module specific) | ++ * ++ * where b -> buffer index (32 DDMA buffers/chnl max) ++ * c -> channel Id (32 DDMA chnls max) ++ * ++ * ++ * ++ * ++ * C: Proc-copy link driver channels (PCPY) : class = 2. ++ * ++ * 15 10 0 ++ * --------------------------------- ++ * |0|0|0|0|1|0|x|x|x|x|x|x|x|x|x|x| ++ * --------------------------------- ++ * | (class) | (module specific) | ++ * ++ * ++ * D: Zero-copy link driver channels (DDZC) : class = 4. ++ * ++ * 15 10 0 ++ * --------------------------------- ++ * |0|0|0|1|0|0|x|x|x|x|x|c|c|c|c|c| ++ * --------------------------------- ++ * | (class) | (module specific) | ++ * ++ * where x -> not used ++ * c -> channel Id (32 ZCPY chnls max) ++ * ++ * ++ * E: Power management : class = 8. ++ * ++ * 15 10 0 ++ * --------------------------------- ++ * |0|0|1|0|0|0|x|x|x|x|x|c|c|c|c|c| ++ ++ * 0010 00xx xxxc cccc ++ * 0010 00nn pppp qqqq ++ * nn: ++ * 00 = reserved ++ * 01 = pwr state change ++ * 10 = opp pre-change ++ * 11 = opp post-change ++ * ++ * if nn = pwr state change: ++ * pppp = don't care ++ * qqqq: ++ * 0010 = hibernate ++ * 0010 0001 0000 0010 ++ * 0110 = retention ++ * 0010 0001 0000 0110 ++ * others reserved ++ * ++ * if nn = opp pre-change: ++ * pppp = current opp ++ * qqqq = next opp ++ * ++ * if nn = opp post-change: ++ * pppp = prev opp ++ * qqqq = current opp ++ * ++ * --------------------------------- ++ * | (class) | (module specific) | ++ * ++ * where x -> not used ++ * c -> Power management command ++ * ++ * ++ * ++ *! Revision History: ++ *! ================ ++ *! 19-Sep-2002 mr Added DEH reset const ++ *! 24-Apr-2002 sg Added more PM commands. ++ *! 04-Mar-2002 gv Added MBX_PM_CLASS ++ *! 22-Jan-2002 ag Bug fix in MBX_SETZCPYVAL(x) macro. ++ *! 21-Dec-2001 ag Added bit masks defns. ++ *! 17-Dec-2001 ag: created. ++ */ ++ ++#ifndef _MBX_SH_H ++#define _MBX_SH_H ++ ++#define MBX_CLASS_MSK 0xFC00 /* Class bits are 10 thru 15 */ ++#define MBX_VALUE_MSK 0x03FF /* Value is 0 thru 9 */ ++ ++#define MBX_DEH_CLASS 0x0000 /* DEH owns Mbx INTR */ ++#define MBX_DDMA_CLASS 0x0400 /* DSP-DMA link drvr chnls owns INTR */ ++#define MBX_PCPY_CLASS 0x0800 /* PROC-COPY " */ ++#define MBX_ZCPY_CLASS 0x1000 /* ZERO-COPY " */ ++#define MBX_PM_CLASS 0x2000 /* Power Management */ ++#define MBX_DBG_CLASS 0x4000 /* For debugging purpose */ ++ ++/* ++ * Exception Handler codes ++ * Magic code used to determine if DSP signaled exception. ++ */ ++#define MBX_DEH_BASE 0x0 ++#define MBX_DEH_USERS_BASE 0x100 /* 256 */ ++#define MBX_DEH_LIMIT 0x3FF /* 1023 */ ++#define MBX_DEH_RESET 0x101 /* DSP RESET (DEH) */ ++#define MBX_DEH_EMMU 0X103 /*DSP MMU FAULT RECOVERY*/ ++ ++/* ++ * Link driver command/status codes. ++ */ ++/* DSP-DMA */ ++#define MBX_DDMA_NUMCHNLBITS 5 /* # chnl Id: # bits available */ ++#define MBX_DDMA_CHNLSHIFT 0 /* # of bits to shift */ ++#define MBX_DDMA_CHNLMSK 0x01F /* bits 0 thru 4 */ ++ ++#define MBX_DDMA_NUMBUFBITS 5 /* buffer index: # of bits avail */ ++#define MBX_DDMA_BUFSHIFT (MBX_DDMA_NUMCHNLBITS + MBX_DDMA_CHNLSHIFT) ++#define MBX_DDMA_BUFMSK 0x3E0 /* bits 5 thru 9 */ ++ ++/* Zero-Copy */ ++#define MBX_ZCPY_NUMCHNLBITS 5 /* # chnl Id: # bits available */ ++#define MBX_ZCPY_CHNLSHIFT 0 /* # of bits to shift */ ++#define MBX_ZCPY_CHNLMSK 0x01F /* bits 0 thru 4 */ ++ ++/* Power Management Commands */ ++#define MBX_PM_DSPIDLE (MBX_PM_CLASS + 0x0) ++#define MBX_PM_DSPWAKEUP (MBX_PM_CLASS + 0x1) ++#define MBX_PM_EMERGENCYSLEEP (MBX_PM_CLASS + 0x2) ++#define MBX_PM_SLEEPUNTILRESTART (MBX_PM_CLASS + 0x3) ++#define MBX_PM_DSPGLOBALIDLE_OFF (MBX_PM_CLASS + 0x4) ++#define MBX_PM_DSPGLOBALIDLE_ON (MBX_PM_CLASS + 0x5) ++#define MBX_PM_SETPOINT_PRENOTIFY (MBX_PM_CLASS + 0x6) ++#define MBX_PM_SETPOINT_POSTNOTIFY (MBX_PM_CLASS + 0x7) ++#define MBX_PM_DSPRETN (MBX_PM_CLASS + 0x8) ++#define MBX_PM_DSPRETENTION (MBX_PM_CLASS + 0x8) ++#define MBX_PM_DSPHIBERNATE (MBX_PM_CLASS + 0x9) ++#define MBX_PM_HIBERNATE_EN (MBX_PM_CLASS + 0xA) ++#define MBX_PM_OPP_REQ (MBX_PM_CLASS + 0xB) ++#define MBX_PM_OPP_CHG (MBX_PM_CLASS + 0xC) ++ ++#define MBX_PM_TYPE_MASK 0x0300 ++#define MBX_PM_TYPE_PWR_CHNG 0x0100 ++#define MBX_PM_TYPE_OPP_PRECHNG 0x0200 ++#define MBX_PM_TYPE_OPP_POSTCHNG 0x0300 ++#define MBX_PM_TYPE_OPP_MASK 0x0300 ++#define MBX_PM_OPP_PRECHNG (MBX_PM_CLASS | MBX_PM_TYPE_OPP_PRECHNG) ++/* DSP to MPU */ ++#define MBX_PM_OPP_CHNG(OPP) (MBX_PM_CLASS | MBX_PM_TYPE_OPP_PRECHNG | (OPP)) ++#define MBX_PM_RET (MBX_PM_CLASS | MBX_PM_TYPE_PWR_CHNG | 0x0006) ++#define MBX_PM_HIB (MBX_PM_CLASS | MBX_PM_TYPE_PWR_CHNG | 0x0002) ++#define MBX_PM_OPP_1 0 ++#define MBX_PM_OPP_2 1 ++#define MBX_PM_OPP_3 2 ++#define MBX_PM_OPP_4 3 ++#define MBX_OLDOPP_EXTRACT(OPPMSG) ((0x00F0 & (OPPMSG)) >> 4) ++#define MBX_NEWOPP_EXTRACT(OPPMSG) (0x000F & (OPPMSG)) ++#define MBX_PREVOPP_EXTRACT(OPPMSG) ((0x00F0 & (OPPMSG)) >> 4) ++#define MBX_CUROPP_EXTRACT(OPPMSG) (0x000F & (OPPMSG)) ++ ++/* Bridge Debug Commands */ ++#define MBX_DBG_SYSPRINTF (MBX_DBG_CLASS + 0x0) ++ ++/* ++ * Useful macros ++ */ ++/* DSP-DMA channel */ ++#define MBX_SETDDMAVAL(x, y) (MBX_DDMA_CLASS | (x << MBX_DDMA_BUFSHIFT) | \ ++ (y << MBX_DDMA_CHNLSHIFT)) ++ ++/* Zero-Copy channel */ ++#define MBX_SETZCPYVAL(x) (MBX_ZCPY_CLASS | (x << MBX_ZCPY_CHNLSHIFT)) ++ ++#endif /* _MBX_SH_H */ +diff --git a/arch/arm/plat-omap/include/dspbridge/mem.h b/arch/arm/plat-omap/include/dspbridge/mem.h +new file mode 100644 +index 0000000..535ac3a +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/mem.h +@@ -0,0 +1,340 @@ ++/* ++ * mem.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== mem.h ======== ++ * Purpose: ++ * Memory management and address mapping services for the DSP/BIOS Bridge ++ * class driver and mini-driver. ++ * ++ * Public Functions: ++ * MEM_Alloc ++ * MEM_AllocObject ++ * MEM_AllocPhysMem ++ * MEM_Calloc ++ * MEM_Exit ++ * MEM_FlushCache ++ * MEM_Free ++ * MEM_FreeObject ++ * MEM_FreePhysMem ++ * MEM_GetNumPages ++ * MEM_Init ++ * MEM_IsValidHandle ++ * MEM_LinearAddress ++ * MEM_PageLock ++ * MEM_PageUnlock ++ * MEM_UnMapLinearAddress ++ * MEM_VirtualToPhysical ++ * ++ * Notes: ++ * ++ *! Revision History: ++ *! ================ ++ *! 19-Apr-2004 sb: Added Alloc/Free PhysMem, FlushCache, VirtualToPhysical ++ *! 01-Sep-2001 ag: Cleaned up notes for MEM_LinearAddress() does not ++ *! require phys address to be page aligned! ++ *! 02-Dec-1999 rr: stdwin.h included for retail build ++ *! 12-Nov-1999 kc: Added warning about use of MEM_LinearAddress. ++ *! 29-Oct-1999 kc: Cleaned up for code review. ++ *! 10-Aug-1999 kc: Based on wsx-c18. ++ *! 07-Jan-1998 gp: Added MEM_AllocUMB and MEM_UMBFree for User Mapped Buffers ++ *! used by WMD_CHNL. ++ *! 23-Dec-1997 cr: Code review cleanup, removed dead Ring 3 code. ++ *! 04-Aug-1997 cr: Added explicit CDECL identifiers. ++ *! 01-Nov-1996 gp: Updated based on code review. ++ *! 04-Sep-1996 gp: Added MEM_PageLock() and MEM_PageUnlock() services. ++ *! 14-Aug-1996 mg: Added MEM_GetPhysAddr() and MEM_GetNumPages() ++ *! 25-Jul-1996 gp: Added MEM_IsValidHandle() macro. ++ *! 10-May-1996 gp: Added MEM_Calloc(). ++ *! 25-Apr-1996 gp: Added MEM_PhysicalAddress() ++ *! 17-Apr-1996 gp: Added MEM_Exit function; updated to latest naming standard. ++ *! 08-Apr-1996 gp: Created. ++ */ ++ ++#ifndef MEM_ ++#define MEM_ ++ ++#include ++#include ++ ++/* ++ * ======== MEM_Alloc ======== ++ * Purpose: ++ * Allocate memory from the paged or non-paged pools. ++ * Parameters: ++ * cBytes: Number of bytes to allocate. ++ * type: Type of memory to allocate; one of: ++ * MEM_PAGED: Allocate from pageable memory. ++ * MEM_NONPAGED: Allocate from page locked memory. ++ * Returns: ++ * Pointer to a block of memory; ++ * NULL if memory couldn't be allocated, if cBytes == 0, or if type is ++ * not one of MEM_PAGED or MEM_NONPAGED. ++ * Requires: ++ * MEM initialized. ++ * Ensures: ++ * The returned pointer, if not NULL, points to a valid memory block of ++ * the size requested. ++ */ ++ extern void *MEM_Alloc(IN u32 cBytes, IN enum MEM_POOLATTRS type); ++ ++/* ++ * ======== MEM_AllocObject ======== ++ * Purpose: ++ * Allocate an object, and set it's signature. ++ * Parameters: ++ * pObj: Pointer to the new object. ++ * Obj: Type of the object to allocate. ++ * Signature: Magic field value. Must be non-zero. ++ * Returns: ++ * Requires: ++ * Same requirements as MEM_Calloc(); and ++ * The object structure has a dwSignature field. The compiler ensures ++ * this requirement. ++ * Ensures: ++ * A subsequent call to MEM_IsValidHandle() will succeed for this object. ++ */ ++#define MEM_AllocObject(pObj, Obj, Signature) \ ++{ \ ++ pObj = MEM_Calloc(sizeof(Obj), MEM_NONPAGED); \ ++ if (pObj) { \ ++ pObj->dwSignature = Signature; \ ++ } \ ++} ++ ++/* ======== MEM_AllocPhysMem ======== ++ * Purpose: ++ * Allocate physically contiguous, uncached memory ++ * Parameters: ++ * cBytes: Number of bytes to allocate. ++ * ulAlign: Alignment Mask. ++ * pPhysicalAddress: Physical address of allocated memory. ++ * Returns: ++ * Pointer to a block of memory; ++ * NULL if memory couldn't be allocated, or if cBytes == 0. ++ * Requires: ++ * MEM initialized. ++ * Ensures: ++ * The returned pointer, if not NULL, points to a valid memory block of ++ * the size requested. Returned physical address refers to physical ++ * location of memory. ++ */ ++ extern void *MEM_AllocPhysMem(IN u32 cBytes, ++ IN u32 ulAlign, ++ OUT u32 *pPhysicalAddress); ++ ++/* ++ * ======== MEM_Calloc ======== ++ * Purpose: ++ * Allocate zero-initialized memory from the paged or non-paged pools. ++ * Parameters: ++ * cBytes: Number of bytes to allocate. ++ * type: Type of memory to allocate; one of: ++ * MEM_PAGED: Allocate from pageable memory. ++ * MEM_NONPAGED: Allocate from page locked memory. ++ * Returns: ++ * Pointer to a block of zeroed memory; ++ * NULL if memory couldn't be allocated, if cBytes == 0, or if type is ++ * not one of MEM_PAGED or MEM_NONPAGED. ++ * Requires: ++ * MEM initialized. ++ * Ensures: ++ * The returned pointer, if not NULL, points to a valid memory block ++ * of the size requested. ++ */ ++ extern void *MEM_Calloc(IN u32 cBytes, IN enum MEM_POOLATTRS type); ++ ++/* ++ * ======== MEM_Exit ======== ++ * Purpose: ++ * Discontinue usage of module; free resources when reference count ++ * reaches 0. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * MEM is initialized. ++ * Ensures: ++ * Resources used by module are freed when cRef reaches zero. ++ */ ++ extern void MEM_Exit(void); ++ ++/* ++ * ======== MEM_FlushCache ======== ++ * Purpose: ++ * Performs system cache sync with discard ++ * Parameters: ++ * pMemBuf: Pointer to memory region to be flushed. ++ * pMemBuf: Size of the memory region to be flushed. ++ * Returns: ++ * Requires: ++ * MEM is initialized. ++ * Ensures: ++ * Cache is synchronized ++ */ ++ extern void MEM_FlushCache(void *pMemBuf, u32 cBytes, s32 FlushType); ++ ++/* ++ * ======== MEM_Free ======== ++ * Purpose: ++ * Free the given block of system memory. ++ * Parameters: ++ * pMemBuf: Pointer to memory allocated by MEM_Calloc/Alloc(). ++ * Returns: ++ * Requires: ++ * MEM initialized. ++ * pMemBuf is a valid memory address returned by MEM_Calloc/Alloc(). ++ * Ensures: ++ * pMemBuf is no longer a valid pointer to memory. ++ */ ++ extern void MEM_Free(IN void *pMemBuf); ++ ++/* ++ * ======== MEM_FreePhysMem ======== ++ * Purpose: ++ * Free the given block of physically contiguous memory. ++ * Parameters: ++ * pVirtualAddress: Pointer to virtual memory region allocated ++ * by MEM_AllocPhysMem(). ++ * pPhysicalAddress: Pointer to physical memory region allocated ++ * by MEM_AllocPhysMem(). ++ * cBytes: Size of the memory region allocated by MEM_AllocPhysMem(). ++ * Returns: ++ * Requires: ++ * MEM initialized. ++ * pVirtualAddress is a valid memory address returned by ++ * MEM_AllocPhysMem() ++ * Ensures: ++ * pVirtualAddress is no longer a valid pointer to memory. ++ */ ++ extern void MEM_FreePhysMem(void *pVirtualAddress, ++ u32 pPhysicalAddress, u32 cBytes); ++ ++/* ++ * ======== MEM_FreeObject ======== ++ * Purpose: ++ * Utility macro to invalidate an object's signature, and deallocate it. ++ * Parameters: ++ * pObj: Pointer to the object to free. ++ * Returns: ++ * Requires: ++ * Same requirements as MEM_Free(). ++ * Ensures: ++ * A subsequent call to MEM_IsValidHandle() will fail for this object. ++ */ ++#define MEM_FreeObject(pObj) \ ++{ \ ++ pObj->dwSignature = 0x00; \ ++ MEM_Free(pObj); \ ++} ++ ++/* ++ * ======== MEM_GetNumPages ======== ++ * Purpose: ++ * Calculate the number of pages corresponding to the supplied buffer. ++ * Parameters: ++ * pAddr: Linear (virtual) address of the buffer. ++ * cBytes: Number of bytes in the buffer. ++ * Returns: ++ * Number of pages. ++ * Requires: ++ * MEM initialized. ++ * Ensures: ++ * If cBytes > 0, number of pages returned > 0. ++ */ ++ extern s32 MEM_GetNumPages(IN void *pAddr, IN u32 cBytes); ++ ++/* ++ * ======== MEM_Init ======== ++ * Purpose: ++ * Initializes private state of MEM module. ++ * Parameters: ++ * Returns: ++ * TRUE if initialized; FALSE if error occured. ++ * Requires: ++ * Ensures: ++ * MEM initialized. ++ */ ++ extern bool MEM_Init(void); ++ ++/* ++ * ======== MEM_IsValidHandle ======== ++ * Purpose: ++ * Validate the object handle. ++ * Parameters: ++ * hObj: Handle to object created with MEM_AllocObject(). ++ * Sig: Expected signature u32. ++ * Returns: ++ * TRUE if handle is valid; FALSE otherwise. ++ * Requires: ++ * The object structure has a dwSignature field. Ensured by compiler. ++ * Ensures: ++ */ ++#define MEM_IsValidHandle(hObj, Sig) \ ++ ((hObj != NULL) && (hObj->dwSignature == Sig)) ++ ++/* ++ * ======== MEM_LinearAddress ======== ++ * Purpose: ++ * Get the linear address corresponding to the given physical address. ++ * Parameters: ++ * pPhysAddr: Physical address to be mapped. ++ * cBytes: Number of bytes in physical range to map. ++ * Returns: ++ * The corresponding linear address, or NULL if unsuccessful. ++ * Requires: ++ * MEM initialized. ++ * Ensures: ++ * Notes: ++ * If valid linear address is returned, be sure to call ++ * MEM_UnmapLinearAddress(). ++ */ ++#define MEM_LinearAddress(pPhyAddr, cBytes) pPhyAddr ++ ++/* ++ * ======== MEM_UnmapLinearAddress ======== ++ * Purpose: ++ * Unmap the linear address mapped in MEM_LinearAddress. ++ * Parameters: ++ * pBaseAddr: Ptr to mapped memory (as returned by MEM_LinearAddress()). ++ * Returns: ++ * Requires: ++ * - MEM initialized. ++ * - pBaseAddr is a valid linear address mapped in MEM_LinearAddress. ++ * Ensures: ++ * - pBaseAddr no longer points to a valid linear address. ++ */ ++#define MEM_UnmapLinearAddress(pBaseAddr) ++ ++/* ++ * ======== MEM_ExtPhysPoolInit ======== ++ * Purpose: ++ * Uses the physical memory chunk passed for internal consitent memory ++ * allocations. ++ * physical address based on the page frame address. ++ * Parameters: ++ * poolPhysBase starting address of the physical memory pool. ++ * poolSize size of the physical memory pool. ++ * Returns: ++ * none. ++ * Requires: ++ * - MEM initialized. ++ * - valid physical address for the base and size > 0 ++ */ ++ extern void MEM_ExtPhysPoolInit(IN u32 poolPhysBase, ++ IN u32 poolSize); ++ ++#endif /* MEM_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/memdefs.h b/arch/arm/plat-omap/include/dspbridge/memdefs.h +new file mode 100644 +index 0000000..a5bb259 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/memdefs.h +@@ -0,0 +1,52 @@ ++/* ++ * memdefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== memdefs.h ======== ++ * Purpose: ++ * Global MEM constants and types, shared between WSX, WCD, and WMD. ++ * ++ *! Revision History: ++ *! ================ ++ *! 28-Aug-2001 ag: Added MEM_[SET][GET]VIRTUALSEGID. ++ *! 10-Aug-1999 kc: Based on wsx-c18. ++ *! 15-Nov-1996 gp: Renamed from wsxmem.h and moved to kwinos. ++ *! 21-Aug-1996 cr: Created from mem.h. ++ */ ++ ++#ifndef MEMDEFS_ ++#define MEMDEFS_ ++ ++/* Memory Pool Attributes: */ ++ enum MEM_POOLATTRS { ++ MEM_PAGED = 0, ++ MEM_NONPAGED = 1, ++ MEM_LARGEVIRTMEM = 2 ++ } ; ++ ++/* ++ * MEM_VIRTUALSEGID is used by Node & Strm to access virtual address space in ++ * the correct client process context. ++ */ ++#define MEM_SETVIRTUALSEGID 0x10000000 ++#define MEM_GETVIRTUALSEGID 0x20000000 ++#define MEM_MASKVIRTUALSEGID (MEM_SETVIRTUALSEGID | MEM_GETVIRTUALSEGID) ++ ++#define TO_VIRTUAL_UNCACHED(x) x ++#define INTREG_TO_VIRTUAL_UNCACHED(x) x ++ ++#endif /* MEMDEFS_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/mgr.h b/arch/arm/plat-omap/include/dspbridge/mgr.h +new file mode 100644 +index 0000000..24c4472 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/mgr.h +@@ -0,0 +1,234 @@ ++/* ++ * mgr.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== mgr.h ======== ++ * Description: ++ * This is the Class driver RM module interface. ++ * ++ * Public Functions: ++ * MGR_Create ++ * MGR_Destroy ++ * MGR_EnumNodeInfo ++ * MGR_EnumProcessorInfo ++ * MGR_Exit ++ * MGR_GetDCDHandle ++ * MGR_Init ++ * ++ * Notes: ++ * ++ *! Revision History: ++ *! ================ ++ *! 15-Oct-2002 kc: Removed legacy PERF definitions. ++ *! 11-Jul-2001 jeh Added CFG_HDEVNODE parameter to MGR_Create(). ++ *! 22-Nov-2000 kc: Added MGR_GetPerfData for acquiring PERF stats. ++ *! 03-Nov-2000 rr: Added MGR_GetDCDHandle. Modified after code review. ++ *! 25-Sep-2000 rr: Updated to Version 0.9 ++ *! 14-Aug-2000 rr: Cleaned up. ++ *! 07-Aug-2000 rr: MGR_Create does the job of Loading DCD Dll. ++ *! 27-Jul-2000 rr: Updated to ver 0.8 of DSPAPI(types). ++ *! 20-Jun-2000 rr: Created. ++ */ ++ ++#ifndef MGR_ ++#define MGR_ ++ ++#include ++ ++#define MAX_EVENTS 32 ++ ++/* ++ * ======== MGR_WaitForBridgeEvents ======== ++ * Purpose: ++ * Block on any Bridge event(s) ++ * Parameters: ++ * aNotifications : array of pointers to notification objects. ++ * uCount : number of elements in above array ++ * puIndex : index of signaled event object ++ * uTimeout : timeout interval in milliseocnds ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_ETIMEOUT : Wait timed out. *puIndex is undetermined. ++ * Details: ++ */ ++ ++ DSP_STATUS MGR_WaitForBridgeEvents(struct DSP_NOTIFICATION ++ **aNotifications, ++ u32 uCount, OUT u32 *puIndex, ++ u32 uTimeout); ++ ++/* ++ * ======== MGR_Create ======== ++ * Purpose: ++ * Creates the Manager Object. This is done during the driver loading. ++ * There is only one Manager Object in the DSP/BIOS Bridge. ++ * Parameters: ++ * phMgrObject: Location to store created MGR Object handle. ++ * hDevNode: Device object as known to Windows system. ++ * Returns: ++ * DSP_SOK: Success ++ * DSP_EMEMORY: Failed to Create the Object ++ * DSP_EFAIL: General Failure ++ * Requires: ++ * MGR Initialized (cRefs > 0 ) ++ * phMgrObject != NULL. ++ * Ensures: ++ * DSP_SOK: *phMgrObject is a valid MGR interface to the device. ++ * MGR Object stores the DCD Manager Handle. ++ * MGR Object stored in the Regsitry. ++ * !DSP_SOK: MGR Object not created ++ * Details: ++ * DCD Dll is loaded and MGR Object stores the handle of the DLL. ++ */ ++ extern DSP_STATUS MGR_Create(OUT struct MGR_OBJECT **hMgrObject, ++ struct CFG_DEVNODE *hDevNode); ++ ++/* ++ * ======== MGR_Destroy ======== ++ * Purpose: ++ * Destroys the MGR object. Called upon driver unloading. ++ * Parameters: ++ * hMgrObject: Handle to Manager object . ++ * Returns: ++ * DSP_SOK: Success. ++ * DCD Manager freed; MGR Object destroyed; ++ * MGR Object deleted from the Registry. ++ * DSP_EFAIL: Failed to destroy MGR Object ++ * Requires: ++ * MGR Initialized (cRefs > 0 ) ++ * hMgrObject is a valid MGR handle . ++ * Ensures: ++ * DSP_SOK: MGR Object destroyed and hMgrObject is Invalid MGR ++ * Handle. ++ */ ++ extern DSP_STATUS MGR_Destroy(struct MGR_OBJECT *hMgrObject); ++ ++/* ++ * ======== MGR_EnumNodeInfo ======== ++ * Purpose: ++ * Enumerate and get configuration information about nodes configured ++ * in the node database. ++ * Parameters: ++ * uNode: The node index (base 0). ++ * pNDBProps: Ptr to the DSP_NDBPROPS structure for output. ++ * uNDBPropsSize: Size of the DSP_NDBPROPS structure. ++ * puNumNodes: Location where the number of nodes configured ++ * in the database will be returned. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EINVALIDARG: Parameter uNode is > than the number of nodes. ++ * configutred in the system ++ * DSP_ECHANGEDURINGENUM: During Enumeration there has been a change in ++ * the number of nodes configured or in the ++ * the properties of the enumerated nodes. ++ * DSP_EFAIL: Failed to querry the Node Data Base ++ * Requires: ++ * pNDBPROPS is not null ++ * uNDBPropsSize >= sizeof(DSP_NDBPROPS) ++ * puNumNodes is not null ++ * MGR Initialized (cRefs > 0 ) ++ * Ensures: ++ * SUCCESS on successful retreival of data and *puNumNodes > 0 OR ++ * DSP_FAILED && *puNumNodes == 0. ++ * Details: ++ */ ++ extern DSP_STATUS MGR_EnumNodeInfo(u32 uNode, ++ OUT struct DSP_NDBPROPS *pNDBProps, ++ u32 uNDBPropsSize, ++ OUT u32 *puNumNodes); ++ ++/* ++ * ======== MGR_EnumProcessorInfo ======== ++ * Purpose: ++ * Enumerate and get configuration information about available DSP ++ * processors ++ * Parameters: ++ * uProcessor: The processor index (zero-based). ++ * pProcessorInfo: Ptr to the DSP_PROCESSORINFO structure . ++ * uProcessorInfoSize: Size of DSP_PROCESSORINFO structure. ++ * puNumProcs: Location where the number of DSPs configured ++ * in the database will be returned ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EINVALIDARG: Parameter uProcessor is > than the number of ++ * DSP Processors in the system. ++ * DSP_EFAIL: Failed to querry the Node Data Base ++ * Requires: ++ * pProcessorInfo is not null ++ * puNumProcs is not null ++ * uProcessorInfoSize >= sizeof(DSP_PROCESSORINFO) ++ * MGR Initialized (cRefs > 0 ) ++ * Ensures: ++ * SUCCESS on successful retreival of data and *puNumProcs > 0 OR ++ * DSP_FAILED && *puNumProcs == 0. ++ * Details: ++ */ ++ extern DSP_STATUS MGR_EnumProcessorInfo(u32 uProcessor, ++ OUT struct DSP_PROCESSORINFO * ++ pProcessorInfo, ++ u32 uProcessorInfoSize, ++ OUT u32 *puNumProcs); ++/* ++ * ======== MGR_Exit ======== ++ * Purpose: ++ * Decrement reference count, and free resources when reference count is ++ * 0. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * MGR is initialized. ++ * Ensures: ++ * When reference count == 0, MGR's private resources are freed. ++ */ ++ extern void MGR_Exit(void); ++ ++/* ++ * ======== MGR_GetDCDHandle ======== ++ * Purpose: ++ * Retrieves the MGR handle. Accessor Function ++ * Parameters: ++ * hMGRHandle: Handle to the Manager Object ++ * phDCDHandle: Ptr to receive the DCD Handle. ++ * Returns: ++ * DSP_SOK: Sucess ++ * DSP_EFAIL: Failure to get the Handle ++ * Requires: ++ * MGR is initialized. ++ * phDCDHandle != NULL ++ * Ensures: ++ * DSP_SOK and *phDCDHandle != NULL || ++ * DSP_EFAIL and *phDCDHandle == NULL ++ */ ++ extern DSP_STATUS MGR_GetDCDHandle(IN struct MGR_OBJECT ++ *hMGRHandle, ++ OUT u32 *phDCDHandle); ++ ++/* ++ * ======== MGR_Init ======== ++ * Purpose: ++ * Initialize MGR's private state, keeping a reference count on each ++ * call. Intializes the DCD. ++ * Parameters: ++ * Returns: ++ * TRUE if initialized; FALSE if error occured. ++ * Requires: ++ * Ensures: ++ * TRUE: A requirement for the other public MGR functions. ++ */ ++ extern bool MGR_Init(void); ++ ++#endif /* MGR_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/mgrpriv.h b/arch/arm/plat-omap/include/dspbridge/mgrpriv.h +new file mode 100644 +index 0000000..4a34086 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/mgrpriv.h +@@ -0,0 +1,55 @@ ++/* ++ * mgrpriv.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== mgrpriv.h ======== ++ * Description: ++ * Global MGR constants and types, shared by PROC, MGR, and WCD. ++ * ++ *! Revision History: ++ *! ================ ++ *! 29-July-2001 ag: added MGR_PROCESSOREXTINFO. ++ *! 05-July-2000 rr: Created ++ */ ++ ++#ifndef MGRPRIV_ ++#define MGRPRIV_ ++ ++/* ++ * OMAP1510 specific ++ */ ++#define MGR_MAXTLBENTRIES 32 ++ ++/* RM MGR Object */ ++ struct MGR_OBJECT; ++ ++ struct MGR_TLBENTRY { ++ u32 ulDspVirt; /* DSP virtual address */ ++ u32 ulGppPhys; /* GPP physical address */ ++ } ; ++ ++/* ++ * The DSP_PROCESSOREXTINFO structure describes additional extended ++ * capabilities of a DSP processor not exposed to user. ++ */ ++ struct MGR_PROCESSOREXTINFO { ++ struct DSP_PROCESSORINFO tyBasic; /* user processor info */ ++ /* private dsp mmu entries */ ++ struct MGR_TLBENTRY tyTlb[MGR_MAXTLBENTRIES]; ++ } ; ++ ++#endif /* MGRPRIV_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/msg.h b/arch/arm/plat-omap/include/dspbridge/msg.h +new file mode 100644 +index 0000000..f2872cc +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/msg.h +@@ -0,0 +1,106 @@ ++/* ++ * msg.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== msg.h ======== ++ * Description: ++ * DSP/BIOS Bridge MSG Module. ++ * ++ * Public Functions: ++ * MSG_Create ++ * MSG_Delete ++ * MSG_Exit ++ * MSG_Init ++ * ++ * Notes: ++ * ++ *! Revision History: ++ *! ================= ++ *! 17-Nov-2000 jeh Removed MSG_Get, MSG_Put, MSG_CreateQueue, ++ *! MSG_DeleteQueue, and MSG_RegisterNotify, since these ++ *! are now part of mini-driver. ++ *! 12-Sep-2000 jeh Created. ++ */ ++ ++#ifndef MSG_ ++#define MSG_ ++ ++#include ++#include ++ ++/* ++ * ======== MSG_Create ======== ++ * Purpose: ++ * Create an object to manage message queues. Only one of these objects ++ * can exist per device object. The MSG manager must be created before ++ * the IO Manager. ++ * Parameters: ++ * phMsgMgr: Location to store MSG manager handle on output. ++ * hDevObject: The device object. ++ * msgCallback: Called whenever an RMS_EXIT message is received. ++ * Returns: ++ * Requires: ++ * MSG_Init(void) called. ++ * phMsgMgr != NULL. ++ * hDevObject != NULL. ++ * msgCallback != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS MSG_Create(OUT struct MSG_MGR **phMsgMgr, ++ struct DEV_OBJECT *hDevObject, ++ MSG_ONEXIT msgCallback); ++ ++/* ++ * ======== MSG_Delete ======== ++ * Purpose: ++ * Delete a MSG manager allocated in MSG_Create(). ++ * Parameters: ++ * hMsgMgr: Handle returned from MSG_Create(). ++ * Returns: ++ * Requires: ++ * MSG_Init(void) called. ++ * Valid hMsgMgr. ++ * Ensures: ++ */ ++ extern void MSG_Delete(struct MSG_MGR *hMsgMgr); ++ ++/* ++ * ======== MSG_Exit ======== ++ * Purpose: ++ * Discontinue usage of MSG module. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * MSG_Init(void) successfully called before. ++ * Ensures: ++ * Any resources acquired in MSG_Init(void) will be freed when last MSG ++ * client calls MSG_Exit(void). ++ */ ++ extern void MSG_Exit(void); ++ ++/* ++ * ======== MSG_Init ======== ++ * Purpose: ++ * Initialize the MSG module. ++ * Parameters: ++ * Returns: ++ * TRUE if initialization succeeded, FALSE otherwise. ++ * Ensures: ++ */ ++ extern bool MSG_Init(void); ++ ++#endif /* MSG_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/msgdefs.h b/arch/arm/plat-omap/include/dspbridge/msgdefs.h +new file mode 100644 +index 0000000..8ea4551 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/msgdefs.h +@@ -0,0 +1,43 @@ ++/* ++ * msgdefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== msgdefs.h ======== ++ * Description: ++ * Global MSG constants and types. ++ * ++ *! Revision History ++ *! ================ ++ *! 09-May-2001 jeh Removed MSG_TODSP, MSG_FROMDSP. ++ *! 17-Nov-2000 jeh Added MSGMGR_SIGNATURE. ++ *! 12-Sep-2000 jeh Created. ++ */ ++ ++#ifndef MSGDEFS_ ++#define MSGDEFS_ ++ ++#define MSGMGR_SIGNATURE 0x4d47534d /* "MGSM" */ ++ ++/* MSG Objects: */ ++ struct MSG_MGR; ++ struct MSG_QUEUE; ++ ++/* Function prototype for callback to be called on RMS_EXIT message received */ ++ typedef void(*MSG_ONEXIT) (HANDLE h, s32 nStatus); ++ ++#endif /* MSGDEFS_ */ ++ +diff --git a/arch/arm/plat-omap/include/dspbridge/nldr.h b/arch/arm/plat-omap/include/dspbridge/nldr.h +new file mode 100644 +index 0000000..0915846 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/nldr.h +@@ -0,0 +1,81 @@ ++/* ++ * nldr.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== nldr.h ======== ++ * ++ * Description: ++ * DSP/BIOS Bridge dynamic loader interface. See the file dldrdefs.h ++ * for a description of these functions. ++ * ++ * Public Functions: ++ * NLDR_Allocate ++ * NLDR_Create ++ * NLDR_Delete ++ * NLDR_Exit ++ * NLDR_Free ++ * NLDR_GetFxnAddr ++ * NLDR_Init ++ * NLDR_Load ++ * NLDR_Unload ++ * ++ * Notes: ++ * ++ *! Revision History ++ *! ================ ++ *! 31-Jul-2002 jeh Removed function header comments. ++ *! 17-Apr-2002 jeh Created. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#ifndef NLDR_ ++#define NLDR_ ++ ++ extern DSP_STATUS NLDR_Allocate(struct NLDR_OBJECT *hNldr, ++ void *pPrivRef, ++ IN CONST struct DCD_NODEPROPS ++ *pNodeProps, ++ OUT struct NLDR_NODEOBJECT **phNldrNode, ++ IN bool *pfPhaseSplit); ++ ++ extern DSP_STATUS NLDR_Create(OUT struct NLDR_OBJECT **phNldr, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct NLDR_ATTRS *pAttrs); ++ ++ extern void NLDR_Delete(struct NLDR_OBJECT *hNldr); ++ extern void NLDR_Exit(void); ++ extern void NLDR_Free(struct NLDR_NODEOBJECT *hNldrNode); ++ ++ extern DSP_STATUS NLDR_GetFxnAddr(struct NLDR_NODEOBJECT *hNldrNode, ++ char *pstrFxn, u32 *pulAddr); ++ ++ extern DSP_STATUS NLDR_GetRmmManager(struct NLDR_OBJECT *hNldrObject, ++ OUT struct RMM_TargetObj ++ **phRmmMgr); ++ ++ extern bool NLDR_Init(void); ++ extern DSP_STATUS NLDR_Load(struct NLDR_NODEOBJECT *hNldrNode, ++ enum NLDR_PHASE phase); ++ extern DSP_STATUS NLDR_Unload(struct NLDR_NODEOBJECT *hNldrNode, ++ enum NLDR_PHASE phase); ++ ++#endif /* NLDR_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/nldrdefs.h b/arch/arm/plat-omap/include/dspbridge/nldrdefs.h +new file mode 100644 +index 0000000..84b36a3 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/nldrdefs.h +@@ -0,0 +1,307 @@ ++/* ++ * nldrdefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== nldrdefs.h ======== ++ * Description: ++ * Global Dynamic + static/overlay Node loader (NLDR) constants and types. ++ * ++ *! Revision History ++ *! ================ ++ *! 07-Apr-2003 map Consolidated dldrdefs.h into nldrdefs.h ++ *! 05-Aug-2002 jeh Created. ++ */ ++ ++#ifndef NLDRDEFS_ ++#define NLDRDEFS_ ++ ++#include ++#include ++ ++#define NLDR_MAXPATHLENGTH 255 ++/* NLDR Objects: */ ++ struct NLDR_OBJECT; ++ struct NLDR_NODEOBJECT; ++ ++/* ++ * ======== NLDR_LOADTYPE ======== ++ * Load types for a node. Must match values in node.h55. ++ */ ++ enum NLDR_LOADTYPE { ++ NLDR_STATICLOAD, /* Linked in base image, not overlay */ ++ NLDR_DYNAMICLOAD, /* Dynamically loaded node */ ++ NLDR_OVLYLOAD /* Linked in base image, overlay node */ ++ } ; ++ ++/* ++ * ======== NLDR_OVLYFXN ======== ++ * Causes code or data to be copied from load address to run address. This ++ * is the "COD_WRITEFXN" that gets passed to the DBLL_Library and is used as ++ * the ZL write function. ++ * ++ * Parameters: ++ * pPrivRef: Handle to identify the node. ++ * ulDspRunAddr: Run address of code or data. ++ * ulDspLoadAddr: Load address of code or data. ++ * ulNumBytes: Number of (GPP) bytes to copy. ++ * nMemSpace: RMS_CODE or RMS_DATA. ++ * Returns: ++ * ulNumBytes: Success. ++ * 0: Failure. ++ * Requires: ++ * Ensures: ++ */ ++ typedef u32(*NLDR_OVLYFXN) (void *pPrivRef, u32 ulDspRunAddr, ++ u32 ulDspLoadAddr, ++ u32 ulNumBytes, u32 nMemSpace); ++ ++/* ++ * ======== NLDR_WRITEFXN ======== ++ * Write memory function. Used for dynamic load writes. ++ * Parameters: ++ * pPrivRef: Handle to identify the node. ++ * ulDspAddr: Address of code or data. ++ * pBuf: Code or data to be written ++ * ulNumBytes: Number of (GPP) bytes to write. ++ * nMemSpace: DBLL_DATA or DBLL_CODE. ++ * Returns: ++ * ulNumBytes: Success. ++ * 0: Failure. ++ * Requires: ++ * Ensures: ++ */ ++ typedef u32(*NLDR_WRITEFXN) (void *pPrivRef, ++ u32 ulDspAddr, void *pBuf, ++ u32 ulNumBytes, u32 nMemSpace); ++ ++/* ++ * ======== NLDR_ATTRS ======== ++ * Attributes passed to NLDR_Create function. ++ */ ++ struct NLDR_ATTRS { ++ NLDR_OVLYFXN pfnOvly; ++ NLDR_WRITEFXN pfnWrite; ++ u16 usDSPWordSize; ++ u16 usDSPMauSize; ++ } ; ++ ++/* ++ * ======== NLDR_PHASE ======== ++ * Indicates node create, delete, or execute phase function. ++ */ ++ enum NLDR_PHASE { ++ NLDR_CREATE, ++ NLDR_DELETE, ++ NLDR_EXECUTE, ++ NLDR_NOPHASE ++ } ; ++ ++/* ++ * Typedefs of loader functions imported from a DLL, or defined in a ++ * function table. ++ */ ++ ++/* ++ * ======== NLDR_Allocate ======== ++ * Allocate resources to manage the loading of a node on the DSP. ++ * ++ * Parameters: ++ * hNldr: Handle of loader that will load the node. ++ * pPrivRef: Handle to identify the node. ++ * pNodeProps: Pointer to a DCD_NODEPROPS for the node. ++ * phNldrNode: Location to store node handle on output. This handle ++ * will be passed to NLDR_Load/NLDR_Unload. ++ * pfPhaseSplit: pointer to boolean variable referenced in node.c ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Insufficient memory on GPP. ++ * Requires: ++ * NLDR_Init(void) called. ++ * Valid hNldr. ++ * pNodeProps != NULL. ++ * phNldrNode != NULL. ++ * Ensures: ++ * DSP_SOK: IsValidNode(*phNldrNode). ++ * error: *phNldrNode == NULL. ++ */ ++ typedef DSP_STATUS(*NLDR_ALLOCATEFXN) (struct NLDR_OBJECT *hNldr, ++ void *pPrivRef, ++ IN CONST struct DCD_NODEPROPS ++ *pNodeProps, ++ OUT struct NLDR_NODEOBJECT ++ **phNldrNode, ++ OUT bool *pfPhaseSplit); ++ ++/* ++ * ======== NLDR_Create ======== ++ * Create a loader object. This object handles the loading and unloading of ++ * create, delete, and execute phase functions of nodes on the DSP target. ++ * ++ * Parameters: ++ * phNldr: Location to store loader handle on output. ++ * hDevObject: Device for this processor. ++ * pAttrs: Loader attributes. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EMEMORY: Insufficient memory for requested resources. ++ * Requires: ++ * NLDR_Init(void) called. ++ * phNldr != NULL. ++ * hDevObject != NULL. ++ * pAttrs != NULL. ++ * Ensures: ++ * DSP_SOK: Valid *phNldr. ++ * error: *phNldr == NULL. ++ */ ++ typedef DSP_STATUS(*NLDR_CREATEFXN) (OUT struct NLDR_OBJECT **phNldr, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct NLDR_ATTRS ++ *pAttrs); ++ ++/* ++ * ======== NLDR_Delete ======== ++ * Delete the NLDR loader. ++ * ++ * Parameters: ++ * hNldr: Node manager object. ++ * Returns: ++ * Requires: ++ * NLDR_Init(void) called. ++ * Valid hNldr. ++ * Ensures: ++ * hNldr invalid ++ */ ++ typedef void(*NLDR_DELETEFXN) (struct NLDR_OBJECT *hNldr); ++ ++/* ++ * ======== NLDR_Exit ======== ++ * Discontinue usage of NLDR module. ++ * ++ * Parameters: ++ * Returns: ++ * Requires: ++ * NLDR_Init(void) successfully called before. ++ * Ensures: ++ * Any resources acquired in NLDR_Init(void) will be freed when last NLDR ++ * client calls NLDR_Exit(void). ++ */ ++ typedef void(*NLDR_EXITFXN) (void); ++ ++/* ++ * ======== NLDR_Free ======== ++ * Free resources allocated in NLDR_Allocate. ++ * ++ * Parameters: ++ * hNldrNode: Handle returned from NLDR_Allocate(). ++ * Returns: ++ * Requires: ++ * NLDR_Init(void) called. ++ * Valid hNldrNode. ++ * Ensures: ++ */ ++ typedef void(*NLDR_FREEFXN) (struct NLDR_NODEOBJECT *hNldrNode); ++ ++/* ++ * ======== NLDR_GetFxnAddr ======== ++ * Get address of create, delete, or execute phase function of a node on ++ * the DSP. ++ * ++ * Parameters: ++ * hNldrNode: Handle returned from NLDR_Allocate(). ++ * pstrFxn: Name of function. ++ * pulAddr: Location to store function address. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ESYMBOL: Address of function not found. ++ * Requires: ++ * NLDR_Init(void) called. ++ * Valid hNldrNode. ++ * pulAddr != NULL; ++ * pstrFxn != NULL; ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*NLDR_GETFXNADDRFXN) (struct NLDR_NODEOBJECT ++ *hNldrNode, ++ char *pstrFxn, u32 *pulAddr); ++ ++/* ++ * ======== NLDR_Init ======== ++ * Initialize the NLDR module. ++ * ++ * Parameters: ++ * Returns: ++ * TRUE if initialization succeeded, FALSE otherwise. ++ * Ensures: ++ */ ++ typedef bool(*NLDR_INITFXN) (void); ++ ++/* ++ * ======== NLDR_Load ======== ++ * Load create, delete, or execute phase function of a node on the DSP. ++ * ++ * Parameters: ++ * hNldrNode: Handle returned from NLDR_Allocate(). ++ * phase: Type of function to load (create, delete, or execute). ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Insufficient memory on GPP. ++ * DSP_EOVERLAYMEMORY: Can't overlay phase because overlay memory ++ * is already in use. ++ * DSP_EDYNLOAD: Failure in dynamic loader library. ++ * DSP_EFWRITE: Failed to write phase's code or date to target. ++ * Requires: ++ * NLDR_Init(void) called. ++ * Valid hNldrNode. ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*NLDR_LOADFXN) (struct NLDR_NODEOBJECT *hNldrNode, ++ enum NLDR_PHASE phase); ++ ++/* ++ * ======== NLDR_Unload ======== ++ * Unload create, delete, or execute phase function of a node on the DSP. ++ * ++ * Parameters: ++ * hNldrNode: Handle returned from NLDR_Allocate(). ++ * phase: Node function to unload (create, delete, or execute). ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Insufficient memory on GPP. ++ * Requires: ++ * NLDR_Init(void) called. ++ * Valid hNldrNode. ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*NLDR_UNLOADFXN) (struct NLDR_NODEOBJECT *hNldrNode, ++ enum NLDR_PHASE phase); ++ ++/* ++ * ======== NLDR_FXNS ======== ++ */ ++ struct NLDR_FXNS { ++ NLDR_ALLOCATEFXN pfnAllocate; ++ NLDR_CREATEFXN pfnCreate; ++ NLDR_DELETEFXN pfnDelete; ++ NLDR_EXITFXN pfnExit; ++ NLDR_FREEFXN pfnFree; ++ NLDR_GETFXNADDRFXN pfnGetFxnAddr; ++ NLDR_INITFXN pfnInit; ++ NLDR_LOADFXN pfnLoad; ++ NLDR_UNLOADFXN pfnUnload; ++ } ; ++ ++#endif /* NLDRDEFS_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/node.h b/arch/arm/plat-omap/include/dspbridge/node.h +new file mode 100644 +index 0000000..d253962 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/node.h +@@ -0,0 +1,619 @@ ++/* ++ * node.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== node.h ======== ++ * Description: ++ * DSP/BIOS Bridge Node Manager. ++ * ++ * Public Functions: ++ * NODE_Allocate ++ * NODE_AllocMsgBuf ++ * NODE_ChangePriority ++ * NODE_Connect ++ * NODE_Create ++ * NODE_CreateMgr ++ * NODE_Delete ++ * NODE_DeleteMgr ++ * NODE_EnumNodes ++ * NODE_Exit ++ * NODE_FreeMsgBuf ++ * NODE_GetAttr ++ * NODE_GetMessage ++ * NODE_GetProcessor ++ * NODE_Init ++ * NODE_OnExit ++ * NODE_Pause ++ * NODE_PutMessage ++ * NODE_RegisterNotify ++ * NODE_Run ++ * NODE_Terminate ++ * ++ * Notes: ++ * ++ *! Revision History: ++ *! ================= ++ *! 23-Apr-2001 jeh Updated with code review changes. ++ *! 16-Jan-2001 jeh Added DSP_ESYMBOL, DSP_EUUID to return codes. ++ *! 17-Nov-2000 jeh Added NODE_OnExit(). ++ *! 27-Oct-2000 jeh Added timeouts to NODE_GetMessage, NODE_PutMessage. ++ *! 12-Oct-2000 jeh Changed NODE_EnumNodeInfo to NODE_EnumNodes. Removed ++ *! NODE_RegisterAllNodes(). ++ *! 07-Sep-2000 jeh Changed type HANDLE in NODE_RegisterNotify to ++ *! DSP_HNOTIFICATION. Added DSP_STRMATTR param to ++ *! NODE_Connect(). Removed NODE_GetMessageStream(). ++ *! 17-Jul-2000 jeh Updated function header descriptions. ++ *! 19-Jun-2000 jeh Created. ++ */ ++ ++#ifndef NODE_ ++#define NODE_ ++ ++#include ++ ++#include ++#include ++#include ++ ++/* ++ * ======== NODE_Allocate ======== ++ * Purpose: ++ * Allocate GPP resources to manage a node on the DSP. ++ * Parameters: ++ * hProcessor: Handle of processor that is allocating the node. ++ * pNodeId: Pointer to a DSP_UUID for the node. ++ * pArgs: Optional arguments to be passed to the node. ++ * pAttrIn: Optional pointer to node attributes (priority, ++ * timeout...) ++ * phNode: Location to store node handle on output. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Insufficient memory on GPP. ++ * DSP_EUUID: Node UUID has not been registered. ++ * DSP_ESYMBOL: iAlg functions not found for a DAIS node. ++ * DSP_ERANGE: pAttrIn != NULL and pAttrIn->iPriority out of ++ * range. ++ * DSP_EFAIL: A failure occured, unable to allocate node. ++ * DSP_EWRONGSTATE: Proccessor is not in the running state. ++ * Requires: ++ * NODE_Init(void) called. ++ * hProcessor != NULL. ++ * pNodeId != NULL. ++ * phNode != NULL. ++ * Ensures: ++ * DSP_SOK: IsValidNode(*phNode). ++ * error: *phNode == NULL. ++ */ ++ extern DSP_STATUS NODE_Allocate(struct PROC_OBJECT *hProcessor, ++ IN CONST struct DSP_UUID *pNodeId, ++ OPTIONAL IN CONST struct DSP_CBDATA ++ *pArgs, ++ OPTIONAL IN CONST struct DSP_NODEATTRIN ++ *pAttrIn, ++ OUT struct NODE_OBJECT **phNode); ++ ++/* ++ * ======== NODE_AllocMsgBuf ======== ++ * Purpose: ++ * Allocate and Prepare a buffer whose descriptor will be passed to a ++ * Node within a (DSP_MSG)message ++ * Parameters: ++ * hNode: The node handle. ++ * uSize: The size of the buffer to be allocated. ++ * pAttr: Pointer to a DSP_BUFFERATTR structure. ++ * pBuffer: Location to store the address of the allocated ++ * buffer on output. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid node handle. ++ * DSP_EMEMORY: Insufficent memory. ++ * DSP_EFAIL: General Failure. ++ * DSP_ESIZE: Invalid Size. ++ * Requires: ++ * NODE_Init(void) called. ++ * pBuffer != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS NODE_AllocMsgBuf(struct NODE_OBJECT *hNode, ++ u32 uSize, ++ OPTIONAL struct DSP_BUFFERATTR ++ *pAttr, ++ OUT u8 **pBuffer); ++ ++/* ++ * ======== NODE_ChangePriority ======== ++ * Purpose: ++ * Change the priority of an allocated node. ++ * Parameters: ++ * hNode: Node handle returned from NODE_Allocate. ++ * nPriority: New priority level to set node's priority to. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hNode. ++ * DSP_ERANGE: nPriority is out of range. ++ * DSP_ENODETYPE: The specified node is not a task node. ++ * DSP_EWRONGSTATE: Node is not in the NODE_ALLOCATED, NODE_PAUSED, ++ * or NODE_RUNNING state. ++ * DSP_ETIMEOUT: A timeout occurred before the DSP responded. ++ * DSP_ERESTART: A critical error has occurred and the DSP is ++ * being restarted. ++ * DSP_EFAIL: Unable to change node's runtime priority level. ++ * Requires: ++ * NODE_Init(void) called. ++ * Ensures: ++ * DSP_SOK && (Node's current priority == nPriority) ++ */ ++ extern DSP_STATUS NODE_ChangePriority(struct NODE_OBJECT *hNode, ++ s32 nPriority); ++ ++/* ++ * ======== NODE_CloseOrphans ======== ++ * Purpose: ++ * Delete all nodes whose owning processor is being destroyed. ++ * Parameters: ++ * hNodeMgr: Node manager object. ++ * hProc: Handle to processor object being destroyed. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Unable to delete all nodes belonging to hProc. ++ * Requires: ++ * Valid hNodeMgr. ++ * hProc != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS NODE_CloseOrphans(struct NODE_MGR *hNodeMgr, ++ struct PROC_OBJECT *hProc); ++ ++/* ++ * ======== NODE_Connect ======== ++ * Purpose: ++ * Connect two nodes on the DSP, or a node on the DSP to the GPP. In the ++ * case that the connnection is being made between a node on the DSP and ++ * the GPP, one of the node handles (either hNode1 or hNode2) must be ++ * the constant NODE_HGPPNODE. ++ * Parameters: ++ * hNode1: Handle of first node to connect to second node. If ++ * this is a connection from the GPP to hNode2, hNode1 ++ * must be the constant NODE_HGPPNODE. Otherwise, hNode1 ++ * must be a node handle returned from a successful call ++ * to Node_Allocate(). ++ * hNode2: Handle of second node. Must be either NODE_HGPPNODE ++ * if this is a connection from DSP node to GPP, or a ++ * node handle returned from a successful call to ++ * NODE_Allocate(). ++ * uStream1: Output stream index on first node, to be connected ++ * to second node's input stream. Value must range from ++ * 0 <= uStream1 < number of output streams. ++ * uStream2: Input stream index on second node. Value must range ++ * from 0 <= uStream2 < number of input streams. ++ * pAttrs: Stream attributes (NULL ==> use defaults). ++ * pConnParam: A pointer to a DSP_CBDATA structure that defines ++ * connection parameter for device nodes to pass to DSP ++ * side. ++ * If the value of this parameter is NULL, then this API ++ * behaves like DSPNode_Connect. This parameter will have ++ * length of the string and the null terminated string in ++ * DSP_CBDATA struct. This can be extended in future tp ++ * pass binary data. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hNode1 or hNode2. ++ * DSP_EMEMORY: Insufficient host memory. ++ * DSP_EVALUE: A stream index parameter is invalid. ++ * DSP_EALREADYCONNECTED: A connection already exists for one of the ++ * indices uStream1 or uStream2. ++ * DSP_EWRONGSTATE: Either hNode1 or hNode2 is not in the ++ * NODE_ALLOCATED state. ++ * DSP_ENOMORECONNECTIONS: No more connections available. ++ * DSP_EFAIL: Attempt to make an illegal connection (eg, ++ * Device node to device node, or device node to ++ * GPP), the two nodes are on different DSPs. ++ * Requires: ++ * NODE_Init(void) called. ++ * Ensures: ++ */ ++ extern DSP_STATUS NODE_Connect(struct NODE_OBJECT *hNode1, ++ u32 uStream1, ++ struct NODE_OBJECT *hNode2, ++ u32 uStream2, ++ OPTIONAL IN struct DSP_STRMATTR *pAttrs, ++ OPTIONAL IN struct DSP_CBDATA ++ *pConnParam); ++ ++/* ++ * ======== NODE_Create ======== ++ * Purpose: ++ * Create a node on the DSP by remotely calling the node's create ++ * function. If necessary, load code that contains the node's create ++ * function. ++ * Parameters: ++ * hNode: Node handle returned from NODE_Allocate(). ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hNode. ++ * DSP_ESYMBOL: Create function not found in the COFF file. ++ * DSP_EWRONGSTATE: Node is not in the NODE_ALLOCATED state. ++ * DSP_EMEMORY: Memory allocation failure on the DSP. ++ * DSP_ETASK: Unable to create node's task or process on the DSP. ++ * DSP_ESTREAM: Stream creation failure on the DSP. ++ * DSP_ETIMEOUT: A timeout occurred before the DSP responded. ++ * DSP_EUSER1-16: A user-defined failure occurred on the DSP. ++ * DSP_EFAIL: A failure occurred, unable to create node. ++ * Requires: ++ * NODE_Init(void) called. ++ * Ensures: ++ */ ++ extern DSP_STATUS NODE_Create(struct NODE_OBJECT *hNode); ++ ++/* ++ * ======== NODE_CreateMgr ======== ++ * Purpose: ++ * Create a NODE Manager object. This object handles the creation, ++ * deletion, and execution of nodes on the DSP target. The NODE Manager ++ * also maintains a pipe map of used and available node connections. ++ * Each DEV object should have exactly one NODE Manager object. ++ * ++ * Parameters: ++ * phNodeMgr: Location to store node manager handle on output. ++ * hDevObject: Device for this processor. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EMEMORY: Insufficient memory for requested resources. ++ * DSP_EFAIL: General failure. ++ * Requires: ++ * NODE_Init(void) called. ++ * phNodeMgr != NULL. ++ * hDevObject != NULL. ++ * Ensures: ++ * DSP_SOK: Valide *phNodeMgr. ++ * error: *phNodeMgr == NULL. ++ */ ++ extern DSP_STATUS NODE_CreateMgr(OUT struct NODE_MGR **phNodeMgr, ++ struct DEV_OBJECT *hDevObject); ++ ++/* ++ * ======== NODE_Delete ======== ++ * Purpose: ++ * Delete resources allocated in NODE_Allocate(). If the node was ++ * created, delete the node on the DSP by remotely calling the node's ++ * delete function. Loads the node's delete function if necessary. ++ * GPP side resources are freed after node's delete function returns. ++ * Parameters: ++ * hNode: Node handle returned from NODE_Allocate(). ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hNode. ++ * DSP_ETIMEOUT: A timeout occurred before the DSP responded. ++ * DSP_EDELETE: A deletion failure occurred. ++ * DSP_EUSER1-16: Node specific failure occurred on the DSP. ++ * DSP_EFAIL: A failure occurred in deleting the node. ++ * DSP_ESYMBOL: Delete function not found in the COFF file. ++ * Requires: ++ * NODE_Init(void) called. ++ * Ensures: ++ * DSP_SOK: hNode is invalid. ++ */ ++ extern DSP_STATUS NODE_Delete(struct NODE_OBJECT *hNode); ++ ++/* ++ * ======== NODE_DeleteMgr ======== ++ * Purpose: ++ * Delete the NODE Manager. ++ * Parameters: ++ * hNodeMgr: Node manager object. ++ * Returns: ++ * DSP_SOK: Success. ++ * Requires: ++ * NODE_Init(void) called. ++ * Valid hNodeMgr. ++ * Ensures: ++ */ ++ extern DSP_STATUS NODE_DeleteMgr(struct NODE_MGR *hNodeMgr); ++ ++/* ++ * ======== NODE_EnumNodes ======== ++ * Purpose: ++ * Enumerate the nodes currently allocated for the DSP. ++ * Parameters: ++ * hNodeMgr: Node manager returned from NODE_CreateMgr(). ++ * aNodeTab: Array to copy node handles into. ++ * uNodeTabSize: Number of handles that can be written to aNodeTab. ++ * puNumNodes: Location where number of node handles written to ++ * aNodeTab will be written. ++ * puAllocated: Location to write total number of allocated nodes. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ESIZE: aNodeTab is too small to hold all node handles. ++ * Requires: ++ * Valid hNodeMgr. ++ * aNodeTab != NULL || uNodeTabSize == 0. ++ * puNumNodes != NULL. ++ * puAllocated != NULL. ++ * Ensures: ++ * - (DSP_ESIZE && *puNumNodes == 0) ++ * - || (DSP_SOK && *puNumNodes <= uNodeTabSize) && ++ * (*puAllocated == *puNumNodes) ++ */ ++ extern DSP_STATUS NODE_EnumNodes(struct NODE_MGR *hNodeMgr, ++ IN DSP_HNODE *aNodeTab, ++ u32 uNodeTabSize, ++ OUT u32 *puNumNodes, ++ OUT u32 *puAllocated); ++ ++/* ++ * ======== NODE_Exit ======== ++ * Purpose: ++ * Discontinue usage of NODE module. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * NODE_Init(void) successfully called before. ++ * Ensures: ++ * Any resources acquired in NODE_Init(void) will be freed when last NODE ++ * client calls NODE_Exit(void). ++ */ ++ extern void NODE_Exit(void); ++ ++/* ++ * ======== NODE_FreeMsgBuf ======== ++ * Purpose: ++ * Free a message buffer previously allocated with NODE_AllocMsgBuf. ++ * Parameters: ++ * hNode: The node handle. ++ * pBuffer: (Address) Buffer allocated by NODE_AllocMsgBuf. ++ * pAttr: Same buffer attributes passed to NODE_AllocMsgBuf. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid node handle. ++ * DSP_EFAIL: Failure to free the buffer. ++ * Requires: ++ * NODE_Init(void) called. ++ * pBuffer != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS NODE_FreeMsgBuf(struct NODE_OBJECT *hNode, ++ IN u8 *pBuffer, ++ OPTIONAL struct DSP_BUFFERATTR ++ *pAttr); ++ ++/* ++ * ======== NODE_GetAttr ======== ++ * Purpose: ++ * Copy the current attributes of the specified node into a DSP_NODEATTR ++ * structure. ++ * Parameters: ++ * hNode: Node object allocated from NODE_Allocate(). ++ * pAttr: Pointer to DSP_NODEATTR structure to copy node's ++ * attributes. ++ * uAttrSize: Size of pAttr. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hNode. ++ * Requires: ++ * NODE_Init(void) called. ++ * pAttr != NULL. ++ * Ensures: ++ * DSP_SOK: *pAttrs contains the node's current attributes. ++ */ ++ extern DSP_STATUS NODE_GetAttr(struct NODE_OBJECT *hNode, ++ OUT struct DSP_NODEATTR *pAttr, ++ u32 uAttrSize); ++ ++/* ++ * ======== NODE_GetMessage ======== ++ * Purpose: ++ * Retrieve a message from a node on the DSP. The node must be either a ++ * message node, task node, or XDAIS socket node. ++ * If a message is not available, this function will block until a ++ * message is available, or the node's timeout value is reached. ++ * Parameters: ++ * hNode: Node handle returned from NODE_Allocate(). ++ * pMessage: Pointer to DSP_MSG structure to copy the ++ * message into. ++ * uTimeout: Timeout in milliseconds to wait for message. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hNode. ++ * DSP_ENODETYPE: Cannot retrieve messages from this type of node. ++ * DSP_ETIMEOUT: Timeout occurred and no message is available. ++ * DSP_EFAIL: Error occurred while trying to retrieve a message. ++ * Requires: ++ * NODE_Init(void) called. ++ * pMessage != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS NODE_GetMessage(struct NODE_OBJECT *hNode, ++ OUT struct DSP_MSG *pMessage, ++ u32 uTimeout); ++ ++/* ++ * ======== NODE_GetNldrObj ======== ++ * Purpose: ++ * Retrieve the Nldr manager ++ * Parameters: ++ * hNodeMgr: Node Manager ++ * phNldrObj: Pointer to a Nldr manager handle ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hNode. ++ * Ensures: ++ */ ++ extern DSP_STATUS NODE_GetNldrObj(struct NODE_MGR *hNodeMgr, ++ OUT struct NLDR_OBJECT **phNldrObj); ++ ++/* ++ * ======== NODE_Init ======== ++ * Purpose: ++ * Initialize the NODE module. ++ * Parameters: ++ * Returns: ++ * TRUE if initialization succeeded, FALSE otherwise. ++ * Ensures: ++ */ ++ extern bool NODE_Init(void); ++ ++/* ++ * ======== NODE_OnExit ======== ++ * Purpose: ++ * Gets called when RMS_EXIT is received for a node. PROC needs to pass ++ * this function as a parameter to MSG_Create(). This function then gets ++ * called by the mini-driver when an exit message for a node is received. ++ * Parameters: ++ * hNode: Handle of the node that the exit message is for. ++ * nStatus: Return status of the node's execute phase. ++ * Returns: ++ * Ensures: ++ */ ++ void NODE_OnExit(struct NODE_OBJECT *hNode, s32 nStatus); ++ ++/* ++ * ======== NODE_Pause ======== ++ * Purpose: ++ * Suspend execution of a node currently running on the DSP. ++ * Parameters: ++ * hNode: Node object representing a node currently ++ * running on the DSP. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hNode. ++ * DSP_ENODETYPE: Node is not a task or socket node. ++ * DSP_ETIMEOUT: A timeout occurred before the DSP responded. ++ * DSP_EWRONGSTSATE: Node is not in NODE_RUNNING state. ++ * DSP_EFAIL: Failed to pause node. ++ * Requires: ++ * NODE_Init(void) called. ++ * Ensures: ++ */ ++ extern DSP_STATUS NODE_Pause(struct NODE_OBJECT *hNode); ++ ++/* ++ * ======== NODE_PutMessage ======== ++ * Purpose: ++ * Send a message to a message node, task node, or XDAIS socket node. ++ * This function will block until the message stream can accommodate ++ * the message, or a timeout occurs. The message will be copied, so Msg ++ * can be re-used immediately after return. ++ * Parameters: ++ * hNode: Node handle returned by NODE_Allocate(). ++ * pMsg: Location of message to be sent to the node. ++ * uTimeout: Timeout in msecs to wait. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hNode. ++ * DSP_ENODETYPE: Messages can't be sent to this type of node. ++ * DSP_ETIMEOUT: Timeout occurred before message could be set. ++ * DSP_EWRONGSTATE: Node is in invalid state for sending messages. ++ * DSP_EFAIL: Unable to send message. ++ * Requires: ++ * NODE_Init(void) called. ++ * pMsg != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS NODE_PutMessage(struct NODE_OBJECT *hNode, ++ IN CONST struct DSP_MSG *pMsg, ++ u32 uTimeout); ++ ++/* ++ * ======== NODE_RegisterNotify ======== ++ * Purpose: ++ * Register to be notified on specific events for this node. ++ * Parameters: ++ * hNode: Node handle returned by NODE_Allocate(). ++ * uEventMask: Mask of types of events to be notified about. ++ * uNotifyType: Type of notification to be sent. ++ * hNotification: Handle to be used for notification. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hNode. ++ * DSP_EMEMORY: Insufficient memory on GPP. ++ * DSP_EVALUE: uEventMask is invalid. ++ * DSP_ENOTIMPL: Notification type specified by uNotifyType is not ++ * supported. ++ * Requires: ++ * NODE_Init(void) called. ++ * hNotification != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS NODE_RegisterNotify(struct NODE_OBJECT *hNode, ++ u32 uEventMask, u32 uNotifyType, ++ struct DSP_NOTIFICATION ++ *hNotification); ++ ++/* ++ * ======== NODE_Run ======== ++ * Purpose: ++ * Start execution of a node's execute phase, or resume execution of ++ * a node that has been suspended (via NODE_Pause()) on the DSP. Load ++ * the node's execute function if necessary. ++ * Parameters: ++ * hNode: Node object representing a node currently ++ * running on the DSP. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hNode. ++ * DSP_ENODETYPE: hNode doesn't represent a message, task or dais ++ * socket node. ++ * DSP_ETIMEOUT: A timeout occurred before the DSP responded. ++ * DSP_EWRONGSTSATE: Node is not in NODE_PAUSED or NODE_CREATED state. ++ * DSP_EFAIL: Unable to start or resume execution. ++ * DSP_ESYMBOL: Execute function not found in the COFF file. ++ * Requires: ++ * NODE_Init(void) called. ++ * Ensures: ++ */ ++ extern DSP_STATUS NODE_Run(struct NODE_OBJECT *hNode); ++ ++/* ++ * ======== NODE_Terminate ======== ++ * Purpose: ++ * Signal a node running on the DSP that it should exit its execute ++ * phase function. ++ * Parameters: ++ * hNode: Node object representing a node currently ++ * running on the DSP. ++ * pStatus: Location to store execute-phase function return ++ * value (DSP_EUSER1-16). ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hNode. ++ * DSP_ETIMEOUT: A timeout occurred before the DSP responded. ++ * DSP_ENODETYPE: Type of node specified cannot be terminated. ++ * DSP_EWRONGSTATE: Operation not valid for the current node state. ++ * DSP_EFAIL: Unable to terminate the node. ++ * Requires: ++ * NODE_Init(void) called. ++ * pStatus != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS NODE_Terminate(struct NODE_OBJECT *hNode, ++ OUT DSP_STATUS *pStatus); ++ ++ ++ ++/* ++ * ======== NODE_GetUUIDProps ======== ++ * Purpose: ++ * Fetch Node properties given the UUID ++ * Parameters: ++ * ++ */ ++ extern DSP_STATUS NODE_GetUUIDProps(DSP_HPROCESSOR hProcessor, ++ IN CONST struct DSP_UUID *pNodeId, ++ OUT struct DSP_NDBPROPS ++ *pNodeProps); ++ ++#endif /* NODE_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/nodedefs.h b/arch/arm/plat-omap/include/dspbridge/nodedefs.h +new file mode 100644 +index 0000000..cdc0c4b +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/nodedefs.h +@@ -0,0 +1,40 @@ ++/* ++ * nodedefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== nodedefs.h ======== ++ * Description: ++ * Global NODE constants and types, shared by PROCESSOR, NODE, and DISP. ++ * ++ *! Revision History ++ *! ================ ++ *! 23-Apr-2001 jeh Removed NODE_MGRATTRS. ++ *! 21-Sep-2000 jeh Removed NODE_TYPE enum. ++ *! 17-Jul-2000 jeh Changed order of node types to match rms_sh.h. ++ *! 20-Jun-2000 jeh Created. ++ */ ++ ++#ifndef NODEDEFS_ ++#define NODEDEFS_ ++ ++#define NODE_SUSPENDEDPRI -1 ++ ++/* NODE Objects: */ ++ struct NODE_MGR; ++ struct NODE_OBJECT; ++ ++#endif /* NODEDEFS_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/nodepriv.h b/arch/arm/plat-omap/include/dspbridge/nodepriv.h +new file mode 100644 +index 0000000..d28b29b +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/nodepriv.h +@@ -0,0 +1,202 @@ ++/* ++ * nodepriv.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== nodepriv.h ======== ++ * Description: ++ * Private node header shared by NODE and DISP. ++ * ++ * Public Functions: ++ * NODE_GetChannelId ++ * NODE_GetStrmMgr ++ * NODE_GetTimeout ++ * NODE_GetType ++ * NODE_GetLoadType ++ * ++ *! Revision History ++ *! ================ ++ *! 19-Nov-2002 map Added NODE_GetLoadType ++ *! 13-Feb-2002 jeh Added uSysStackSize to NODE_TASKARGS. ++ *! 23-Apr-2001 jeh Removed unused typedefs, defines. ++ *! 10-Oct-2000 jeh Added alignment to NODE_STRMDEF. ++ *! 20-Jun-2000 jeh Created. ++ */ ++ ++#ifndef NODEPRIV_ ++#define NODEPRIV_ ++ ++#include ++#include ++#include ++ ++/* DSP address of node environment structure */ ++ typedef u32 NODE_ENV; ++ ++/* ++ * Node create structures ++ */ ++ ++/* Message node */ ++ struct NODE_MSGARGS { ++ u32 uMaxMessages; /* Max # of simultaneous messages for node */ ++ u32 uSegid; /* Segment for allocating message buffers */ ++ u32 uNotifyType; /* Notify type (SEM_post, SWI_post, etc.) */ ++ u32 uArgLength; /* Length in 32-bit words of arg data block */ ++ u8 *pData; /* Argument data for node */ ++ } ; ++ ++ struct NODE_STRMDEF { ++ u32 uBufsize; /* Size of buffers for SIO stream */ ++ u32 uNumBufs; /* max # of buffers in SIO stream at once */ ++ u32 uSegid; /* Memory segment id to allocate buffers */ ++ u32 uTimeout; /* Timeout for blocking SIO calls */ ++ u32 uAlignment; /* Buffer alignment */ ++ char *szDevice; /* Device name for stream */ ++ } ; ++ ++/* Task node */ ++ struct NODE_TASKARGS { ++ struct NODE_MSGARGS msgArgs; ++ s32 nPriority; ++ u32 uStackSize; ++ u32 uSysStackSize; ++ u32 uStackSeg; ++ u32 uDSPHeapResAddr; /* DSP virtual heap address */ ++ u32 uDSPHeapAddr; /* DSP virtual heap address */ ++ u32 uHeapSize; /* Heap size */ ++ u32 uGPPHeapAddr; /* GPP virtual heap address */ ++ u32 uProfileID; /* Profile ID */ ++ u32 uNumInputs; ++ u32 uNumOutputs; ++ u32 ulDaisArg; /* Address of iAlg object */ ++ struct NODE_STRMDEF *strmInDef; ++ struct NODE_STRMDEF *strmOutDef; ++ } ; ++ ++/* ++ * ======== NODE_CREATEARGS ======== ++ */ ++ struct NODE_CREATEARGS { ++ union { ++ struct NODE_MSGARGS msgArgs; ++ struct NODE_TASKARGS taskArgs; ++ } asa; ++ } ; ++ ++/* ++ * ======== NODE_GetChannelId ======== ++ * Purpose: ++ * Get the channel index reserved for a stream connection between the ++ * host and a node. This index is reserved when NODE_Connect() is called ++ * to connect the node with the host. This index should be passed to ++ * the CHNL_Open function when the stream is actually opened. ++ * Parameters: ++ * hNode: Node object allocated from NODE_Allocate(). ++ * uDir: Input (DSP_TONODE) or output (DSP_FROMNODE). ++ * uIndex: Stream index. ++ * pulId: Location to store channel index. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hNode. ++ * DSP_ENODETYPE: Not a task or DAIS socket node. ++ * DSP_EVALUE: The node's stream corresponding to uIndex and uDir ++ * is not a stream to or from the host. ++ * Requires: ++ * NODE_Init(void) called. ++ * Valid uDir. ++ * pulId != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS NODE_GetChannelId(struct NODE_OBJECT *hNode, ++ u32 uDir, ++ u32 uIndex, OUT u32 *pulId); ++ ++/* ++ * ======== NODE_GetStrmMgr ======== ++ * Purpose: ++ * Get the STRM manager for a node. ++ * Parameters: ++ * hNode: Node allocated with NODE_Allocate(). ++ * phStrmMgr: Location to store STRM manager on output. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hNode. ++ * Requires: ++ * phStrmMgr != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS NODE_GetStrmMgr(struct NODE_OBJECT *hNode, ++ struct STRM_MGR **phStrmMgr); ++ ++/* ++ * ======== NODE_GetTimeout ======== ++ * Purpose: ++ * Get the timeout value of a node. ++ * Parameters: ++ * hNode: Node allocated with NODE_Allocate(), or DSP_HGPPNODE. ++ * Returns: ++ * Node's timeout value. ++ * Requires: ++ * Valid hNode. ++ * Ensures: ++ */ ++ extern u32 NODE_GetTimeout(struct NODE_OBJECT *hNode); ++ ++/* ++ * ======== NODE_GetType ======== ++ * Purpose: ++ * Get the type (device, message, task, or XDAIS socket) of a node. ++ * Parameters: ++ * hNode: Node allocated with NODE_Allocate(), or DSP_HGPPNODE. ++ * Returns: ++ * Node type: NODE_DEVICE, NODE_TASK, NODE_XDAIS, or NODE_GPP. ++ * Requires: ++ * Valid hNode. ++ * Ensures: ++ */ ++ extern enum NODE_TYPE NODE_GetType(struct NODE_OBJECT *hNode); ++ ++/* ++ * ======== GetNodeInfo ======== ++ * Purpose: ++ * Get node information without holding semaphore. ++ * Parameters: ++ * hNode: Node allocated with NODE_Allocate(), or DSP_HGPPNODE. ++ * Returns: ++ * Node info: priority, device owner, no. of streams, execution state ++ * NDB properties. ++ * Requires: ++ * Valid hNode. ++ * Ensures: ++ */ ++ extern void GetNodeInfo(struct NODE_OBJECT *hNode, ++ struct DSP_NODEINFO *pNodeInfo); ++ ++/* ++ * ======== NODE_GetLoadType ======== ++ * Purpose: ++ * Get the load type (dynamic, overlay, static) of a node. ++ * Parameters: ++ * hNode: Node allocated with NODE_Allocate(), or DSP_HGPPNODE. ++ * Returns: ++ * Node type: NLDR_DYNAMICLOAD, NLDR_OVLYLOAD, NLDR_STATICLOAD ++ * Requires: ++ * Valid hNode. ++ * Ensures: ++ */ ++ extern enum NLDR_LOADTYPE NODE_GetLoadType(struct NODE_OBJECT *hNode); ++ ++#endif /* NODEPRIV_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/ntfy.h b/arch/arm/plat-omap/include/dspbridge/ntfy.h +new file mode 100644 +index 0000000..5a0992a +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/ntfy.h +@@ -0,0 +1,146 @@ ++/* ++ * ntfy.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== ntfy.h ======== ++ * Purpose: ++ * Manage lists of notification events. ++ * ++ * Public Functions: ++ * NTFY_Create ++ * NTFY_Delete ++ * NTFY_Exit ++ * NTFY_Init ++ * NTFY_Notify ++ * NTFY_Register ++ * ++ *! Revision History: ++ *! ================= ++ *! 05-Nov-2001 kc: Updated NTFY_Register. ++ *! 07-Sep-2000 jeh Created. ++ */ ++ ++#ifndef NTFY_ ++#define NTFY_ ++ ++ struct NTFY_OBJECT; ++ ++/* ++ * ======== NTFY_Create ======== ++ * Purpose: ++ * Create an empty list of notifications. ++ * Parameters: ++ * phNtfy: Location to store handle on output. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Memory allocation failure. ++ * Requires: ++ * NTFY_Init(void) called. ++ * phNtfy != NULL. ++ * Ensures: ++ * DSP_SUCCEEDED(status) <==> IsValid(*phNtfy). ++ */ ++ extern DSP_STATUS NTFY_Create(OUT struct NTFY_OBJECT **phNtfy); ++ ++/* ++ * ======== NTFY_Delete ======== ++ * Purpose: ++ * Free resources allocated in NTFY_Create. ++ * Parameters: ++ * hNtfy: Handle returned from NTFY_Create(). ++ * Returns: ++ * Requires: ++ * NTFY_Init(void) called. ++ * IsValid(hNtfy). ++ * Ensures: ++ */ ++ extern void NTFY_Delete(IN struct NTFY_OBJECT *hNtfy); ++ ++/* ++ * ======== NTFY_Exit ======== ++ * Purpose: ++ * Discontinue usage of NTFY module. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * NTFY_Init(void) successfully called before. ++ * Ensures: ++ */ ++ extern void NTFY_Exit(void); ++ ++/* ++ * ======== NTFY_Init ======== ++ * Purpose: ++ * Initialize the NTFY module. ++ * Parameters: ++ * Returns: ++ * TRUE if initialization succeeded, FALSE otherwise. ++ * Ensures: ++ */ ++ extern bool NTFY_Init(void); ++ ++/* ++ * ======== NTFY_Notify ======== ++ * Purpose: ++ * Execute notify function (signal event or post message) for every ++ * element in the notification list that is to be notified about the ++ * event specified in uEventMask. ++ * Parameters: ++ * hNtfy: Handle returned from NTFY_Create(). ++ * uEventMask: The type of event that has occurred. ++ * Returns: ++ * Requires: ++ * NTFY_Init(void) called. ++ * IsValid(hNtfy). ++ * Ensures: ++ */ ++ extern void NTFY_Notify(IN struct NTFY_OBJECT *hNtfy, ++ IN u32 uEventMask); ++ ++/* ++ * ======== NTFY_Register ======== ++ * Purpose: ++ * Add a notification element to the list. If the notification is already ++ * registered, and uEventMask != 0, the notification will get posted for ++ * events specified in the new event mask. If the notification is already ++ * registered and uEventMask == 0, the notification will be unregistered. ++ * Parameters: ++ * hNtfy: Handle returned from NTFY_Create(). ++ * hNotification: Handle to a DSP_NOTIFICATION object. ++ * uEventMask: Events to be notified about. ++ * uNotifyType: Type of notification: DSP_SIGNALEVENT. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Insufficient memory. ++ * DSP_EVALUE: uEventMask is 0 and hNotification was not ++ * previously registered. ++ * DSP_EHANDLE: NULL hNotification, hNotification event name ++ * too long, or hNotification event name NULL. ++ * Requires: ++ * NTFY_Init(void) called. ++ * IsValid(hNtfy). ++ * hNotification != NULL. ++ * uNotifyType is DSP_SIGNALEVENT ++ * Ensures: ++ */ ++ extern DSP_STATUS NTFY_Register(IN struct NTFY_OBJECT *hNtfy, ++ IN struct DSP_NOTIFICATION ++ *hNotification, ++ IN u32 uEventMask, ++ IN u32 uNotifyType); ++ ++#endif /* NTFY_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/proc.h b/arch/arm/plat-omap/include/dspbridge/proc.h +new file mode 100644 +index 0000000..486652e +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/proc.h +@@ -0,0 +1,648 @@ ++/* ++ * proc.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== proc.h ======== ++ * Description: ++ * This is the Class driver RM module interface. ++ * ++ * Public Functions: ++ * PROC_Attach ++ * PROC_Create ++ * PROC_Ctrl (OEM-function) ++ * PROC_Destroy ++ * PROC_Detach ++ * PROC_EnumNodes ++ * PROC_Exit ++ * PROC_FlushMemory ++ * PROC_GetDevObject (OEM-function) ++ * PROC_GetResourceInfo ++ * PROC_GetState ++ * PROC_GetProcessorId ++ * PROC_GetTrace (OEM-function) ++ * PROC_Init ++ * PROC_Load (OEM-function) ++ * PROC_Map ++ * PROC_NotifyAllclients ++ * PROC_NotifyClients (OEM-function) ++ * PROC_RegisterNotify ++ * PROC_ReserveMemory ++ * PROC_Start (OEM-function) ++ * PROC_UnMap ++ * PROC_UnReserveMemory ++ * ++ * Notes: ++ * ++ *! Revision History: ++ *! ================ ++ *! 19-Apr-2004 sb Aligned DMM definitions with Symbian ++ *! 08-Mar-2004 sb Added the Dynamic Memory Mapping APIs ++ *! 09-Feb-2003 vp: Added PROC_GetProcessorID function ++ *! 29-Nov-2000 rr: Incorporated code review changes. ++ *! 28-Sep-2000 rr: Updated to Version 0.9. ++ *! 10-Aug-2000 rr: PROC_NotifyClients, PROC_GetProcessorHandle Added ++ *! 27-Jul-2000 rr: Updated to ver 0.8 of DSPAPI(types). GetTrace added. ++ *! 27-Jun-2000 rr: Created from dspapi.h ++ */ ++ ++#ifndef PROC_ ++#define PROC_ ++ ++#include ++#include ++ ++/* ++ * ======== PROC_Attach ======== ++ * Purpose: ++ * Prepare for communication with a particular DSP processor, and return ++ * a handle to the processor object. The PROC Object gets created ++ * Parameters: ++ * uProcessor : The processor index (zero-based). ++ * hMgrObject : Handle to the Manager Object ++ * pAttrIn : Ptr to the DSP_PROCESSORATTRIN structure. ++ * A NULL value means use default values. ++ * phProcessor : Ptr to location to store processor handle. ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EFAIL : General failure. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_SALREADYATTACHED: Success; Processor already attached. ++ * Requires: ++ * phProcessor != NULL. ++ * PROC Initialized. ++ * Ensures: ++ * DSP_EFAIL, and *phProcessor == NULL, OR ++ * Success and *phProcessor is a Valid Processor handle OR ++ * DSP_SALREADYATTACHED and *phProcessor is a Valid Processor. ++ * Details: ++ * When pAttrIn is NULL, the default timeout value is 10 seconds. ++ */ ++ extern DSP_STATUS PROC_Attach(u32 uProcessor, ++ OPTIONAL CONST struct DSP_PROCESSORATTRIN ++ *pAttrIn, ++ OUT DSP_HPROCESSOR *phProcessor); ++ ++/* ++ * ======== PROC_AutoStart ========= ++ * Purpose: ++ * A Particular device gets loaded with the default image ++ * if the AutoStart flag is set. ++ * Parameters: ++ * hDevObject : Handle to the Device ++ * Returns: ++ * DSP_SOK : On Successful Loading ++ * DSP_EFILE : No DSP exec file found. ++ * DSP_EFAIL : General Failure ++ * Requires: ++ * hDevObject != NULL. ++ * hDevNode != NULL. ++ * PROC Initialized. ++ * Ensures: ++ */ ++ extern DSP_STATUS PROC_AutoStart(struct CFG_DEVNODE *hDevNode, ++ struct DEV_OBJECT *hDevObject); ++ ++/* ++ * ======== PROC_Ctrl ======== ++ * Purpose: ++ * Pass control information to the GPP device driver managing the DSP ++ * processor. This will be an OEM-only function, and not part of the ++ * 'Bridge application developer's API. ++ * Parameters: ++ * hProcessor : The processor handle. ++ * dwCmd : Private driver IOCTL cmd ID. ++ * pArgs : Ptr to an driver defined argument structure. ++ * Returns: ++ * DSP_SOK : SUCCESS ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_ETIMEOUT: A Timeout Occured before the Control information ++ * could be sent. ++ * DSP_EACCESSDENIED: Client does not have the access rights required ++ * to call this function. ++ * DSP_ERESTART: A Critical error has occured and the DSP is being ++ * restarted. ++ * DSP_EFAIL : General Failure. ++ * Requires: ++ * PROC Initialized. ++ * Ensures ++ * Details: ++ * This function Calls WMD_BRD_Ioctl. ++ */ ++ extern DSP_STATUS PROC_Ctrl(DSP_HPROCESSOR hProcessor, ++ u32 dwCmd, IN struct DSP_CBDATA *pArgs); ++ ++/* ++ * ======== PROC_Detach ======== ++ * Purpose: ++ * Close a DSP processor and de-allocate all (GPP) resources reserved ++ * for it. The Processor Object is deleted. ++ * Parameters: ++ * hProcessor : The processor handle. ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : InValid Handle. ++ * DSP_EFAIL : General failure. ++ * Requires: ++ * PROC Initialized. ++ * Ensures: ++ * PROC Object is destroyed. ++ */ ++ extern DSP_STATUS PROC_Detach(DSP_HPROCESSOR hProcessor); ++ ++/* ++ * ======== PROC_EnumNodes ======== ++ * Purpose: ++ * Enumerate the nodes currently allocated on a processor. ++ * Parameters: ++ * hProcessor : The processor handle. ++ * aNodeTab : The first Location of an array allocated for node ++ * handles. ++ * uNodeTabSize: The number of (DSP_HNODE) handles that can be held ++ * to the memory the client has allocated for aNodeTab ++ * puNumNodes : Location where DSPProcessor_EnumNodes will return ++ * the number of valid handles written to aNodeTab ++ * puAllocated : Location where DSPProcessor_EnumNodes will return ++ * the number of nodes that are allocated on the DSP. ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_ESIZE : The amount of memory allocated for aNodeTab is ++ * insufficent. That is the number of nodes actually ++ * allocated on the DSP is greater than the value ++ * specified for uNodeTabSize. ++ * DSP_EFAIL : Unable to get Resource Information. ++ * Details: ++ * Requires ++ * puNumNodes is not NULL. ++ * puAllocated is not NULL. ++ * aNodeTab is not NULL. ++ * PROC Initialized. ++ * Ensures: ++ * Details: ++ */ ++ extern DSP_STATUS PROC_EnumNodes(DSP_HPROCESSOR hProcessor, ++ IN DSP_HNODE *aNodeTab, ++ IN u32 uNodeTabSize, ++ OUT u32 *puNumNodes, ++ OUT u32 *puAllocated); ++ ++/* ++ * ======== PROC_GetResourceInfo ======== ++ * Purpose: ++ * Enumerate the resources currently available on a processor. ++ * Parameters: ++ * hProcessor : The processor handle. ++ * uResourceType: Type of resource . ++ * pResourceInfo: Ptr to the DSP_RESOURCEINFO structure. ++ * uResourceInfoSize: Size of the structure. ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_EWRONGSTATE: The processor is not in the PROC_RUNNING state. ++ * DSP_ETIMEOUT: A timeout occured before the DSP responded to the ++ * querry. ++ * DSP_ERESTART: A Critical error has occured and the DSP is being ++ * restarted. ++ * DSP_EFAIL : Unable to get Resource Information ++ * Requires: ++ * pResourceInfo is not NULL. ++ * Parameter uResourceType is Valid.[TBD] ++ * uResourceInfoSize is >= sizeof DSP_RESOURCEINFO struct. ++ * PROC Initialized. ++ * Ensures: ++ * Details: ++ * This function currently returns ++ * DSP_ENOTIMPL, and does not write any data to the pResourceInfo struct. ++ */ ++ extern DSP_STATUS PROC_GetResourceInfo(DSP_HPROCESSOR hProcessor, ++ u32 uResourceType, ++ OUT struct DSP_RESOURCEINFO * ++ pResourceInfo, ++ u32 uResourceInfoSize); ++ ++/* ++ * ======== PROC_Exit ======== ++ * Purpose: ++ * Decrement reference count, and free resources when reference count is ++ * 0. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * PROC is initialized. ++ * Ensures: ++ * When reference count == 0, PROC's private resources are freed. ++ */ ++ extern void PROC_Exit(void); ++ ++/* ++ * ======== PROC_GetDevObject ========= ++ * Purpose: ++ * Returns the DEV Hanlde for a given Processor handle ++ * Parameters: ++ * hProcessor : Processor Handle ++ * phDevObject : Location to store the DEV Handle. ++ * Returns: ++ * DSP_SOK : Success; *phDevObject has Dev handle ++ * DSP_EFAIL : Failure; *phDevObject is zero. ++ * Requires: ++ * phDevObject is not NULL ++ * PROC Initialized. ++ * Ensures: ++ * DSP_SOK : *phDevObject is not NULL ++ * DSP_EFAIL : *phDevObject is NULL. ++ */ ++ extern DSP_STATUS PROC_GetDevObject(DSP_HPROCESSOR hProcessor, ++ struct DEV_OBJECT **phDevObject); ++ ++/* ++ * ======== PROC_Init ======== ++ * Purpose: ++ * Initialize PROC's private state, keeping a reference count on each ++ * call. ++ * Parameters: ++ * Returns: ++ * TRUE if initialized; FALSE if error occured. ++ * Requires: ++ * Ensures: ++ * TRUE: A requirement for the other public PROC functions. ++ */ ++ extern bool PROC_Init(void); ++ ++/* ++ * ======== PROC_GetState ======== ++ * Purpose: ++ * Report the state of the specified DSP processor. ++ * Parameters: ++ * hProcessor : The processor handle. ++ * pProcStatus : Ptr to location to store the DSP_PROCESSORSTATE ++ * structure. ++ * uStateInfoSize: Size of DSP_PROCESSORSTATE. ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_EFAIL : General failure while querying processor state. ++ * Requires: ++ * pProcStatus is not NULL ++ * uStateInfoSize is >= than the size of DSP_PROCESSORSTATE structure. ++ * PROC Initialized. ++ * Ensures: ++ * Details: ++ */ ++ extern DSP_STATUS PROC_GetState(DSP_HPROCESSOR hProcessor, ++ OUT struct DSP_PROCESSORSTATE ++ *pProcStatus, ++ u32 uStateInfoSize); ++ ++/* ++ * ======== PROC_GetProcessorID ======== ++ * Purpose: ++ * Report the state of the specified DSP processor. ++ * Parameters: ++ * hProcessor : The processor handle. ++ * procID : Processor ID ++ * ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_EFAIL : General failure while querying processor state. ++ * Requires: ++ * pProcStatus is not NULL ++ * uStateInfoSize is >= than the size of DSP_PROCESSORSTATE structure. ++ * PROC Initialized. ++ * Ensures: ++ * Details: ++ */ ++ extern DSP_STATUS PROC_GetProcessorId(DSP_HPROCESSOR hProcessor, ++ u32 *procID); ++ ++/* ++ * ======== PROC_GetTrace ======== ++ * Purpose: ++ * Retrieve the trace buffer from the specified DSP processor. ++ * Parameters: ++ * hProcessor : The processor handle. ++ * pBuf : Ptr to buffer to hold trace output. ++ * uMaxSize : Maximum size of the output buffer. ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_EFAIL : General failure while retireving processor trace ++ * Buffer. ++ * Requires: ++ * pBuf is not NULL ++ * uMaxSize is > 0. ++ * PROC Initialized. ++ * Ensures: ++ * Details: ++ */ ++ extern DSP_STATUS PROC_GetTrace(DSP_HPROCESSOR hProcessor, u8 *pBuf, ++ u32 uMaxSize); ++ ++/* ++ * ======== PROC_Load ======== ++ * Purpose: ++ * Reset a processor and load a new base program image. ++ * This will be an OEM-only function. ++ * Parameters: ++ * hProcessor : The processor handle. ++ * iArgc : The number of Arguments(strings)in the aArgV[] ++ * aArgv : An Array of Arguments(Unicode Strings) ++ * aEnvp : An Array of Environment settings(Unicode Strings) ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EFILE : The DSP Execuetable was not found. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_ECORRUTFILE: Unable to Parse the DSP Execuetable ++ * DSP_EATTACHED: Abort because a GPP Client is attached to the ++ * specified Processor ++ * DSP_EACCESSDENIED: Client does not have the required access rights ++ * to reset and load the Processor ++ * DSP_EFAIL : Unable to Load the Processor ++ * Requires: ++ * aArgv is not NULL ++ * iArgc is > 0 ++ * PROC Initialized. ++ * Ensures: ++ * Success and ProcState == PROC_LOADED ++ * or DSP_FAILED status. ++ * Details: ++ * Does not implement access rights to control which GPP application ++ * can load the processor. ++ */ ++ extern DSP_STATUS PROC_Load(DSP_HPROCESSOR hProcessor, ++ IN CONST s32 iArgc, IN CONST char **aArgv, ++ IN CONST char **aEnvp); ++ ++/* ++ * ======== PROC_RegisterNotify ======== ++ * Purpose: ++ * Register to be notified of specific processor events ++ * Parameters: ++ * hProcessor : The processor handle. ++ * uEventMask : Mask of types of events to be notified about. ++ * uNotifyType : Type of notification to be sent. ++ * hNotification: Handle to be used for notification. ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : Invalid processor handle or hNotification. ++ * DSP_EVALUE : Parameter uEventMask is Invalid ++ * DSP_ENOTIMP : The notification type specified in uNotifyMask ++ * is not supported. ++ * DSP_EFAIL : Unable to register for notification. ++ * Requires: ++ * hNotification is not NULL ++ * PROC Initialized. ++ * Ensures: ++ * Details: ++ */ ++ extern DSP_STATUS PROC_RegisterNotify(DSP_HPROCESSOR hProcessor, ++ u32 uEventMask, u32 uNotifyType, ++ struct DSP_NOTIFICATION ++ *hNotification); ++ ++/* ++ * ======== PROC_NotifyClients ======== ++ * Purpose: ++ * Notify the Processor Clients ++ * Parameters: ++ * hProc : The processor handle. ++ * uEvents : Event to be notified about. ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_EFAIL : Failure to Set or Reset the Event ++ * Requires: ++ * uEvents is Supported or Valid type of Event ++ * hProc is a valid handle ++ * PROC Initialized. ++ * Ensures: ++ */ ++ extern DSP_STATUS PROC_NotifyClients(DSP_HPROCESSOR hProc, ++ u32 uEvents); ++ ++/* ++ * ======== PROC_NotifyAllClients ======== ++ * Purpose: ++ * Notify the Processor Clients ++ * Parameters: ++ * hProc : The processor handle. ++ * uEvents : Event to be notified about. ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_EFAIL : Failure to Set or Reset the Event ++ * Requires: ++ * uEvents is Supported or Valid type of Event ++ * hProc is a valid handle ++ * PROC Initialized. ++ * Ensures: ++ * Details: ++ * NODE And STRM would use this function to notify their clients ++ * about the state changes in NODE or STRM. ++ */ ++ extern DSP_STATUS PROC_NotifyAllClients(DSP_HPROCESSOR hProc, ++ u32 uEvents); ++ ++/* ++ * ======== PROC_Start ======== ++ * Purpose: ++ * Start a processor running. ++ * Processor must be in PROC_LOADED state. ++ * This will be an OEM-only function, and not part of the 'Bridge ++ * application developer's API. ++ * Parameters: ++ * hProcessor : The processor handle. ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_EWRONGSTATE: Processor is not in PROC_LOADED state. ++ * DSP_EFAIL : Unable to start the processor. ++ * Requires: ++ * PROC Initialized. ++ * Ensures: ++ * Success and ProcState == PROC_RUNNING or DSP_FAILED status. ++ * Details: ++ */ ++ extern DSP_STATUS PROC_Start(DSP_HPROCESSOR hProcessor); ++ ++/* ++ * ======== PROC_Stop ======== ++ * Purpose: ++ * Start a processor running. ++ * Processor must be in PROC_LOADED state. ++ * This will be an OEM-only function, and not part of the 'Bridge ++ * application developer's API. ++ * Parameters: ++ * hProcessor : The processor handle. ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_EWRONGSTATE: Processor is not in PROC_LOADED state. ++ * DSP_EFAIL : Unable to start the processor. ++ * Requires: ++ * PROC Initialized. ++ * Ensures: ++ * Success and ProcState == PROC_RUNNING or DSP_FAILED status. ++ * Details: ++ */ ++ extern DSP_STATUS PROC_Stop(DSP_HPROCESSOR hProcessor); ++ ++/* ++ * ======== PROC_FlushMemory ======== ++ * Purpose: ++ * Flushes a buffer from the MPU data cache. ++ * Parameters: ++ * hProcessor : The processor handle. ++ * pMpuAddr : Buffer start address ++ * ulSize : Buffer size ++ * ulFlags : Reserved. ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_EFAIL : General failure. ++ * Requires: ++ * PROC Initialized. ++ * Ensures: ++ * Details: ++ * All the arguments are currently ignored. ++ */ ++ extern DSP_STATUS PROC_FlushMemory(DSP_HPROCESSOR hProcessor, ++ void *pMpuAddr, ++ u32 ulSize, u32 ulFlags); ++ ++ ++/* ++ * ======== PROC_InvalidateMemory ======== ++ * Purpose: ++ * Invalidates a buffer from the MPU data cache. ++ * Parameters: ++ * hProcessor : The processor handle. ++ * pMpuAddr : Buffer start address ++ * ulSize : Buffer size ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_EFAIL : General failure. ++ * Requires: ++ * PROC Initialized. ++ * Ensures: ++ * Details: ++ * All the arguments are currently ignored. ++ */ ++ extern DSP_STATUS PROC_InvalidateMemory(DSP_HPROCESSOR hProcessor, ++ void *pMpuAddr, ++ u32 ulSize); ++ ++/* ++ * ======== PROC_Map ======== ++ * Purpose: ++ * Maps a MPU buffer to DSP address space. ++ * Parameters: ++ * hProcessor : The processor handle. ++ * pMpuAddr : Starting address of the memory region to map. ++ * ulSize : Size of the memory region to map. ++ * pReqAddr : Requested DSP start address. Offset-adjusted actual ++ * mapped address is in the last argument. ++ * ppMapAddr : Ptr to DSP side mapped u8 address. ++ * ulMapAttr : Optional endianness attributes, virt to phys flag. ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_EFAIL : General failure. ++ * DSP_EMEMORY : MPU side memory allocation error. ++ * DSP_ENOTFOUND : Cannot find a reserved region starting with this ++ * : address. ++ * Requires: ++ * pMpuAddr is not NULL ++ * ulSize is not zero ++ * ppMapAddr is not NULL ++ * PROC Initialized. ++ * Ensures: ++ * Details: ++ */ ++ extern DSP_STATUS PROC_Map(DSP_HPROCESSOR hProcessor, ++ void *pMpuAddr, ++ u32 ulSize, ++ void *pReqAddr, ++ void **ppMapAddr, u32 ulMapAttr); ++ ++/* ++ * ======== PROC_ReserveMemory ======== ++ * Purpose: ++ * Reserve a virtually contiguous region of DSP address space. ++ * Parameters: ++ * hProcessor : The processor handle. ++ * ulSize : Size of the address space to reserve. ++ * ppRsvAddr : Ptr to DSP side reserved u8 address. ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_EFAIL : General failure. ++ * DSP_EMEMORY : Cannot reserve chunk of this size. ++ * Requires: ++ * ppRsvAddr is not NULL ++ * PROC Initialized. ++ * Ensures: ++ * Details: ++ */ ++ extern DSP_STATUS PROC_ReserveMemory(DSP_HPROCESSOR hProcessor, ++ u32 ulSize, void **ppRsvAddr); ++ ++/* ++ * ======== PROC_UnMap ======== ++ * Purpose: ++ * Removes a MPU buffer mapping from the DSP address space. ++ * Parameters: ++ * hProcessor : The processor handle. ++ * pMapAddr : Starting address of the mapped memory region. ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_EFAIL : General failure. ++ * DSP_ENOTFOUND : Cannot find a mapped region starting with this ++ * : address. ++ * Requires: ++ * pMapAddr is not NULL ++ * PROC Initialized. ++ * Ensures: ++ * Details: ++ */ ++ extern DSP_STATUS PROC_UnMap(DSP_HPROCESSOR hProcessor, void *pMapAddr); ++ ++/* ++ * ======== PROC_UnReserveMemory ======== ++ * Purpose: ++ * Frees a previously reserved region of DSP address space. ++ * Parameters: ++ * hProcessor : The processor handle. ++ * pRsvAddr : Ptr to DSP side reservedBYTE address. ++ * Returns: ++ * DSP_SOK : Success. ++ * DSP_EHANDLE : Invalid processor handle. ++ * DSP_EFAIL : General failure. ++ * DSP_ENOTFOUND : Cannot find a reserved region starting with this ++ * : address. ++ * Requires: ++ * pRsvAddr is not NULL ++ * PROC Initialized. ++ * Ensures: ++ * Details: ++ */ ++ extern DSP_STATUS PROC_UnReserveMemory(DSP_HPROCESSOR hProcessor, ++ void *pRsvAddr); ++ ++#endif /* PROC_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/procpriv.h b/arch/arm/plat-omap/include/dspbridge/procpriv.h +new file mode 100644 +index 0000000..21d4b3e +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/procpriv.h +@@ -0,0 +1,35 @@ ++/* ++ * procpriv.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== procpriv.h ======== ++ * Description: ++ * Global PROC constants and types, shared by PROC, MGR, and WCD. ++ * ++ *! Revision History: ++ *! ================ ++ *! 05-July-2000 rr: Created ++ */ ++ ++#ifndef PROCPRIV_ ++#define PROCPRIV_ ++ ++/* RM PROC Object */ ++ struct PROC_OBJECT; ++ ++#endif /* PROCPRIV_ */ ++ +diff --git a/arch/arm/plat-omap/include/dspbridge/pwr.h b/arch/arm/plat-omap/include/dspbridge/pwr.h +new file mode 100644 +index 0000000..a6645ca +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/pwr.h +@@ -0,0 +1,129 @@ ++/* ++ * pwr.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== pwr.h ======== ++ * ++ * Public Functions: ++ * ++ * PWR_SleepDSP ++ * PWR_WakeDSP ++ * ++ * Notes: ++ * ++ *! Revision History: ++ *! ================ ++ *! 06-Jun-2002 sg Replaced dspdefs.h with includes of dbdefs.h and errbase.h. ++ *! 13-May-2002 sg Added DSP_SAREADYASLEEP and DSP_SALREADYAWAKE. ++ *! 09-May-2002 sg Updated, added timeouts. ++ *! 02-May-2002 sg Initial. ++ */ ++ ++#ifndef PWR_ ++#define PWR_ ++ ++#include ++#include ++#include ++ ++/* ++ * ======== PWR_SleepDSP ======== ++ * Signal the DSP to go to sleep. ++ * ++ * Parameters: ++ * sleepCode: New sleep state for DSP. (Initially, valid codes ++ * are PWR_DEEPSLEEP or PWR_EMERGENCYDEEPSLEEP; both of ++ * these codes will simply put the DSP in deep sleep.) ++ * ++ * timeout: Maximum time (msec) that PWR should wait for ++ * confirmation that the DSP sleep state has been ++ * reached. If PWR should simply send the command to ++ * the DSP to go to sleep and then return (i.e., ++ * asynchrounous sleep), the timeout should be ++ * specified as zero. ++ * ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_SALREADYASLEEP: Success, but the DSP was already asleep. ++ * DSP_EINVALIDARG: The specified sleepCode is not supported. ++ * DSP_ETIMEOUT: A timeout occured while waiting for DSP sleep ++ * confirmation. ++ * DSP_EFAIL: General failure, unable to send sleep command to ++ * the DSP. ++ */ ++ extern DSP_STATUS PWR_SleepDSP(IN CONST u32 sleepCode, ++ IN CONST u32 timeout); ++ ++/* ++ * ======== PWR_WakeDSP ======== ++ * Signal the DSP to wake from sleep. ++ * ++ * Parameters: ++ * timeout: Maximum time (msec) that PWR should wait for ++ * confirmation that the DSP is awake. If PWR should ++ * simply send a command to the DSP to wake and then ++ * return (i.e., asynchrounous wake), timeout should ++ * be specified as zero. ++ * ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_SALREADYAWAKE: Success, but the DSP was already awake. ++ * DSP_ETIMEOUT: A timeout occured while waiting for wake ++ * confirmation. ++ * DSP_EFAIL: General failure, unable to send wake command to ++ * the DSP. ++ */ ++ extern DSP_STATUS PWR_WakeDSP(IN CONST u32 timeout); ++ ++/* ++ * ======== PWR_PM_PreScale ======== ++ * Prescale notification to DSP. ++ * ++ * Parameters: ++ * voltage_domain: The voltage domain for which notification is sent ++ * level: The level of voltage domain ++ * ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_SALREADYAWAKE: Success, but the DSP was already awake. ++ * DSP_ETIMEOUT: A timeout occured while waiting for wake ++ * confirmation. ++ * DSP_EFAIL: General failure, unable to send wake command to ++ * the DSP. ++ */ ++ extern DSP_STATUS PWR_PM_PreScale(IN u16 voltage_domain, u32 level); ++ ++/* ++ * ======== PWR_PM_PostScale ======== ++ * PostScale notification to DSP. ++ * ++ * Parameters: ++ * voltage_domain: The voltage domain for which notification is sent ++ * level: The level of voltage domain ++ * ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_SALREADYAWAKE: Success, but the DSP was already awake. ++ * DSP_ETIMEOUT: A timeout occured while waiting for wake ++ * confirmation. ++ * DSP_EFAIL: General failure, unable to send wake command to ++ * the DSP. ++ */ ++ extern DSP_STATUS PWR_PM_PostScale(IN u16 voltage_domain, ++ u32 level); ++ ++#endif /* PWR_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/pwr_sh.h b/arch/arm/plat-omap/include/dspbridge/pwr_sh.h +new file mode 100644 +index 0000000..40f1b84 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/pwr_sh.h +@@ -0,0 +1,41 @@ ++/* ++ * pwr_sh.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== pwr_sh.h ======== ++ * ++ * Power Manager shared definitions (used on both GPP and DSP sides). ++ * ++ *! Revision History ++ *! ================ ++ *! 17-Apr-2002 sg: Initial. ++ */ ++ ++#ifndef PWR_SH_ ++#define PWR_SH_ ++ ++#include ++ ++/* valid sleep command codes that can be sent by GPP via mailbox: */ ++#define PWR_DEEPSLEEP MBX_PM_DSPIDLE ++#define PWR_EMERGENCYDEEPSLEEP MBX_PM_EMERGENCYSLEEP ++#define PWR_SLEEPUNTILRESTART MBX_PM_SLEEPUNTILRESTART ++#define PWR_WAKEUP MBX_PM_DSPWAKEUP ++#define PWR_AUTOENABLE MBX_PM_PWRENABLE ++#define PWR_AUTODISABLE MBX_PM_PWRDISABLE ++#define PWR_RETENTION MBX_PM_DSPRETN ++ ++#endif /* PWR_SH_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/reg.h b/arch/arm/plat-omap/include/dspbridge/reg.h +new file mode 100644 +index 0000000..5b34952 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/reg.h +@@ -0,0 +1,257 @@ ++/* ++ * reg.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== reg.h ======== ++ * Purpose: ++ * Provide registry functions. ++ * ++ * Public Functions: ++ * REG_DeleteValue ++ * REG_EnumKey ++ * REG_EnumValue ++ * REG_Exit ++ * REG_GetValue ++ * REG_Init ++ * REG_SetValue ++ * ++ *! Revision History: ++ *! ================= ++ *! 30-Oct-2000 kc: Updated REG_SetValue & REG_GetValue; renamed ++ *! REG_DeleteEntry to REG_DeleteValue. ++ *! 29-Sep-2000 kc: Updated a REG functions for code review. ++ *! 12-Aug-2000 kc: Renamed REG_EnumValue to REG_EnumKey. Re-implemented ++ *! REG_EnumValue. ++ *! 03-Feb-2000 rr: REG_EnumValue Fxn Added ++ *! 13-Dec-1999 rr: windows.h removed ++ *! 02-Dec-1999 rr: windows.h included for retail build ++ *! 22-Nov-1999 kc: Changes from code review. ++ *! 29-Dec-1997 cr: Changes from code review. ++ *! 27-Oct-1997 cr: Added REG_DeleteValue. ++ *! 20-Oct-1997 cr: Added ability to pass bValue = NULL to REG_GetValue ++ *! and return size of reg entry in pdwValueSize. ++ *! 29-Sep-1997 cr: Added REG_SetValue ++ *! 29-Aug-1997 cr: Created. ++ */ ++ ++#ifndef _REG_H ++#define _REG_H ++ ++#include ++ ++/* ------------------------- Defines, Data Structures, Typedefs for Linux */ ++#ifndef UNDER_CE ++ ++#ifndef REG_SZ ++#define REG_SZ 1 ++#endif ++ ++#ifndef REG_BINARY ++#define REG_BINARY 3 ++#endif ++ ++#ifndef REG_DWORD ++#define REG_DWORD 4 ++#endif ++ ++#endif /* UNDER_CE */ ++ ++#define REG_MAXREGPATHLENGTH 255 ++ ++/* ++ * ======== REG_DeleteValue ======== ++ * Purpose: ++ * Deletes a registry entry. NOTE: A registry entry is not the same as ++ * a registry key. ++ * Parameters: ++ * phKey: Currently reserved; must be NULL. ++ * pstrSubkey: Path to key to open. ++ * pstrValue: Name of entry to delete. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: General failure. ++ * Requires: ++ * - REG initialized. ++ * - pstrSubkey & pstrValue are non-NULL values. ++ * - phKey is NULL. ++ * - length of pstrSubkey < REG_MAXREGPATHLENGTH. ++ * - length of pstrValue < REG_MAXREGPATHLENGTH. ++ * Ensures: ++ * Details: ++ */ ++ extern DSP_STATUS REG_DeleteValue(OPTIONAL IN HANDLE *phKey, ++ IN CONST char *pstrSubkey, ++ IN CONST char *pstrValue); ++ ++/* ++ * ======== REG_EnumKey ======== ++ * Purpose: ++ * Enumerates subkeys of the specified path to the registry key ++ * Retrieves the name of the subkey(given the index) and ++ * appends with the orignal path to form the full path. ++ * Parameters: ++ * phKey: Currently reserved; must be NULL. ++ * pstrKey The name of the registry key to be enumerated. ++ * dwIndex Specifies the index of the subkey to retrieve. ++ * pstrSubkey: Pointer to buffer that receives full path name of the ++ * specified key + the sub-key ++ * pdwValueSize: Specifies bytes of memory pstrSubkey points to on input, ++ * on output, specifies actual memory bytes written into. ++ * If there is no sub key,pdwValueSize returns NULL. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: General failure. ++ * Requires: ++ * - REG initialized. ++ * - pstrKey is non-NULL value. ++ * - pdwValueSize is a valid pointer. ++ * - phKey is NULL. ++ * - length of pstrKey < REG_MAXREGPATHLENGTH. ++ * Ensures: ++ * - strlen(pstrSubkey) is > strlen(pstrKey) && ++ * - strlen(pstrSubkey) is < REG_MAXREGPATHLENGTH ++ */ ++ extern DSP_STATUS REG_EnumKey(OPTIONAL IN HANDLE *phKey, ++ IN u32 dwIndex, IN CONST char *pstrKey, ++ IN OUT char *pstrSubkey, ++ IN OUT u32 *pdwValueSize); ++ ++/* ++ * ======== REG_EnumValue ======== ++ * Purpose: ++ * Enumerates values of a specified key. Retrieves each value name and ++ * the data associated with the value. ++ * Parameters: ++ * phKey: Currently reserved; must be NULL. ++ * dwIndex: Specifies the index of the value to retrieve. ++ * pstrKey: The name of the registry key to be enumerated. ++ * pstrValue: Pointer to buffer that receives the name of the value. ++ * pdwValueSize: Specifies bytes of memory pstrValue points to on input, ++ * On output, specifies actual memory bytes written into. ++ * If there is no value, pdwValueSize returns NULL ++ * pstrData: Pointer to buffer that receives the data of a value. ++ * pdwDataSize: Specifies bytes of memory in pstrData on input and ++ * bytes of memory written into pstrData on output. ++ * If there is no data, pdwDataSize returns NULL. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: General failure. ++ * Requires: ++ * REG initialized. ++ * phKey is NULL. ++ * pstrKey is a non-NULL value. ++ * pstrValue, pstrData, pdwValueSize and pdwDataSize are valid pointers. ++ * Length of pstrKey is less than REG_MAXREGPATHLENGTH. ++ * Ensures: ++ */ ++ extern DSP_STATUS REG_EnumValue(IN HANDLE *phKey, ++ IN u32 dwIndex, ++ IN CONST char *pstrKey, ++ IN OUT char *pstrValue, ++ IN OUT u32 *pdwValueSize, ++ IN OUT char *pstrData, ++ IN OUT u32 *pdwDataSize); ++ ++/* ++ * ======== REG_Exit ======== ++ * Purpose: ++ * Discontinue usage of module; free resources when reference count ++ * reaches 0. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * REG initialized. ++ * Ensures: ++ * Resources used by module are freed when cRef reaches zero. ++ */ ++ extern void REG_Exit(void); ++ ++/* ++ * ======== REG_GetValue ======== ++ * Purpose: ++ * Retrieve a value from the registry. ++ * Parameters: ++ * phKey: Currently reserved; must be NULL. ++ * pstrSubkey: Path to key to open. ++ * pstrEntry: Name of entry to retrieve. ++ * pbValue: Upon return, points to retrieved value. ++ * pdwValueSize: Specifies bytes of memory pbValue points to on input, ++ * on output, specifies actual memory bytes written into. ++ * If pbValue is NULL, pdwValueSize reports the size of ++ * the entry in pstrEntry. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: General failure. ++ * Requires: ++ * - REG initialized. ++ * - pstrSubkey & pstrEntry are non-NULL values. ++ * - pbValue is a valid pointer. ++ * - phKey is NULL. ++ * - length of pstrSubkey < REG_MAXREGPATHLENGTH. ++ * - length of pstrEntry < REG_MAXREGPATHLENGTH. ++ * Ensures: ++ */ ++ extern DSP_STATUS REG_GetValue(OPTIONAL IN HANDLE *phKey, ++ IN CONST char *pstrSubkey, ++ IN CONST char *pstrEntry, ++ OUT u8 *pbValue, ++ IN OUT u32 *pdwValueSize); ++ ++/* ++ * ======== REG_Init ======== ++ * Purpose: ++ * Initializes private state of REG module. ++ * Parameters: ++ * Returns: ++ * TRUE if initialized; FALSE if error occured. ++ * Requires: ++ * Ensures: ++ * REG initialized. ++ */ ++ extern bool REG_Init(void); ++ ++/* ++ * ======== REG_SetValue ======== ++ * Purpose: ++ * Set a value in the registry. ++ * Parameters: ++ * phKey: Handle to open reg key, or NULL if pSubkey is full path. ++ * pstrSubkey: Path to key to open, could be based on phKey. ++ * pstrEntry: Name of entry to set. ++ * dwType: Data type of new registry value. ++ * pbValue: Points to buffer containing new data. ++ * dwValueSize: Specifies bytes of memory bValue points to. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: General failure. ++ * Requires: ++ * - REG initialized. ++ * - pstrSubkey & pstrEntry are non-NULL values. ++ * - pbValue is a valid pointer. ++ * - phKey is NULL. ++ * - dwValuSize > 0. ++ * - length of pstrSubkey < REG_MAXREGPATHLENGTH. ++ * - length of pstrEntry < REG_MAXREGPATHLENGTH. ++ * Ensures: ++ */ ++ extern DSP_STATUS REG_SetValue(OPTIONAL IN HANDLE *phKey, ++ IN CONST char *pstrSubKey, ++ IN CONST char *pstrEntry, ++ IN CONST u32 dwType, ++ IN u8 *pbValue, IN u32 dwValueSize); ++ ++#endif /* _REG_H */ +diff --git a/arch/arm/plat-omap/include/dspbridge/resourcecleanup.h b/arch/arm/plat-omap/include/dspbridge/resourcecleanup.h +new file mode 100644 +index 0000000..b43fa16 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/resourcecleanup.h +@@ -0,0 +1,88 @@ ++/* ++ * resourcecleanup.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef RES_CLEANUP_DISABLE ++ ++#include ++#include ++ ++ ++extern DSP_STATUS DRV_GetProcCtxtList(struct PROCESS_CONTEXT **pPctxt, ++ struct DRV_OBJECT *hDrvObject); ++ ++extern DSP_STATUS DRV_InsertProcContext(struct DRV_OBJECT *hDrVObject, ++ HANDLE hPCtxt); ++ ++extern DSP_STATUS DRV_RemoveAllDMMResElements(HANDLE pCtxt); ++ ++extern DSP_STATUS DRV_RemoveAllNodeResElements(HANDLE pCtxt); ++ ++extern DSP_STATUS DRV_ProcUpdatestate(HANDLE pCtxt, ++ enum GPP_PROC_RES_STATE resState); ++ ++extern DSP_STATUS DRV_ProcSetPID(HANDLE pCtxt, s32 hProcess); ++ ++extern DSP_STATUS DRV_GetProcContext(u32 phProcess, ++ struct DRV_OBJECT *hDrvObject, ++ HANDLE hPCtxt, DSP_HNODE hNode, ++ u32 pMapAddr); ++ ++extern DSP_STATUS DRV_RemoveAllResources(HANDLE pPctxt); ++ ++extern DSP_STATUS DRV_RemoveProcContext(struct DRV_OBJECT *hDRVObject, ++ HANDLE hPCtxt, HANDLE hProcess); ++ ++extern DSP_STATUS DRV_GetNodeResElement(HANDLE hNode, HANDLE nodeRes, ++ HANDLE pCtxt); ++ ++extern DSP_STATUS DRV_InsertNodeResElement(HANDLE hNode, HANDLE nodeRes, ++ HANDLE pCtxt); ++ ++extern void DRV_ProcNodeUpdateHeapStatus(HANDLE hNodeRes, s32 status); ++ ++extern DSP_STATUS DRV_RemoveNodeResElement(HANDLE nodeRes, HANDLE status); ++ ++extern void DRV_ProcNodeUpdateStatus(HANDLE hNodeRes, s32 status); ++ ++extern DSP_STATUS DRV_UpdateDMMResElement(HANDLE dmmRes, u32 pMpuAddr, ++ u32 ulSize, u32 pReqAddr, ++ u32 ppMapAddr, HANDLE hProcesso); ++ ++extern DSP_STATUS DRV_InsertDMMResElement(HANDLE dmmRes, HANDLE pCtxt); ++ ++extern DSP_STATUS DRV_GetDMMResElement(u32 pMapAddr, HANDLE dmmRes, ++ HANDLE pCtxt); ++ ++extern DSP_STATUS DRV_RemoveDMMResElement(HANDLE dmmRes, HANDLE pCtxt); ++ ++extern DSP_STATUS DRV_ProcUpdateSTRMRes(u32 uNumBufs, HANDLE STRMRes, ++ HANDLE pCtxt); ++ ++extern DSP_STATUS DRV_ProcInsertSTRMResElement(HANDLE hStrm, HANDLE STRMRes, ++ HANDLE pPctxt); ++ ++extern DSP_STATUS DRV_GetSTRMResElement(HANDLE hStrm, HANDLE STRMRes, ++ HANDLE pCtxt); ++ ++extern DSP_STATUS DRV_ProcRemoveSTRMResElement(HANDLE STRMRes, HANDLE pCtxt); ++ ++extern DSP_STATUS DRV_RemoveAllSTRMResElements(HANDLE pCtxt); ++ ++extern DSP_STATUS DRV_ProcDisplayResInfo(u8 *pBuf, u32 *pSize); ++ ++extern enum NODE_STATE NODE_GetState(HANDLE hNode); ++ ++#endif +diff --git a/arch/arm/plat-omap/include/dspbridge/rmm.h b/arch/arm/plat-omap/include/dspbridge/rmm.h +new file mode 100644 +index 0000000..5b14b8f +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/rmm.h +@@ -0,0 +1,199 @@ ++/* ++ * rmm.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== rmm.h ======== ++ * ++ * This memory manager provides general heap management and arbitrary ++ * alignment for any number of memory segments, and management of overlay ++ * memory. ++ * ++ * Public functions: ++ * RMM_alloc ++ * RMM_create ++ * RMM_delete ++ * RMM_exit ++ * RMM_free ++ * RMM_init ++ * ++ *! Revision History ++ *! ================ ++ *! 25-Jun-2002 jeh Added RMM_Addr. Removed RMM_reserve, RMM_stat. ++ *! 15-Oct-2001 jeh Based on rm.h in gen tree. ++ */ ++ ++#ifndef RMM_ ++#define RMM_ ++ ++/* ++ * ======== RMM_Addr ======== ++ * DSP address + segid ++ */ ++struct RMM_Addr { ++ u32 addr; ++ s32 segid; ++} ; ++ ++/* ++ * ======== RMM_Segment ======== ++ * Memory segment on the DSP available for remote allocations. ++ */ ++struct RMM_Segment { ++ u32 base; /* Base of the segment */ ++ u32 length; /* Size of the segment (target MAUs) */ ++ s32 space; /* Code or data */ ++ u32 number; /* Number of Allocated Blocks */ ++} ; ++ ++/* ++ * ======== RMM_Target ======== ++ */ ++struct RMM_TargetObj; ++ ++/* ++ * ======== RMM_alloc ======== ++ * ++ * RMM_alloc is used to remotely allocate or reserve memory on the DSP. ++ * ++ * Parameters: ++ * target - Target returned from RMM_create(). ++ * segid - Memory segment to allocate from. ++ * size - Size (target MAUS) to allocate. ++ * align - alignment. ++ * dspAddr - If reserve is FALSE, the location to store allocated ++ * address on output, otherwise, the DSP address to ++ * reserve. ++ * reserve - If TRUE, reserve the memory specified by dspAddr. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Memory allocation on GPP failed. ++ * DSP_EOVERLAYMEMORY: Cannot "allocate" overlay memory because it's ++ * already in use. ++ * Requires: ++ * RMM initialized. ++ * Valid target. ++ * dspAddr != NULL. ++ * size > 0 ++ * reserve || target->numSegs > 0. ++ * Ensures: ++ */ ++extern DSP_STATUS RMM_alloc(struct RMM_TargetObj *target, u32 segid, u32 size, ++ u32 align, u32 *dspAdr, bool reserve); ++ ++/* ++ * ======== RMM_create ======== ++ * Create a target object with memory segments for remote allocation. If ++ * segTab == NULL or numSegs == 0, memory can only be reserved through ++ * RMM_alloc(). ++ * ++ * Parameters: ++ * pTarget: - Location to store target on output. ++ * segTab: - Table of memory segments. ++ * numSegs: - Number of memory segments. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Memory allocation failed. ++ * Requires: ++ * RMM initialized. ++ * pTarget != NULL. ++ * numSegs == 0 || segTab != NULL. ++ * Ensures: ++ * Success: Valid *pTarget. ++ * Failure: *pTarget == NULL. ++ */ ++extern DSP_STATUS RMM_create(struct RMM_TargetObj **pTarget, ++ struct RMM_Segment segTab[], u32 numSegs); ++ ++/* ++ * ======== RMM_delete ======== ++ * Delete target allocated in RMM_create(). ++ * ++ * Parameters: ++ * target - Target returned from RMM_create(). ++ * Returns: ++ * Requires: ++ * RMM initialized. ++ * Valid target. ++ * Ensures: ++ */ ++extern void RMM_delete(struct RMM_TargetObj *target); ++ ++/* ++ * ======== RMM_exit ======== ++ * Exit the RMM module ++ * ++ * Parameters: ++ * Returns: ++ * Requires: ++ * RMM_init successfully called. ++ * Ensures: ++ */ ++extern void RMM_exit(void); ++ ++/* ++ * ======== RMM_free ======== ++ * Free or unreserve memory allocated through RMM_alloc(). ++ * ++ * Parameters: ++ * target: - Target returned from RMM_create(). ++ * segid: - Segment of memory to free. ++ * dspAddr: - Address to free or unreserve. ++ * size: - Size of memory to free or unreserve. ++ * reserved: - TRUE if memory was reserved only, otherwise FALSE. ++ * Returns: ++ * Requires: ++ * RMM initialized. ++ * Valid target. ++ * reserved || segid < target->numSegs. ++ * reserve || [dspAddr, dspAddr + size] is a valid memory range. ++ * Ensures: ++ */ ++extern bool RMM_free(struct RMM_TargetObj *target, u32 segid, u32 dspAddr, ++ u32 size, bool reserved); ++ ++/* ++ * ======== RMM_init ======== ++ * Initialize the RMM module ++ * ++ * Parameters: ++ * Returns: ++ * TRUE: Success. ++ * FALSE: Failure. ++ * Requires: ++ * Ensures: ++ */ ++extern bool RMM_init(void); ++ ++/* ++ * ======== RMM_stat ======== ++ * Obtain memory segment status ++ * ++ * Parameters: ++ * segid: Segment ID of the dynamic loading segment. ++ * pMemStatBuf: Pointer to allocated buffer into which memory stats are ++ * placed. ++ * Returns: ++ * TRUE: Success. ++ * FALSE: Failure. ++ * Requires: ++ * segid < target->numSegs ++ * Ensures: ++ */ ++extern bool RMM_stat(struct RMM_TargetObj *target, enum DSP_MEMTYPE segid, ++ struct DSP_MEMSTAT *pMemStatBuf); ++ ++#endif /* RMM_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/rms_sh.h b/arch/arm/plat-omap/include/dspbridge/rms_sh.h +new file mode 100644 +index 0000000..5d4b49a +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/rms_sh.h +@@ -0,0 +1,125 @@ ++/* ++ * rms_sh.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== rms_sh.h ======== ++ * ++ * DSP/BIOS Bridge Resource Manager Server shared definitions (used on both ++ * GPP and DSP sides). ++ * ++ *! Revision History ++ *! ================ ++ *! 24-Mar-2003 vp Merged updates required for CCS2.2 transition. ++ *! 24-Feb-2003 kc Rearranged order of node types to temporarily support ++ *! legacy message node code ++ *! 23-Nov-2002 gp Converted tabs -> spaces, to fix formatting. ++ *! 13-Feb-2002 jeh Added sysstacksize element to RMS_MoreTaskArgs. ++ *! 11-Dec-2000 sg Added 'misc' element to RMS_MoreTaskArgs. ++ *! 04-Dec-2000 ag Added RMS_BUFDESC command code. ++ *! C/R code value changed to allow ORing of system/user codes. ++ *! 10-Oct-2000 sg Added 'align' field to RMS_StrmDef. ++ *! 09-Oct-2000 sg Moved pre-defined message codes here from rmsdefs.h. ++ *! 02-Oct-2000 sg Changed ticks to msec. ++ *! 24-Aug-2000 sg Moved definitions that will be exposed to app developers ++ *! to a separate file, rmsdefs.h. ++ *! 10-Aug-2000 sg Added RMS_COMMANDBUFSIZE and RMS_RESPONSEBUFSIZE; added ++ *! pre-defined command/response codes; more comments. ++ *! 09-Aug-2000 sg Added RMS_ETASK. ++ *! 08-Aug-2000 jeh Define RMS_WORD for GPP, rename DSP_MSG to RMS_DSPMSG. ++ *! Added RMS_MsgArgs, RMS_MoreTaskArgs. ++ *! 25-Jul-2000 sg: Changed SIO to STRM. ++ *! 30-Jun-2000 sg: Initial. ++ */ ++ ++#ifndef RMS_SH_ ++#define RMS_SH_ ++ ++#include ++ ++/* Node Types: */ ++#define RMS_TASK 1 /* Task node */ ++#define RMS_DAIS 2 /* xDAIS socket node */ ++#define RMS_MSG 3 /* Message node */ ++ ++/* Memory Types: */ ++#define RMS_CODE 0 /* Program space */ ++#define RMS_DATA 1 /* Data space */ ++#define RMS_IO 2 /* I/O space */ ++ ++/* RM Server Command and Response Buffer Sizes: */ ++#define RMS_COMMANDBUFSIZE 256 /* Size of command buffer */ ++#define RMS_RESPONSEBUFSIZE 16 /* Size of response buffer */ ++ ++/* Pre-Defined Command/Response Codes: */ ++#define RMS_EXIT 0x80000000 /* GPP->Node: shutdown */ ++#define RMS_EXITACK 0x40000000 /* Node->GPP: ack shutdown */ ++#define RMS_BUFDESC 0x20000000 /* Arg1 SM buf, Arg2 is SM size */ ++#define RMS_KILLTASK 0x10000000 /* GPP->Node: Kill Task */ ++#define RMS_USER 0x0 /* Start of user-defined msg codes */ ++#define RMS_MAXUSERCODES 0xfff /* Maximum user defined C/R Codes */ ++ ++ ++/* RM Server RPC Command Structure: */ ++ struct RMS_Command { ++ RMS_WORD fxn; /* Server function address */ ++ RMS_WORD arg1; /* First argument */ ++ RMS_WORD arg2; /* Second argument */ ++ RMS_WORD data; /* Function-specific data array */ ++ } ; ++ ++/* ++ * The RMS_StrmDef structure defines the parameters for both input and output ++ * streams, and is passed to a node's create function. ++ */ ++ struct RMS_StrmDef { ++ RMS_WORD bufsize; /* Buffer size (in DSP words) */ ++ RMS_WORD nbufs; /* Max number of bufs in stream */ ++ RMS_WORD segid; /* Segment to allocate buffers */ ++ RMS_WORD align; /* Alignment for allocated buffers */ ++ RMS_WORD timeout; /* Timeout (msec) for blocking calls */ ++ RMS_CHAR name[1]; /* Device Name (terminated by '\0') */ ++ } ; ++ ++/* Message node create args structure: */ ++ struct RMS_MsgArgs { ++ RMS_WORD maxMessages; /* Max # simultaneous msgs to node */ ++ RMS_WORD segid; /* Mem segment for NODE_allocMsgBuf */ ++ RMS_WORD notifyType; /* Type of message notification */ ++ RMS_WORD argLength; /* Length (in DSP chars) of arg data */ ++ RMS_WORD argData; /* Arg data for node */ ++ } ; ++ ++/* Partial task create args structure */ ++ struct RMS_MoreTaskArgs { ++ RMS_WORD priority; /* Task's runtime priority level */ ++ RMS_WORD stackSize; /* Task's stack size */ ++ RMS_WORD sysstackSize; /* Task's system stack size (55x) */ ++ RMS_WORD stackSeg; /* Memory segment for task's stack */ ++ RMS_WORD heapAddr; /* base address of the node memory heap in ++ * external memory (DSP virtual address) */ ++ RMS_WORD heapSize; /* size in MAUs of the node memory heap in ++ * external memory */ ++ RMS_WORD misc; /* Misc field. Not used for 'normal' ++ * task nodes; for xDAIS socket nodes ++ * specifies the IALG_Fxn pointer. ++ */ ++ /* # input STRM definition structures */ ++ RMS_WORD numInputStreams; ++ } ; ++ ++#endif /* RMS_SH_ */ ++ +diff --git a/arch/arm/plat-omap/include/dspbridge/rmstypes.h b/arch/arm/plat-omap/include/dspbridge/rmstypes.h +new file mode 100644 +index 0000000..13d752e +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/rmstypes.h +@@ -0,0 +1,40 @@ ++/* ++ * rmstypes.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== rmstypes.h ======== ++ * ++ * DSP/BIOS Bridge Resource Manager Server shared data type definitions. ++ * ++ *! Revision History ++ *! ================ ++ *! 06-Oct-2000 sg Added LgFxn type. ++ *! 05-Oct-2000 sg Changed RMS_STATUS to LgUns. ++ *! 31-Aug-2000 sg Added RMS_DSPMSG. ++ *! 25-Aug-2000 sg Initial. ++ */ ++ ++#ifndef RMSTYPES_ ++#define RMSTYPES_ ++#include ++/* ++ * DSP-side definitions. ++ */ ++#include ++typedef u32 RMS_WORD; ++typedef char RMS_CHAR; ++ ++#endif /* RMSTYPES_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/services.h b/arch/arm/plat-omap/include/dspbridge/services.h +new file mode 100644 +index 0000000..35bab0d +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/services.h +@@ -0,0 +1,63 @@ ++/* ++ * services.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== services.h ======== ++ * Purpose: ++ * Provide loading and unloading of SERVICES modules. ++ * ++ * Public Functions: ++ * SERVICES_Exit(void) ++ * SERVICES_Init(void) ++ * ++ *! Revision History: ++ *! ================ ++ *! 01-Feb-2000 kc: Created. ++ */ ++ ++#ifndef SERVICES_ ++#define SERVICES_ ++ ++#include ++/* ++ * ======== SERVICES_Exit ======== ++ * Purpose: ++ * Discontinue usage of module; free resources when reference count ++ * reaches 0. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * SERVICES initialized. ++ * Ensures: ++ * Resources used by module are freed when cRef reaches zero. ++ */ ++ extern void SERVICES_Exit(void); ++ ++/* ++ * ======== SERVICES_Init ======== ++ * Purpose: ++ * Initializes SERVICES modules. ++ * Parameters: ++ * Returns: ++ * TRUE if all modules initialized; otherwise FALSE. ++ * Requires: ++ * Ensures: ++ * SERVICES modules initialized. ++ */ ++ extern bool SERVICES_Init(void); ++ ++#endif /* SERVICES_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/std.h b/arch/arm/plat-omap/include/dspbridge/std.h +new file mode 100644 +index 0000000..ec849f4 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/std.h +@@ -0,0 +1,143 @@ ++/* ++ * std.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== std.h ======== ++ * ++ *! Revision History ++ *! ================ ++ *! 16-Feb-2004 vp GNU compiler 3.x defines inline keyword. Added ++ *! appropriate macros not to redefine inline keyword in ++ *! this file. ++ *! 24-Oct-2002 ashu defined _TI_ and _FIXED_ symbols for 28x. ++ *! 24-Oct-2002 ashu defined _TI_ for 24x. ++ *! 01-Mar-2002 kln changed LARGE_MODEL and Arg definition for 28x ++ *! 01-Feb-2002 kln added definitions for 28x ++ *! 08-Dec-2000 kw: added 'ArgToInt' and 'ArgToPtr' macros ++ *! 30-Nov-2000 mf: Added _64_, _6x_; removed _7d_ ++ *! 30-May-2000 srid: Added __TMS320C55X__ for 55x; Arg is void * for 55 . ++ *! 18-Jun-1999 dr: Added '_TI_', fixed __inline for SUN4, added inline ++ *! 10-Feb-1999 rt: Added '55' support, changed 54's symbol to _TMS320C5XX ++ *! 29-Aug-1998 mf: fixed typo, removed obsolete targets ++ *! 08-Jun-1998 mf: _67_ is synonym for _7d_ ++ *! 10-Oct-1997 rt; added _7d_ for Raytheon C7DSP triggered by _TMS320C6700 ++ *! 04-Aug-1997 cc: added _29_ for _TMS320C2XX ++ *! 11-Jul-1997 dlr: _5t_, and STD_SPOXTASK keyword for Tasking ++ *! 12-Jun-1997 mf: _TMS320C60 -> _TMS320C6200 ++ *! 13-Feb-1997 mf: _62_, with 32-bit LgInt ++ *! 26-Nov-1996 kw: merged bios-c00's and wsx-a27's changes ++ *! *and* revision history ++ *! 12-Sep-1996 kw: added C54x #ifdef's ++ *! 21-Aug-1996 mf: removed #define main smain for _21_ ++ *! 14-May-1996 gp: def'd out INT, FLOAT, and COMPLEX defines for WSX. ++ *! 11-Apr-1996 kw: define _W32_ based on _WIN32 (defined by MS compiler) ++ *! 07-Mar-1996 mg: added Win32 support ++ *! 06-Sep-1995 dh: added _77_ dynamic stack support via fxns77.h ++ *! 27-Jun-1995 dh: added _77_ support ++ *! 16-Mar-1995 mf: for _21_: #define main smain ++ *! 01-Mar-1995 mf: set _20_ and _60_ (as well as _21_ for both) ++ *! 22-Feb-1995 mf: Float is float for _SUN_ and _80_ ++ *! 22-Dec-1994 mf: Added _80_ definition, for PP or MP. ++ *! 09-Dec-1994 mf: Added _53_ definition. ++ *! Added definitions of _30_, etc. ++ *! 23-Aug-1994 dh removed _21_ special case (kw) ++ *! 17-Aug-1994 dh added _51_ support ++ *! 03-Aug-1994 kw updated _80_ support ++ *! 30-Jun-1994 kw added _80_ support ++ *! 05-Apr-1994 kw: Added _SUN_ to _FLOAT_ definition ++ *! 01-Mar-1994 kw: Made Bool an int (was u16) for _56_ (more efficient). ++ *! Added _53_ support. ++ */ ++ ++#ifndef STD_ ++#define STD_ ++ ++#include ++ ++/* ++ * ======== _TI_ ======== ++ * _TI_ is defined for all TI targets ++ */ ++#if defined(_29_) || defined(_30_) || defined(_40_) || defined(_50_) || \ ++ defined(_54_) || defined(_55_) || defined(_6x_) || defined(_80_) || \ ++ defined(_28_) || defined(_24_) ++#define _TI_ 1 ++#endif ++ ++/* ++ * ======== _FLOAT_ ======== ++ * _FLOAT_ is defined for all targets that natively support floating point ++ */ ++#if defined(_SUN_) || defined(_30_) || defined(_40_) || defined(_67_) || \ ++ defined(_80_) ++#define _FLOAT_ 1 ++#endif ++ ++/* ++ * ======== _FIXED_ ======== ++ * _FIXED_ is defined for all fixed point target architectures ++ */ ++#if defined(_29_) || defined(_50_) || defined(_54_) || defined(_55_) || \ ++ defined(_62_) || defined(_64_) || defined(_28_) ++#define _FIXED_ 1 ++#endif ++ ++/* ++ * ======== _TARGET_ ======== ++ * _TARGET_ is defined for all target architectures (as opposed to ++ * host-side software) ++ */ ++#if defined(_FIXED_) || defined(_FLOAT_) ++#define _TARGET_ 1 ++#endif ++ ++/* ++ * 8, 16, 32-bit type definitions ++ * ++ * Sm* - 8-bit type ++ * Md* - 16-bit type ++ * Lg* - 32-bit type ++ * ++ * *s32 - signed type ++ * *u32 - unsigned type ++ * *Bits - unsigned type (bit-maps) ++ */ ++ ++/* ++ * Aliases for standard C types ++ */ ++ ++typedef s32(*Fxn) (void); /* generic function type */ ++ ++#ifndef NULL ++#define NULL 0 ++#endif ++ ++ ++/* ++ * These macros are used to cast 'Arg' types to 's32' or 'Ptr'. ++ * These macros were added for the 55x since Arg is not the same ++ * size as s32 and Ptr in 55x large model. ++ */ ++#if defined(_28l_) || defined(_55l_) ++#define ArgToInt(A) ((s32)((long)(A) & 0xffff)) ++#define ArgToPtr(A) ((Ptr)(A)) ++#else ++#define ArgToInt(A) ((s32)(A)) ++#define ArgToPtr(A) ((Ptr)(A)) ++#endif ++ ++#endif /* STD_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/strm.h b/arch/arm/plat-omap/include/dspbridge/strm.h +new file mode 100644 +index 0000000..5825615 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/strm.h +@@ -0,0 +1,441 @@ ++/* ++ * strm.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== strm.h ======== ++ * Description: ++ * DSPBridge Stream Manager. ++ * ++ * Public Functions: ++ * STRM_AllocateBuffer ++ * STRM_Close ++ * STRM_Create ++ * STRM_Delete ++ * STRM_Exit ++ * STRM_FreeBuffer ++ * STRM_GetEventHandle ++ * STRM_GetInfo ++ * STRM_Idle ++ * STRM_Init ++ * STRM_Issue ++ * STRM_Open ++ * STRM_PrepareBuffer ++ * STRM_Reclaim ++ * STRM_RegisterNotify ++ * STRM_Select ++ * STRM_UnprepareBuffer ++ * ++ * Notes: ++ * ++ *! Revision History: ++ *! ================= ++ *! 15-Nov-2001 ag Changed DSP_STREAMINFO to STRM_INFO in STRM_GetInfo(). ++ *! Added DSP_ESIZE error to STRM_AllocateBuffer(). ++ *! 07-Jun-2001 sg Made DSPStream_AllocateBuffer fxn name plural. ++ *! 10-May-2001 jeh Code review cleanup. ++ *! 13-Feb-2001 kc DSP/BIOS Bridge name updates. ++ *! 06-Feb-2001 kc Updated DBC_Ensure for STRM_Select(). ++ *! 23-Oct-2000 jeh Allow NULL STRM_ATTRS passed to STRM_Open(). ++ *! 25-Sep-2000 jeh Created. ++ */ ++ ++#ifndef STRM_ ++#define STRM_ ++ ++#include ++ ++#include ++ ++/* ++ * ======== STRM_AllocateBuffer ======== ++ * Purpose: ++ * Allocate data buffer(s) for use with a stream. ++ * Parameter: ++ * hStrm: Stream handle returned from STRM_Open(). ++ * uSize: Size (GPP bytes) of the buffer(s). ++ * uNumBufs: Number of buffers to allocate. ++ * apBuffer: Array to hold buffer addresses. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hStrm. ++ * DSP_EMEMORY: Insufficient memory. ++ * DSP_EFAIL: Failure occurred, unable to allocate buffers. ++ * DSP_ESIZE: uSize must be > 0 bytes. ++ * Requires: ++ * STRM_Init(void) called. ++ * apBuffer != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS STRM_AllocateBuffer(struct STRM_OBJECT *hStrm, ++ u32 uSize, ++ OUT u8 **apBuffer, ++ u32 uNumBufs); ++ ++/* ++ * ======== STRM_Close ======== ++ * Purpose: ++ * Close a stream opened with STRM_Open(). ++ * Parameter: ++ * hStrm: Stream handle returned from STRM_Open(). ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hStrm. ++ * DSP_EPENDING: Some data buffers issued to the stream have not ++ * been reclaimed. ++ * DSP_EFAIL: Failure to close stream. ++ * Requires: ++ * STRM_Init(void) called. ++ * Ensures: ++ */ ++ extern DSP_STATUS STRM_Close(struct STRM_OBJECT *hStrm); ++ ++/* ++ * ======== STRM_Create ======== ++ * Purpose: ++ * Create a STRM manager object. This object holds information about the ++ * device needed to open streams. ++ * Parameters: ++ * phStrmMgr: Location to store handle to STRM manager object on ++ * output. ++ * hDev: Device for this processor. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EMEMORY: Insufficient memory for requested resources. ++ * DSP_EFAIL: General failure. ++ * Requires: ++ * STRM_Init(void) called. ++ * phStrmMgr != NULL. ++ * hDev != NULL. ++ * Ensures: ++ * DSP_SOK: Valid *phStrmMgr. ++ * error: *phStrmMgr == NULL. ++ */ ++ extern DSP_STATUS STRM_Create(OUT struct STRM_MGR **phStrmMgr, ++ struct DEV_OBJECT *hDev); ++ ++/* ++ * ======== STRM_Delete ======== ++ * Purpose: ++ * Delete the STRM Object. ++ * Parameters: ++ * hStrmMgr: Handle to STRM manager object from STRM_Create. ++ * Returns: ++ * Requires: ++ * STRM_Init(void) called. ++ * Valid hStrmMgr. ++ * Ensures: ++ * hStrmMgr is not valid. ++ */ ++ extern void STRM_Delete(struct STRM_MGR *hStrmMgr); ++ ++/* ++ * ======== STRM_Exit ======== ++ * Purpose: ++ * Discontinue usage of STRM module. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * STRM_Init(void) successfully called before. ++ * Ensures: ++ */ ++ extern void STRM_Exit(void); ++ ++/* ++ * ======== STRM_FreeBuffer ======== ++ * Purpose: ++ * Free buffer(s) allocated with STRM_AllocateBuffer. ++ * Parameter: ++ * hStrm: Stream handle returned from STRM_Open(). ++ * apBuffer: Array containing buffer addresses. ++ * uNumBufs: Number of buffers to be freed. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid stream handle. ++ * DSP_EFAIL: Failure occurred, unable to free buffers. ++ * Requires: ++ * STRM_Init(void) called. ++ * apBuffer != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS STRM_FreeBuffer(struct STRM_OBJECT *hStrm, ++ u8 **apBuffer, u32 uNumBufs); ++ ++/* ++ * ======== STRM_GetEventHandle ======== ++ * Purpose: ++ * Get stream's user event handle. This function is used when closing ++ * a stream, so the event can be closed. ++ * Parameter: ++ * hStrm: Stream handle returned from STRM_Open(). ++ * phEvent: Location to store event handle on output. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hStrm. ++ * Requires: ++ * STRM_Init(void) called. ++ * phEvent != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS STRM_GetEventHandle(struct STRM_OBJECT *hStrm, ++ OUT HANDLE *phEvent); ++ ++/* ++ * ======== STRM_GetInfo ======== ++ * Purpose: ++ * Get information about a stream. User's DSP_STREAMINFO is contained ++ * in STRM_INFO struct. STRM_INFO also contains Bridge private info. ++ * Parameters: ++ * hStrm: Stream handle returned from STRM_Open(). ++ * pStreamInfo: Location to store stream info on output. ++ * uSteamInfoSize: Size of user's DSP_STREAMINFO structure. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hStrm. ++ * DSP_ESIZE: uStreamInfoSize < sizeof(DSP_STREAMINFO). ++ * DSP_EFAIL: Unable to get stream info. ++ * Requires: ++ * STRM_Init(void) called. ++ * pStreamInfo != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS STRM_GetInfo(struct STRM_OBJECT *hStrm, ++ OUT struct STRM_INFO *pStreamInfo, ++ u32 uStreamInfoSize); ++ ++/* ++ * ======== STRM_Idle ======== ++ * Purpose: ++ * Idle a stream and optionally flush output data buffers. ++ * If this is an output stream and fFlush is TRUE, all data currently ++ * enqueued will be discarded. ++ * If this is an output stream and fFlush is FALSE, this function ++ * will block until all currently buffered data is output, or the timeout ++ * specified has been reached. ++ * After a successful call to STRM_Idle(), all buffers can immediately ++ * be reclaimed. ++ * Parameters: ++ * hStrm: Stream handle returned from STRM_Open(). ++ * fFlush: If TRUE, discard output buffers. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hStrm. ++ * DSP_ETIMEOUT: A timeout occurred before the stream could be idled. ++ * DSP_ERESTART: A critical error occurred, DSP is being restarted. ++ * DSP_EFAIL: Unable to idle stream. ++ * Requires: ++ * STRM_Init(void) called. ++ * Ensures: ++ */ ++ extern DSP_STATUS STRM_Idle(struct STRM_OBJECT *hStrm, bool fFlush); ++ ++/* ++ * ======== STRM_Init ======== ++ * Purpose: ++ * Initialize the STRM module. ++ * Parameters: ++ * Returns: ++ * TRUE if initialization succeeded, FALSE otherwise. ++ * Requires: ++ * Ensures: ++ */ ++ extern bool STRM_Init(void); ++ ++/* ++ * ======== STRM_Issue ======== ++ * Purpose: ++ * Send a buffer of data to a stream. ++ * Parameters: ++ * hStrm: Stream handle returned from STRM_Open(). ++ * pBuf: Pointer to buffer of data to be sent to the stream. ++ * ulBytes: Number of bytes of data in the buffer. ++ * ulBufSize: Actual buffer size in bytes. ++ * dwArg: A user argument that travels with the buffer. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hStrm. ++ * DSP_ESTREAMFULL: The stream is full. ++ * DSP_EFAIL: Failure occurred, unable to issue buffer. ++ * Requires: ++ * STRM_Init(void) called. ++ * pBuf != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS STRM_Issue(struct STRM_OBJECT *hStrm, IN u8 *pBuf, ++ u32 ulBytes, u32 ulBufSize, ++ IN u32 dwArg); ++ ++/* ++ * ======== STRM_Open ======== ++ * Purpose: ++ * Open a stream for sending/receiving data buffers to/from a task of ++ * DAIS socket node on the DSP. ++ * Parameters: ++ * hNode: Node handle returned from NODE_Allocate(). ++ * uDir: DSP_TONODE or DSP_FROMNODE. ++ * uIndex: Stream index. ++ * pAttr: Pointer to structure containing attributes to be ++ * applied to stream. Cannot be NULL. ++ * phStrm: Location to store stream handle on output. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hNode. ++ * DSP_EDIRECTION: Invalid uDir. ++ * DSP_EVALUE: Invalid uIndex. ++ * DSP_ENODETYPE: hNode is not a task or DAIS socket node. ++ * DSP_EFAIL: Unable to open stream. ++ * Requires: ++ * STRM_Init(void) called. ++ * phStrm != NULL. ++ * pAttr != NULL. ++ * Ensures: ++ * DSP_SOK: *phStrm is valid. ++ * error: *phStrm == NULL. ++ */ ++ extern DSP_STATUS STRM_Open(struct NODE_OBJECT *hNode, u32 uDir, ++ u32 uIndex, IN struct STRM_ATTR *pAttr, ++ OUT struct STRM_OBJECT **phStrm); ++ ++/* ++ * ======== STRM_PrepareBuffer ======== ++ * Purpose: ++ * Prepare a data buffer not allocated by DSPStream_AllocateBuffers() ++ * for use with a stream. ++ * Parameter: ++ * hStrm: Stream handle returned from STRM_Open(). ++ * uSize: Size (GPP bytes) of the buffer. ++ * pBuffer: Buffer address. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hStrm. ++ * DSP_EFAIL: Failure occurred, unable to prepare buffer. ++ * Requires: ++ * STRM_Init(void) called. ++ * pBuffer != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS STRM_PrepareBuffer(struct STRM_OBJECT *hStrm, ++ u32 uSize, ++ u8 *pBuffer); ++ ++/* ++ * ======== STRM_Reclaim ======== ++ * Purpose: ++ * Request a buffer back from a stream. ++ * Parameters: ++ * hStrm: Stream handle returned from STRM_Open(). ++ * pBufPtr: Location to store pointer to reclaimed buffer. ++ * pulBytes: Location where number of bytes of data in the ++ * buffer will be written. ++ * pulBufSize: Location where actual buffer size will be written. ++ * pdwArg: Location where user argument that travels with ++ * the buffer will be written. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hStrm. ++ * DSP_ETIMEOUT: A timeout occurred before a buffer could be ++ * retrieved. ++ * DSP_EFAIL: Failure occurred, unable to reclaim buffer. ++ * Requires: ++ * STRM_Init(void) called. ++ * pBufPtr != NULL. ++ * pulBytes != NULL. ++ * pdwArg != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS STRM_Reclaim(struct STRM_OBJECT *hStrm, ++ OUT u8 **pBufPtr, u32 *pulBytes, ++ u32 *pulBufSize, u32 *pdwArg); ++ ++/* ++ * ======== STRM_RegisterNotify ======== ++ * Purpose: ++ * Register to be notified on specific events for this stream. ++ * Parameters: ++ * hStrm: Stream handle returned by STRM_Open(). ++ * uEventMask: Mask of types of events to be notified about. ++ * uNotifyType: Type of notification to be sent. ++ * hNotification: Handle to be used for notification. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hStrm. ++ * DSP_EMEMORY: Insufficient memory on GPP. ++ * DSP_EVALUE: uEventMask is invalid. ++ * DSP_ENOTIMPL: Notification type specified by uNotifyType is not ++ * supported. ++ * Requires: ++ * STRM_Init(void) called. ++ * hNotification != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS STRM_RegisterNotify(struct STRM_OBJECT *hStrm, ++ u32 uEventMask, u32 uNotifyType, ++ struct DSP_NOTIFICATION ++ *hNotification); ++ ++/* ++ * ======== STRM_Select ======== ++ * Purpose: ++ * Select a ready stream. ++ * Parameters: ++ * aStrmTab: Array of stream handles returned from STRM_Open(). ++ * nStrms: Number of stream handles in array. ++ * pMask: Location to store mask of ready streams on output. ++ * uTimeout: Timeout value (milliseconds). ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ERANGE: nStrms out of range. ++ ++ * DSP_EHANDLE: Invalid stream handle in array. ++ * DSP_ETIMEOUT: A timeout occurred before a stream became ready. ++ * DSP_EFAIL: Failure occurred, unable to select a stream. ++ * Requires: ++ * STRM_Init(void) called. ++ * aStrmTab != NULL. ++ * nStrms > 0. ++ * pMask != NULL. ++ * Ensures: ++ * DSP_SOK: *pMask != 0 || uTimeout == 0. ++ * Error: *pMask == 0. ++ */ ++ extern DSP_STATUS STRM_Select(IN struct STRM_OBJECT **aStrmTab, ++ u32 nStrms, ++ OUT u32 *pMask, u32 uTimeout); ++ ++/* ++ * ======== STRM_UnprepareBuffer ======== ++ * Purpose: ++ * Unprepare a data buffer that was previously prepared for a stream ++ * with DSPStream_PrepareBuffer(), and that will no longer be used with ++ * the stream. ++ * Parameter: ++ * hStrm: Stream handle returned from STRM_Open(). ++ * uSize: Size (GPP bytes) of the buffer. ++ * pBuffer: Buffer address. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hStrm. ++ * DSP_EFAIL: Failure occurred, unable to unprepare buffer. ++ * Requires: ++ * STRM_Init(void) called. ++ * pBuffer != NULL. ++ * Ensures: ++ */ ++ extern DSP_STATUS STRM_UnprepareBuffer(struct STRM_OBJECT *hStrm, ++ u32 uSize, ++ u8 *pBuffer); ++ ++#endif /* STRM_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/strmdefs.h b/arch/arm/plat-omap/include/dspbridge/strmdefs.h +new file mode 100644 +index 0000000..44d217a +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/strmdefs.h +@@ -0,0 +1,57 @@ ++/* ++ * strmdefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== strmdefs.h ======== ++ * Purpose: ++ * Global STRM constants and types. ++ * ++ *! Revision History ++ *! ================ ++ *! 19-Nov-2001 ag Added STRM_INFO.. ++ *! 25-Sep-2000 jeh Created. ++ */ ++ ++#ifndef STRMDEFS_ ++#define STRMDEFS_ ++ ++#define STRM_MAXEVTNAMELEN 32 ++ ++ struct STRM_MGR; ++ ++ struct STRM_OBJECT; ++ ++ struct STRM_ATTR { ++ HANDLE hUserEvent; ++ char *pstrEventName; ++ void *pVirtBase; /* Process virtual base address of ++ * mapped SM */ ++ u32 ulVirtSize; /* Size of virtual space in bytes */ ++ struct DSP_STREAMATTRIN *pStreamAttrIn; ++ } ; ++ ++ struct STRM_INFO { ++ enum DSP_STRMMODE lMode; /* transport mode of ++ * stream(DMA, ZEROCOPY..) */ ++ u32 uSegment; /* Segment strm allocs from. 0 is local mem */ ++ void *pVirtBase; /* " " Stream'process virt base */ ++ struct DSP_STREAMINFO *pUser; /* User's stream information ++ * returned */ ++ } ; ++ ++#endif /* STRMDEFS_ */ ++ +diff --git a/arch/arm/plat-omap/include/dspbridge/sync.h b/arch/arm/plat-omap/include/dspbridge/sync.h +new file mode 100644 +index 0000000..fa3ff8d +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/sync.h +@@ -0,0 +1,340 @@ ++/* ++ * sync.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== sync.h ======== ++ * Purpose: ++ * Provide synchronization services. ++ * ++ * Public Functions: ++ * SYNC_CloseEvent ++ * SYNC_DeleteCS ++ * SYNC_EnterCS ++ * SYNC_Exit ++ * SYNC_Init ++ * SYNC_InitializeCS ++ * SYNC_LeaveCS ++ * SYNC_OpenEvent ++ * SYNC_PostMessage ++ * SYNC_ResetEvent ++ * SYNC_SetEvent ++ * SYNC_WaitOnEvent ++ * SYNC_WaitOnMultipleEvents ++ * ++ *! Revision History: ++ *! ================ ++ *! 05-Oct-2000 jeh Added SYNC_WaitOnMultipleEvents(). ++ *! 01-Dec-1999 ag: Added #define SYNC_MAXNAMELENGTH. ++ *! 04-Nov-1999 kc: Added critical section functions and objects to SYNC. ++ *! 29-Oct-1999 kc: Cleaned up for code review. ++ *! 24-Sep-1999 kc: Added WinCE notes. ++ *! 20-Oct-1997 gp: Removed unused SYNC_ critical section and must complete fxns ++ *! Added SYNC_HOBJECT, SYNC_ATTRS, and object validation, and ++ *! merged SYNC_DestroyEvent into SYNC_CloseEvent, and merged ++ *! SYNC_CreateEvent into SYNC_OpenEvent. ++ *! 07-Oct-1997 gp: Added SYNC_Create/DestroyEvent (for NT testing). ++ *! 06-Oct-1997 gp: Added SYNC_OpenEvent. ++ *! 03-Jun-1997 gp: Added SYNC_{Begin|End}CritSection() functions. ++ *! 03-Jan-1997 gp: Added SYNC_INFINITE define. ++ *! 05-Aug-1996 gp: Created. ++ */ ++ ++#ifndef _SYNC_H ++#define _SYNC_H ++ ++#define SIGNATURECS 0x53435953 /* "SYCS" (in reverse) */ ++#define SIGNATUREDPCCS 0x53445953 /* "SYDS" (in reverse) */ ++ ++/* Special timeout value indicating an infinite wait: */ ++#define SYNC_INFINITE 0xffffffff ++ ++/* Maximum string length of a named event */ ++#define SYNC_MAXNAMELENGTH 32 ++ ++/* Generic SYNC object: */ ++ struct SYNC_OBJECT; ++ ++/* Generic SYNC CS object: */ ++struct SYNC_CSOBJECT { ++ u32 dwSignature; /* used for object validation */ ++ struct semaphore sem; ++} ; ++ ++/* SYNC object attributes: */ ++ struct SYNC_ATTRS { ++ HANDLE hUserEvent; /* Platform's User Mode synch. object. */ ++ HANDLE hKernelEvent; /* Platform's Kernel Mode sync. object. */ ++ u32 dwReserved1; /* For future expansion. */ ++ u32 dwReserved2; /* For future expansion. */ ++ } ; ++ ++/* ++ * ======== SYNC_CloseEvent ======== ++ * Purpose: ++ * Close this event handle, freeing resources allocated in SYNC_OpenEvent ++ * if necessary. ++ * Parameters: ++ * hEvent: Handle to a synchronization event, created/opened in ++ * SYNC_OpenEvent. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EFAIL: Failed to close event handle. ++ * DSP_EHANDLE: Invalid handle. ++ * Requires: ++ * SYNC initialized. ++ * Ensures: ++ * Any subsequent usage of hEvent would be invalid. ++ */ ++ extern DSP_STATUS SYNC_CloseEvent(IN struct SYNC_OBJECT *hEvent); ++ ++/* ++ * ======== SYNC_DeleteCS ======== ++ * Purpose: ++ * Delete a critical section. ++ * Parameters: ++ * hCSObj: critical section handle. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid handle. ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS SYNC_DeleteCS(IN struct SYNC_CSOBJECT *hCSObj); ++ ++/* ++ * ======== SYNC_EnterCS ======== ++ * Purpose: ++ * Enter the critical section. ++ * Parameters: ++ * hCSObj: critical section handle. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid handle. ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS SYNC_EnterCS(IN struct SYNC_CSOBJECT *hCSObj); ++ ++/* ++ * ======== SYNC_Exit ======== ++ * Purpose: ++ * Discontinue usage of module; free resources when reference count ++ * reaches 0. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * SYNC initialized. ++ * Ensures: ++ * Resources used by module are freed when cRef reaches zero. ++ */ ++ extern void SYNC_Exit(void); ++ ++/* ++ * ======== SYNC_Init ======== ++ * Purpose: ++ * Initializes private state of SYNC module. ++ * Parameters: ++ * Returns: ++ * TRUE if initialized; FALSE if error occured. ++ * Requires: ++ * Ensures: ++ * SYNC initialized. ++ */ ++ extern bool SYNC_Init(void); ++ ++/* ++ * ======== SYNC_InitializeCS ======== ++ * Purpose: ++ * Initialize the critical section. ++ * Parameters: ++ * hCSObj: critical section handle. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Out of memory. ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS SYNC_InitializeCS(OUT struct SYNC_CSOBJECT **phCSObj); ++ ++/* ++ * ======== SYNC_InitializeDPCCS ======== ++ * Purpose: ++ * Initialize the critical section between process context and DPC. ++ * Parameters: ++ * hCSObj: critical section handle. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Out of memory. ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS SYNC_InitializeDPCCS(OUT struct SYNC_CSOBJECT ++ **phCSObj); ++ ++/* ++ * ======== SYNC_LeaveCS ======== ++ * Purpose: ++ * Leave the critical section. ++ * Parameters: ++ * hCSObj: critical section handle. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid handle. ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS SYNC_LeaveCS(IN struct SYNC_CSOBJECT *hCSObj); ++ ++/* ++ * ======== SYNC_OpenEvent ======== ++ * Purpose: ++ * Create/open and initialize an event object for thread synchronization, ++ * which is initially in the non-signalled state. ++ * Parameters: ++ * phEvent: Pointer to location to receive the event object handle. ++ * pAttrs: Pointer to SYNC_ATTRS object containing initial SYNC ++ * SYNC_OBJECT attributes. If this pointer is NULL, then ++ * SYNC_OpenEvent will create and manage an OS specific ++ * syncronization object. ++ * pAttrs->hUserEvent: Platform's User Mode synchronization object. ++ * ++ * The behaviour of the SYNC methods depend on the value of ++ * the hUserEvent attr: ++ * ++ * 1. (hUserEvent == NULL): ++ * A user mode event is created. ++ * 2. (hUserEvent != NULL): ++ * A user mode event is supplied by the caller of SYNC_OpenEvent(). ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Unable to create user mode event. ++ * DSP_EMEMORY: Insufficient memory. ++ * DSP_EINVALIDARG SYNC_ATTRS values are invalid. ++ * Requires: ++ * - SYNC initialized. ++ * - phEvent != NULL. ++ * Ensures: ++ * If function succeeded, pEvent->hEvent must be a valid event handle. ++ */ ++ extern DSP_STATUS SYNC_OpenEvent(OUT struct SYNC_OBJECT **phEvent, ++ IN OPTIONAL struct SYNC_ATTRS ++ *pAttrs); ++ ++/* ++ * ========= SYNC_PostMessage ======== ++ * Purpose: ++ * To post a windows message ++ * Parameters: ++ * hWindow: Handle to the window ++ * uMsg: Message to be posted ++ * Returns: ++ * DSP_SOK: Success ++ * DSP_EFAIL: Post message failed ++ * DSP_EHANDLE: Invalid Window handle ++ * Requires: ++ * SYNC initialized ++ * Ensures ++ */ ++ extern DSP_STATUS SYNC_PostMessage(IN HANDLE hWindow, IN u32 uMsg); ++ ++/* ++ * ======== SYNC_ResetEvent ======== ++ * Purpose: ++ * Reset a syncronization event object state to non-signalled. ++ * Parameters: ++ * hEvent: Handle to a sync event. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EFAIL: Failed to reset event. ++ * DSP_EHANDLE: Invalid handle. ++ * Requires: ++ * SYNC initialized. ++ * Ensures: ++ */ ++ extern DSP_STATUS SYNC_ResetEvent(IN struct SYNC_OBJECT *hEvent); ++ ++/* ++ * ======== SYNC_SetEvent ======== ++ * Purpose: ++ * Signal the event. Will unblock one waiting thread. ++ * Parameters: ++ * hEvent: Handle to an event object. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Failed to signal event. ++ * DSP_EHANDLE: Invalid handle. ++ * Requires: ++ * SYNC initialized. ++ * Ensures: ++ */ ++ extern DSP_STATUS SYNC_SetEvent(IN struct SYNC_OBJECT *hEvent); ++ ++/* ++ * ======== SYNC_WaitOnEvent ======== ++ * Purpose: ++ * Wait for an event to be signalled, up to the specified timeout. ++ * Parameters: ++ * hEvent: Handle to an event object. ++ * dwTimeOut: The time-out interval, in milliseconds. ++ * The function returns if the interval elapses, even if ++ * the object's state is nonsignaled. ++ * If zero, the function tests the object's state and ++ * returns immediately. ++ * If SYNC_INFINITE, the function's time-out interval ++ * never elapses. ++ * Returns: ++ * DSP_SOK: The object was signalled. ++ * DSP_EHANDLE: Invalid handle. ++ * SYNC_E_FAIL: Wait failed, possibly because the process terminated. ++ * SYNC_E_TIMEOUT: Timeout expired while waiting for event to be signalled. ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS SYNC_WaitOnEvent(IN struct SYNC_OBJECT *hEvent, ++ IN u32 dwTimeOut); ++ ++/* ++ * ======== SYNC_WaitOnMultipleEvents ======== ++ * Purpose: ++ * Wait for any of an array of events to be signalled, up to the ++ * specified timeout. ++ * Note: dwTimeOut must be SYNC_INFINITE to signal infinite wait. ++ * Parameters: ++ * hSyncEvents: Array of handles to event objects. ++ * uCount: Number of event handles. ++ * dwTimeOut: The time-out interval, in milliseconds. ++ * The function returns if the interval elapses, even if ++ * no event is signalled. ++ * If zero, the function tests the object's state and ++ * returns immediately. ++ * If SYNC_INFINITE, the function's time-out interval ++ * never elapses. ++ * puIndex: Location to store index of event that was signalled. ++ * Returns: ++ * DSP_SOK: The object was signalled. ++ * SYNC_E_FAIL: Wait failed, possibly because the process terminated. ++ * SYNC_E_TIMEOUT: Timeout expired before event was signalled. ++ * DSP_EMEMORY: Memory allocation failed. ++ * Requires: ++ * Ensures: ++ */ ++ extern DSP_STATUS SYNC_WaitOnMultipleEvents(IN struct SYNC_OBJECT ++ **hSyncEvents, ++ IN u32 uCount, ++ IN u32 dwTimeout, ++ OUT u32 *puIndex); ++ ++#endif /* _SYNC_H */ +diff --git a/arch/arm/plat-omap/include/dspbridge/util.h b/arch/arm/plat-omap/include/dspbridge/util.h +new file mode 100644 +index 0000000..e6815ca +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/util.h +@@ -0,0 +1,122 @@ ++/* ++ * util.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== util.h ======== ++ * Purpose: ++ * Provide general purpose utility functions. ++ * ++ * Public Functions: ++ * UTIL_CDTestDll ++ * UTIL_CmdLineToArgs ++ * UTIL_Exit ++ * UTIL_GetSysInfo ++ * UTIL_Init ++ */ ++ ++#ifndef _UTIL_H ++#define _UTIL_H ++ ++#include ++#include ++ ++#include ++ ++/* ++ * ======== UTIL_CDTestDll ======== ++ * Purpose: ++ * Provides test entry point in class driver context. ++ * Parameters: ++ * cArgc: test module command line input count. ++ * ppArgv: test module command line args. ++ * Returns: ++ * 0 if successful, a negative value otherwise. ++ * Requires: ++ * UTIL initialized. ++ * Ensures: ++ */ ++ extern u32 UTIL_CDTestDll(IN s32 cArgc, IN char **ppArgv); ++ ++/* ++ * ======== UTIL_CmdLineToArgs ======== ++ * Purpose: ++ * This function re-creates C-style cmd line argc & argv from WinMain() ++ * cmd line args. ++ * Parameters: ++ * s8 *pszProgName - The name of the program currently being executed. ++ * s8 *argv[] - The argument vector. ++ * s8 *pCmdLine - The pointer to the command line. ++ * bool fHasProgName - Indicats whether a program name is supplied. ++ * Returns: ++ * Returns the number of arguments found. ++ * Requires: ++ * UTIL initialized. ++ * Ensures: ++ */ ++ extern s32 UTIL_CmdLineToArgs(IN char *pszProgName, ++ IN char *argv[UTIL_MAXARGVS], ++ IN char *pCmdLine, IN bool fHasProgName); ++ ++/* ++ * ======== UTIL_Exit ======== ++ * Purpose: ++ * Discontinue usage of module; free resources when reference count ++ * reaches 0. ++ * Parameters: ++ * Returns: ++ * Requires: ++ * UTIL initialized. ++ * Ensures: ++ * Resources used by module are freed when cRef reaches zero. ++ */ ++ extern inline void UTIL_Exit(void) ++ { ++ } ++/* ++ * ======== UTIL_GetSysInfo ======== ++ * Purpose: ++ * This function return platform specific system information. ++ * ++ * Parameters: ++ * pSysInfo - address to store the system information. ++ * Returns: ++ * DSP_SOK ++ * S_FAIL ++ * Requires: ++ * UTIL initialized. ++ * pSysInfo != NULL ++ * Ensures: ++ */ ++ extern DSP_STATUS UTIL_GetSysInfo(OUT struct UTIL_SYSINFO *pSysInfo); ++ ++/* ++ * ======== UTIL_Init ======== ++ * Purpose: ++ * Initializes private state of UTIL module. ++ * Parameters: ++ * Returns: ++ * TRUE if success, else FALSE. ++ * Requires: ++ * Ensures: ++ * UTIL initialized. ++ */ ++ extern inline bool UTIL_Init(void) ++ { ++ return true; ++ } ++ ++#endif /* _UTIL_H */ +diff --git a/arch/arm/plat-omap/include/dspbridge/utildefs.h b/arch/arm/plat-omap/include/dspbridge/utildefs.h +new file mode 100644 +index 0000000..bd53a5a +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/utildefs.h +@@ -0,0 +1,51 @@ ++/* ++ * utildefs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== utildefs.h ======== ++ * Purpose: ++ * Global UTIL constants and types, shared between WCD and DSPSYS. ++ * ++ *! Revision History: ++ *! ================ ++ *! 24-Feb-2003 kc Removed wIOPort* entries from UTIL_HOSTCONFIG. ++ *! 12-Aug-2000 ag Added UTIL_SYSINFO typedef. ++ *! 08-Oct-1999 rr Adopted for WinCE where test fxns will be added in util.h ++ *! 26-Dec-1996 cr Created. ++ */ ++ ++#ifndef UTILDEFS_ ++#define UTILDEFS_ ++ ++/* constants taken from configmg.h */ ++#define UTIL_MAXMEMREGS 9 ++#define UTIL_MAXIOPORTS 20 ++#define UTIL_MAXIRQS 7 ++#define UTIL_MAXDMACHNLS 7 ++ ++/* misc. constants */ ++#define UTIL_MAXARGVS 10 ++ ++/* Platform specific important info */ ++ struct UTIL_SYSINFO { ++ /* Granularity of page protection; usually 1k or 4k */ ++ u32 dwPageSize; ++ u32 dwAllocationGranularity; /* VM granularity, usually 64K */ ++ u32 dwNumberOfProcessors; /* Used as sanity check */ ++ } ; ++ ++#endif /* UTILDEFS_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/uuidutil.h b/arch/arm/plat-omap/include/dspbridge/uuidutil.h +new file mode 100644 +index 0000000..af4aaec +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/uuidutil.h +@@ -0,0 +1,74 @@ ++/* ++ * uuidutil.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== uuidutil.h ======== ++ * Description: ++ * This file contains the specification of UUID helper functions. ++ * ++ *! Revision History ++ *! ================ ++ *! 09-Nov-2000 kc: Modified description of UUID utility functions. ++ *! 29-Sep-2000 kc: Appended "UUID_" prefix to UUID helper functions. ++ *! 10-Aug-2000 kc: Created. ++ *! ++ */ ++ ++#ifndef UUIDUTIL_ ++#define UUIDUTIL_ ++ ++#define MAXUUIDLEN 37 ++ ++/* ++ * ======== UUID_UuidToString ======== ++ * Purpose: ++ * Converts a DSP_UUID to an ANSI string. ++ * Parameters: ++ * pUuid: Pointer to a DSP_UUID object. ++ * pszUuid: Pointer to a buffer to receive a NULL-terminated UUID ++ * string. ++ * size: Maximum size of the pszUuid string. ++ * Returns: ++ * Requires: ++ * pUuid & pszUuid are non-NULL values. ++ * Ensures: ++ * Lenghth of pszUuid is less than MAXUUIDLEN. ++ * Details: ++ * UUID string limit currently set at MAXUUIDLEN. ++ */ ++ void UUID_UuidToString(IN struct DSP_UUID *pUuid, OUT char *pszUuid, ++ s32 size); ++ ++/* ++ * ======== UUID_UuidFromString ======== ++ * Purpose: ++ * Converts an ANSI string to a DSP_UUID. ++ * Parameters: ++ * pszUuid: Pointer to a string that represents a DSP_UUID object. ++ * pUuid: Pointer to a DSP_UUID object. ++ * Returns: ++ * Requires: ++ * pUuid & pszUuid are non-NULL values. ++ * Ensures: ++ * Details: ++ * We assume the string representation of a UUID has the following format: ++ * "12345678_1234_1234_1234_123456789abc". ++ */ ++ extern void UUID_UuidFromString(IN char *pszUuid, ++ OUT struct DSP_UUID *pUuid); ++ ++#endif /* UUIDUTIL_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/wcd.h b/arch/arm/plat-omap/include/dspbridge/wcd.h +new file mode 100644 +index 0000000..5a7d47a +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/wcd.h +@@ -0,0 +1,61 @@ ++/* ++ * wcd.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== wcd.h ======== ++ * Description: ++ * 'Bridge class driver library functions, object definitions, and ++ * return error/status codes. To be included by 'Bridge mini drivers. ++ * ++ * Public Functions: ++ * See mem.h and dbg.h. ++ * ++ * Notes: ++ * 'Bridge Class Driver services exported to WMD's are initialized by the ++ * WCD on behalf of the WMD. WMD's must not call module Init/Exit ++ * functions. ++ * ++ * To ensure WMD binary compatibility across different platforms, ++ * for the same processor, a WMD must restrict its usage of system ++ * services to those exported by the 'Bridge class library. ++ * ++ *! Revision History: ++ *! ================ ++ *! 07-Jun-2000 jeh Added dev.h ++ *! 01-Nov-1999 ag: #WINCE# WCD_MAJOR_VERSION=8 & WCD_MINOR_VERSION=0 to match ++ *! dll stamps. ++ *! 0.80 - 0.89 Alpha, 0.90 - 0.99 Beta, 1.00 - 1.10 FCS. ++ *! 17-Sep-1997 gp: Changed size of CFG_HOSTRES structure; and ISR_Install API; ++ *! Changed WCD_MINOR_VERSION 3 -> 4. ++ *! 15-Sep-1997 gp: Moved WCD_(Un)registerMinidriver to drv. ++ *! 25-Jul-1997 cr: Added WCD_UnregisterMinidriver. ++ *! 22-Jul-1997 cr: Added WCD_RegisterMinidriver, WCD_MINOR_VERSION 2 -> 3. ++ *! 12-Nov-1996 gp: Defined port io macros. ++ *! 07-Nov-1996 gp: Updated for code review. ++ *! 16-Jul-1996 gp: Added CHNL fxns; updated WCD lib version to 2. ++ *! 10-May-1996 gp: Separated WMD def.s' into wmd.h. ++ *! 03-May-1996 gp: Created. ++ */ ++ ++#ifndef WCD_ ++#define WCD_ ++ ++/* This WCD Library Version: */ ++#define WCD_MAJOR_VERSION (u32)8 /* .8x - Alpha, .9x - Beta, 1.x FCS */ ++#define WCD_MINOR_VERSION (u32)0 ++ ++#endif /* WCD_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/wcdioctl.h b/arch/arm/plat-omap/include/dspbridge/wcdioctl.h +new file mode 100644 +index 0000000..04b13ab +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/wcdioctl.h +@@ -0,0 +1,519 @@ ++/* ++ * wcdioctl.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== wcdioctl.h ======== ++ * Purpose: ++ * Contains structures and commands that are used for interaction ++ * between the DDSP API and class driver. ++ * ++ *! Revision History ++ *! ================ ++ *! 19-Apr-2004 sb Aligned DMM definitions with Symbian ++ *! 08-Mar-2004 sb Added the Dynamic Memory Mapping structs & offsets ++ *! 15-Oct-2002 kc Updated definitions for private PERF module. ++ *! 16-Aug-2002 map Added ARGS_MGR_REGISTEROBJECT & ARGS_MGR_UNREGISTEROBJECT ++ *! Added CMD_MGR_REGISTEROBJECT_OFFSET & ++ *! CMD_MGR_UNREGISTEROBJECT_OFFSET ++ *! 15-Jan-2002 ag Added actaul bufSize to ARGS_STRM_[RECLAIM][ISSUE]. ++ *! 15-Nov-2001 ag change to STRMINFO in ARGS_STRM_GETINFO. ++ *! 11-Sep-2001 ag ARGS_CMM_GETHANDLE defn uses DSP_HPROCESSOR. ++ *! 23-Apr-2001 jeh Added pStatus to NODE_TERMINATE args. ++ *! 13-Feb-2001 kc DSP/BIOS Bridge name updates. ++ *! 22-Nov-2000 kc: Added CMD_MGR_GETPERF_DATA_OFFSET for acquiring PERF stats. ++ *! 27-Oct-2000 jeh Added timeouts to NODE_GETMESSAGE, NODE_PUTMESSAGE args. ++ *! Removed NODE_GETMESSAGESTRM args. ++ *! 11-Oct-2000 ag: Added SM mgr(CMM) args. ++ *! 27-Sep-2000 jeh Removed struct DSP_BUFFERATTR param from ++ *! ARGS_STRM_ALLOCATEBUFFER. ++ *! 25-Sep-2000 rr: Updated to Version 0.9 ++ *! 07-Sep-2000 jeh Changed HANDLE to DSP_HNOTIFICATION in RegisterNotify args. ++ *! Added DSP_STRMATTR to DSPNode_Connect args. ++ *! 04-Aug-2000 rr: MEM and UTIL added to RM. ++ *! 27-Jul-2000 rr: NODE, MGR,STRM and PROC added ++ *! 27-Jun-2000 rr: Modifed to Use either PM or DSP/BIOS Bridge ++ *! IFDEF to build for PM or DSP/BIOS Bridge ++ *! 28-Jan-2000 rr: NT_CMD_FROM_OFFSET moved out to dsptrap.h ++ *! 24-Jan-2000 rr: Merged with Scott's code. ++ *! 21-Jan-2000 sg: In ARGS_CHNL_GETMODE changed mode to be u32 to be ++ *! consistent with chnldefs.h. ++ *! 11-Jan-2000 rr: CMD_CFG_GETCDVERSION_OFFSET added. ++ *! 12-Nov-1999 rr: CMD_BRD_MONITOR_OFFSET added ++ *! 09-Nov-1999 kc: Added MEMRY and enabled CMD_BRD_IOCTL_OFFSET. ++ *! 05-Nov-1999 ag: Added CHNL. ++ *! 02-Nov-1999 kc: Removed field from ARGS_UTIL_TESTDLL. ++ *! 29-Oct-1999 kc: Cleaned up for code review. ++ *! 08-Oct-1999 rr: Util control offsets added. ++ *! 13-Sep-1999 kc: Added ARGS_UTIL_TESTDLL for PM test infrastructure. ++ *! 19-Aug-1999 rr: Created from WSX. Minimal Implementaion of BRD_Start and BRD ++ *! and BRD_Stop. IOCTL Offsets and CTRL Code. ++ */ ++ ++#ifndef WCDIOCTL_ ++#define WCDIOCTL_ ++ ++#include ++#include ++#include ++#include ++ ++union Trapped_Args { ++ ++ /* MGR Module */ ++ struct { ++ u32 uNode; ++ struct DSP_NDBPROPS __user *pNDBProps; ++ u32 uNDBPropsSize; ++ u32 __user *puNumNodes; ++ } ARGS_MGR_ENUMNODE_INFO; ++ ++ struct { ++ u32 uProcessor; ++ struct DSP_PROCESSORINFO __user *pProcessorInfo; ++ u32 uProcessorInfoSize; ++ u32 __user *puNumProcs; ++ } ARGS_MGR_ENUMPROC_INFO; ++ ++ struct { ++ struct DSP_UUID *pUuid; ++ enum DSP_DCDOBJTYPE objType; ++ char *pszPathName; ++ } ARGS_MGR_REGISTEROBJECT; ++ ++ struct { ++ struct DSP_UUID *pUuid; ++ enum DSP_DCDOBJTYPE objType; ++ } ARGS_MGR_UNREGISTEROBJECT; ++ ++ struct { ++ struct DSP_NOTIFICATION __user*__user *aNotifications; ++ u32 uCount; ++ u32 __user *puIndex; ++ u32 uTimeout; ++ } ARGS_MGR_WAIT; ++ ++ /* PROC Module */ ++ struct { ++ u32 uProcessor; ++ struct DSP_PROCESSORATTRIN __user *pAttrIn; ++ DSP_HPROCESSOR __user *phProcessor; ++ } ARGS_PROC_ATTACH; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ u32 dwCmd; ++ struct DSP_CBDATA __user *pArgs; ++ } ARGS_PROC_CTRL; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ } ARGS_PROC_DETACH; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ DSP_HNODE __user *aNodeTab; ++ u32 uNodeTabSize; ++ u32 __user *puNumNodes; ++ u32 __user *puAllocated; ++ } ARGS_PROC_ENUMNODE_INFO; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ u32 uResourceType; ++ struct DSP_RESOURCEINFO *pResourceInfo; ++ u32 uResourceInfoSize; ++ } ARGS_PROC_ENUMRESOURCES; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ struct DSP_PROCESSORSTATE __user *pProcStatus; ++ u32 uStateInfoSize; ++ } ARGS_PROC_GETSTATE; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ u8 __user *pBuf; ++ ++ #ifndef RES_CLEANUP_DISABLE ++ u8 __user *pSize; ++ #endif ++ u32 uMaxSize; ++ } ARGS_PROC_GETTRACE; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ s32 iArgc; ++ char __user*__user *aArgv; ++ char *__user *aEnvp; ++ } ARGS_PROC_LOAD; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ u32 uEventMask; ++ u32 uNotifyType; ++ struct DSP_NOTIFICATION __user *hNotification; ++ } ARGS_PROC_REGISTER_NOTIFY; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ } ARGS_PROC_START; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ u32 ulSize; ++ void *__user *ppRsvAddr; ++ } ARGS_PROC_RSVMEM; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ u32 ulSize; ++ void *pRsvAddr; ++ } ARGS_PROC_UNRSVMEM; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ void *pMpuAddr; ++ u32 ulSize; ++ void *pReqAddr; ++ void *__user *ppMapAddr; ++ u32 ulMapAttr; ++ } ARGS_PROC_MAPMEM; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ u32 ulSize; ++ void *pMapAddr; ++ } ARGS_PROC_UNMAPMEM; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ void *pMpuAddr; ++ u32 ulSize; ++ u32 ulFlags; ++ } ARGS_PROC_FLUSHMEMORY; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ } ARGS_PROC_STOP; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ void *pMpuAddr; ++ u32 ulSize; ++ } ARGS_PROC_INVALIDATEMEMORY; ++ ++ ++ /* NODE Module */ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ struct DSP_UUID __user *pNodeID; ++ struct DSP_CBDATA __user *pArgs; ++ struct DSP_NODEATTRIN __user *pAttrIn; ++ DSP_HNODE __user *phNode; ++ } ARGS_NODE_ALLOCATE; ++ ++ struct { ++ DSP_HNODE hNode; ++ u32 uSize; ++ struct DSP_BUFFERATTR __user *pAttr; ++ u8 *__user *pBuffer; ++ } ARGS_NODE_ALLOCMSGBUF; ++ ++ struct { ++ DSP_HNODE hNode; ++ s32 iPriority; ++ } ARGS_NODE_CHANGEPRIORITY; ++ ++ struct { ++ DSP_HNODE hNode; ++ u32 uStream; ++ DSP_HNODE hOtherNode; ++ u32 uOtherStream; ++ struct DSP_STRMATTR __user *pAttrs; ++ struct DSP_CBDATA __user *pConnParam; ++ } ARGS_NODE_CONNECT; ++ ++ struct { ++ DSP_HNODE hNode; ++ } ARGS_NODE_CREATE; ++ ++ struct { ++ DSP_HNODE hNode; ++ } ARGS_NODE_DELETE; ++ ++ struct { ++ DSP_HNODE hNode; ++ struct DSP_BUFFERATTR __user *pAttr; ++ u8 *pBuffer; ++ } ARGS_NODE_FREEMSGBUF; ++ ++ struct { ++ DSP_HNODE hNode; ++ struct DSP_NODEATTR __user *pAttr; ++ u32 uAttrSize; ++ } ARGS_NODE_GETATTR; ++ ++ struct { ++ DSP_HNODE hNode; ++ struct DSP_MSG __user *pMessage; ++ u32 uTimeout; ++ } ARGS_NODE_GETMESSAGE; ++ ++ struct { ++ DSP_HNODE hNode; ++ } ARGS_NODE_PAUSE; ++ ++ struct { ++ DSP_HNODE hNode; ++ struct DSP_MSG __user *pMessage; ++ u32 uTimeout; ++ } ARGS_NODE_PUTMESSAGE; ++ ++ struct { ++ DSP_HNODE hNode; ++ u32 uEventMask; ++ u32 uNotifyType; ++ struct DSP_NOTIFICATION __user *hNotification; ++ } ARGS_NODE_REGISTERNOTIFY; ++ ++ struct { ++ DSP_HNODE hNode; ++ } ARGS_NODE_RUN; ++ ++ struct { ++ DSP_HNODE hNode; ++ DSP_STATUS __user *pStatus; ++ } ARGS_NODE_TERMINATE; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ struct DSP_UUID __user *pNodeID; ++ struct DSP_NDBPROPS __user *pNodeProps; ++ } ARGS_NODE_GETUUIDPROPS; ++ ++ /* STRM module */ ++ ++ struct { ++ DSP_HSTREAM hStream; ++ u32 uSize; ++ u8 *__user *apBuffer; ++ u32 uNumBufs; ++ } ARGS_STRM_ALLOCATEBUFFER; ++ ++ struct { ++ DSP_HSTREAM hStream; ++ } ARGS_STRM_CLOSE; ++ ++ struct { ++ DSP_HSTREAM hStream; ++ u8 *__user *apBuffer; ++ u32 uNumBufs; ++ } ARGS_STRM_FREEBUFFER; ++ ++ struct { ++ DSP_HSTREAM hStream; ++ HANDLE *phEvent; ++ } ARGS_STRM_GETEVENTHANDLE; ++ ++ struct { ++ DSP_HSTREAM hStream; ++ struct STRM_INFO __user *pStreamInfo; ++ u32 uStreamInfoSize; ++ } ARGS_STRM_GETINFO; ++ ++ struct { ++ DSP_HSTREAM hStream; ++ bool bFlush; ++ } ARGS_STRM_IDLE; ++ ++ struct { ++ DSP_HSTREAM hStream; ++ u8 *pBuffer; ++ u32 dwBytes; ++ u32 dwBufSize; ++ u32 dwArg; ++ } ARGS_STRM_ISSUE; ++ ++ struct { ++ DSP_HNODE hNode; ++ u32 uDirection; ++ u32 uIndex; ++ struct STRM_ATTR __user *pAttrIn; ++ DSP_HSTREAM __user *phStream; ++ } ARGS_STRM_OPEN; ++ ++ struct { ++ DSP_HSTREAM hStream; ++ u8 *__user *pBufPtr; ++ u32 __user *pBytes; ++ u32 __user *pBufSize; ++ u32 __user *pdwArg; ++ } ARGS_STRM_RECLAIM; ++ ++ struct { ++ DSP_HSTREAM hStream; ++ u32 uEventMask; ++ u32 uNotifyType; ++ struct DSP_NOTIFICATION __user *hNotification; ++ } ARGS_STRM_REGISTERNOTIFY; ++ ++ struct { ++ DSP_HSTREAM __user *aStreamTab; ++ u32 nStreams; ++ u32 __user *pMask; ++ u32 uTimeout; ++ } ARGS_STRM_SELECT; ++ ++ /* CMM Module */ ++ struct { ++ struct CMM_OBJECT *hCmmMgr; ++ u32 uSize; ++ struct CMM_ATTRS *pAttrs; ++ OUT void **ppBufVA; ++ } ARGS_CMM_ALLOCBUF; ++ ++ struct { ++ struct CMM_OBJECT *hCmmMgr; ++ void *pBufPA; ++ u32 ulSegId; ++ } ARGS_CMM_FREEBUF; ++ ++ struct { ++ DSP_HPROCESSOR hProcessor; ++ struct CMM_OBJECT *__user *phCmmMgr; ++ } ARGS_CMM_GETHANDLE; ++ ++ struct { ++ struct CMM_OBJECT *hCmmMgr; ++ struct CMM_INFO __user *pCmmInfo; ++ } ARGS_CMM_GETINFO; ++ ++ /* MEM Module */ ++ struct { ++ u32 cBytes; ++ enum MEM_POOLATTRS type; ++ void *pMem; ++ } ARGS_MEM_ALLOC; ++ ++ struct { ++ u32 cBytes; ++ enum MEM_POOLATTRS type; ++ void *pMem; ++ } ARGS_MEM_CALLOC; ++ ++ struct { ++ void *pMem; ++ } ARGS_MEM_FREE; ++ ++ struct { ++ void *pBuffer; ++ u32 cSize; ++ void *pLockedBuffer; ++ } ARGS_MEM_PAGELOCK; ++ ++ struct { ++ void *pBuffer; ++ u32 cSize; ++ } ARGS_MEM_PAGEUNLOCK; ++ ++ /* UTIL module */ ++ struct { ++ s32 cArgc; ++ char **ppArgv; ++ } ARGS_UTIL_TESTDLL; ++} ; ++ ++#define CMD_BASE 1 ++ ++/* MGR module offsets */ ++#define CMD_MGR_BASE_OFFSET CMD_BASE ++#define CMD_MGR_ENUMNODE_INFO_OFFSET (CMD_MGR_BASE_OFFSET + 0) ++#define CMD_MGR_ENUMPROC_INFO_OFFSET (CMD_MGR_BASE_OFFSET + 1) ++#define CMD_MGR_REGISTEROBJECT_OFFSET (CMD_MGR_BASE_OFFSET + 2) ++#define CMD_MGR_UNREGISTEROBJECT_OFFSET (CMD_MGR_BASE_OFFSET + 3) ++#define CMD_MGR_WAIT_OFFSET (CMD_MGR_BASE_OFFSET + 4) ++ ++#ifndef RES_CLEANUP_DISABLE ++#define CMD_MGR_RESOUCES_OFFSET (CMD_MGR_BASE_OFFSET + 5) ++#define CMD_MGR_END_OFFSET CMD_MGR_RESOUCES_OFFSET ++#else ++#define CMD_MGR_END_OFFSET CMD_MGR_WAIT_OFFSET ++#endif ++ ++#define CMD_PROC_BASE_OFFSET (CMD_MGR_END_OFFSET + 1) ++#define CMD_PROC_ATTACH_OFFSET (CMD_PROC_BASE_OFFSET + 0) ++#define CMD_PROC_CTRL_OFFSET (CMD_PROC_BASE_OFFSET + 1) ++#define CMD_PROC_DETACH_OFFSET (CMD_PROC_BASE_OFFSET + 2) ++#define CMD_PROC_ENUMNODE_OFFSET (CMD_PROC_BASE_OFFSET + 3) ++#define CMD_PROC_ENUMRESOURCES_OFFSET (CMD_PROC_BASE_OFFSET + 4) ++#define CMD_PROC_GETSTATE_OFFSET (CMD_PROC_BASE_OFFSET + 5) ++#define CMD_PROC_GETTRACE_OFFSET (CMD_PROC_BASE_OFFSET + 6) ++#define CMD_PROC_LOAD_OFFSET (CMD_PROC_BASE_OFFSET + 7) ++#define CMD_PROC_REGISTERNOTIFY_OFFSET (CMD_PROC_BASE_OFFSET + 8) ++#define CMD_PROC_START_OFFSET (CMD_PROC_BASE_OFFSET + 9) ++#define CMD_PROC_RSVMEM_OFFSET (CMD_PROC_BASE_OFFSET + 10) ++#define CMD_PROC_UNRSVMEM_OFFSET (CMD_PROC_BASE_OFFSET + 11) ++#define CMD_PROC_MAPMEM_OFFSET (CMD_PROC_BASE_OFFSET + 12) ++#define CMD_PROC_UNMAPMEM_OFFSET (CMD_PROC_BASE_OFFSET + 13) ++#define CMD_PROC_FLUSHMEMORY_OFFSET (CMD_PROC_BASE_OFFSET + 14) ++#define CMD_PROC_STOP_OFFSET (CMD_PROC_BASE_OFFSET + 15) ++#define CMD_PROC_INVALIDATEMEMORY_OFFSET (CMD_PROC_BASE_OFFSET + 16) ++#define CMD_PROC_END_OFFSET CMD_PROC_INVALIDATEMEMORY_OFFSET ++ ++ ++#define CMD_NODE_BASE_OFFSET (CMD_PROC_END_OFFSET + 1) ++#define CMD_NODE_ALLOCATE_OFFSET (CMD_NODE_BASE_OFFSET + 0) ++#define CMD_NODE_ALLOCMSGBUF_OFFSET (CMD_NODE_BASE_OFFSET + 1) ++#define CMD_NODE_CHANGEPRIORITY_OFFSET (CMD_NODE_BASE_OFFSET + 2) ++#define CMD_NODE_CONNECT_OFFSET (CMD_NODE_BASE_OFFSET + 3) ++#define CMD_NODE_CREATE_OFFSET (CMD_NODE_BASE_OFFSET + 4) ++#define CMD_NODE_DELETE_OFFSET (CMD_NODE_BASE_OFFSET + 5) ++#define CMD_NODE_FREEMSGBUF_OFFSET (CMD_NODE_BASE_OFFSET + 6) ++#define CMD_NODE_GETATTR_OFFSET (CMD_NODE_BASE_OFFSET + 7) ++#define CMD_NODE_GETMESSAGE_OFFSET (CMD_NODE_BASE_OFFSET + 8) ++#define CMD_NODE_PAUSE_OFFSET (CMD_NODE_BASE_OFFSET + 9) ++#define CMD_NODE_PUTMESSAGE_OFFSET (CMD_NODE_BASE_OFFSET + 10) ++#define CMD_NODE_REGISTERNOTIFY_OFFSET (CMD_NODE_BASE_OFFSET + 11) ++#define CMD_NODE_RUN_OFFSET (CMD_NODE_BASE_OFFSET + 12) ++#define CMD_NODE_TERMINATE_OFFSET (CMD_NODE_BASE_OFFSET + 13) ++#define CMD_NODE_GETUUIDPROPS_OFFSET (CMD_NODE_BASE_OFFSET + 14) ++#define CMD_NODE_END_OFFSET CMD_NODE_GETUUIDPROPS_OFFSET ++ ++#define CMD_STRM_BASE_OFFSET (CMD_NODE_END_OFFSET + 1) ++#define CMD_STRM_ALLOCATEBUFFER_OFFSET (CMD_STRM_BASE_OFFSET + 0) ++#define CMD_STRM_CLOSE_OFFSET (CMD_STRM_BASE_OFFSET + 1) ++#define CMD_STRM_FREEBUFFER_OFFSET (CMD_STRM_BASE_OFFSET + 2) ++#define CMD_STRM_GETEVENTHANDLE_OFFSET (CMD_STRM_BASE_OFFSET + 3) ++#define CMD_STRM_GETINFO_OFFSET (CMD_STRM_BASE_OFFSET + 4) ++#define CMD_STRM_IDLE_OFFSET (CMD_STRM_BASE_OFFSET + 5) ++#define CMD_STRM_ISSUE_OFFSET (CMD_STRM_BASE_OFFSET + 6) ++#define CMD_STRM_OPEN_OFFSET (CMD_STRM_BASE_OFFSET + 7) ++#define CMD_STRM_RECLAIM_OFFSET (CMD_STRM_BASE_OFFSET + 8) ++#define CMD_STRM_REGISTERNOTIFY_OFFSET (CMD_STRM_BASE_OFFSET + 9) ++#define CMD_STRM_SELECT_OFFSET (CMD_STRM_BASE_OFFSET + 10) ++#define CMD_STRM_END_OFFSET CMD_STRM_SELECT_OFFSET ++ ++/* Communication Memory Manager (UCMM) */ ++#define CMD_CMM_BASE_OFFSET (CMD_STRM_END_OFFSET + 1) ++#define CMD_CMM_ALLOCBUF_OFFSET (CMD_CMM_BASE_OFFSET + 0) ++#define CMD_CMM_FREEBUF_OFFSET (CMD_CMM_BASE_OFFSET + 1) ++#define CMD_CMM_GETHANDLE_OFFSET (CMD_CMM_BASE_OFFSET + 2) ++#define CMD_CMM_GETINFO_OFFSET (CMD_CMM_BASE_OFFSET + 3) ++#define CMD_CMM_END_OFFSET CMD_CMM_GETINFO_OFFSET ++ ++#define CMD_BASE_END_OFFSET CMD_CMM_END_OFFSET ++#endif /* WCDIOCTL_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/wmd.h b/arch/arm/plat-omap/include/dspbridge/wmd.h +new file mode 100644 +index 0000000..f584038 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/wmd.h +@@ -0,0 +1,1193 @@ ++/* ++ * wmd.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== wmd.h ======== ++ * Purpose: ++ * 'Bridge mini driver entry point and interface function declarations. ++ * ++ * Public Functions: ++ * WMD_DRV_Entry ++ * ++ * Notes: ++ * The 'Bridge class driver obtains it's function interface to ++ * the 'Bridge mini driver via a call to WMD_DRV_Entry(). ++ * ++ * 'Bridge Class Driver services exported to WMD's are initialized by the ++ * WCD on behalf of the WMD. ++ * ++ * WMD function DBC Requires and Ensures are also made by the WCD on ++ * behalf of the WMD, to simplify the WMD code. ++ * ++ *! Revision History: ++ *! ================ ++ *! 19-Apr-2004 sb Aligned DMM definitions with Symbian ++ *! 08-Mar-2004 sb Added the Dynamic Memory Mapping APIs - WMD_BRD_MemMap/UnMap ++ *! 01-Mar-2004 vp Added filename argument to WMD_DRV_Entry function. ++ *! 29-Aug-2002 map Added WMD_BRD_MemWrite() ++ *! 26-Aug-2002 map Added WMD_BRD_MemCopy() ++ *! 07-Jan-2002 ag Added cBufSize to WMD_CHNL_AddIOReq(). ++ *! 05-Nov-2001 kc: Added error handling DEH functions. ++ *! 06-Dec-2000 jeh Added uEventMask to WMD_MSG_RegisterNotify(). ++ *! 17-Nov-2000 jeh Added WMD_MSG and WMD_IO definitions. ++ *! 01-Nov-2000 jeh Added more error codes to WMD_CHNL_RegisterNotify(). ++ *! 13-Oct-2000 jeh Added dwArg to WMD_CHNL_AddIOReq(), added WMD_CHNL_IDLE ++ *! and WMD_CHNL_RegisterNotify for DSPStream support. ++ *! 17-Jan-2000 rr: WMD_BRD_SETSTATE Added. ++ *! 30-Jul-1997 gp: Split wmd IOCTL space into reserved and private. ++ *! 07-Nov-1996 gp: Updated for code review. ++ *! 18-Oct-1996 gp: Added WMD_E_HARDWARE return code from WMD_BRD_Monitor. ++ *! 09-Sep-1996 gp: Subtly altered the semantics of WMD_CHNL_GetInfo(). ++ *! 02-Aug-1996 gp: Ensured on BRD_Start that interrupts to the PC are enabled. ++ *! 11-Jul-1996 gp: Added CHNL interface. Note stronger DBC_Require conditions. ++ *! 29-May-1996 gp: Removed WCD_ prefix from functions imported from WCD.LIB. ++ *! 29-May-1996 gp: Made OUT param first in WMD_DEV_Create(). ++ *! 09-May-1996 gp: Created. ++ */ ++ ++#ifndef WMD_ ++#define WMD_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Any IOCTLS at or above this value are reserved for standard WMD ++ * interfaces. ++ */ ++#define WMD_RESERVEDIOCTLBASE 0x8000 ++ ++/* Handle to mini-driver's private device context. */ ++ struct WMD_DEV_CONTEXT; ++ ++/*---------------------------------------------------------------------------*/ ++/* 'Bridge MINI DRIVER FUNCTION TYPES */ ++/*---------------------------------------------------------------------------*/ ++ ++/* ++ * ======== WMD_BRD_Monitor ======== ++ * Purpose: ++ * Bring the board to the BRD_IDLE (monitor) state. ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device context. ++ * Returns: ++ * DSP_SOK: Success. ++ * WMD_E_HARDWARE: A test of hardware assumptions/integrity failed. ++ * WMD_E_TIMEOUT: Timeout occured waiting for a response from hardware. ++ * DSP_EFAIL: Other, unspecified error. ++ * Requires: ++ * hDevContext != NULL ++ * Ensures: ++ * DSP_SOK: Board is in BRD_IDLE state; ++ * else: Board state is indeterminate. ++ */ ++ typedef DSP_STATUS( ++ *WMD_BRD_MONITOR) (struct WMD_DEV_CONTEXT ++ *hDevContext); ++ ++/* ++ * ======== WMD_BRD_SETSTATE ======== ++ * Purpose: ++ * Sets the Mini driver state ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device info. ++ * ulBrdState: Board state ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Other, unspecified error. ++ * Requires: ++ * hDevContext != NULL; ++ * ulBrdState <= BRD_LASTSTATE. ++ * Ensures: ++ * ulBrdState <= BRD_LASTSTATE. ++ * Update the Board state to the specified state. ++ */ ++ typedef DSP_STATUS( ++ *WMD_BRD_SETSTATE) (struct WMD_DEV_CONTEXT ++ *hDevContext, u32 ulBrdState); ++ ++/* ++ * ======== WMD_BRD_Start ======== ++ * Purpose: ++ * Bring board to the BRD_RUNNING (start) state. ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device context. ++ * dwDSPAddr: DSP address at which to start execution. ++ * Returns: ++ * DSP_SOK: Success. ++ * WMD_E_TIMEOUT: Timeout occured waiting for a response from hardware. ++ * DSP_EFAIL: Other, unspecified error. ++ * Requires: ++ * hDevContext != NULL ++ * Board is in monitor (BRD_IDLE) state. ++ * Ensures: ++ * DSP_SOK: Board is in BRD_RUNNING state. ++ * Interrupts to the PC are enabled. ++ * else: Board state is indeterminate. ++ */ ++ typedef DSP_STATUS(*WMD_BRD_START) (struct WMD_DEV_CONTEXT ++ *hDevContext, u32 dwDSPAddr); ++ ++/* ++ * ======== WMD_BRD_MemCopy ======== ++ * Purpose: ++ * Copy memory from one DSP address to another ++ * Parameters: ++ * pDevContext: Pointer to context handle ++ * ulDspDestAddr: DSP address to copy to ++ * ulDspSrcAddr: DSP address to copy from ++ * ulNumBytes: Number of bytes to copy ++ * ulMemType: What section of memory to copy to ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Other, unspecified error. ++ * Requires: ++ * pDevContext != NULL ++ * Ensures: ++ * DSP_SOK: Board is in BRD_RUNNING state. ++ * Interrupts to the PC are enabled. ++ * else: Board state is indeterminate. ++ */ ++ typedef DSP_STATUS(*WMD_BRD_MEMCOPY) (struct WMD_DEV_CONTEXT ++ *hDevContext, ++ u32 ulDspDestAddr, ++ u32 ulDspSrcAddr, ++ u32 ulNumBytes, u32 ulMemType); ++/* ++ * ======== WMD_BRD_MemWrite ======== ++ * Purpose: ++ * Write a block of host memory into a DSP address, into a given memory ++ * space. Unlike WMD_BRD_Write, this API does reset the DSP ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device info. ++ * dwDSPAddr: Address on DSP board (Destination). ++ * pHostBuf: Pointer to host buffer (Source). ++ * ulNumBytes: Number of bytes to transfer. ++ * ulMemType: Memory space on DSP to which to transfer. ++ * Returns: ++ * DSP_SOK: Success. ++ * WMD_E_TIMEOUT: Timeout occured waiting for a response from hardware. ++ * DSP_EFAIL: Other, unspecified error. ++ * Requires: ++ * hDevContext != NULL; ++ * pHostBuf != NULL. ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_BRD_MEMWRITE) (struct WMD_DEV_CONTEXT ++ *hDevContext, ++ IN u8 *pHostBuf, ++ u32 dwDSPAddr, u32 ulNumBytes, ++ u32 ulMemType); ++ ++/* ++ * ======== WMD_BRD_MemMap ======== ++ * Purpose: ++ * Map a MPU memory region to a DSP/IVA memory space ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device info. ++ * ulMpuAddr: MPU memory region start address. ++ * ulVirtAddr: DSP/IVA memory region u8 address. ++ * ulNumBytes: Number of bytes to map. ++ * mapAttrs: Mapping attributes (e.g. endianness). ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Other, unspecified error. ++ * Requires: ++ * hDevContext != NULL; ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_BRD_MEMMAP) (struct WMD_DEV_CONTEXT ++ *hDevContext, u32 ulMpuAddr, ++ u32 ulVirtAddr, u32 ulNumBytes, ++ u32 ulMapAttrs); ++ ++/* ++ * ======== WMD_BRD_MemUnMap ======== ++ * Purpose: ++ * UnMap an MPU memory region from DSP/IVA memory space ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device info. ++ * ulVirtAddr: DSP/IVA memory region u8 address. ++ * ulNumBytes: Number of bytes to unmap. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Other, unspecified error. ++ * Requires: ++ * hDevContext != NULL; ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_BRD_MEMUNMAP) (struct WMD_DEV_CONTEXT ++ *hDevContext, ++ u32 ulVirtAddr, ++ u32 ulNumBytes); ++ ++/* ++ * ======== WMD_BRD_Stop ======== ++ * Purpose: ++ * Bring board to the BRD_STOPPED state. ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device context. ++ * Returns: ++ * DSP_SOK: Success. ++ * WMD_E_TIMEOUT: Timeout occured waiting for a response from hardware. ++ * DSP_EFAIL: Other, unspecified error. ++ * Requires: ++ * hDevContext != NULL ++ * Ensures: ++ * DSP_SOK: Board is in BRD_STOPPED (stop) state; ++ * Interrupts to the PC are disabled. ++ * else: Board state is indeterminate. ++ */ ++ typedef DSP_STATUS(*WMD_BRD_STOP) (struct WMD_DEV_CONTEXT ++ *hDevContext); ++ ++/* ++ * ======== WMD_BRD_Status ======== ++ * Purpose: ++ * Report the current state of the board. ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device context. ++ * pdwState: Ptr to BRD status variable. ++ * Returns: ++ * DSP_SOK: ++ * Requires: ++ * pdwState != NULL; ++ * hDevContext != NULL ++ * Ensures: ++ * *pdwState is one of {BRD_STOPPED, BRD_IDLE, BRD_RUNNING, BRD_UNKNOWN}; ++ */ ++ typedef DSP_STATUS(* ++ WMD_BRD_STATUS) (struct WMD_DEV_CONTEXT *hDevContext, ++ OUT BRD_STATUS * pdwState); ++ ++/* ++ * ======== WMD_BRD_Read ======== ++ * Purpose: ++ * Read a block of DSP memory, from a given memory space, into a host ++ * buffer. ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device info. ++ * pHostBuf: Pointer to host buffer (Destination). ++ * dwDSPAddr: Address on DSP board (Source). ++ * ulNumBytes: Number of bytes to transfer. ++ * ulMemType: Memory space on DSP from which to transfer. ++ * Returns: ++ * DSP_SOK: Success. ++ * WMD_E_TIMEOUT: Timeout occured waiting for a response from hardware. ++ * DSP_EFAIL: Other, unspecified error. ++ * Requires: ++ * hDevContext != NULL; ++ * pHostBuf != NULL. ++ * Ensures: ++ * Will not write more than ulNumBytes bytes into pHostBuf. ++ */ ++typedef DSP_STATUS(*WMD_BRD_READ) (struct WMD_DEV_CONTEXT *hDevContext, ++ OUT u8 *pHostBuf, ++ u32 dwDSPAddr, ++ u32 ulNumBytes, ++ u32 ulMemType); ++ ++/* ++ * ======== WMD_BRD_Write ======== ++ * Purpose: ++ * Write a block of host memory into a DSP address, into a given memory ++ * space. ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device info. ++ * dwDSPAddr: Address on DSP board (Destination). ++ * pHostBuf: Pointer to host buffer (Source). ++ * ulNumBytes: Number of bytes to transfer. ++ * ulMemType: Memory space on DSP to which to transfer. ++ * Returns: ++ * DSP_SOK: Success. ++ * WMD_E_TIMEOUT: Timeout occured waiting for a response from hardware. ++ * DSP_EFAIL: Other, unspecified error. ++ * Requires: ++ * hDevContext != NULL; ++ * pHostBuf != NULL. ++ * Ensures: ++ */ ++typedef DSP_STATUS(*WMD_BRD_WRITE)(struct WMD_DEV_CONTEXT *hDevContext, ++ IN u8 *pHostBuf, ++ u32 dwDSPAddr, ++ u32 ulNumBytes, ++ u32 ulMemType); ++ ++/* ++ * ======== WMD_CHNL_Create ======== ++ * Purpose: ++ * Create a channel manager object, responsible for opening new channels ++ * and closing old ones for a given 'Bridge board. ++ * Parameters: ++ * phChnlMgr: Location to store a channel manager object on output. ++ * hDevObject: Handle to a device object. ++ * pMgrAttrs: Channel manager attributes. ++ * pMgrAttrs->cChannels: Max channels ++ * pMgrAttrs->bIRQ: Channel's I/O IRQ number. ++ * pMgrAttrs->fShared: TRUE if the IRQ is shareable. ++ * pMgrAttrs->uWordSize: DSP Word size in equivalent PC bytes.. ++ * pMgrAttrs->dwSMBase: Base physical address of shared memory, if any. ++ * pMgrAttrs->uSMLength: Bytes of shared memory block. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EMEMORY: Insufficient memory for requested resources. ++ * CHNL_E_ISR: Unable to plug ISR for given IRQ. ++ * CHNL_E_NOMEMMAP: Couldn't map physical address to a virtual one. ++ * Requires: ++ * phChnlMgr != NULL. ++ * pMgrAttrs != NULL ++ * pMgrAttrs field are all valid: ++ * 0 < cChannels <= CHNL_MAXCHANNELS. ++ * bIRQ <= 15. ++ * uWordSize > 0. ++ * IsValidHandle(hDevObject) ++ * No channel manager exists for this board. ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_CHNL_CREATE)(OUT struct CHNL_MGR ++ **phChnlMgr, ++ struct DEV_OBJECT ++ *hDevObject, ++ IN CONST struct ++ CHNL_MGRATTRS *pMgrAttrs); ++ ++/* ++ * ======== WMD_CHNL_Destroy ======== ++ * Purpose: ++ * Close all open channels, and destroy the channel manager. ++ * Parameters: ++ * hChnlMgr: Channel manager object. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: hChnlMgr was invalid. ++ * Requires: ++ * Ensures: ++ * DSP_SOK: Cancels I/O on each open channel. Closes each open channel. ++ * CHNL_Create may subsequently be called for the same device. ++ */ ++ typedef DSP_STATUS(*WMD_CHNL_DESTROY) (struct CHNL_MGR ++ *hChnlMgr); ++/* ++ * ======== WMD_DEH_Notify ======== ++ * Purpose: ++ * When notified of DSP error, take appropriate action. ++ * Parameters: ++ * hDehMgr: Handle to DEH manager object. ++ * ulEventMask: Indicate the type of exception ++ * dwErrInfo: Error information ++ * Returns: ++ * ++ * Requires: ++ * hDehMgr != NULL; ++ * ulEventMask with a valid exception ++ * Ensures: ++ */ ++ typedef void (*WMD_DEH_NOTIFY)(struct DEH_MGR *hDehMgr, ++ u32 ulEventMask, u32 dwErrInfo); ++ ++ ++/* ++ * ======== WMD_CHNL_Open ======== ++ * Purpose: ++ * Open a new half-duplex channel to the DSP board. ++ * Parameters: ++ * phChnl: Location to store a channel object handle. ++ * hChnlMgr: Handle to channel manager, as returned by CHNL_GetMgr(). ++ * uMode: One of {CHNL_MODETODSP, CHNL_MODEFROMDSP} specifies ++ * direction of data transfer. ++ * uChnlId: If CHNL_PICKFREE is specified, the channel manager will ++ * select a free channel id (default); ++ * otherwise this field specifies the id of the channel. ++ * pAttrs: Channel attributes. Attribute fields are as follows: ++ * pAttrs->uIOReqs: Specifies the maximum number of I/O requests which can ++ * be pending at any given time. All request packets are ++ * preallocated when the channel is opened. ++ * pAttrs->hEvent: This field allows the user to supply an auto reset ++ * event object for channel I/O completion notifications. ++ * It is the responsibility of the user to destroy this ++ * object AFTER closing the channel. ++ * This channel event object can be retrieved using ++ * CHNL_GetEventHandle(). ++ * pAttrs->hReserved: The kernel mode handle of this event object. ++ * ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: hChnlMgr is invalid. ++ * DSP_EMEMORY: Insufficient memory for requested resources. ++ * DSP_EINVALIDARG: Invalid number of IOReqs. ++ * CHNL_E_OUTOFSTREAMS: No free channels available. ++ * CHNL_E_BADCHANID: Channel ID is out of range. ++ * CHNL_E_CHANBUSY: Channel is in use. ++ * CHNL_E_NOIORPS: No free IO request packets available for ++ * queuing. ++ * Requires: ++ * phChnl != NULL. ++ * pAttrs != NULL. ++ * pAttrs->hEvent is a valid event handle. ++ * pAttrs->hReserved is the kernel mode handle for pAttrs->hEvent. ++ * Ensures: ++ * DSP_SOK: *phChnl is a valid channel. ++ * else: *phChnl is set to NULL if (phChnl != NULL); ++ */ ++ typedef DSP_STATUS(*WMD_CHNL_OPEN) (OUT struct CHNL_OBJECT ++ **phChnl, ++ struct CHNL_MGR *hChnlMgr, ++ CHNL_MODE uMode, ++ u32 uChnlId, ++ CONST IN OPTIONAL struct ++ CHNL_ATTRS *pAttrs); ++ ++/* ++ * ======== WMD_CHNL_Close ======== ++ * Purpose: ++ * Ensures all pending I/O on this channel is cancelled, discards all ++ * queued I/O completion notifications, then frees the resources allocated ++ * for this channel, and makes the corresponding logical channel id ++ * available for subsequent use. ++ * Parameters: ++ * hChnl: Handle to a channel object. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EHANDLE: Invalid hChnl. ++ * Requires: ++ * No thread must be blocked on this channel's I/O completion event. ++ * Ensures: ++ * DSP_SOK: hChnl is no longer valid. ++ */ ++ typedef DSP_STATUS(*WMD_CHNL_CLOSE) (struct CHNL_OBJECT *hChnl); ++ ++/* ++ * ======== WMD_CHNL_AddIOReq ======== ++ * Purpose: ++ * Enqueue an I/O request for data transfer on a channel to the DSP. ++ * The direction (mode) is specified in the channel object. Note the DSP ++ * address is specified for channels opened in direct I/O mode. ++ * Parameters: ++ * hChnl: Channel object handle. ++ * pHostBuf: Host buffer address source. ++ * cBytes: Number of PC bytes to transfer. A zero value indicates ++ * that this buffer is the last in the output channel. ++ * A zero value is invalid for an input channel. ++ *! cBufSize: Actual buffer size in host bytes. ++ * dwDspAddr: DSP address for transfer. (Currently ignored). ++ * dwArg: A user argument that travels with the buffer. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EHANDLE: Invalid hChnl. ++ * DSP_EPOINTER: pHostBuf is invalid. ++ * CHNL_E_NOEOS: User cannot mark EOS on an input channel. ++ * CHNL_E_CANCELLED: I/O has been cancelled on this channel. No further ++ * I/O is allowed. ++ * CHNL_E_EOS: End of stream was already marked on a previous ++ * IORequest on this channel. No further I/O is expected. ++ * CHNL_E_BUFSIZE: Buffer submitted to this output channel is larger than ++ * the size of the physical shared memory output window. ++ * Requires: ++ * Ensures: ++ * DSP_SOK: The buffer will be transferred if the channel is ready; ++ * otherwise, will be queued for transfer when the channel becomes ++ * ready. In any case, notifications of I/O completion are ++ * asynchronous. ++ * If cBytes is 0 for an output channel, subsequent CHNL_AddIOReq's ++ * on this channel will fail with error code CHNL_E_EOS. The ++ * corresponding IOC for this I/O request will have its status flag ++ * set to CHNL_IOCSTATEOS. ++ */ ++ typedef DSP_STATUS(*WMD_CHNL_ADDIOREQ) (struct CHNL_OBJECT ++ *hChnl, ++ void *pHostBuf, ++ u32 cBytes, ++ u32 cBufSize, ++ OPTIONAL u32 dwDspAddr, ++ u32 dwArg); ++ ++/* ++ * ======== WMD_CHNL_GetIOC ======== ++ * Purpose: ++ * Dequeue an I/O completion record, which contains information about the ++ * completed I/O request. ++ * Parameters: ++ * hChnl: Channel object handle. ++ * dwTimeOut: A value of CHNL_IOCNOWAIT will simply dequeue the ++ * first available IOC. ++ * pIOC: On output, contains host buffer address, bytes ++ * transferred, and status of I/O completion. ++ * pIOC->status: See chnldefs.h. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hChnl. ++ * DSP_EPOINTER: pIOC is invalid. ++ * CHNL_E_NOIOC: CHNL_IOCNOWAIT was specified as the dwTimeOut parameter ++ * yet no I/O completions were queued. ++ * Requires: ++ * dwTimeOut == CHNL_IOCNOWAIT. ++ * Ensures: ++ * DSP_SOK: if there are any remaining IOC's queued before this call ++ * returns, the channel event object will be left in a signalled ++ * state. ++ */ ++ typedef DSP_STATUS(*WMD_CHNL_GETIOC) (struct CHNL_OBJECT *hChnl, ++ u32 dwTimeOut, ++ OUT struct CHNL_IOC *pIOC); ++ ++/* ++ * ======== WMD_CHNL_CancelIO ======== ++ * Purpose: ++ * Return all I/O requests to the client which have not yet been ++ * transferred. The channel's I/O completion object is ++ * signalled, and all the I/O requests are queued as IOC's, with the ++ * status field set to CHNL_IOCSTATCANCEL. ++ * This call is typically used in abort situations, and is a prelude to ++ * CHNL_Close(); ++ * Parameters: ++ * hChnl: Channel object handle. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EHANDLE: Invalid hChnl. ++ * Requires: ++ * Ensures: ++ * Subsequent I/O requests to this channel will not be accepted. ++ */ ++ typedef DSP_STATUS(*WMD_CHNL_CANCELIO) (struct CHNL_OBJECT ++ *hChnl); ++ ++/* ++ * ======== WMD_CHNL_FlushIO ======== ++ * Purpose: ++ * For an output stream (to the DSP), indicates if any IO requests are in ++ * the output request queue. For input streams (from the DSP), will ++ * cancel all pending IO requests. ++ * Parameters: ++ * hChnl: Channel object handle. ++ * dwTimeOut: Timeout value for flush operation. ++ * Returns: ++ * DSP_SOK: Success; ++ * S_CHNLIOREQUEST: Returned if any IORequests are in the output queue. ++ * DSP_EHANDLE: Invalid hChnl. ++ * Requires: ++ * Ensures: ++ * DSP_SOK: No I/O requests will be pending on this channel. ++ */ ++ typedef DSP_STATUS(*WMD_CHNL_FLUSHIO) (struct CHNL_OBJECT *hChnl, ++ u32 dwTimeOut); ++ ++/* ++ * ======== WMD_CHNL_GetInfo ======== ++ * Purpose: ++ * Retrieve information related to a channel. ++ * Parameters: ++ * hChnl: Handle to a valid channel object, or NULL. ++ * pInfo: Location to store channel info. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EHANDLE: Invalid hChnl. ++ * DSP_EPOINTER: pInfo == NULL. ++ * Requires: ++ * Ensures: ++ * DSP_SOK: pInfo points to a filled in CHNL_INFO struct, ++ * if (pInfo != NULL). ++ */ ++ typedef DSP_STATUS(*WMD_CHNL_GETINFO) (struct CHNL_OBJECT *hChnl, ++ OUT struct CHNL_INFO ++ *pChnlInfo); ++ ++/* ++ * ======== WMD_CHNL_GetMgrInfo ======== ++ * Purpose: ++ * Retrieve information related to the channel manager. ++ * Parameters: ++ * hChnlMgr: Handle to a valid channel manager, or NULL. ++ * uChnlID: Channel ID. ++ * pMgrInfo: Location to store channel manager info. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EHANDLE: Invalid hChnlMgr. ++ * DSP_EPOINTER: pMgrInfo == NULL. ++ * CHNL_E_BADCHANID: Invalid channel ID. ++ * Requires: ++ * Ensures: ++ * DSP_SOK: pMgrInfo points to a filled in CHNL_MGRINFO ++ * struct, if (pMgrInfo != NULL). ++ */ ++ typedef DSP_STATUS(*WMD_CHNL_GETMGRINFO) (struct CHNL_MGR ++ *hChnlMgr, ++ u32 uChnlID, ++ OUT struct CHNL_MGRINFO ++ *pMgrInfo); ++ ++/* ++ * ======== WMD_CHNL_Idle ======== ++ * Purpose: ++ * Idle a channel. If this is an input channel, or if this is an output ++ * channel and fFlush is TRUE, all currently enqueued buffers will be ++ * dequeued (data discarded for output channel). ++ * If this is an output channel and fFlush is FALSE, this function ++ * will block until all currently buffered data is output, or the timeout ++ * specified has been reached. ++ * ++ * Parameters: ++ * hChnl: Channel object handle. ++ * dwTimeOut: If output channel and fFlush is FALSE, timeout value ++ * to wait for buffers to be output. (Not used for ++ * input channel). ++ * fFlush: If output channel and fFlush is TRUE, discard any ++ * currently buffered data. If FALSE, wait for currently ++ * buffered data to be output, or timeout, whichever ++ * occurs first. fFlush is ignored for input channel. ++ * Returns: ++ * DSP_SOK: Success; ++ * DSP_EHANDLE: Invalid hChnl. ++ * CHNL_E_WAITTIMEOUT: Timeout occured before channel could be idled. ++ * Requires: ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_CHNL_IDLE) (struct CHNL_OBJECT *hChnl, ++ u32 dwTimeOut, ++ bool fFlush); ++ ++/* ++ * ======== WMD_CHNL_RegisterNotify ======== ++ * Purpose: ++ * Register for notification of events on a channel. ++ * Parameters: ++ * hChnl: Channel object handle. ++ * uEventMask: Type of events to be notified about: IO completion ++ * (DSP_STREAMIOCOMPLETION) or end of stream ++ * (DSP_STREAMDONE). ++ * uNotifyType: DSP_SIGNALEVENT. ++ * hNotification: Handle of a DSP_NOTIFICATION object. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Insufficient memory. ++ * DSP_EVALUE: uEventMask is 0 and hNotification was not ++ * previously registered. ++ * DSP_EHANDLE: NULL hNotification, hNotification event name ++ * too long, or hNotification event name NULL. ++ * Requires: ++ * Valid hChnl. ++ * hNotification != NULL. ++ * (uEventMask & ~(DSP_STREAMIOCOMPLETION | DSP_STREAMDONE)) == 0. ++ * uNotifyType == DSP_SIGNALEVENT. ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_CHNL_REGISTERNOTIFY) ++ (struct CHNL_OBJECT *hChnl, ++ u32 uEventMask, ++ u32 uNotifyType, ++ struct DSP_NOTIFICATION *hNotification); ++ ++/* ++ * ======== WMD_DEV_Create ======== ++ * Purpose: ++ * Complete creation of the device object for this board. ++ * Parameters: ++ * phDevContext: Ptr to location to store a WMD device context. ++ * hDevObject: Handle to a Device Object, created and managed by WCD. ++ * pConfig: Ptr to configuration parameters provided by the Windows ++ * Configuration Manager during device loading. ++ * pDspConfig: DSP resources, as specified in the registry key for this ++ * device. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Unable to allocate memory for device context. ++ * WMD_E_BADCONFIG: One or more of the host or DSP configuration ++ * parameters did not satisfy hardware assumptions ++ * made by this WMD. ++ * Requires: ++ * phDevContext != NULL; ++ * hDevObject != NULL; ++ * pConfig != NULL; ++ * pDspConfig != NULL; ++ * Fields in pConfig and pDspConfig contain valid values. ++ * Ensures: ++ * DSP_SOK: All mini-driver specific DSP resource and other ++ * board context has been allocated. ++ * DSP_EMEMORY: WMD failed to allocate resources. ++ * Any acquired resources have been freed. The WCD will ++ * not call WMD_DEV_Destroy() if WMD_DEV_Create() fails. ++ * Details: ++ * Called during the CONFIGMG's Device_Init phase. Based on host and ++ * DSP configuration information, create a board context, a handle to ++ * which is passed into other WMD BRD and CHNL functions. The ++ * board context contains state information for the device. Since the ++ * addresses of all IN pointer parameters may be invalid when this ++ * function returns, they must not be stored into the device context ++ * structure. ++ */ ++ typedef DSP_STATUS(*WMD_DEV_CREATE) (OUT struct WMD_DEV_CONTEXT ++ **phDevContext, ++ struct DEV_OBJECT ++ *hDevObject, ++ IN CONST struct CFG_HOSTRES ++ *pConfig, ++ IN CONST struct CFG_DSPRES ++ *pDspConfig); ++ ++/* ++ * ======== WMD_DEV_Ctrl ======== ++ * Purpose: ++ * Mini-driver specific interface. ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device info. ++ * dwCmd: WMD defined command code. ++ * pArgs: Pointer to an arbitrary argument structure. ++ * Returns: ++ * DSP_SOK or DSP_EFAIL. Actual command error codes should be passed back ++ * in the pArgs structure, and are defined by the WMD implementor. ++ * Requires: ++ * All calls are currently assumed to be synchronous. There are no ++ * IOCTL completion routines provided. ++ * Ensures: ++ */ ++typedef DSP_STATUS(*WMD_DEV_CTRL)(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 dwCmd, ++ IN OUT void *pArgs); ++ ++/* ++ * ======== WMD_DEV_Destroy ======== ++ * Purpose: ++ * Deallocate WMD device extension structures and all other resources ++ * acquired by the mini-driver. ++ * No calls to other mini driver functions may subsequently ++ * occur, except for WMD_DEV_Create(). ++ * Parameters: ++ * hDevContext: Handle to mini-driver defined device information. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Failed to release a resource previously acquired. ++ * Requires: ++ * hDevContext != NULL; ++ * Ensures: ++ * DSP_SOK: Device context is freed. ++ */ ++ typedef DSP_STATUS(*WMD_DEV_DESTROY) (struct WMD_DEV_CONTEXT ++ *hDevContext); ++ ++/* ++ * ======== WMD_DEH_Create ======== ++ * Purpose: ++ * Create an object that manages DSP exceptions from the GPP. ++ * Parameters: ++ * phDehMgr: Location to store DEH manager on output. ++ * hDevObject: Handle to DEV object. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Memory allocation failure. ++ * DSP_EFAIL: Creation failed. ++ * Requires: ++ * hDevObject != NULL; ++ * phDehMgr != NULL; ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_DEH_CREATE) (OUT struct DEH_MGR ++ **phDehMgr, ++ struct DEV_OBJECT ++ *hDevObject); ++ ++/* ++ * ======== WMD_DEH_Destroy ======== ++ * Purpose: ++ * Destroy the DEH object. ++ * Parameters: ++ * hDehMgr: Handle to DEH manager object. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Destroy failed. ++ * Requires: ++ * hDehMgr != NULL; ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_DEH_DESTROY) (struct DEH_MGR *hDehMgr); ++ ++/* ++ * ======== WMD_DEH_RegisterNotify ======== ++ * Purpose: ++ * Register for DEH event notification. ++ * Parameters: ++ * hDehMgr: Handle to DEH manager object. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Destroy failed. ++ * Requires: ++ * hDehMgr != NULL; ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_DEH_REGISTERNOTIFY) ++ (struct DEH_MGR *hDehMgr, ++ u32 uEventMask, u32 uNotifyType, ++ struct DSP_NOTIFICATION *hNotification); ++ ++/* ++ * ======== WMD_DEH_GetInfo ======== ++ * Purpose: ++ * Get DSP exception info. ++ * Parameters: ++ * phDehMgr: Location to store DEH manager on output. ++ * pErrInfo: Ptr to error info structure. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Creation failed. ++ * Requires: ++ * phDehMgr != NULL; ++ * pErrorInfo != NULL; ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_DEH_GETINFO) (struct DEH_MGR *phDehMgr, ++ struct DSP_ERRORINFO *pErrInfo); ++ ++/* ++ * ======== WMD_IO_Create ======== ++ * Purpose: ++ * Create an object that manages I/O between CHNL and MSG. ++ * Parameters: ++ * phIOMgr: Location to store IO manager on output. ++ * hChnlMgr: Handle to channel manager. ++ * hMsgMgr: Handle to message manager. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Memory allocation failure. ++ * DSP_EFAIL: Creation failed. ++ * Requires: ++ * hDevObject != NULL; ++ * Channel manager already created; ++ * Message manager already created; ++ * pMgrAttrs != NULL; ++ * phIOMgr != NULL; ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_IO_CREATE) (OUT struct IO_MGR **phIOMgr, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct IO_ATTRS *pMgrAttrs); ++ ++/* ++ * ======== WMD_IO_Destroy ======== ++ * Purpose: ++ * Destroy object created in WMD_IO_Create. ++ * Parameters: ++ * hIOMgr: IO Manager. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Memory allocation failure. ++ * DSP_EFAIL: Creation failed. ++ * Requires: ++ * Valid hIOMgr; ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_IO_DESTROY) (struct IO_MGR *hIOMgr); ++ ++/* ++ * ======== WMD_IO_OnLoaded ======== ++ * Purpose: ++ * Called whenever a program is loaded to update internal data. For ++ * example, if shared memory is used, this function would update the ++ * shared memory location and address. ++ * Parameters: ++ * hIOMgr: IO Manager. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Internal failure occurred. ++ * Requires: ++ * Valid hIOMgr; ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_IO_ONLOADED) (struct IO_MGR *hIOMgr); ++ ++/* ++ * ======== WMD_IO_GETPROCLOAD ======== ++ * Purpose: ++ * Called to get the Processor's current and predicted load ++ * Parameters: ++ * hIOMgr: IO Manager. ++ * pProcLoadStat Processor Load statistics ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EFAIL: Internal failure occurred. ++ * Requires: ++ * Valid hIOMgr; ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_IO_GETPROCLOAD)(struct IO_MGR *hIOMgr, ++ struct DSP_PROCLOADSTAT *pProcLoadStat); ++ ++/* ++ * ======== WMD_MSG_Create ======== ++ * Purpose: ++ * Create an object to manage message queues. Only one of these objects ++ * can exist per device object. ++ * Parameters: ++ * phMsgMgr: Location to store MSG manager on output. ++ * hDevObject: Handle to a device object. ++ * msgCallback: Called whenever an RMS_EXIT message is received. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Insufficient memory. ++ * Requires: ++ * phMsgMgr != NULL. ++ * msgCallback != NULL. ++ * hDevObject != NULL. ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_MSG_CREATE) ++ (OUT struct MSG_MGR **phMsgMgr, ++ struct DEV_OBJECT *hDevObject, ++ MSG_ONEXIT msgCallback); ++ ++/* ++ * ======== WMD_MSG_CreateQueue ======== ++ * Purpose: ++ * Create a MSG queue for sending or receiving messages from a Message ++ * node on the DSP. ++ * Parameters: ++ * hMsgMgr: MSG queue manager handle returned from ++ * WMD_MSG_Create. ++ * phMsgQueue: Location to store MSG queue on output. ++ * dwId: Identifier for messages (node environment pointer). ++ * uMaxMsgs: Max number of simultaneous messages for the node. ++ * h: Handle passed to hMsgMgr->msgCallback(). ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Insufficient memory. ++ * Requires: ++ * phMsgQueue != NULL. ++ * h != NULL. ++ * uMaxMsgs > 0. ++ * Ensures: ++ * phMsgQueue !=NULL <==> DSP_SOK. ++ */ ++ typedef DSP_STATUS(*WMD_MSG_CREATEQUEUE) ++ (struct MSG_MGR *hMsgMgr, ++ OUT struct MSG_QUEUE **phMsgQueue, ++ u32 dwId, u32 uMaxMsgs, HANDLE h); ++ ++/* ++ * ======== WMD_MSG_Delete ======== ++ * Purpose: ++ * Delete a MSG manager allocated in WMD_MSG_Create(). ++ * Parameters: ++ * hMsgMgr: Handle returned from WMD_MSG_Create(). ++ * Returns: ++ * Requires: ++ * Valid hMsgMgr. ++ * Ensures: ++ */ ++ typedef void(*WMD_MSG_DELETE) (struct MSG_MGR *hMsgMgr); ++ ++/* ++ * ======== WMD_MSG_DeleteQueue ======== ++ * Purpose: ++ * Delete a MSG queue allocated in WMD_MSG_CreateQueue. ++ * Parameters: ++ * hMsgQueue: Handle to MSG queue returned from ++ * WMD_MSG_CreateQueue. ++ * Returns: ++ * Requires: ++ * Valid hMsgQueue. ++ * Ensures: ++ */ ++ typedef void(*WMD_MSG_DELETEQUEUE) (struct MSG_QUEUE *hMsgQueue); ++ ++/* ++ * ======== WMD_MSG_Get ======== ++ * Purpose: ++ * Get a message from a MSG queue. ++ * Parameters: ++ * hMsgQueue: Handle to MSG queue returned from ++ * WMD_MSG_CreateQueue. ++ * pMsg: Location to copy message into. ++ * uTimeout: Timeout to wait for a message. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ETIMEOUT: Timeout occurred. ++ * DSP_EFAIL: No frames available for message (uMaxMessages too ++ * small). ++ * Requires: ++ * Valid hMsgQueue. ++ * pMsg != NULL. ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_MSG_GET) (struct MSG_QUEUE *hMsgQueue, ++ struct DSP_MSG *pMsg, ++ u32 uTimeout); ++ ++/* ++ * ======== WMD_MSG_Put ======== ++ * Purpose: ++ * Put a message onto a MSG queue. ++ * Parameters: ++ * hMsgQueue: Handle to MSG queue returned from ++ * WMD_MSG_CreateQueue. ++ * pMsg: Pointer to message. ++ * uTimeout: Timeout to wait for a message. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_ETIMEOUT: Timeout occurred. ++ * DSP_EFAIL: No frames available for message (uMaxMessages too ++ * small). ++ * Requires: ++ * Valid hMsgQueue. ++ * pMsg != NULL. ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_MSG_PUT) (struct MSG_QUEUE *hMsgQueue, ++ IN CONST struct DSP_MSG *pMsg, ++ u32 uTimeout); ++ ++/* ++ * ======== WMD_MSG_RegisterNotify ======== ++ * Purpose: ++ * Register notification for when a message is ready. ++ * Parameters: ++ * hMsgQueue: Handle to MSG queue returned from ++ * WMD_MSG_CreateQueue. ++ * uEventMask: Type of events to be notified about: Must be ++ * DSP_NODEMESSAGEREADY, or 0 to unregister. ++ * uNotifyType: DSP_SIGNALEVENT. ++ * hNotification: Handle of notification object. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Insufficient memory. ++ * Requires: ++ * Valid hMsgQueue. ++ * hNotification != NULL. ++ * uNotifyType == DSP_SIGNALEVENT. ++ * uEventMask == DSP_NODEMESSAGEREADY || uEventMask == 0. ++ * Ensures: ++ */ ++ typedef DSP_STATUS(*WMD_MSG_REGISTERNOTIFY) ++ (struct MSG_QUEUE *hMsgQueue, ++ u32 uEventMask, u32 uNotifyType, ++ struct DSP_NOTIFICATION *hNotification); ++ ++/* ++ * ======== WMD_MSG_SetQueueId ======== ++ * Purpose: ++ * Set message queue id to node environment. Allows WMD_MSG_CreateQueue ++ * to be called in NODE_Allocate, before the node environment is known. ++ * Parameters: ++ * hMsgQueue: Handle to MSG queue returned from ++ * WMD_MSG_CreateQueue. ++ * dwId: Node environment pointer. ++ * Returns: ++ * Requires: ++ * Valid hMsgQueue. ++ * dwId != 0. ++ * Ensures: ++ */ ++ typedef void(*WMD_MSG_SETQUEUEID) (struct MSG_QUEUE *hMsgQueue, ++ u32 dwId); ++ ++/* ++ * 'Bridge Mini Driver (WMD) interface function table. ++ * ++ * The information in this table is filled in by the specific mini-driver, ++ * and copied into the 'Bridge class driver's own space. If any interface ++ * function field is set to a value of NULL, then the class driver will ++ * consider that function not implemented, and return the error code ++ * DSP_ENOTIMPL when a WMD client attempts to call that function. ++ * ++ * This function table contains WCD version numbers, which are used by the ++ * WMD loader to help ensure backwards compatility between older WMD's and a ++ * newer 'Bridge Class Driver. These must be set to WCD_MAJOR_VERSION ++ * and WCD_MINOR_VERSION, respectively. ++ * ++ * A mini-driver need not export a CHNL interface. In this case, *all* of ++ * the WMD_CHNL_* entries must be set to NULL. ++ */ ++ struct WMD_DRV_INTERFACE { ++ u32 dwWCDMajorVersion; /* Set to WCD_MAJOR_VERSION. */ ++ u32 dwWCDMinorVersion; /* Set to WCD_MINOR_VERSION. */ ++ WMD_DEV_CREATE pfnDevCreate; /* Create device context */ ++ WMD_DEV_DESTROY pfnDevDestroy; /* Destroy device context */ ++ WMD_DEV_CTRL pfnDevCntrl; /* Optional vendor interface */ ++ WMD_BRD_MONITOR pfnBrdMonitor; /* Load and/or start monitor */ ++ WMD_BRD_START pfnBrdStart; /* Start DSP program. */ ++ WMD_BRD_STOP pfnBrdStop; /* Stop/reset board. */ ++ WMD_BRD_STATUS pfnBrdStatus; /* Get current board status. */ ++ WMD_BRD_READ pfnBrdRead; /* Read board memory */ ++ WMD_BRD_WRITE pfnBrdWrite; /* Write board memory. */ ++ WMD_BRD_SETSTATE pfnBrdSetState; /* Sets the Board State */ ++ WMD_BRD_MEMCOPY pfnBrdMemCopy; /* Copies DSP Memory */ ++ WMD_BRD_MEMWRITE pfnBrdMemWrite; /* Write DSP Memory w/o halt */ ++ WMD_BRD_MEMMAP pfnBrdMemMap; /* Maps MPU mem to DSP mem */ ++ WMD_BRD_MEMUNMAP pfnBrdMemUnMap; /* Unmaps MPU mem to DSP mem */ ++ WMD_CHNL_CREATE pfnChnlCreate; /* Create channel manager. */ ++ WMD_CHNL_DESTROY pfnChnlDestroy; /* Destroy channel manager. */ ++ WMD_CHNL_OPEN pfnChnlOpen; /* Create a new channel. */ ++ WMD_CHNL_CLOSE pfnChnlClose; /* Close a channel. */ ++ WMD_CHNL_ADDIOREQ pfnChnlAddIOReq; /* Req I/O on a channel. */ ++ WMD_CHNL_GETIOC pfnChnlGetIOC; /* Wait for I/O completion. */ ++ WMD_CHNL_CANCELIO pfnChnlCancelIO; /* Cancl I/O on a channel. */ ++ WMD_CHNL_FLUSHIO pfnChnlFlushIO; /* Flush I/O. */ ++ WMD_CHNL_GETINFO pfnChnlGetInfo; /* Get channel specific info */ ++ /* Get channel manager info. */ ++ WMD_CHNL_GETMGRINFO pfnChnlGetMgrInfo; ++ WMD_CHNL_IDLE pfnChnlIdle; /* Idle the channel */ ++ /* Register for notif. */ ++ WMD_CHNL_REGISTERNOTIFY pfnChnlRegisterNotify; ++ WMD_DEH_CREATE pfnDehCreate; /* Create DEH manager */ ++ WMD_DEH_DESTROY pfnDehDestroy; /* Destroy DEH manager */ ++ WMD_DEH_NOTIFY pfnDehNotify; /* Notify of DSP error */ ++ /* register for deh notif. */ ++ WMD_DEH_REGISTERNOTIFY pfnDehRegisterNotify; ++ WMD_DEH_GETINFO pfnDehGetInfo; /* register for deh notif. */ ++ WMD_IO_CREATE pfnIOCreate; /* Create IO manager */ ++ WMD_IO_DESTROY pfnIODestroy; /* Destroy IO manager */ ++ WMD_IO_ONLOADED pfnIOOnLoaded; /* Notify of program loaded */ ++ /* Get Processor's current and predicted load */ ++ WMD_IO_GETPROCLOAD pfnIOGetProcLoad; ++ WMD_MSG_CREATE pfnMsgCreate; /* Create message manager */ ++ /* Create message queue */ ++ WMD_MSG_CREATEQUEUE pfnMsgCreateQueue; ++ WMD_MSG_DELETE pfnMsgDelete; /* Delete message manager */ ++ /* Delete message queue */ ++ WMD_MSG_DELETEQUEUE pfnMsgDeleteQueue; ++ WMD_MSG_GET pfnMsgGet; /* Get a message */ ++ WMD_MSG_PUT pfnMsgPut; /* Send a message */ ++ /* Register for notif. */ ++ WMD_MSG_REGISTERNOTIFY pfnMsgRegisterNotify; ++ /* Set message queue id */ ++ WMD_MSG_SETQUEUEID pfnMsgSetQueueId; ++ } ; ++ ++/* ++ * ======== WMD_DRV_Entry ======== ++ * Purpose: ++ * Registers WMD functions with the class driver. Called only once ++ * by the WCD. The caller will first check WCD version compatibility, and ++ * then copy the interface functions into its own memory space. ++ * Parameters: ++ * ppDrvInterface Pointer to a location to receive a pointer to the ++ * mini driver interface. ++ * Returns: ++ * Requires: ++ * The code segment this function resides in must expect to be discarded ++ * after completion. ++ * Ensures: ++ * ppDrvInterface pointer initialized to WMD's function interface. ++ * No system resources are acquired by this function. ++ * Details: ++ * Win95: Called during the Device_Init phase. ++ */ ++ void WMD_DRV_Entry(OUT struct WMD_DRV_INTERFACE **ppDrvInterface, ++ IN CONST char *pstrWMDFileName); ++ ++#endif /* WMD_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/wmdchnl.h b/arch/arm/plat-omap/include/dspbridge/wmdchnl.h +new file mode 100644 +index 0000000..2c1f072 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/wmdchnl.h +@@ -0,0 +1,90 @@ ++/* ++ * wmdchnl.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== wmdchnl.h ======== ++ * Description: ++ * Declares the upper edge channel class library functions required by ++ * all WMD / WCD driver interface tables. These functions are implemented ++ * by every class of WMD channel library. ++ * ++ * Public Functions: ++ * ++ * Notes: ++ * The function comment headers reside with the function typedefs in wmd.h. ++ * ++ *! Revision History: ++ *! ================ ++ *! 07-Jan-2002 ag Added cBufSize to WMD_CHNL_AddIOReq(). ++ *! 13-Oct-2000 jeh Added dwArg parameter to WMD_CHNL_AddIOReq(), added ++ *! WMD_CHNL_Idle and WMD_CHNL_RegisterNotify for DSPStream ++ *! support. ++ *! 11-Jul-1996 gp: Created. ++ */ ++ ++#ifndef WMDCHNL_ ++#define WMDCHNL_ ++ ++ extern DSP_STATUS WMD_CHNL_Create(OUT struct CHNL_MGR **phChnlMgr, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct CHNL_MGRATTRS ++ *pMgrAttrs); ++ ++ extern DSP_STATUS WMD_CHNL_Destroy(struct CHNL_MGR *hChnlMgr); ++ ++ extern DSP_STATUS WMD_CHNL_Open(OUT struct CHNL_OBJECT **phChnl, ++ struct CHNL_MGR *hChnlMgr, ++ CHNL_MODE uMode, ++ u32 uChnlId, ++ CONST IN OPTIONAL struct CHNL_ATTRS ++ *pAttrs); ++ ++ extern DSP_STATUS WMD_CHNL_Close(struct CHNL_OBJECT *hChnl); ++ ++ extern DSP_STATUS WMD_CHNL_AddIOReq(struct CHNL_OBJECT *hChnl, ++ void *pHostBuf, ++ u32 cBytes, u32 cBufSize, ++ OPTIONAL u32 dwDspAddr, ++ u32 dwArg); ++ ++ extern DSP_STATUS WMD_CHNL_GetIOC(struct CHNL_OBJECT *hChnl, ++ u32 dwTimeOut, ++ OUT struct CHNL_IOC *pIOC); ++ ++ extern DSP_STATUS WMD_CHNL_CancelIO(struct CHNL_OBJECT *hChnl); ++ ++ extern DSP_STATUS WMD_CHNL_FlushIO(struct CHNL_OBJECT *hChnl, ++ u32 dwTimeOut); ++ ++ extern DSP_STATUS WMD_CHNL_GetInfo(struct CHNL_OBJECT *hChnl, ++ OUT struct CHNL_INFO *pInfo); ++ ++ extern DSP_STATUS WMD_CHNL_GetMgrInfo(struct CHNL_MGR *hChnlMgr, ++ u32 uChnlID, ++ OUT struct CHNL_MGRINFO ++ *pMgrInfo); ++ ++ extern DSP_STATUS WMD_CHNL_Idle(struct CHNL_OBJECT *hChnl, ++ u32 dwTimeOut, bool fFlush); ++ ++ extern DSP_STATUS WMD_CHNL_RegisterNotify(struct CHNL_OBJECT *hChnl, ++ u32 uEventMask, ++ u32 uNotifyType, ++ struct DSP_NOTIFICATION ++ *hNotification); ++ ++#endif /* WMDCHNL_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/wmddeh.h b/arch/arm/plat-omap/include/dspbridge/wmddeh.h +new file mode 100644 +index 0000000..dd50a3a +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/wmddeh.h +@@ -0,0 +1,64 @@ ++/* ++ * wmddeh.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== wmddeh.h ======== ++ * Description: ++ * Defines upper edge DEH functions required by all WMD/WCD driver ++ * interface tables. ++ * ++ * Public Functions: ++ * WMD_DEH_Create ++ * IVA_DEH_Create ++ * WMD_DEH_Destroy ++ * WMD_DEH_GetInfo ++ * WMD_DEH_RegisterNotify ++ * WMD_DEH_Notify ++ * ++ * Notes: ++ * Function comment headers reside with the function typedefs in wmd.h. ++ * ++ *! Revision History: ++ *! ================ ++ *! 26-Dec-2004 hn: added IVA_DEH_Create. ++ *! 13-Sep-2001 kc: created. ++ */ ++ ++#ifndef WMDDEH_ ++#define WMDDEH_ ++ ++#include ++ ++#include ++ ++ extern DSP_STATUS WMD_DEH_Create(OUT struct DEH_MGR **phDehMgr, ++ struct DEV_OBJECT *hDevObject); ++ ++ extern DSP_STATUS WMD_DEH_Destroy(struct DEH_MGR *hDehMgr); ++ ++ extern DSP_STATUS WMD_DEH_GetInfo(struct DEH_MGR *hDehMgr, ++ struct DSP_ERRORINFO *pErrInfo); ++ ++ extern DSP_STATUS WMD_DEH_RegisterNotify(struct DEH_MGR *hDehMgr, ++ u32 uEventMask, ++ u32 uNotifyType, ++ struct DSP_NOTIFICATION ++ *hNotification); ++ ++ extern void WMD_DEH_Notify(struct DEH_MGR *hDehMgr, ++ u32 ulEventMask, u32 dwErrInfo); ++#endif /* WMDDEH_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/wmdio.h b/arch/arm/plat-omap/include/dspbridge/wmdio.h +new file mode 100644 +index 0000000..8525474 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/wmdio.h +@@ -0,0 +1,53 @@ ++/* ++ * wmdio.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== wmdio.h ======== ++ * Description: ++ * Declares the upper edge IO functions required by ++ * all WMD / WCD driver interface tables. ++ * ++ * Public Functions: ++ * ++ * Notes: ++ * Function comment headers reside with the function typedefs in wmd.h. ++ * ++ *! Revision History: ++ *! ================ ++ *! 27-Feb-2004 vp Added IVA releated function. ++ *! 06-Nov-2000 jeh Created. ++ */ ++ ++#ifndef WMDIO_ ++#define WMDIO_ ++ ++#include ++#include ++ ++ extern DSP_STATUS WMD_IO_Create(OUT struct IO_MGR **phIOMgr, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct IO_ATTRS *pMgrAttrs); ++ ++ extern DSP_STATUS WMD_IO_Destroy(struct IO_MGR *hIOMgr); ++ ++ extern DSP_STATUS WMD_IO_OnLoaded(struct IO_MGR *hIOMgr); ++ ++ extern DSP_STATUS IVA_IO_OnLoaded(struct IO_MGR *hIOMgr); ++ extern DSP_STATUS WMD_IO_GetProcLoad(IN struct IO_MGR *hIOMgr, ++ OUT struct DSP_PROCLOADSTAT *pProcStat); ++ ++#endif /* WMDIO_ */ +diff --git a/arch/arm/plat-omap/include/dspbridge/wmdioctl.h b/arch/arm/plat-omap/include/dspbridge/wmdioctl.h +new file mode 100644 +index 0000000..a41c61a +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/wmdioctl.h +@@ -0,0 +1,91 @@ ++/* ++ * wmdioctl.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== wmdioctl.h ======== ++ * Description: ++ * BRIDGE Minidriver BRD_IOCtl reserved command definitions. ++ * ++ *! Revision History ++ *! ================ ++ *! 19-Apr-2004 sb Updated HW typedefs ++ *! 16-Feb-2004 vp Added MMU endianness attributes to WMDIOCTL_EXTPROC ++ *! 21-Mar-2003 sb Changed WMDIOCTL_NUMOFMMUTLB from 7 to 32 ++ *! 14-May-2001 sg Added codes for PWR. ++ *! 10-Aug-2001 ag Added _SETMMUCONFIG ioctl used for DSP-MMU init. ++ *! 16-Nov-1999 rajesh ? ++ *! 18-Jun-1998 ag Moved EMIF, SDRAM_C, & CE space init to ENBLEXTMEM ioctl. ++ *! Added ENBLEXTMEM, RESETDSP, UNRESETDSP & ASSERTSIG ioctls. ++ *! 07-Jun-1998 ag Added JTAG_SELECT, MAP_TBC, GET_CONFIGURATION ioctls. ++ *! 26-Jan-1998 jeh: Added START, RECV, and SEND ioctls. ++ *! 07-Nov-1997 nn: Added command to interrupt DSP for interrupt test. ++ *! 20-Oct-1997 nn: Added commands for getting and resetting interrupt count. ++ *! 17-Oct-1997 gp: Moved to src/wmd. Standardized prefix. ++ *! 08-Oct-1997 nn: Created. ++ */ ++ ++#ifndef WMDIOCTL_ ++#define WMDIOCTL_ ++ ++/* ------------------------------------ Hardware Abstraction Layer */ ++#include ++#include ++ ++/* Any IOCTLS at or above this value are reserved for standard WMD interfaces.*/ ++#define WMDIOCTL_RESERVEDBASE 0x8000 ++ ++#define WMDIOCTL_CHNLREAD (WMDIOCTL_RESERVEDBASE + 0x10) ++#define WMDIOCTL_CHNLWRITE (WMDIOCTL_RESERVEDBASE + 0x20) ++#define WMDIOCTL_GETINTRCOUNT (WMDIOCTL_RESERVEDBASE + 0x30) ++#define WMDIOCTL_RESETINTRCOUNT (WMDIOCTL_RESERVEDBASE + 0x40) ++#define WMDIOCTL_INTERRUPTDSP (WMDIOCTL_RESERVEDBASE + 0x50) ++#define WMDIOCTL_SETMMUCONFIG (WMDIOCTL_RESERVEDBASE + 0x60) /* DMMU */ ++#define WMDIOCTL_PWRCONTROL (WMDIOCTL_RESERVEDBASE + 0x70) /* PWR */ ++ ++/* attention, modifiers: ++ * Some of these control enumerations are made visible to user for power ++ * control, so any changes to this list, should also be updated in the user ++ * header file 'dbdefs.h' ***/ ++/* These ioctls are reserved for PWR power commands for the DSP */ ++#define WMDIOCTL_DEEPSLEEP (WMDIOCTL_PWRCONTROL + 0x0) ++#define WMDIOCTL_EMERGENCYSLEEP (WMDIOCTL_PWRCONTROL + 0x1) ++#define WMDIOCTL_WAKEUP (WMDIOCTL_PWRCONTROL + 0x2) ++#define WMDIOCTL_PWRENABLE (WMDIOCTL_PWRCONTROL + 0x3) ++#define WMDIOCTL_PWRDISABLE (WMDIOCTL_PWRCONTROL + 0x4) ++#define WMDIOCTL_CLK_CTRL (WMDIOCTL_PWRCONTROL + 0x7) ++#define WMDIOCTL_PWR_HIBERNATE (WMDIOCTL_PWRCONTROL + 0x8) /*DSP Initiated ++ * Hibernate*/ ++#define WMDIOCTL_PRESCALE_NOTIFY (WMDIOCTL_PWRCONTROL + 0x9) ++#define WMDIOCTL_POSTSCALE_NOTIFY (WMDIOCTL_PWRCONTROL + 0xA) ++#define WMDIOCTL_CONSTRAINT_REQUEST (WMDIOCTL_PWRCONTROL + 0xB) ++ ++/* Number of actual DSP-MMU TLB entrries */ ++#define WMDIOCTL_NUMOFMMUTLB 32 ++ ++struct WMDIOCTL_EXTPROC { ++ u32 ulDspVa; /* DSP virtual address */ ++ u32 ulGppPa; /* GPP physical address */ ++ /* GPP virtual address. __va does not work for ioremapped addresses */ ++ u32 ulGppVa; ++ u32 ulSize; /* Size of the mapped memory in bytes */ ++ enum HW_Endianism_t endianism; ++ enum HW_MMUMixedSize_t mixedMode; ++ enum HW_ElementSize_t elemSize; ++}; ++ ++#endif /* WMDIOCTL_ */ ++ +diff --git a/arch/arm/plat-omap/include/dspbridge/wmdmsg.h b/arch/arm/plat-omap/include/dspbridge/wmdmsg.h +new file mode 100644 +index 0000000..81198d4 +--- /dev/null ++++ b/arch/arm/plat-omap/include/dspbridge/wmdmsg.h +@@ -0,0 +1,70 @@ ++/* ++ * wmdmsg.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== wmdmsg.h ======== ++ * Description: ++ * Declares the upper edge message class library functions required by ++ * all WMD / WCD driver interface tables. These functions are ++ * implemented by every class of WMD channel library. ++ * ++ * Public Functions: ++ * ++ * Notes: ++ * Function comment headers reside with the function typedefs in wmd.h. ++ * ++ *! Revision History: ++ *! ================ ++ *! 06-Dec-2000 jeh Added uEventMask to WMD_MSG_RegisterNotify(). Added ++ *! WMD_MSG_SetQueueId(). ++ *! 17-Nov-2000 jeh Created. ++ */ ++ ++#ifndef WMDMSG_ ++#define WMDMSG_ ++ ++#include ++ ++ extern DSP_STATUS WMD_MSG_Create(OUT struct MSG_MGR **phMsgMgr, ++ struct DEV_OBJECT *hDevObject, ++ MSG_ONEXIT msgCallback); ++ ++ extern DSP_STATUS WMD_MSG_CreateQueue(struct MSG_MGR *hMsgMgr, ++ OUT struct MSG_QUEUE **phMsgQueue, ++ u32 dwId, u32 uMaxMsgs, ++ HANDLE h); ++ ++ extern void WMD_MSG_Delete(struct MSG_MGR *hMsgMgr); ++ ++ extern void WMD_MSG_DeleteQueue(struct MSG_QUEUE *hMsgQueue); ++ ++ extern DSP_STATUS WMD_MSG_Get(struct MSG_QUEUE *hMsgQueue, ++ struct DSP_MSG *pMsg, u32 uTimeout); ++ ++ extern DSP_STATUS WMD_MSG_Put(struct MSG_QUEUE *hMsgQueue, ++ IN CONST struct DSP_MSG *pMsg, ++ u32 uTimeout); ++ ++ extern DSP_STATUS WMD_MSG_RegisterNotify(struct MSG_QUEUE *hMsgQueue, ++ u32 uEventMask, ++ u32 uNotifyType, ++ struct DSP_NOTIFICATION ++ *hNotification); ++ ++ extern void WMD_MSG_SetQueueId(struct MSG_QUEUE *hMsgQueue, u32 dwId); ++ ++#endif /* WMDMSG_ */ +diff --git a/drivers/Makefile b/drivers/Makefile +index fec4d8e..6fcee09 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -42,6 +42,7 @@ obj-$(CONFIG_PARPORT) += parport/ + obj-y += base/ block/ misc/ mfd/ net/ media/ + obj-y += i2c/ + obj-y += cbus/ ++obj-$(CONFIG_MPU_BRIDGE) += dsp/bridge/ + obj-$(CONFIG_NUBUS) += nubus/ + obj-$(CONFIG_ATM) += atm/ + obj-y += macintosh/ +diff --git a/drivers/dsp/bridge/Kbuild b/drivers/dsp/bridge/Kbuild +new file mode 100644 +index 0000000..3432ff2 +--- /dev/null ++++ b/drivers/dsp/bridge/Kbuild +@@ -0,0 +1,39 @@ ++obj-$(CONFIG_MPU_BRIDGE) += bridgedriver.o ++ ++libgen = gen/gb.o gen/gt.o gen/gs.o gen/gh.o gen/_gt_para.o gen/uuidutil.o ++libservices = services/csl.o services/mem.o services/list.o services/dpc.o \ ++ services/kfile.o services/sync.o \ ++ services/clk.o services/cfg.o services/reg.o \ ++ services/regsup.o services/ntfy.o \ ++ services/dbg.o services/services.o ++libwmd = wmd/chnl_sm.o wmd/msg_sm.o wmd/io_sm.o wmd/tiomap3430.o \ ++ wmd/tiomap3430_pwr.o wmd/tiomap_sm.o wmd/tiomap_io.o \ ++ wmd/mmu_fault.o wmd/ue_deh.o ++libpmgr = pmgr/chnl.o pmgr/io.o pmgr/msg.o pmgr/cod.o pmgr/dev.o pmgr/wcd.o \ ++ pmgr/dmm.o pmgr/cmm.o pmgr/dbll.o ++librmgr = rmgr/dbdcd.o rmgr/disp.o rmgr/drv.o rmgr/mgr.o rmgr/node.o \ ++ rmgr/proc.o rmgr/pwr.o rmgr/rmm.o rmgr/strm.o rmgr/dspdrv.o \ ++ rmgr/nldr.o rmgr/drv_interface.o ++libdload = dynload/cload.o dynload/getsection.o dynload/reloc.o ++libhw = hw/hw_prcm.o hw/hw_dspssC64P.o hw/hw_mmu.o hw/hw_mbox.o ++ ++bridgedriver-objs = $(libgen) $(libservices) $(libwmd) $(libpmgr) $(librmgr) \ ++ $(libdload) $(libhw) ++ ++# Debug ++ifeq ($(CONFIG_BRIDGE_DEBUG),y) ++ccflags-y += -DGT_TRACE -DDEBUG ++endif ++ ++#Machine dependent ++ccflags-y += -D_TI_ -D_DB_TIOMAP -DTMS32060 \ ++ -DTICFG_PROC_VER -DTICFG_EVM_TYPE -DCHNL_SMCLASS \ ++ -DCHNL_MESSAGES -DUSE_LEVEL_1_MACROS ++ ++#Header files ++ccflags-y += -Idrivers/dsp/bridge/services ++ccflags-y += -Idrivers/dsp/bridge/wmd ++ccflags-y += -Idrivers/dsp/bridge/pmgr ++ccflags-y += -Idrivers/dsp/bridge/rmgr ++ccflags-y += -Idrivers/dsp/bridge/hw ++ccflags-y += -Iarch/arm +diff --git a/drivers/dsp/bridge/Kconfig b/drivers/dsp/bridge/Kconfig +new file mode 100644 +index 0000000..2fed82c +--- /dev/null ++++ b/drivers/dsp/bridge/Kconfig +@@ -0,0 +1,36 @@ ++# ++# DSP Bridge Driver Support ++# ++ ++menuconfig MPU_BRIDGE ++ tristate "DSP Bridge driver" ++ default n ++ help ++ DSP/BIOS Bridge is designed for platforms that contain a GPP and ++ one or more attached DSPs. The GPP is considered the master or ++ "host" processor, and the attached DSPs are processing resources ++ that can be utilized by applications and drivers running on the GPP. ++ ++config BRIDGE_DVFS ++ bool "Enable Bridge Dynamic Voltage and Frequency Scaling (DVFS)" ++ depends on MPU_BRIDGE && OMAP_PM_SRF ++ default n ++ help ++ DVFS allows DSP Bridge to initiate the operating point change to ++ scale the chip voltage and frequency in order to match the ++ performance and power consumption to the current processing ++ requirements. ++ ++config BRIDGE_MEMPOOL_SIZE ++ hex "Physical memory pool size (Byte)" ++ depends on MPU_BRIDGE ++ default 0x600000 ++ help ++ Allocate specified size of memory at booting time to avoid allocation ++ failure under heavy memory fragmentation after some use time. ++ ++config BRIDGE_DEBUG ++ bool "DSP Bridge Debug Support" ++ depends on MPU_BRIDGE ++ help ++ Say Y to enable Bridge debugging capabilities +diff --git a/drivers/dsp/bridge/dynload/cload.c b/drivers/dsp/bridge/dynload/cload.c +new file mode 100644 +index 0000000..271ab81 +--- /dev/null ++++ b/drivers/dsp/bridge/dynload/cload.c +@@ -0,0 +1,1854 @@ ++/* ++ * cload.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#include "header.h" ++ ++#include "module_list.h" ++#define LINKER_MODULES_HEADER ("_" MODULES_HEADER) ++ ++/* ++ * we use the fact that DOFF section records are shaped just like ++ * LDR_SECTION_INFO to reduce our section storage usage. This macro marks ++ * the places where that assumption is made ++ */ ++#define DOFFSEC_IS_LDRSEC(pdoffsec) ((struct LDR_SECTION_INFO *)(pdoffsec)) ++ ++/* ++ * forward references ++ */ ++static void dload_symbols(struct dload_state *dlthis); ++static void dload_data(struct dload_state *dlthis); ++static void allocate_sections(struct dload_state *dlthis); ++static void string_table_free(struct dload_state *dlthis); ++static void symbol_table_free(struct dload_state *dlthis); ++static void section_table_free(struct dload_state *dlthis); ++static void init_module_handle(struct dload_state *dlthis); ++#if BITS_PER_AU > BITS_PER_BYTE ++static char *unpack_name(struct dload_state *dlthis, u32 soffset); ++#endif ++ ++static const char CINITNAME[] = { ".cinit" }; ++static const char LOADER_DLLVIEW_ROOT[] = { "?DLModules?" }; ++ ++/* ++ * Error strings ++ */ ++static const char E_READSTRM[] = { "Error reading %s from input stream" }; ++static const char E_ALLOC[] = { "Syms->Allocate( %d ) failed" }; ++static const char E_TGTALLOC[] = ++ { "Target memory allocate failed, section %s size " FMT_UI32 }; ++static const char E_INITFAIL[] = { "%s to target address " FMT_UI32 " failed" }; ++static const char E_DLVWRITE[] = { "Write to DLLview list failed" }; ++static const char E_ICONNECT[] = { "Connect call to init interface failed" }; ++static const char E_CHECKSUM[] = { "Checksum failed on %s" }; ++ ++/************************************************************************* ++ * Procedure dload_error ++ * ++ * Parameters: ++ * errtxt description of the error, printf style ++ * ... additional information ++ * ++ * Effect: ++ * Reports or records the error as appropriate. ++ ************************************************************************/ ++void dload_error(struct dload_state *dlthis, const char *errtxt, ...) ++{ ++ va_list args; ++ ++ va_start(args, errtxt); ++ dlthis->mysym->Error_Report(dlthis->mysym, errtxt, args); ++ va_end(args); ++ dlthis->dload_errcount += 1; ++ ++} /* dload_error */ ++ ++#define DL_ERROR(zza, zzb) dload_error(dlthis, zza, zzb) ++ ++/************************************************************************* ++ * Procedure dload_syms_error ++ * ++ * Parameters: ++ * errtxt description of the error, printf style ++ * ... additional information ++ * ++ * Effect: ++ * Reports or records the error as appropriate. ++ ************************************************************************/ ++void dload_syms_error(struct Dynamic_Loader_Sym *syms, const char *errtxt, ...) ++{ ++ va_list args; ++ ++ va_start(args, errtxt); ++ syms->Error_Report(syms, errtxt, args); ++ va_end(args); ++} ++ ++/************************************************************************* ++ * Procedure Dynamic_Load_Module ++ * ++ * Parameters: ++ * module The input stream that supplies the module image ++ * syms Host-side symbol table and malloc/free functions ++ * alloc Target-side memory allocation ++ * init Target-side memory initialization ++ * options Option flags DLOAD_* ++ * mhandle A module handle for use with Dynamic_Unload ++ * ++ * Effect: ++ * The module image is read using *module. Target storage for the new ++ * image is ++ * obtained from *alloc. Symbols defined and referenced by the module are ++ * managed using *syms. The image is then relocated and references ++ * resolved as necessary, and the resulting executable bits are placed ++ * into target memory using *init. ++ * ++ * Returns: ++ * On a successful load, a module handle is placed in *mhandle, ++ * and zero is returned. On error, the number of errors detected is ++ * returned. Individual errors are reported during the load process ++ * using syms->Error_Report(). ++ ***********************************************************************/ ++int Dynamic_Load_Module(struct Dynamic_Loader_Stream *module, ++ struct Dynamic_Loader_Sym *syms , ++ struct Dynamic_Loader_Allocate *alloc, ++ struct Dynamic_Loader_Initialize *init, ++ unsigned options, DLOAD_mhandle *mhandle) ++{ ++ register unsigned *dp, sz; ++ struct dload_state dl_state; /* internal state for this call */ ++ ++ /* blast our internal state */ ++ dp = (unsigned *)&dl_state; ++ for (sz = sizeof(dl_state) / sizeof(unsigned); sz > 0; sz -= 1) ++ *dp++ = 0; ++ ++ /* Enable _only_ BSS initialization if enabled by user */ ++ if ((options & DLOAD_INITBSS) == DLOAD_INITBSS) ++ dl_state.myoptions = DLOAD_INITBSS; ++ ++ /* Check that mandatory arguments are present */ ++ if (!module || !syms) { ++ dload_error(&dl_state, "Required parameter is NULL"); ++ } else { ++ dl_state.strm = module; ++ dl_state.mysym = syms; ++ dload_headers(&dl_state); ++ if (!dl_state.dload_errcount) ++ dload_strings(&dl_state, false); ++ if (!dl_state.dload_errcount) ++ dload_sections(&dl_state); ++ ++ if (init && !dl_state.dload_errcount) { ++ if (init->connect(init)) { ++ dl_state.myio = init; ++ dl_state.myalloc = alloc; ++ /* do now, before reducing symbols */ ++ allocate_sections(&dl_state); ++ } else ++ dload_error(&dl_state, E_ICONNECT); ++ } ++ ++ if (!dl_state.dload_errcount) { ++ /* fix up entry point address */ ++ unsigned sref = dl_state.dfile_hdr.df_entry_secn - 1; ++ if (sref < dl_state.allocated_secn_count) ++ dl_state.dfile_hdr.df_entrypt += ++ dl_state.ldr_sections[sref].run_addr; ++ ++ dload_symbols(&dl_state); ++ } ++ ++ if (init && !dl_state.dload_errcount) ++ dload_data(&dl_state); ++ ++ init_module_handle(&dl_state); ++ ++ if (dl_state.myio) { ++ if ((!dl_state.dload_errcount) && ++ (dl_state.dfile_hdr.df_entry_secn != DN_UNDEF)) { ++ if (init != NULL) { ++ if (!init->execute(init, ++ dl_state.dfile_hdr.df_entrypt)) ++ dload_error(&dl_state, ++ "Init->Execute Failed"); ++ } else { ++ dload_error(&dl_state, "init is NULL"); ++ } ++ } ++ init->release(init); ++ } ++ ++ symbol_table_free(&dl_state); ++ section_table_free(&dl_state); ++ string_table_free(&dl_state); ++ ++ if (dl_state.dload_errcount) { ++ Dynamic_Unload_Module(dl_state.myhandle, syms, alloc, ++ init); ++ dl_state.myhandle = NULL; ++ } ++ } ++ ++ if (mhandle) ++ *mhandle = dl_state.myhandle; /* give back the handle */ ++ ++ return dl_state.dload_errcount; ++} /* DLOAD_File */ ++ ++/************************************************************************* ++ * Procedure Dynamic_Open_Module ++ * ++ * Parameters: ++ * module The input stream that supplies the module image ++ * syms Host-side symbol table and malloc/free functions ++ * alloc Target-side memory allocation ++ * init Target-side memory initialization ++ * options Option flags DLOAD_* ++ * mhandle A module handle for use with Dynamic_Unload ++ * ++ * Effect: ++ * The module image is read using *module. Target storage for the new ++ * image is ++ * obtained from *alloc. Symbols defined and referenced by the module are ++ * managed using *syms. The image is then relocated and references ++ * resolved as necessary, and the resulting executable bits are placed ++ * into target memory using *init. ++ * ++ * Returns: ++ * On a successful load, a module handle is placed in *mhandle, ++ * and zero is returned. On error, the number of errors detected is ++ * returned. Individual errors are reported during the load process ++ * using syms->Error_Report(). ++ ***********************************************************************/ ++int ++Dynamic_Open_Module(struct Dynamic_Loader_Stream *module, ++ struct Dynamic_Loader_Sym *syms, ++ struct Dynamic_Loader_Allocate *alloc, ++ struct Dynamic_Loader_Initialize *init, ++ unsigned options, DLOAD_mhandle *mhandle) ++{ ++ register unsigned *dp, sz; ++ struct dload_state dl_state; /* internal state for this call */ ++ ++ /* blast our internal state */ ++ dp = (unsigned *)&dl_state; ++ for (sz = sizeof(dl_state) / sizeof(unsigned); sz > 0; sz -= 1) ++ *dp++ = 0; ++ ++ /* Enable _only_ BSS initialization if enabled by user */ ++ if ((options & DLOAD_INITBSS) == DLOAD_INITBSS) ++ dl_state.myoptions = DLOAD_INITBSS; ++ ++ /* Check that mandatory arguments are present */ ++ if (!module || !syms) { ++ dload_error(&dl_state, "Required parameter is NULL"); ++ } else { ++ dl_state.strm = module; ++ dl_state.mysym = syms; ++ dload_headers(&dl_state); ++ if (!dl_state.dload_errcount) ++ dload_strings(&dl_state, false); ++ if (!dl_state.dload_errcount) ++ dload_sections(&dl_state); ++ ++ if (init && !dl_state.dload_errcount) { ++ if (init->connect(init)) { ++ dl_state.myio = init; ++ dl_state.myalloc = alloc; ++ /* do now, before reducing symbols */ ++ allocate_sections(&dl_state); ++ } else ++ dload_error(&dl_state, E_ICONNECT); ++ } ++ ++ if (!dl_state.dload_errcount) { ++ /* fix up entry point address */ ++ unsigned sref = dl_state.dfile_hdr.df_entry_secn - 1; ++ if (sref < dl_state.allocated_secn_count) ++ dl_state.dfile_hdr.df_entrypt += ++ dl_state.ldr_sections[sref].run_addr; ++ ++ dload_symbols(&dl_state); ++ } ++ ++ init_module_handle(&dl_state); ++ ++ if (dl_state.myio) { ++ if ((!dl_state.dload_errcount) ++ && (dl_state.dfile_hdr.df_entry_secn != DN_UNDEF)) ++ if (!init->execute(init, ++ dl_state.dfile_hdr.df_entrypt)) ++ dload_error(&dl_state, ++ "Init->Execute Failed"); ++ init->release(init); ++ } ++ ++ symbol_table_free(&dl_state); ++ section_table_free(&dl_state); ++ string_table_free(&dl_state); ++ ++ if (dl_state.dload_errcount) { ++ Dynamic_Unload_Module(dl_state.myhandle, syms, alloc, ++ init); ++ dl_state.myhandle = NULL; ++ } ++ } ++ ++ if (mhandle) ++ *mhandle = dl_state.myhandle; /* give back the handle */ ++ ++ return dl_state.dload_errcount; ++} /* DLOAD_File */ ++ ++/************************************************************************* ++ * Procedure dload_headers ++ * ++ * Parameters: ++ * none ++ * ++ * Effect: ++ * Loads the DOFF header and verify record. Deals with any byte-order ++ * issues and checks them for validity. ++ ************************************************************************/ ++#define COMBINED_HEADER_SIZE (sizeof(struct doff_filehdr_t)+ \ ++ sizeof(struct doff_verify_rec_t)) ++ ++void dload_headers(struct dload_state *dlthis) ++{ ++ u32 map; ++ ++ /* Read the header and the verify record as one. If we don't get it ++ all, we're done */ ++ if (dlthis->strm->read_buffer(dlthis->strm, &dlthis->dfile_hdr, ++ COMBINED_HEADER_SIZE) != COMBINED_HEADER_SIZE) { ++ DL_ERROR(E_READSTRM, "File Headers"); ++ return; ++ } ++ /* ++ * Verify that we have the byte order of the file correct. ++ * If not, must fix it before we can continue ++ */ ++ map = REORDER_MAP(dlthis->dfile_hdr.df_byte_reshuffle); ++ if (map != REORDER_MAP(BYTE_RESHUFFLE_VALUE)) { ++ /* input is either byte-shuffled or bad */ ++ if ((map & 0xFCFCFCFC) == 0) { /* no obviously bogus bits */ ++ dload_reorder(&dlthis->dfile_hdr, COMBINED_HEADER_SIZE, ++ map); ++ } ++ if (dlthis->dfile_hdr.df_byte_reshuffle != ++ BYTE_RESHUFFLE_VALUE) { ++ /* didn't fix the problem, the byte swap map is bad */ ++ dload_error(dlthis, ++ "Bad byte swap map " FMT_UI32 " in header", ++ dlthis->dfile_hdr.df_byte_reshuffle); ++ return; ++ } ++ dlthis->reorder_map = map; /* keep map for future use */ ++ } ++ ++ /* ++ * Verify checksum of header and verify record ++ */ ++ if (~dload_checksum(&dlthis->dfile_hdr, ++ sizeof(struct doff_filehdr_t)) || ++ ~dload_checksum(&dlthis->verify, ++ sizeof(struct doff_verify_rec_t))) { ++ DL_ERROR(E_CHECKSUM, "header or verify record"); ++ return; ++ } ++#if HOST_ENDIANNESS ++ dlthis->dfile_hdr.df_byte_reshuffle = map; /* put back for later */ ++#endif ++ ++ /* Check for valid target ID */ ++ if ((dlthis->dfile_hdr.df_target_id != TARGET_ID) && ++ -(dlthis->dfile_hdr.df_target_id != TMS470_ID)) { ++ dload_error(dlthis, "Bad target ID 0x%x and TARGET_ID 0x%x", ++ dlthis->dfile_hdr.df_target_id, TARGET_ID); ++ return; ++ } ++ /* Check for valid file format */ ++ if ((dlthis->dfile_hdr.df_doff_version != DOFF0)) { ++ dload_error(dlthis, "Bad DOFF version 0x%x", ++ dlthis->dfile_hdr.df_doff_version); ++ return; ++ } ++ ++ /* ++ * Apply reasonableness checks to count fields ++ */ ++ if (dlthis->dfile_hdr.df_strtab_size > MAX_REASONABLE_STRINGTAB) { ++ dload_error(dlthis, "Excessive string table size " FMT_UI32, ++ dlthis->dfile_hdr.df_strtab_size); ++ return; ++ } ++ if (dlthis->dfile_hdr.df_no_scns > MAX_REASONABLE_SECTIONS) { ++ dload_error(dlthis, "Excessive section count 0x%x", ++ dlthis->dfile_hdr.df_no_scns); ++ return; ++ } ++#ifndef TARGET_ENDIANNESS ++ /* ++ * Check that endianness does not disagree with explicit specification ++ */ ++ if ((dlthis->dfile_hdr.df_flags >> ALIGN_COFF_ENDIANNESS) & ++ dlthis->myoptions & ENDIANNESS_MASK) { ++ dload_error(dlthis, ++ "Input endianness disagrees with specified option"); ++ return; ++ } ++ dlthis->big_e_target = dlthis->dfile_hdr.df_flags & DF_BIG; ++#endif ++ ++} /* dload_headers */ ++ ++/* COFF Section Processing ++ * ++ * COFF sections are read in and retained intact. Each record is embedded ++ * in a new structure that records the updated load and ++ * run addresses of the section */ ++ ++static const char SECN_ERRID[] = { "section" }; ++ ++/************************************************************************* ++ * Procedure dload_sections ++ * ++ * Parameters: ++ * none ++ * ++ * Effect: ++ * Loads the section records into an internal table. ++ ************************************************************************/ ++void ++dload_sections(struct dload_state *dlthis) ++{ ++ s16 siz; ++ struct doff_scnhdr_t *shp; ++ unsigned nsecs = dlthis->dfile_hdr.df_no_scns; ++ ++ /* allocate space for the DOFF section records */ ++ siz = nsecs * sizeof(struct doff_scnhdr_t); ++ shp = (struct doff_scnhdr_t *)dlthis->mysym->Allocate(dlthis->mysym, ++ siz); ++ if (!shp) { /* not enough storage */ ++ DL_ERROR(E_ALLOC, siz); ++ return; ++ } ++ dlthis->sect_hdrs = shp; ++ ++ /* read in the section records */ ++ if (dlthis->strm->read_buffer(dlthis->strm, shp, siz) != siz) { ++ DL_ERROR(E_READSTRM, SECN_ERRID); ++ return; ++ } ++ ++ /* if we need to fix up byte order, do it now */ ++ if (dlthis->reorder_map) ++ dload_reorder(shp, siz, dlthis->reorder_map); ++ ++ /* check for validity */ ++ if (~dload_checksum(dlthis->sect_hdrs, siz) != ++ dlthis->verify.dv_scn_rec_checksum) { ++ DL_ERROR(E_CHECKSUM, SECN_ERRID); ++ return; ++ } ++ ++} /* dload_sections */ ++ ++/***************************************************************************** ++ * Procedure allocate_sections ++ * ++ * Parameters: ++ * alloc target memory allocator class ++ * ++ * Effect: ++ * Assigns new (target) addresses for sections ++ *****************************************************************************/ ++static void allocate_sections(struct dload_state *dlthis) ++{ ++ u16 curr_sect, nsecs, siz; ++ struct doff_scnhdr_t *shp; ++ struct LDR_SECTION_INFO *asecs; ++ struct my_handle *hndl; ++ nsecs = dlthis->dfile_hdr.df_no_scns; ++ if (!nsecs) ++ return; ++ if ((dlthis->myalloc == NULL) && ++ (dlthis->dfile_hdr.df_target_scns > 0)) { ++ DL_ERROR("Arg 3 (alloc) required but NULL", 0); ++ return; ++ } ++ /* allocate space for the module handle, which we will ++ * keep for unload purposes */ ++ siz = dlthis->dfile_hdr.df_target_scns * ++ sizeof(struct LDR_SECTION_INFO) + MY_HANDLE_SIZE; ++ hndl = (struct my_handle *)dlthis->mysym->Allocate(dlthis->mysym, siz); ++ if (!hndl) { /* not enough storage */ ++ DL_ERROR(E_ALLOC, siz); ++ return; ++ } ++ /* initialize the handle header */ ++ hndl->dm.hnext = hndl->dm.hprev = hndl; /* circular list */ ++ hndl->dm.hroot = NULL; ++ hndl->dm.dbthis = 0; ++ dlthis->myhandle = hndl; /* save away for return */ ++ /* pointer to the section list of allocated sections */ ++ dlthis->ldr_sections = asecs = hndl->secns; ++ /* * Insert names into all sections, make copies of ++ the sections we allocate */ ++ shp = dlthis->sect_hdrs; ++ for (curr_sect = 0; curr_sect < nsecs; curr_sect++) { ++ u32 soffset = shp->ds_offset; ++#if BITS_PER_AU <= BITS_PER_BYTE ++ /* attempt to insert the name of this section */ ++ if (soffset < dlthis->dfile_hdr.df_strtab_size) ++ DOFFSEC_IS_LDRSEC(shp)->name = dlthis->str_head + ++ soffset; ++ else { ++ dload_error(dlthis, "Bad name offset in section %d", ++ curr_sect); ++ DOFFSEC_IS_LDRSEC(shp)->name = NULL; ++ } ++#endif ++ /* allocate target storage for sections that require it */ ++ if (DS_NEEDS_ALLOCATION(shp)) { ++ *asecs = *DOFFSEC_IS_LDRSEC(shp); ++ asecs->context = 0; /* zero the context field */ ++#if BITS_PER_AU > BITS_PER_BYTE ++ asecs->name = unpack_name(dlthis, soffset); ++ dlthis->debug_string_size = soffset + dlthis->temp_len; ++#else ++ dlthis->debug_string_size = soffset; ++#endif ++ if (dlthis->myalloc != NULL) { ++ if (!dlthis->myalloc->Allocate(dlthis->myalloc, asecs, ++ DS_ALIGNMENT(asecs->type))) { ++ dload_error(dlthis, E_TGTALLOC, asecs->name, ++ asecs->size); ++ return; ++ } ++ } ++ /* keep address deltas in original section table */ ++ shp->ds_vaddr = asecs->load_addr - shp->ds_vaddr; ++ shp->ds_paddr = asecs->run_addr - shp->ds_paddr; ++ dlthis->allocated_secn_count += 1; ++ } /* allocate target storage */ ++ shp += 1; ++ asecs += 1; ++ } ++#if BITS_PER_AU <= BITS_PER_BYTE ++ dlthis->debug_string_size += ++ strlen(dlthis->str_head + dlthis->debug_string_size) + 1; ++#endif ++} /* allocate sections */ ++ ++/************************************************************************* ++ * Procedure section_table_free ++ * ++ * Parameters: ++ * none ++ * ++ * Effect: ++ * Frees any state used by the symbol table. ++ * ++ * WARNING: ++ * This routine is not allowed to declare errors! ++ ************************************************************************/ ++static void section_table_free(struct dload_state *dlthis) ++{ ++ struct doff_scnhdr_t *shp; ++ ++ shp = dlthis->sect_hdrs; ++ if (shp) ++ dlthis->mysym->Deallocate(dlthis->mysym, shp); ++ ++} /* section_table_free */ ++ ++/************************************************************************* ++ * Procedure dload_strings ++ * ++ * Parameters: ++ * sec_names_only If true only read in the "section names" ++ * portion of the string table ++ * ++ * Effect: ++ * Loads the DOFF string table into memory. DOFF keeps all strings in a ++ * big unsorted array. We just read that array into memory in bulk. ++ ************************************************************************/ ++static const char S_STRINGTBL[] = { "string table" }; ++void dload_strings(struct dload_state *dlthis, boolean sec_names_only) ++{ ++ u32 ssiz; ++ char *strbuf; ++ ++ if (sec_names_only) { ++ ssiz = BYTE_TO_HOST(DOFF_ALIGN ++ (dlthis->dfile_hdr.df_scn_name_size)); ++ } else { ++ ssiz = BYTE_TO_HOST(DOFF_ALIGN ++ (dlthis->dfile_hdr.df_strtab_size)); ++ } ++ if (ssiz == 0) ++ return; ++ ++ /* get some memory for the string table */ ++#if BITS_PER_AU > BITS_PER_BYTE ++ strbuf = (char *)dlthis->mysym->Allocate(dlthis->mysym, ssiz + ++ dlthis->dfile_hdr.df_max_str_len); ++#else ++ strbuf = (char *)dlthis->mysym->Allocate(dlthis->mysym, ssiz); ++#endif ++ if (strbuf == NULL) { ++ DL_ERROR(E_ALLOC, ssiz); ++ return; ++ } ++ dlthis->str_head = strbuf; ++#if BITS_PER_AU > BITS_PER_BYTE ++ dlthis->str_temp = strbuf + ssiz; ++#endif ++ /* read in the strings and verify them */ ++ if ((unsigned)(dlthis->strm->read_buffer(dlthis->strm, strbuf, ++ ssiz)) != ssiz) { ++ DL_ERROR(E_READSTRM, S_STRINGTBL); ++ } ++ /* if we need to fix up byte order, do it now */ ++#ifndef _BIG_ENDIAN ++ if (dlthis->reorder_map) ++ dload_reorder(strbuf, ssiz, dlthis->reorder_map); ++ ++ if ((!sec_names_only) && (~dload_checksum(strbuf, ssiz) != ++ dlthis->verify.dv_str_tab_checksum)) { ++ DL_ERROR(E_CHECKSUM, S_STRINGTBL); ++ } ++#else ++ if (dlthis->dfile_hdr.df_byte_reshuffle != ++ HOST_BYTE_ORDER(REORDER_MAP(BYTE_RESHUFFLE_VALUE))) { ++ /* put strings in big-endian order, not in PC order */ ++ dload_reorder(strbuf, ssiz, HOST_BYTE_ORDER(dlthis->dfile_hdr. ++ df_byte_reshuffle)); ++ } ++ if ((!sec_names_only) && (~dload_reverse_checksum(strbuf, ssiz) != ++ dlthis->verify.dv_str_tab_checksum)) { ++ DL_ERROR(E_CHECKSUM, S_STRINGTBL); ++ } ++#endif ++} /* dload_strings */ ++ ++/************************************************************************* ++ * Procedure string_table_free ++ * ++ * Parameters: ++ * none ++ * ++ * Effect: ++ * Frees any state used by the string table. ++ * ++ * WARNING: ++ * This routine is not allowed to declare errors! ++ *************************************************************************/ ++static void string_table_free(struct dload_state *dlthis) ++{ ++ if (dlthis->str_head) ++ dlthis->mysym->Deallocate(dlthis->mysym, dlthis->str_head); ++ ++} /* string_table_free */ ++ ++/* ++ * Symbol Table Maintenance Functions ++ * ++ * COFF symbols are read by dload_symbols(), which is called after ++ * sections have been allocated. Symbols which might be used in ++ * relocation (ie, not debug info) are retained in an internal temporary ++ * compressed table (type Local_Symbol). A particular symbol is recovered ++ * by index by calling dload_find_symbol(). dload_find_symbol ++ * reconstructs a more explicit representation (type SLOTVEC) which is ++ * used by reloc.c ++ */ ++/* real size of debug header */ ++#define DBG_HDR_SIZE (sizeof(struct dll_module) - sizeof(struct dll_sect)) ++ ++static const char SYM_ERRID[] = { "symbol" }; ++ ++/************************************************************************** ++ * Procedure dload_symbols ++ * ++ * Parameters: ++ * none ++ * ++ * Effect: ++ * Reads in symbols and retains ones that might be needed for relocation ++ * purposes. ++ ************************************************************************/ ++/* size of symbol buffer no bigger than target data buffer, to limit stack ++ * usage*/ ++#define MY_SYM_BUF_SIZ (BYTE_TO_HOST(IMAGE_PACKET_SIZE)/\ ++ sizeof(struct doff_syment_t)) ++ ++static void dload_symbols(struct dload_state *dlthis) ++{ ++ u32 s_count, siz, dsiz, symbols_left; ++ u32 checks; ++ struct Local_Symbol *sp; ++ struct dynload_symbol *symp; ++ struct dynload_symbol *newsym; ++ ++ s_count = dlthis->dfile_hdr.df_no_syms; ++ if (s_count == 0) ++ return; ++ ++ /* We keep a local symbol table for all of the symbols in the input. ++ * This table contains only section & value info, as we do not have ++ * to do any name processing for locals. We reuse this storage ++ * as a temporary for .dllview record construction. ++ * Allocate storage for the whole table.*/ ++ siz = s_count * sizeof(struct Local_Symbol); ++ dsiz = DBG_HDR_SIZE + ++ (sizeof(struct dll_sect) * dlthis->allocated_secn_count) + ++ BYTE_TO_HOST_ROUND(dlthis->debug_string_size + 1); ++ if (dsiz > siz) ++ siz = dsiz; /* larger of symbols and .dllview temp */ ++ sp = (struct Local_Symbol *)dlthis->mysym->Allocate(dlthis->mysym, siz); ++ if (!sp) { ++ DL_ERROR(E_ALLOC, siz); ++ return; ++ } ++ dlthis->local_symtab = sp; ++ /* Read the symbols in the input, store them in the table, and post any ++ * globals to the global symbol table. In the process, externals ++ become defined from the global symbol table */ ++ checks = dlthis->verify.dv_sym_tab_checksum; ++ symbols_left = s_count; ++ do { /* read all symbols */ ++ char *sname; ++ u32 val; ++ s32 delta; ++ struct doff_syment_t *input_sym; ++ unsigned syms_in_buf; ++ struct doff_syment_t my_sym_buf[MY_SYM_BUF_SIZ]; ++ input_sym = my_sym_buf; ++ syms_in_buf = symbols_left > MY_SYM_BUF_SIZ ? ++ MY_SYM_BUF_SIZ : symbols_left; ++ siz = syms_in_buf * sizeof(struct doff_syment_t); ++ if (dlthis->strm->read_buffer(dlthis->strm, input_sym, siz) != ++ siz) { ++ DL_ERROR(E_READSTRM, SYM_ERRID); ++ return; ++ } ++ if (dlthis->reorder_map) ++ dload_reorder(input_sym, siz, dlthis->reorder_map); ++ ++ checks += dload_checksum(input_sym, siz); ++ do { /* process symbols in buffer */ ++ symbols_left -= 1; ++ /* attempt to derive the name of this symbol */ ++ sname = NULL; ++ if (input_sym->dn_offset > 0) { ++#if BITS_PER_AU <= BITS_PER_BYTE ++ if ((u32) input_sym->dn_offset < ++ dlthis->dfile_hdr.df_strtab_size) ++ sname = dlthis->str_head + ++ BYTE_TO_HOST(input_sym->dn_offset); ++ else ++ dload_error(dlthis, ++ "Bad name offset in symbol %d", ++ symbols_left); ++#else ++ sname = unpack_name(dlthis, ++ input_sym->dn_offset); ++#endif ++ } ++ val = input_sym->dn_value; ++ delta = 0; ++ sp->sclass = input_sym->dn_sclass; ++ sp->secnn = input_sym->dn_scnum; ++ /* if this is an undefined symbol, ++ * define it (or fail) now */ ++ if (sp->secnn == DN_UNDEF) { ++ /* pointless for static undefined */ ++ if (input_sym->dn_sclass != DN_EXT) ++ goto loop_cont; ++ ++ /* try to define symbol from previously ++ * loaded images */ ++ symp = dlthis->mysym->Find_Matching_Symbol ++ (dlthis->mysym, sname); ++ if (!symp) { ++ DL_ERROR ++ ("Undefined external symbol %s", ++ sname); ++ goto loop_cont; ++ } ++ val = delta = symp->value; ++ goto loop_cont; ++ } ++ /* symbol defined by this module */ ++ if (sp->secnn > 0) { /* symbol references a section */ ++ if ((unsigned)sp->secnn <= ++ dlthis->allocated_secn_count) { ++ /* section was allocated */ ++ struct doff_scnhdr_t *srefp = ++ &dlthis->sect_hdrs ++ [sp->secnn - 1]; ++ ++ if (input_sym->dn_sclass == ++ DN_STATLAB || ++ input_sym->dn_sclass == DN_EXTLAB){ ++ /* load */ ++ delta = srefp->ds_vaddr; ++ } else { ++ /* run */ ++ delta = srefp->ds_paddr; ++ } ++ val += delta; ++ } ++ goto loop_itr; ++ } ++ /* This symbol is an absolute symbol */ ++ if (sp->secnn == DN_ABS && ((sp->sclass == DN_EXT) || ++ (sp->sclass == DN_EXTLAB))) { ++ symp = dlthis->mysym->Find_Matching_Symbol ++ (dlthis->mysym, sname); ++ if (!symp) ++ goto loop_itr; ++ /* This absolute symbol is already defined. */ ++ if (symp->value == input_sym->dn_value) { ++ /* If symbol values are equal, continue ++ * but don't add to the global symbol ++ * table */ ++ sp->value = val; ++ sp->delta = delta; ++ sp += 1; ++ input_sym += 1; ++ continue; ++ } else { ++ /* If symbol values are not equal, ++ * return with redefinition error */ ++ DL_ERROR("Absolute symbol %s is " ++ "defined multiple times with " ++ "different values", sname); ++ return; ++ } ++ } ++loop_itr: ++ /* if this is a global symbol, post it to the ++ * global table */ ++ if (input_sym->dn_sclass == DN_EXT || ++ input_sym->dn_sclass == DN_EXTLAB) { ++ /* Keep this global symbol for subsequent ++ * modules. Don't complain on error, to allow ++ * symbol API to suppress global symbols */ ++ if (!sname) ++ goto loop_cont; ++ ++ newsym = dlthis->mysym->Add_To_Symbol_Table ++ (dlthis->mysym, sname, ++ (unsigned)dlthis->myhandle); ++ if (newsym) ++ newsym->value = val; ++ ++ } /* global */ ++loop_cont: ++ sp->value = val; ++ sp->delta = delta; ++ sp += 1; ++ input_sym += 1; ++ } while ((syms_in_buf -= 1) > 0); /* process sym in buffer */ ++ } while (symbols_left > 0); /* read all symbols */ ++ if (~checks) ++ dload_error(dlthis, "Checksum of symbols failed"); ++ ++} /* dload_symbols */ ++ ++/***************************************************************************** ++ * Procedure symbol_table_free ++ * ++ * Parameters: ++ * none ++ * ++ * Effect: ++ * Frees any state used by the symbol table. ++ * ++ * WARNING: ++ * This routine is not allowed to declare errors! ++ *****************************************************************************/ ++static void symbol_table_free(struct dload_state *dlthis) ++{ ++ if (dlthis->local_symtab) { ++ if (dlthis->dload_errcount) { /* blow off our symbols */ ++ dlthis->mysym->Purge_Symbol_Table(dlthis->mysym, ++ (unsigned)dlthis->myhandle); ++ } ++ dlthis->mysym->Deallocate(dlthis->mysym, dlthis->local_symtab); ++ } ++} /* symbol_table_free */ ++ ++/* .cinit Processing ++ * ++ * The dynamic loader does .cinit interpretation. cload_cinit() ++ * acts as a special write-to-target function, in that it takes relocated ++ * data from the normal data flow, and interprets it as .cinit actions. ++ * Because the normal data flow does not necessarily process the whole ++ * .cinit section in one buffer, cload_cinit() must be prepared to ++ * interpret the data piecemeal. A state machine is used for this ++ * purpose. ++ */ ++ ++/* The following are only for use by reloc.c and things it calls */ ++static const struct LDR_SECTION_INFO CINIT_INFO_INIT = { CINITNAME, 0, 0, ++ (LDR_ADDR) -1, 0, DLOAD_BSS, 0 }; ++ ++/************************************************************************* ++ * Procedure cload_cinit ++ * ++ * Parameters: ++ * ipacket Pointer to data packet to be loaded ++ * ++ * Effect: ++ * Interprets the data in the buffer as .cinit data, and performs the ++ * appropriate initializations. ++ ************************************************************************/ ++static void cload_cinit(struct dload_state *dlthis, ++ struct image_packet_t *ipacket) ++{ ++#if TDATA_TO_HOST(CINIT_COUNT)*BITS_PER_AU > 16 ++ s32 init_count, left; ++#else ++ s16 init_count, left; ++#endif ++ unsigned char *pktp = ipacket->i_bits; ++ unsigned char *pktend = pktp + ++ BYTE_TO_HOST_ROUND(ipacket->i_packet_size); ++ int temp; ++ LDR_ADDR atmp; ++ struct LDR_SECTION_INFO cinit_info; ++ ++ /* PROCESS ALL THE INITIALIZATION RECORDS IN THE BUFFER. */ ++ while (true) { ++ left = pktend - pktp; ++ switch (dlthis->cinit_state) { ++ case CI_count: /* count field */ ++ if (left < TDATA_TO_HOST(CINIT_COUNT)) ++ goto loopexit; ++ temp = dload_unpack(dlthis, (TgtAU_t *)pktp, ++ CINIT_COUNT * TDATA_AU_BITS, 0, ++ ROP_SGN); ++ pktp += TDATA_TO_HOST(CINIT_COUNT); ++ /* negative signifies BSS table, zero means done */ ++ if (temp <= 0) { ++ dlthis->cinit_state = CI_done; ++ break; ++ } ++ dlthis->cinit_count = temp; ++ dlthis->cinit_state = CI_address; ++ break; ++#if CINIT_ALIGN < CINIT_ADDRESS ++ case CI_partaddress: ++ pktp -= TDATA_TO_HOST(CINIT_ALIGN); ++ /* back up pointer into space courtesy of caller */ ++ *(uint16_t *)pktp = dlthis->cinit_addr; ++ /* stuff in saved bits !! FALL THRU !! */ ++#endif ++ case CI_address: /* Address field for a copy packet */ ++ if (left < TDATA_TO_HOST(CINIT_ADDRESS)) { ++#if CINIT_ALIGN < CINIT_ADDRESS ++ if (left == TDATA_TO_HOST(CINIT_ALIGN)) { ++ /* address broken into halves */ ++ dlthis->cinit_addr = *(uint16_t *)pktp; ++ /* remember 1st half */ ++ dlthis->cinit_state = CI_partaddress; ++ left = 0; ++ } ++#endif ++ goto loopexit; ++ } ++ atmp = dload_unpack(dlthis, (TgtAU_t *)pktp, ++ CINIT_ADDRESS * TDATA_AU_BITS, 0, ++ ROP_UNS); ++ pktp += TDATA_TO_HOST(CINIT_ADDRESS); ++#if CINIT_PAGE_BITS > 0 ++ dlthis->cinit_page = atmp & ++ ((1 << CINIT_PAGE_BITS) - 1); ++ atmp >>= CINIT_PAGE_BITS; ++#else ++ dlthis->cinit_page = CINIT_DEFAULT_PAGE; ++#endif ++ dlthis->cinit_addr = atmp; ++ dlthis->cinit_state = CI_copy; ++ break; ++ case CI_copy: /* copy bits to the target */ ++ init_count = HOST_TO_TDATA(left); ++ if (init_count > dlthis->cinit_count) ++ init_count = dlthis->cinit_count; ++ if (init_count == 0) ++ goto loopexit; /* get more bits */ ++ cinit_info = CINIT_INFO_INIT; ++ cinit_info.page = dlthis->cinit_page; ++ if (!dlthis->myio->writemem(dlthis->myio, pktp, ++ TDATA_TO_TADDR(dlthis->cinit_addr), ++ &cinit_info, ++ TDATA_TO_HOST(init_count))) { ++ dload_error(dlthis, E_INITFAIL, "write", ++ dlthis->cinit_addr); ++ } ++ dlthis->cinit_count -= init_count; ++ if (dlthis->cinit_count <= 0) { ++ dlthis->cinit_state = CI_count; ++ init_count = (init_count + CINIT_ALIGN - 1) & ++ -CINIT_ALIGN; ++ /* align to next init */ ++ } ++ pktp += TDATA_TO_HOST(init_count); ++ dlthis->cinit_addr += init_count; ++ break; ++ case CI_done: /* no more .cinit to do */ ++ return; ++ } /* switch (cinit_state) */ ++ } /* while */ ++ ++loopexit: ++ if (left > 0) { ++ dload_error(dlthis, "%d bytes left over in cinit packet", left); ++ dlthis->cinit_state = CI_done; /* left over bytes are bad */ ++ } ++} /* cload_cinit */ ++ ++/* Functions to interface to reloc.c ++ * ++ * reloc.c is the relocation module borrowed from the linker, with ++ * minimal (we hope) changes for our purposes. cload_sect_data() invokes ++ * this module on a section to relocate and load the image data for that ++ * section. The actual read and write actions are supplied by the global ++ * routines below. ++ */ ++ ++/************************************************************************ ++ * Procedure relocate_packet ++ * ++ * Parameters: ++ * ipacket Pointer to an image packet to relocate ++ * ++ * Effect: ++ * Performs the required relocations on the packet. Returns a checksum ++ * of the relocation operations. ++ ************************************************************************/ ++#define MY_RELOC_BUF_SIZ 8 ++/* careful! exists at the same time as the image buffer*/ ++static int relocate_packet(struct dload_state *dlthis, ++ struct image_packet_t *ipacket, u32 *checks) ++{ ++ u32 rnum; ++ ++ rnum = ipacket->i_num_relocs; ++ do { /* all relocs */ ++ unsigned rinbuf; ++ int siz; ++ struct reloc_record_t *rp, rrec[MY_RELOC_BUF_SIZ]; ++ rp = rrec; ++ rinbuf = rnum > MY_RELOC_BUF_SIZ ? MY_RELOC_BUF_SIZ : rnum; ++ siz = rinbuf * sizeof(struct reloc_record_t); ++ if (dlthis->strm->read_buffer(dlthis->strm, rp, siz) != siz) { ++ DL_ERROR(E_READSTRM, "relocation"); ++ return 0; ++ } ++ /* reorder the bytes if need be */ ++ if (dlthis->reorder_map) ++ dload_reorder(rp, siz, dlthis->reorder_map); ++ ++ *checks += dload_checksum(rp, siz); ++ do { ++ /* perform the relocation operation */ ++ dload_relocate(dlthis, (TgtAU_t *) ipacket->i_bits, rp); ++ rp += 1; ++ rnum -= 1; ++ } while ((rinbuf -= 1) > 0); ++ } while (rnum > 0); /* all relocs */ ++ return 1; ++} /* dload_read_reloc */ ++ ++#define IPH_SIZE (sizeof(struct image_packet_t) - sizeof(u32)) ++ ++/* VERY dangerous */ ++static const char IMAGEPAK[] = { "image packet" }; ++ ++/************************************************************************* ++ * Procedure dload_data ++ * ++ * Parameters: ++ * none ++ * ++ * Effect: ++ * Read image data from input file, relocate it, and download it to the ++ * target. ++ ************************************************************************/ ++static void dload_data(struct dload_state *dlthis) ++{ ++ u16 curr_sect; ++ struct doff_scnhdr_t *sptr = dlthis->sect_hdrs; ++ struct LDR_SECTION_INFO *lptr = dlthis->ldr_sections; ++#ifdef OPT_ZERO_COPY_LOADER ++ boolean bZeroCopy = false; ++#endif ++ u8 *pDest; ++ ++ struct { ++ struct image_packet_t ipacket; ++ u8 bufr[BYTE_TO_HOST(IMAGE_PACKET_SIZE)]; ++ } ibuf; ++ ++ /* Indicates whether CINIT processing has occurred */ ++ boolean cinit_processed = false; ++ ++ /* Loop through the sections and load them one at a time. ++ */ ++ for (curr_sect = 0; curr_sect < dlthis->dfile_hdr.df_no_scns; ++ curr_sect += 1) { ++ if (DS_NEEDS_DOWNLOAD(sptr)) { ++ s32 nip; ++ LDR_ADDR image_offset = 0; ++ /* set relocation info for this section */ ++ if (curr_sect < dlthis->allocated_secn_count) ++ dlthis->delta_runaddr = sptr->ds_paddr; ++ else { ++ lptr = DOFFSEC_IS_LDRSEC(sptr); ++ dlthis->delta_runaddr = 0; ++ } ++ dlthis->image_secn = lptr; ++#if BITS_PER_AU > BITS_PER_BYTE ++ lptr->name = unpack_name(dlthis, sptr->ds_offset); ++#endif ++ nip = sptr->ds_nipacks; ++ while ((nip -= 1) >= 0) { /* process packets */ ++ ++ s32 ipsize; ++ u32 checks; ++ /* get the fixed header bits */ ++ if (dlthis->strm->read_buffer(dlthis->strm, ++ &ibuf.ipacket, IPH_SIZE) != IPH_SIZE) { ++ DL_ERROR(E_READSTRM, IMAGEPAK); ++ return; ++ } ++ /* reorder the header if need be */ ++ if (dlthis->reorder_map) { ++ dload_reorder(&ibuf.ipacket, IPH_SIZE, ++ dlthis->reorder_map); ++ } ++ /* now read the rest of the packet */ ++ ipsize = ++ BYTE_TO_HOST(DOFF_ALIGN ++ (ibuf.ipacket.i_packet_size)); ++ if (ipsize > BYTE_TO_HOST(IMAGE_PACKET_SIZE)) { ++ DL_ERROR("Bad image packet size %d", ++ ipsize); ++ return; ++ } ++ pDest = ibuf.bufr; ++#ifdef OPT_ZERO_COPY_LOADER ++ bZeroCopy = false; ++ if (DLOAD_SECT_TYPE(sptr) != DLOAD_CINIT) { ++ dlthis->myio->writemem(dlthis->myio, ++ &pDest, lptr->load_addr + ++ image_offset, lptr, 0); ++ bZeroCopy = (pDest != ibuf.bufr); ++ } ++#endif ++ /* End of determination */ ++ ++ if (dlthis->strm->read_buffer(dlthis->strm, ++ ibuf.bufr, ipsize) != ipsize) { ++ DL_ERROR(E_READSTRM, IMAGEPAK); ++ return; ++ } ++ ibuf.ipacket.i_bits = pDest; ++ ++ /* reorder the bytes if need be */ ++#if !defined(_BIG_ENDIAN) || (TARGET_AU_BITS > 16) ++ if (dlthis->reorder_map) { ++ dload_reorder(pDest, ipsize, ++ dlthis->reorder_map); ++ } ++ checks = dload_checksum(pDest, ipsize); ++#else ++ if (dlthis->dfile_hdr.df_byte_reshuffle != ++ TARGET_ORDER(REORDER_MAP ++ (BYTE_RESHUFFLE_VALUE))) { ++ /* put image bytes in big-endian order, ++ * not PC order */ ++ dload_reorder(pDest, ipsize, ++ TARGET_ORDER ++ (dlthis->dfile_hdr.df_byte_reshuffle)); ++ } ++#if TARGET_AU_BITS > 8 ++ checks = dload_reverse_checksum_16(pDest, ++ ipsize); ++#else ++ checks = dload_reverse_checksum(pDest, ++ ipsize); ++#endif ++#endif ++ ++ checks += dload_checksum(&ibuf.ipacket, ++ IPH_SIZE); ++ /* relocate the image bits as needed */ ++ if (ibuf.ipacket.i_num_relocs) { ++ dlthis->image_offset = image_offset; ++ if (!relocate_packet(dlthis, ++ &ibuf.ipacket, &checks)) ++ return; /* serious error */ ++ } ++ if (~checks) ++ DL_ERROR(E_CHECKSUM, IMAGEPAK); ++ /* stuff the result into target memory */ ++ if (DLOAD_SECT_TYPE(sptr) == DLOAD_CINIT) { ++ cload_cinit(dlthis, &ibuf.ipacket); ++ cinit_processed = true; ++ } else { ++#ifdef OPT_ZERO_COPY_LOADER ++ if (!bZeroCopy) { ++#endif ++ ++ if (!dlthis->myio->writemem ++ (dlthis->myio, ibuf.bufr, ++ lptr->load_addr + image_offset, lptr, ++ BYTE_TO_HOST ++ (ibuf.ipacket.i_packet_size))) { ++ DL_ERROR( ++ "Write to " FMT_UI32 " failed", ++ lptr->load_addr + image_offset); ++ } ++#ifdef OPT_ZERO_COPY_LOADER ++ } ++#endif ++ ++ } ++ image_offset += ++ BYTE_TO_TADDR(ibuf.ipacket.i_packet_size); ++ } /* process packets */ ++ /* if this is a BSS section, we may want to fill it */ ++ if (DLOAD_SECT_TYPE(sptr) != DLOAD_BSS) ++ goto loop_cont; ++ ++ if (!(dlthis->myoptions & DLOAD_INITBSS)) ++ goto loop_cont; ++ ++ if (cinit_processed) { ++ /* Don't clear BSS after load-time ++ * initialization */ ++ DL_ERROR ++ ("Zero-initialization at " FMT_UI32 " after " ++ "load-time initialization!", lptr->load_addr); ++ goto loop_cont; ++ } ++ /* fill the .bss area */ ++ dlthis->myio->fillmem(dlthis->myio, ++ TADDR_TO_HOST(lptr->load_addr), ++ lptr, TADDR_TO_HOST(lptr->size), ++ dload_fill_bss); ++ goto loop_cont; ++ } /* if DS_DOWNLOAD_MASK */ ++ /* If not loading, but BSS, zero initialize */ ++ if (DLOAD_SECT_TYPE(sptr) != DLOAD_BSS) ++ goto loop_cont; ++ ++ if (!(dlthis->myoptions & DLOAD_INITBSS)) ++ goto loop_cont; ++ ++ if (curr_sect >= dlthis->allocated_secn_count) ++ lptr = DOFFSEC_IS_LDRSEC(sptr); ++ ++ if (cinit_processed) { ++ /*Don't clear BSS after load-time initialization */ ++ DL_ERROR( ++ "Zero-initialization at " FMT_UI32 " attempted after " ++ "load-time initialization!", lptr->load_addr); ++ goto loop_cont; ++ } ++ /* fill the .bss area */ ++ dlthis->myio->fillmem(dlthis->myio, ++ TADDR_TO_HOST(lptr->load_addr), lptr, ++ TADDR_TO_HOST(lptr->size), dload_fill_bss); ++loop_cont: ++ sptr += 1; ++ lptr += 1; ++ } /* load sections */ ++} /* dload_data */ ++ ++/************************************************************************* ++ * Procedure dload_reorder ++ * ++ * Parameters: ++ * data 32-bit aligned pointer to data to be byte-swapped ++ * dsiz size of the data to be reordered in sizeof() units. ++ * map 32-bit map defining how to reorder the data. Value ++ * must be REORDER_MAP() of some permutation ++ * of 0x00 01 02 03 ++ * ++ * Effect: ++ * Re-arranges the bytes in each word according to the map specified. ++ * ++ ************************************************************************/ ++/* mask for byte shift count */ ++#define SHIFT_COUNT_MASK (3 << LOG_BITS_PER_BYTE) ++ ++void dload_reorder(void *data, int dsiz, unsigned int map) ++{ ++ register u32 tmp, tmap, datv; ++ u32 *dp = (u32 *)data; ++ ++ map <<= LOG_BITS_PER_BYTE; /* align map with SHIFT_COUNT_MASK */ ++ do { ++ tmp = 0; ++ datv = *dp; ++ tmap = map; ++ do { ++ tmp |= (datv & BYTE_MASK) << (tmap & SHIFT_COUNT_MASK); ++ tmap >>= BITS_PER_BYTE; ++ } while (datv >>= BITS_PER_BYTE); ++ *dp++ = tmp; ++ } while ((dsiz -= sizeof(u32)) > 0); ++} /* dload_reorder */ ++ ++/************************************************************************* ++ * Procedure dload_checksum ++ * ++ * Parameters: ++ * data 32-bit aligned pointer to data to be checksummed ++ * siz size of the data to be checksummed in sizeof() units. ++ * ++ * Effect: ++ * Returns a checksum of the specified block ++ * ++ ************************************************************************/ ++u32 dload_checksum(void *data, unsigned siz) ++{ ++ u32 sum; ++ u32 *dp; ++ int left; ++ ++ sum = 0; ++ dp = (u32 *)data; ++ for (left = siz; left > 0; left -= sizeof(u32)) ++ sum += *dp++; ++ return sum; ++} /* dload_checksum */ ++ ++#if HOST_ENDIANNESS ++/************************************************************************* ++ * Procedure dload_reverse_checksum ++ * ++ * Parameters: ++ * data 32-bit aligned pointer to data to be checksummed ++ * siz size of the data to be checksummed in sizeof() units. ++ * ++ * Effect: ++ * Returns a checksum of the specified block, which is assumed to be bytes ++ * in big-endian order. ++ * ++ * Notes: ++ * In a big-endian host, things like the string table are stored as bytes ++ * in host order. But dllcreate always checksums in little-endian order. ++ * It is most efficient to just handle the difference a word at a time. ++ * ++ ***********************************************************************/ ++u32 dload_reverse_checksum(void *data, unsigned siz) ++{ ++ u32 sum, temp; ++ u32 *dp; ++ int left; ++ ++ sum = 0; ++ dp = (u32 *)data; ++ ++ for (left = siz; left > 0; left -= sizeof(u32)) { ++ temp = *dp++; ++ sum += temp << BITS_PER_BYTE * 3; ++ sum += temp >> BITS_PER_BYTE * 3; ++ sum += (temp >> BITS_PER_BYTE) & (BYTE_MASK << BITS_PER_BYTE); ++ sum += (temp & (BYTE_MASK << BITS_PER_BYTE)) << BITS_PER_BYTE; ++ } ++ ++ return sum; ++} /* dload_reverse_checksum */ ++ ++#if (TARGET_AU_BITS > 8) && (TARGET_AU_BITS < 32) ++u32 dload_reverse_checksum_16(void *data, unsigned siz) ++{ ++ uint_fast32_t sum, temp; ++ u32 *dp; ++ int left; ++ ++ sum = 0; ++ dp = (u32 *)data; ++ ++ for (left = siz; left > 0; left -= sizeof(u32)) { ++ temp = *dp++; ++ sum += temp << BITS_PER_BYTE * 2; ++ sum += temp >> BITS_PER_BYTE * 2; ++ } ++ ++ return sum; ++} /* dload_reverse_checksum_16 */ ++#endif ++#endif ++ ++/************************************************************************* ++ * Procedure swap_words ++ * ++ * Parameters: ++ * data 32-bit aligned pointer to data to be swapped ++ * siz size of the data to be swapped. ++ * bitmap Bit map of how to swap each 32-bit word; 1 => 2 shorts, ++ * 0 => 1 long ++ * ++ * Effect: ++ * Swaps the specified data according to the specified map ++ * ++ ************************************************************************/ ++static void swap_words(void *data, unsigned siz, unsigned bitmap) ++{ ++ register int i; ++#if TARGET_AU_BITS < 16 ++ register u16 *sp; ++#endif ++ register u32 *lp; ++ ++ siz /= sizeof(u16); ++ ++#if TARGET_AU_BITS < 16 ++ /* pass 1: do all the bytes */ ++ i = siz; ++ sp = (u16 *) data; ++ do { ++ register u16 tmp; ++ tmp = *sp; ++ *sp++ = SWAP16BY8(tmp); ++ } while ((i -= 1) > 0); ++#endif ++ ++#if TARGET_AU_BITS < 32 ++ /* pass 2: fixup the 32-bit words */ ++ i = siz >> 1; ++ lp = (u32 *) data; ++ do { ++ if ((bitmap & 1) == 0) { ++ register u32 tmp; ++ tmp = *lp; ++ *lp = SWAP32BY16(tmp); ++ } ++ lp += 1; ++ bitmap >>= 1; ++ } while ((i -= 1) > 0); ++#endif ++} /* swap_words */ ++ ++/************************************************************************* ++ * Procedure copy_tgt_strings ++ * ++ * Parameters: ++ * dstp Destination address. Assumed to be 32-bit aligned ++ * srcp Source address. Assumed to be 32-bit aligned ++ * charcount Number of characters to copy. ++ * ++ * Effect: ++ * Copies strings from the source (which is in usual .dof file order on ++ * the loading processor) to the destination buffer (which should be in proper ++ * target addressable unit order). Makes sure the last string in the ++ * buffer is NULL terminated (for safety). ++ * Returns the first unused destination address. ++ ************************************************************************/ ++static char *copy_tgt_strings(void *dstp, void *srcp, unsigned charcount) ++{ ++ register TgtAU_t *src = (TgtAU_t *)srcp; ++ register TgtAU_t *dst = (TgtAU_t *)dstp; ++ register int cnt = charcount; ++ do { ++#if TARGET_AU_BITS <= BITS_PER_AU ++ /* byte-swapping issues may exist for strings on target */ ++ *dst++ = *src++; ++#elif TARGET_ENDIANNESS_DIFFERS(TARGET_BIG_ENDIAN) ++ register TgtAU_t tmp; ++ tmp = *src++; ++ *dst++ = SWAP16BY8(tmp); /* right for TARGET_AU_BITS == 16 */ ++#else ++ *dst++ = *src++; ++#endif ++ } while ((cnt -= (sizeof(TgtAU_t) * BITS_PER_AU / BITS_PER_BYTE)) > 0); ++ /*apply force to make sure that the string table has null terminator */ ++#if (BITS_PER_AU == BITS_PER_BYTE) && (TARGET_AU_BITS == BITS_PER_BYTE) ++ dst[-1] = 0; ++#elif TARGET_BIG_ENDIAN ++ dst[-1] &= ~BYTE_MASK; /* big-endian */ ++#else ++ dst[-1] &= (1 << (BITS_PER_AU - BITS_PER_BYTE)) - 1; /* little endian */ ++#endif ++ return (char *)dst; ++} /* copy_tgt_strings */ ++ ++/************************************************************************* ++ * Procedure init_module_handle ++ * ++ * Parameters: ++ * none ++ * ++ * Effect: ++ * Initializes the module handle we use to enable unloading, and installs ++ * the debug information required by the target. ++ * ++ * Notes: ++ * The handle returned from Dynamic_Load_Module needs to encapsulate all the ++ * allocations done for the module, and enable them plus the modules symbols to ++ * be deallocated. ++ * ++ ************************************************************************/ ++#ifndef _BIG_ENDIAN ++static const struct LDR_SECTION_INFO DLLVIEW_INFO_INIT = { ".dllview", 0, 0, ++ (LDR_ADDR) -1, DBG_LIST_PAGE, DLOAD_DATA, 0 }; ++#else ++static const struct LDR_SECTION_INFO DLLVIEW_INFO_INIT = { ".dllview", 0, 0, ++ (LDR_ADDR) -1, DLOAD_DATA, DBG_LIST_PAGE, 0 }; ++#endif ++static void init_module_handle(struct dload_state *dlthis) ++{ ++ struct my_handle *hndl; ++ u16 curr_sect; ++ struct LDR_SECTION_INFO *asecs; ++ struct dll_module *dbmod; ++ struct dll_sect *dbsec; ++ struct dbg_mirror_root *mlist; ++ register char *cp; ++ struct modules_header mhdr; ++ struct LDR_SECTION_INFO dllview_info; ++ struct dynload_symbol *debug_mirror_sym; ++ hndl = dlthis->myhandle; ++ if (!hndl) ++ return; /* must be errors detected, so forget it */ ++ hndl->secn_count = dlthis->allocated_secn_count << 1; ++#ifndef TARGET_ENDIANNESS ++ if (dlthis->big_e_target) ++ hndl->secn_count += 1; /* flag for big-endian */ ++#endif ++ if (dlthis->dload_errcount) ++ return; /* abandon if errors detected */ ++ /* Locate the symbol that names the header for the CCS debug list ++ of modules. If not found, we just don't generate the debug record. ++ If found, we create our modules list. We make sure to create the ++ LOADER_DLLVIEW_ROOT even if there is no relocation info to record, ++ just to try to put both symbols in the same symbol table and ++ module.*/ ++ debug_mirror_sym = dlthis->mysym->Find_Matching_Symbol(dlthis->mysym, ++ LOADER_DLLVIEW_ROOT); ++ if (!debug_mirror_sym) { ++ struct dynload_symbol *dlmodsym; ++ struct dbg_mirror_root *mlst; ++ ++ /* our root symbol is not yet present; ++ check if we have DLModules defined */ ++ dlmodsym = dlthis->mysym->Find_Matching_Symbol(dlthis->mysym, ++ LINKER_MODULES_HEADER); ++ if (!dlmodsym) ++ return; /* no DLModules list so no debug info */ ++ /* if we have DLModules defined, construct our header */ ++ mlst = (struct dbg_mirror_root *) ++ dlthis->mysym->Allocate(dlthis->mysym, ++ sizeof(struct dbg_mirror_root)); ++ if (!mlst) { ++ DL_ERROR(E_ALLOC, sizeof(struct dbg_mirror_root)); ++ return; ++ } ++ mlst->hnext = NULL; ++ mlst->changes = 0; ++ mlst->refcount = 0; ++ mlst->dbthis = TDATA_TO_TADDR(dlmodsym->value); ++ /* add our root symbol */ ++ debug_mirror_sym = dlthis->mysym->Add_To_Symbol_Table ++ (dlthis->mysym, LOADER_DLLVIEW_ROOT, ++ (unsigned)dlthis->myhandle); ++ if (!debug_mirror_sym) { ++ /* failed, recover memory */ ++ dlthis->mysym->Deallocate(dlthis->mysym, mlst); ++ return; ++ } ++ debug_mirror_sym->value = (u32)mlst; ++ } ++ /* First create the DLLview record and stuff it into the buffer. ++ Then write it to the DSP. Record pertinent locations in our hndl, ++ and add it to the per-processor list of handles with debug info.*/ ++#ifndef DEBUG_HEADER_IN_LOADER ++ mlist = (struct dbg_mirror_root *)debug_mirror_sym->value; ++ if (!mlist) ++ return; ++#else ++ mlist = (struct dbg_mirror_root *)&debug_list_header; ++#endif ++ hndl->dm.hroot = mlist; /* set pointer to root into our handle */ ++ if (!dlthis->allocated_secn_count) ++ return; /* no load addresses to be recorded */ ++ /* reuse temporary symbol storage */ ++ dbmod = (struct dll_module *) dlthis->local_symtab; ++ /* Create the DLLview record in the memory we retain for our handle*/ ++ dbmod->num_sects = dlthis->allocated_secn_count; ++ dbmod->timestamp = dlthis->verify.dv_timdat; ++ dbmod->version = INIT_VERSION; ++ dbmod->verification = VERIFICATION; ++ asecs = dlthis->ldr_sections; ++ dbsec = dbmod->sects; ++ for (curr_sect = dlthis->allocated_secn_count; ++ curr_sect > 0; curr_sect -= 1) { ++ dbsec->sect_load_adr = asecs->load_addr; ++ dbsec->sect_run_adr = asecs->run_addr; ++ dbsec += 1; ++ asecs += 1; ++ } ++ /* now cram in the names */ ++ cp = copy_tgt_strings(dbsec, dlthis->str_head, ++ dlthis->debug_string_size); ++ ++ /* round off the size of the debug record, and remember same */ ++ hndl->dm.dbsiz = HOST_TO_TDATA_ROUND(cp - (char *)dbmod); ++ *cp = 0; /* strictly to make our test harness happy */ ++ dllview_info = DLLVIEW_INFO_INIT; ++ dllview_info.size = TDATA_TO_TADDR(hndl->dm.dbsiz); ++ /* Initialize memory context to default heap */ ++ dllview_info.context = 0; ++ hndl->dm.context = 0; ++ /* fill in next pointer and size */ ++ if (mlist->hnext) { ++ dbmod->next_module = TADDR_TO_TDATA(mlist->hnext->dm.dbthis); ++ dbmod->next_module_size = mlist->hnext->dm.dbsiz; ++ } else { ++ dbmod->next_module_size = 0; ++ dbmod->next_module = 0; ++ } ++ /* allocate memory for on-DSP DLLview debug record */ ++ if (!dlthis->myalloc) ++ return; ++ if (!dlthis->myalloc->Allocate(dlthis->myalloc, &dllview_info, ++ HOST_TO_TADDR(sizeof(u32)))) { ++ return; ++ } ++ /* Store load address of .dllview section */ ++ hndl->dm.dbthis = dllview_info.load_addr; ++ /* Store memory context (segid) in which .dllview section ++ * was allocated */ ++ hndl->dm.context = dllview_info.context; ++ mlist->refcount += 1; ++ /* swap bytes in the entire debug record, but not the string table */ ++ if (TARGET_ENDIANNESS_DIFFERS(TARGET_BIG_ENDIAN)) { ++ swap_words(dbmod, (char *)dbsec - (char *)dbmod, ++ DLL_MODULE_BITMAP); ++ } ++ /* Update the DLLview list on the DSP write new record */ ++ if (!dlthis->myio->writemem(dlthis->myio, dbmod, ++ dllview_info.load_addr, &dllview_info, ++ TADDR_TO_HOST(dllview_info.size))) { ++ return; ++ } ++ /* write new header */ ++ mhdr.first_module_size = hndl->dm.dbsiz; ++ mhdr.first_module = TADDR_TO_TDATA(dllview_info.load_addr); ++ /* swap bytes in the module header, if needed */ ++ if (TARGET_ENDIANNESS_DIFFERS(TARGET_BIG_ENDIAN)) { ++ swap_words(&mhdr, sizeof(struct modules_header) - sizeof(u16), ++ MODULES_HEADER_BITMAP); ++ } ++ dllview_info = DLLVIEW_INFO_INIT; ++ if (!dlthis->myio->writemem(dlthis->myio, &mhdr, mlist->dbthis, ++ &dllview_info, sizeof(struct modules_header) - ++ sizeof(u16))) { ++ return; ++ } ++ /* Add the module handle to this processor's list ++ of handles with debug info */ ++ hndl->dm.hnext = mlist->hnext; ++ if (hndl->dm.hnext) ++ hndl->dm.hnext->dm.hprev = hndl; ++ hndl->dm.hprev = (struct my_handle *) mlist; ++ mlist->hnext = hndl; /* insert after root*/ ++} /* init_module_handle */ ++ ++/************************************************************************* ++ * Procedure Dynamic_Unload_Module ++ * ++ * Parameters: ++ * mhandle A module handle from Dynamic_Load_Module ++ * syms Host-side symbol table and malloc/free functions ++ * alloc Target-side memory allocation ++ * ++ * Effect: ++ * The module specified by mhandle is unloaded. Unloading causes all ++ * target memory to be deallocated, all symbols defined by the module to ++ * be purged, and any host-side storage used by the dynamic loader for ++ * this module to be released. ++ * ++ * Returns: ++ * Zero for success. On error, the number of errors detected is returned. ++ * Individual errors are reported using syms->Error_Report(). ++ ************************************************************************/ ++int Dynamic_Unload_Module(DLOAD_mhandle mhandle, ++ struct Dynamic_Loader_Sym *syms, ++ struct Dynamic_Loader_Allocate *alloc, ++ struct Dynamic_Loader_Initialize *init) ++{ ++ s16 curr_sect; ++ struct LDR_SECTION_INFO *asecs; ++ struct my_handle *hndl; ++ struct dbg_mirror_root *root; ++ unsigned errcount = 0; ++ struct LDR_SECTION_INFO dllview_info = DLLVIEW_INFO_INIT; ++ struct modules_header mhdr; ++ ++ hndl = (struct my_handle *)mhandle; ++ if (!hndl) ++ return 0; /* if handle is null, nothing to do */ ++ /* Clear out the module symbols ++ * Note that if this is the module that defined MODULES_HEADER ++ (the head of the target debug list) ++ * then this operation will blow away that symbol. ++ It will therefore be impossible for subsequent ++ * operations to add entries to this un-referenceable list.*/ ++ if (!syms) ++ return 1; ++ syms->Purge_Symbol_Table(syms, (unsigned) hndl); ++ /* Deallocate target memory for sections */ ++ asecs = hndl->secns; ++ if (alloc) ++ for (curr_sect = (hndl->secn_count >> 1); curr_sect > 0; ++ curr_sect -= 1) { ++ asecs->name = NULL; ++ alloc->Deallocate(alloc, asecs++); ++ } ++ root = hndl->dm.hroot; ++ if (!root) { ++ /* there is a debug list containing this module */ ++ goto func_end; ++ } ++ if (!hndl->dm.dbthis) { /* target-side dllview record exists */ ++ goto loop_end; ++ } ++ /* Retrieve memory context in which .dllview was allocated */ ++ dllview_info.context = hndl->dm.context; ++ if (hndl->dm.hprev == hndl) ++ goto exitunltgt; ++ ++ /* target-side dllview record is in list */ ++ /* dequeue this record from our GPP-side mirror list */ ++ hndl->dm.hprev->dm.hnext = hndl->dm.hnext; ++ if (hndl->dm.hnext) ++ hndl->dm.hnext->dm.hprev = hndl->dm.hprev; ++ /* Update next_module of previous entry in target list ++ * We are using mhdr here as a surrogate for either a ++ struct modules_header or a dll_module */ ++ if (hndl->dm.hnext) { ++ mhdr.first_module = TADDR_TO_TDATA(hndl->dm.hnext->dm.dbthis); ++ mhdr.first_module_size = hndl->dm.hnext->dm.dbsiz; ++ } else { ++ mhdr.first_module = 0; ++ mhdr.first_module_size = 0; ++ } ++ if (!init) ++ goto exitunltgt; ++ ++ if (!init->connect(init)) { ++ dload_syms_error(syms, E_ICONNECT); ++ errcount += 1; ++ goto exitunltgt; ++ } ++ /* swap bytes in the module header, if needed */ ++ if (TARGET_ENDIANNESS_DIFFERS(hndl->secn_count & 0x1)) { ++ swap_words(&mhdr, sizeof(struct modules_header) - sizeof(u16), ++ MODULES_HEADER_BITMAP); ++ } ++ if (!init->writemem(init, &mhdr, hndl->dm.hprev->dm.dbthis, ++ &dllview_info, sizeof(struct modules_header) - ++ sizeof(mhdr.update_flag))) { ++ dload_syms_error(syms, E_DLVWRITE); ++ errcount += 1; ++ } ++ /* update change counter */ ++ root->changes += 1; ++ if (!init->writemem(init, &(root->changes), ++ root->dbthis + HOST_TO_TADDR ++ (sizeof(mhdr.first_module) + ++ sizeof(mhdr.first_module_size)), ++ &dllview_info, ++ sizeof(mhdr.update_flag))) { ++ dload_syms_error(syms, E_DLVWRITE); ++ errcount += 1; ++ } ++ init->release(init); ++exitunltgt: ++ /* release target storage */ ++ dllview_info.size = TDATA_TO_TADDR(hndl->dm.dbsiz); ++ dllview_info.load_addr = hndl->dm.dbthis; ++ if (alloc) ++ alloc->Deallocate(alloc, &dllview_info); ++ root->refcount -= 1; ++ /* target-side dllview record exists */ ++loop_end: ++#ifndef DEBUG_HEADER_IN_LOADER ++ if (root->refcount <= 0) { ++ /* if all references gone, blow off the header */ ++ /* our root symbol may be gone due to the Purge above, ++ but if not, do not destroy the root */ ++ if (syms->Find_Matching_Symbol ++ (syms, LOADER_DLLVIEW_ROOT) == NULL) ++ syms->Deallocate(syms, root); ++ } ++#endif ++func_end: ++ /* there is a debug list containing this module */ ++ syms->Deallocate(syms, mhandle); /* release our storage */ ++ return errcount; ++} /* Dynamic_Unload_Module */ ++ ++#if BITS_PER_AU > BITS_PER_BYTE ++/************************************************************************* ++ * Procedure unpack_name ++ * ++ * Parameters: ++ * soffset Byte offset into the string table ++ * ++ * Effect: ++ * Returns a pointer to the string specified by the offset supplied, or ++ * NULL for error. ++ * ++ ************************************************************************/ ++static char *unpack_name(struct dload_state *dlthis, u32 soffset) ++{ ++ u8 tmp, *src; ++ char *dst; ++ ++ if (soffset >= dlthis->dfile_hdr.df_strtab_size) { ++ dload_error(dlthis, "Bad string table offset " FMT_UI32, ++ soffset); ++ return NULL; ++ } ++ src = (uint_least8_t *)dlthis->str_head + ++ (soffset >> (LOG_BITS_PER_AU - LOG_BITS_PER_BYTE)); ++ dst = dlthis->str_temp; ++ if (soffset & 1) ++ *dst++ = *src++; /* only 1 character in first word */ ++ do { ++ tmp = *src++; ++ *dst = (tmp >> BITS_PER_BYTE); ++ if (!(*dst++)) ++ break; ++ } while ((*dst++ = tmp & BYTE_MASK)); ++ dlthis->temp_len = dst - dlthis->str_temp; ++ /* squirrel away length including terminating null */ ++ return dlthis->str_temp; ++} /* unpack_name */ ++#endif +diff --git a/drivers/dsp/bridge/dynload/dlclasses_hdr.h b/drivers/dsp/bridge/dynload/dlclasses_hdr.h +new file mode 100644 +index 0000000..04f136e +--- /dev/null ++++ b/drivers/dsp/bridge/dynload/dlclasses_hdr.h +@@ -0,0 +1,41 @@ ++/* ++ * dlclasses_hdr.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++ ++#ifndef _DLCLASSES_HDR_H ++#define _DLCLASSES_HDR_H ++ ++/***************************************************************************** ++ ***************************************************************************** ++ * ++ * DLCLASSES_HDR.H ++ * ++ * Sample classes in support of the dynamic loader ++ * ++ * These are just concrete derivations of the virtual ones in dynamic_loader.h ++ * with a few additional interfaces for init, etc. ++ ***************************************************************************** ++ *****************************************************************************/ ++ ++#include ++ ++#include "DLstream.h" ++#include "DLsymtab.h" ++#include "DLalloc.h" ++#include "DLinit.h" ++ ++#endif /* _DLCLASSES_HDR_H */ +diff --git a/drivers/dsp/bridge/dynload/dload_internal.h b/drivers/dsp/bridge/dynload/dload_internal.h +new file mode 100644 +index 0000000..78f5058 +--- /dev/null ++++ b/drivers/dsp/bridge/dynload/dload_internal.h +@@ -0,0 +1,237 @@ ++/* ++ * dload_internal.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++ ++#ifndef __DLOAD_INTERNAL__ ++#define __DLOAD_INTERNAL__ ++ ++#include ++ ++/* ++ * Internal state definitions for the dynamic loader ++ */ ++ ++#define TRUE 1 ++#define FALSE 0 ++typedef int boolean; ++ ++ ++/* type used for relocation intermediate results */ ++typedef s32 RVALUE; ++ ++/* unsigned version of same; must have at least as many bits */ ++typedef u32 URVALUE; ++ ++/* ++ * Dynamic loader configuration constants ++ */ ++/* error issued if input has more sections than this limit */ ++#define REASONABLE_SECTION_LIMIT 100 ++ ++/* (Addressable unit) value used to clear BSS section */ ++#define dload_fill_bss 0 ++ ++/* ++ * Reorder maps explained (?) ++ * ++ * The doff file format defines a 32-bit pattern used to determine the ++ * byte order of an image being read. That value is ++ * BYTE_RESHUFFLE_VALUE == 0x00010203 ++ * For purposes of the reorder routine, we would rather have the all-is-OK ++ * for 32-bits pattern be 0x03020100. This first macro makes the ++ * translation from doff file header value to MAP value: */ ++#define REORDER_MAP(rawmap) ((rawmap) ^ 0x3030303) ++/* This translation is made in dload_headers. Thereafter, the all-is-OK ++ * value for the maps stored in dlthis is REORDER_MAP(BYTE_RESHUFFLE_VALUE). ++ * But sadly, not all bits of the doff file are 32-bit integers. ++ * The notable exceptions are strings and image bits. ++ * Strings obey host byte order: */ ++#if defined(_BIG_ENDIAN) ++#define HOST_BYTE_ORDER(cookedmap) ((cookedmap) ^ 0x3030303) ++#else ++#define HOST_BYTE_ORDER(cookedmap) (cookedmap) ++#endif ++/* Target bits consist of target AUs (could be bytes, or 16-bits, ++ * or 32-bits) stored as an array in host order. A target order ++ * map is defined by: */ ++#if !defined(_BIG_ENDIAN) || TARGET_AU_BITS > 16 ++#define TARGET_ORDER(cookedmap) (cookedmap) ++#elif TARGET_AU_BITS > 8 ++#define TARGET_ORDER(cookedmap) ((cookedmap) ^ 0x2020202) ++#else ++#define TARGET_ORDER(cookedmap) ((cookedmap) ^ 0x3030303) ++#endif ++ ++/* forward declaration for handle returned by dynamic loader */ ++struct my_handle; ++ ++/* ++ * a list of module handles, which mirrors the debug list on the target ++ */ ++struct dbg_mirror_root { ++ /* must be same as dbg_mirror_list; __DLModules address on target */ ++ u32 dbthis; ++ struct my_handle *hnext; /* must be same as dbg_mirror_list */ ++ u16 changes; /* change counter */ ++ u16 refcount; /* number of modules referencing this root */ ++} ; ++ ++struct dbg_mirror_list { ++ u32 dbthis; ++ struct my_handle *hnext, *hprev; ++ struct dbg_mirror_root *hroot; ++ u16 dbsiz; ++ u32 context; /* Save context for .dllview memory allocation */ ++} ; ++ ++#define VARIABLE_SIZE 1 ++/* ++ * the structure we actually return as an opaque module handle ++ */ ++struct my_handle { ++ struct dbg_mirror_list dm; /* !!! must be first !!! */ ++ /* sections following << 1, LSB is set for big-endian target */ ++ u16 secn_count; ++ struct LDR_SECTION_INFO secns[VARIABLE_SIZE]; ++} ; ++#define MY_HANDLE_SIZE (sizeof(struct my_handle) -\ ++ sizeof(struct LDR_SECTION_INFO)) ++/* real size of my_handle */ ++ ++/* ++ * reduced symbol structure used for symbols during relocation ++ */ ++struct Local_Symbol { ++ s32 value; /* Relocated symbol value */ ++ s32 delta; /* Original value in input file */ ++ s16 secnn; /* section number */ ++ s16 sclass; /* symbol class */ ++} ; ++ ++/* ++ * States of the .cinit state machine ++ */ ++enum cinit_mode { ++ CI_count = 0, /* expecting a count */ ++ CI_address, /* expecting an address */ ++#if CINIT_ALIGN < CINIT_ADDRESS /* handle case of partial address field */ ++ CI_partaddress, /* have only part of the address */ ++#endif ++ CI_copy, /* in the middle of copying data */ ++ CI_done /* end of .cinit table */ ++}; ++ ++/* ++ * The internal state of the dynamic loader, which is passed around as ++ * an object ++ */ ++struct dload_state { ++ struct Dynamic_Loader_Stream *strm; /* The module input stream */ ++ struct Dynamic_Loader_Sym *mysym; /* Symbols for this session */ ++ struct Dynamic_Loader_Allocate *myalloc; /* target memory allocator */ ++ struct Dynamic_Loader_Initialize *myio; /* target memory initializer */ ++ unsigned myoptions; /* Options parameter Dynamic_Load_Module */ ++ ++ char *str_head; /* Pointer to string table */ ++#if BITS_PER_AU > BITS_PER_BYTE ++ char *str_temp; /* Pointer to temporary buffer for strings */ ++ /* big enough to hold longest string */ ++ unsigned temp_len; /* length of last temporary string */ ++ char *xstrings; /* Pointer to buffer for expanded */ ++ /* strings for sec names */ ++#endif ++ /* Total size of strings for DLLView section names */ ++ unsigned debug_string_size; ++ /* Pointer to parallel section info for allocated sections only */ ++ struct doff_scnhdr_t *sect_hdrs; /* Pointer to section table */ ++ struct LDR_SECTION_INFO *ldr_sections; ++#if TMS32060 ++ /* The address of the start of the .bss section */ ++ LDR_ADDR bss_run_base; ++#endif ++ struct Local_Symbol *local_symtab; /* Relocation symbol table */ ++ ++ /* pointer to DL section info for the section being relocated */ ++ struct LDR_SECTION_INFO *image_secn; ++ /* change in run address for current section during relocation */ ++ LDR_ADDR delta_runaddr; ++ LDR_ADDR image_offset; /* offset of current packet in section */ ++ enum cinit_mode cinit_state; /* current state of cload_cinit() */ ++ int cinit_count; /* the current count */ ++ LDR_ADDR cinit_addr; /* the current address */ ++ s16 cinit_page; /* the current page */ ++ /* Handle to be returned by Dynamic_Load_Module */ ++ struct my_handle *myhandle; ++ unsigned dload_errcount; /* Total # of errors reported so far */ ++ /* Number of target sections that require allocation and relocation */ ++ unsigned allocated_secn_count; ++#ifndef TARGET_ENDIANNESS ++ boolean big_e_target; /* Target data in big-endian format */ ++#endif ++ /* map for reordering bytes, 0 if not needed */ ++ u32 reorder_map; ++ struct doff_filehdr_t dfile_hdr; /* DOFF file header structure */ ++ struct doff_verify_rec_t verify; /* Verify record */ ++ ++ int relstkidx; /* index into relocation value stack */ ++ /* relocation value stack used in relexp.c */ ++ RVALUE relstk[STATIC_EXPR_STK_SIZE]; ++ ++} ; ++ ++#ifdef TARGET_ENDIANNESS ++#define TARGET_BIG_ENDIAN TARGET_ENDIANNESS ++#else ++#define TARGET_BIG_ENDIAN (dlthis->big_e_target) ++#endif ++ ++/* ++ * Exports from cload.c to rest of the world ++ */ ++extern void dload_error(struct dload_state *dlthis, const char *errtxt, ...); ++extern void dload_syms_error(struct Dynamic_Loader_Sym *syms, ++ const char *errtxt, ...); ++extern void dload_headers(struct dload_state *dlthis); ++extern void dload_strings(struct dload_state *dlthis, boolean sec_names_only); ++extern void dload_sections(struct dload_state *dlthis); ++extern void dload_reorder(void *data, int dsiz, u32 map); ++extern u32 dload_checksum(void *data, unsigned siz); ++ ++#if HOST_ENDIANNESS ++extern uint32_t dload_reverse_checksum(void *data, unsigned siz); ++#if (TARGET_AU_BITS > 8) && (TARGET_AU_BITS < 32) ++extern uint32_t dload_reverse_checksum_16(void *data, unsigned siz); ++#endif ++#endif ++ ++#define is_data_scn(zzz) (DLOAD_SECTION_TYPE((zzz)->type) != DLOAD_TEXT) ++#define is_data_scn_num(zzz) \ ++ (DLOAD_SECT_TYPE(&dlthis->sect_hdrs[(zzz)-1]) != DLOAD_TEXT) ++ ++/* ++ * exported by reloc.c ++ */ ++extern void dload_relocate(struct dload_state *dlthis, TgtAU_t *data, ++ struct reloc_record_t *rp); ++ ++extern RVALUE dload_unpack(struct dload_state *dlthis, TgtAU_t *data, ++ int fieldsz, int offset, unsigned sgn); ++ ++extern int dload_repack(struct dload_state *dlthis, RVALUE val, TgtAU_t *data, ++ int fieldsz, int offset, unsigned sgn); ++ ++#endif /* __DLOAD_INTERNAL__ */ +diff --git a/drivers/dsp/bridge/dynload/doff.h b/drivers/dsp/bridge/dynload/doff.h +new file mode 100644 +index 0000000..2b8fc37 +--- /dev/null ++++ b/drivers/dsp/bridge/dynload/doff.h +@@ -0,0 +1,347 @@ ++/* ++ * doff.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/*****************************************************************************/ ++/* DOFF.H - Structures & definitions used for dynamically */ ++/* loaded modules file format. This format is a reformatted */ ++/* version of COFF.(see coff.h for details) It optimizes the */ ++/* layout for the dynamic loader. */ ++/* */ ++/* .dof files, when viewed as a sequence of 32-bit integers, look the same */ ++/* on big-endian and little-endian machines. */ ++/*****************************************************************************/ ++#ifndef _DOFF_H ++#define _DOFF_H ++ ++#ifndef UINT32_C ++#define UINT32_C(zzz) ((u32)zzz) ++#endif ++ ++#define BYTE_RESHUFFLE_VALUE UINT32_C(0x00010203) ++ ++/* DOFF file header containing fields categorizing the remainder of the file */ ++struct doff_filehdr_t { ++ ++ /* string table size, including filename, in bytes */ ++ u32 df_strtab_size; ++ ++ /* entry point if one exists */ ++ u32 df_entrypt; ++ ++ /* identifies byte ordering of file; ++ * always set to BYTE_RESHUFFLE_VALUE */ ++ u32 df_byte_reshuffle; ++ ++ /* Size of the string table up to and including the last section name */ ++ /* Size includes the name of the COFF file also */ ++ u32 df_scn_name_size; ++ ++#ifndef _BIG_ENDIAN ++ /* number of symbols */ ++ u16 df_no_syms; ++ ++ /* length in bytes of the longest string, including terminating NULL */ ++ /* excludes the name of the file */ ++ u16 df_max_str_len; ++ ++ /* total number of sections including no-load ones */ ++ u16 df_no_scns; ++ ++ /* number of sections containing target code allocated or downloaded */ ++ u16 df_target_scns; ++ ++ /* unique id for dll file format & version */ ++ u16 df_doff_version; ++ ++ /* identifies ISA */ ++ u16 df_target_id; ++ ++ /* useful file flags */ ++ u16 df_flags; ++ ++ /* section reference for entry point, N_UNDEF for none, */ ++ /* N_ABS for absolute address */ ++ s16 df_entry_secn; ++#else ++ /* length of the longest string, including terminating NULL */ ++ u16 df_max_str_len; ++ ++ /* number of symbols */ ++ u16 df_no_syms; ++ ++ /* number of sections containing target code allocated or downloaded */ ++ u16 df_target_scns; ++ ++ /* total number of sections including no-load ones */ ++ u16 df_no_scns; ++ ++ /* identifies ISA */ ++ u16 df_target_id; ++ ++ /* unique id for dll file format & version */ ++ u16 df_doff_version; ++ ++ /* section reference for entry point, N_UNDEF for none, */ ++ /* N_ABS for absolute address */ ++ s16 df_entry_secn; ++ ++ /* useful file flags */ ++ u16 df_flags; ++#endif ++ /* checksum for file header record */ ++ u32 df_checksum; ++ ++} ; ++ ++/* flags in the df_flags field */ ++#define DF_LITTLE 0x100 ++#define DF_BIG 0x200 ++#define DF_BYTE_ORDER (DF_LITTLE | DF_BIG) ++ ++/* Supported processors */ ++#define TMS470_ID 0x97 ++#define LEAD_ID 0x98 ++#define TMS32060_ID 0x99 ++#define LEAD3_ID 0x9c ++ ++/* Primary processor for loading */ ++#if TMS32060 ++#define TARGET_ID TMS32060_ID ++#endif ++ ++/* Verification record containing values used to test integrity of the bits */ ++struct doff_verify_rec_t { ++ ++ /* time and date stamp */ ++ u32 dv_timdat; ++ ++ /* checksum for all section records */ ++ u32 dv_scn_rec_checksum; ++ ++ /* checksum for string table */ ++ u32 dv_str_tab_checksum; ++ ++ /* checksum for symbol table */ ++ u32 dv_sym_tab_checksum; ++ ++ /* checksum for verification record */ ++ u32 dv_verify_rec_checksum; ++ ++} ; ++ ++/* String table is an array of null-terminated strings. The first entry is ++ * the filename, which is added by DLLcreate. No new structure definitions ++ * are required. ++ */ ++ ++/* Section Records including information on the corresponding image packets */ ++/* ++ * !!WARNING!! ++ * ++ * This structure is expected to match in form LDR_SECTION_INFO in ++ * dynamic_loader.h ++ */ ++ ++struct doff_scnhdr_t { ++ ++ s32 ds_offset; /* offset into string table of name */ ++ s32 ds_paddr; /* RUN address, in target AU */ ++ s32 ds_vaddr; /* LOAD address, in target AU */ ++ s32 ds_size; /* section size, in target AU */ ++#ifndef _BIG_ENDIAN ++ u16 ds_page; /* memory page id */ ++ u16 ds_flags; /* section flags */ ++#else ++ u16 ds_flags; /* section flags */ ++ u16 ds_page; /* memory page id */ ++#endif ++ u32 ds_first_pkt_offset; ++ /* Absolute byte offset into the file */ ++ /* where the first image record resides */ ++ ++ s32 ds_nipacks; /* number of image packets */ ++ ++}; ++ ++/* Symbol table entry */ ++struct doff_syment_t { ++ ++ s32 dn_offset; /* offset into string table of name */ ++ s32 dn_value; /* value of symbol */ ++#ifndef _BIG_ENDIAN ++ s16 dn_scnum; /* section number */ ++ s16 dn_sclass; /* storage class */ ++#else ++ s16 dn_sclass; /* storage class */ ++ s16 dn_scnum; /* section number, 1-based */ ++#endif ++ ++} ; ++ ++/* special values for dn_scnum */ ++#define DN_UNDEF 0 /* undefined symbol */ ++#define DN_ABS (-1) /* value of symbol is absolute */ ++/* special values for dn_sclass */ ++#define DN_EXT 2 ++#define DN_STATLAB 20 ++#define DN_EXTLAB 21 ++ ++/* Default value of image bits in packet */ ++/* Configurable by user on the command line */ ++#define IMAGE_PACKET_SIZE 1024 ++ ++/* An image packet contains a chunk of data from a section along with */ ++/* information necessary for its processing. */ ++struct image_packet_t { ++ ++ s32 i_num_relocs; /* number of relocations for */ ++ /* this packet */ ++ ++ s32 i_packet_size; /* number of bytes in array */ ++ /* "bits" occupied by */ ++ /* valid data. Could be */ ++ /* < IMAGE_PACKET_SIZE to */ ++ /* prevent splitting a */ ++ /* relocation across packets. */ ++ /* Last packet of a section */ ++ /* will most likely contain */ ++ /* < IMAGE_PACKET_SIZE bytes */ ++ /* of valid data */ ++ ++ s32 i_checksum; /* Checksum for image packet */ ++ /* and the corresponding */ ++ /* relocation records */ ++ ++ u8 *i_bits; /* Actual data in section */ ++ ++}; ++ ++/* The relocation structure definition matches the COFF version. Offsets */ ++/* however are relative to the image packet base not the section base. */ ++struct reloc_record_t { ++ ++ s32 r_vaddr; ++ ++ /* expressed in target AUs */ ++ ++ union { ++ struct { ++#ifndef _BIG_ENDIAN ++ u8 _offset; /* bit offset of rel fld */ ++ u8 _fieldsz; /* size of rel fld */ ++ u8 _wordsz; /* # bytes containing rel fld */ ++ u8 _dum1; ++ u16 _dum2; ++ u16 _type; ++#else ++ unsigned _dum1:8; ++ unsigned _wordsz:8; /* # bytes containing rel fld */ ++ unsigned _fieldsz:8; /* size of rel fld */ ++ unsigned _offset:8; /* bit offset of rel fld */ ++ u16 _type; ++ u16 _dum2; ++#endif ++ } _r_field; ++ ++ struct { ++ u32 _spc; /* image packet relative PC */ ++#ifndef _BIG_ENDIAN ++ u16 _dum; ++ u16 _type; /* relocation type */ ++#else ++ u16 _type; /* relocation type */ ++ u16 _dum; ++#endif ++ } _r_spc; ++ ++ struct { ++ u32 _uval; /* constant value */ ++#ifndef _BIG_ENDIAN ++ u16 _dum; ++ u16 _type; /* relocation type */ ++#else ++ u16 _type; /* relocation type */ ++ u16 _dum; ++#endif ++ } _r_uval; ++ ++ struct { ++ s32 _symndx; /* 32-bit sym tbl index */ ++#ifndef _BIG_ENDIAN ++ u16 _disp; /* extra addr encode data */ ++ u16 _type; /* relocation type */ ++#else ++ u16 _type; /* relocation type */ ++ u16 _disp; /* extra addr encode data */ ++#endif ++ } _r_sym; ++ } _u_reloc; ++ ++} ; ++ ++/* abbreviations for convenience */ ++#ifndef r_type ++#define r_type _u_reloc._r_sym._type ++#define r_uval _u_reloc._r_uval._uval ++#define r_symndx _u_reloc._r_sym._symndx ++#define r_offset _u_reloc._r_field._offset ++#define r_fieldsz _u_reloc._r_field._fieldsz ++#define r_wordsz _u_reloc._r_field._wordsz ++#define r_disp _u_reloc._r_sym._disp ++#endif ++ ++/*****************************************************************************/ ++/* */ ++/* Important DOFF macros used for file processing */ ++/* */ ++/*****************************************************************************/ ++ ++/* DOFF Versions */ ++#define DOFF0 0 ++ ++/* Return the address/size >= to addr that is at a 32-bit boundary */ ++/* This assumes that a byte is 8 bits */ ++#define DOFF_ALIGN(addr) (((addr) + 3) & ~UINT32_C(3)) ++ ++/*****************************************************************************/ ++/* */ ++/* The DOFF section header flags field is laid out as follows: */ ++/* */ ++/* Bits 0-3 : Section Type */ ++/* Bit 4 : Set when section requires target memory to be allocated by DL */ ++/* Bit 5 : Set when section requires downloading */ ++/* Bits 8-11: Alignment, same as COFF */ ++/* */ ++/*****************************************************************************/ ++ ++/* Enum for DOFF section types (bits 0-3 of flag): See dynamic_loader.h */ ++ ++/* Macros to help processing of sections */ ++#define DLOAD_SECT_TYPE(s_hdr) ((s_hdr)->ds_flags & 0xF) ++ ++/* DS_ALLOCATE indicates whether a section needs space on the target */ ++#define DS_ALLOCATE_MASK 0x10 ++#define DS_NEEDS_ALLOCATION(s_hdr) ((s_hdr)->ds_flags & DS_ALLOCATE_MASK) ++ ++/* DS_DOWNLOAD indicates that the loader needs to copy bits */ ++#define DS_DOWNLOAD_MASK 0x20 ++#define DS_NEEDS_DOWNLOAD(s_hdr) ((s_hdr)->ds_flags & DS_DOWNLOAD_MASK) ++ ++/* Section alignment requirement in AUs */ ++#define DS_ALIGNMENT(ds_flags) (1 << (((ds_flags) >> 8) & 0xF)) ++ ++#endif /* _DOFF_H */ +diff --git a/drivers/dsp/bridge/dynload/getsection.c b/drivers/dsp/bridge/dynload/getsection.c +new file mode 100644 +index 0000000..78a301a +--- /dev/null ++++ b/drivers/dsp/bridge/dynload/getsection.c +@@ -0,0 +1,412 @@ ++/* ++ * getsection.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++ ++#include ++#include "header.h" ++ ++/* ++ * Error strings ++ */ ++static const char E_READSTRM[] = { "Error reading %s from input stream" }; ++static const char E_SEEK[] = { "Set file position to %d failed" }; ++static const char E_ISIZ[] = { "Bad image packet size %d" }; ++static const char E_CHECKSUM[] = { "Checksum failed on %s" }; ++static const char E_RELOC[] = { "DLOAD_GetSection unable to read" ++ "sections containing relocation entries"}; ++#if BITS_PER_AU > BITS_PER_BYTE ++static const char E_ALLOC[] = { "Syms->Allocate( %d ) failed" }; ++static const char E_STBL[] = { "Bad string table offset " FMT_UI32 }; ++#endif ++ ++/* ++ * we use the fact that DOFF section records are shaped just like ++ * LDR_SECTION_INFO to reduce our section storage usage. These macros ++ * marks the places where that assumption is made ++ */ ++#define DOFFSEC_IS_LDRSEC(pdoffsec) ((struct LDR_SECTION_INFO *)(pdoffsec)) ++#define LDRSEC_IS_DOFFSEC(ldrsec) ((struct doff_scnhdr_t *)(ldrsec)) ++ ++/***************************************************************/ ++/********************* SUPPORT FUNCTIONS ***********************/ ++/***************************************************************/ ++ ++#if BITS_PER_AU > BITS_PER_BYTE ++/************************************************************************** ++ * Procedure unpack_sec_name ++ * ++ * Parameters: ++ * dlthis Handle from DLOAD_module_open for this module ++ * soffset Byte offset into the string table ++ * dst Place to store the expanded string ++ * ++ * Effect: ++ * Stores a string from the string table into the destination, expanding ++ * it in the process. Returns a pointer just past the end of the stored ++ * string on success, or NULL on failure. ++ * ++ *************************************************************************/ ++static char *unpack_sec_name(struct dload_state *dlthis, ++ u32 soffset, char *dst) ++{ ++ u8 tmp, *src; ++ ++ if (soffset >= dlthis->dfile_hdr.df_scn_name_size) { ++ dload_error(dlthis, E_STBL, soffset); ++ return NULL; ++ } ++ src = (u8 *)dlthis->str_head + ++ (soffset >> (LOG_BITS_PER_AU - LOG_BITS_PER_BYTE)); ++ if (soffset & 1) ++ *dst++ = *src++; /* only 1 character in first word */ ++ do { ++ tmp = *src++; ++ *dst = (tmp >> BITS_PER_BYTE) ++ if (!(*dst++)) ++ break; ++ } while ((*dst++ = tmp & BYTE_MASK)); ++ ++ return dst; ++} ++ ++/************************************************************************** ++ * Procedure expand_sec_names ++ * ++ * Parameters: ++ * dlthis Handle from DLOAD_module_open for this module ++ * ++ * Effect: ++ * Allocates a buffer, unpacks and copies strings from string table into it. ++ * Stores a pointer to the buffer into a state variable. ++ **************************************************************************/ ++static void expand_sec_names(struct dload_state *dlthis) ++{ ++ char *xstrings, *curr, *next; ++ u32 xsize; ++ u16 sec; ++ struct LDR_SECTION_INFO *shp; ++ /* assume worst-case size requirement */ ++ xsize = dlthis->dfile_hdr.df_max_str_len * dlthis->dfile_hdr.df_no_scns; ++ xstrings = (char *)dlthis->mysym->Allocate(dlthis->mysym, xsize); ++ if (xstrings == NULL) { ++ dload_error(dlthis, E_ALLOC, xsize); ++ return; ++ } ++ dlthis->xstrings = xstrings; ++ /* For each sec, copy and expand its name */ ++ curr = xstrings; ++ for (sec = 0; sec < dlthis->dfile_hdr.df_no_scns; sec++) { ++ shp = DOFFSEC_IS_LDRSEC(&dlthis->sect_hdrs[sec]); ++ next = unpack_sec_name(dlthis, *(u32 *) &shp->name, curr); ++ if (next == NULL) ++ break; /* error */ ++ shp->name = curr; ++ curr = next; ++ } ++} ++ ++#endif ++ ++/***************************************************************/ ++/********************* EXPORTED FUNCTIONS **********************/ ++/***************************************************************/ ++ ++/************************************************************************** ++ * Procedure DLOAD_module_open ++ * ++ * Parameters: ++ * module The input stream that supplies the module image ++ * syms Host-side malloc/free and error reporting functions. ++ * Other methods are unused. ++ * ++ * Effect: ++ * Reads header information from a dynamic loader module using the ++ specified ++ * stream object, and returns a handle for the module information. This ++ * handle may be used in subsequent query calls to obtain information ++ * contained in the module. ++ * ++ * Returns: ++ * NULL if an error is encountered, otherwise a module handle for use ++ * in subsequent operations. ++ **************************************************************************/ ++DLOAD_module_info DLOAD_module_open(struct Dynamic_Loader_Stream *module, ++ struct Dynamic_Loader_Sym *syms) ++{ ++ struct dload_state *dlthis; /* internal state for this call */ ++ unsigned *dp, sz; ++ u32 sec_start; ++#if BITS_PER_AU <= BITS_PER_BYTE ++ u16 sec; ++#endif ++ ++ /* Check that mandatory arguments are present */ ++ if (!module || !syms) { ++ if (syms != NULL) ++ dload_syms_error(syms, "Required parameter is NULL"); ++ ++ return NULL; ++ } ++ ++ dlthis = (struct dload_state *) ++ syms->Allocate(syms, sizeof(struct dload_state)); ++ if (!dlthis) { ++ /* not enough storage */ ++ dload_syms_error(syms, "Can't allocate module info"); ++ return NULL; ++ } ++ ++ /* clear our internal state */ ++ dp = (unsigned *)dlthis; ++ for (sz = sizeof(struct dload_state) / sizeof(unsigned); ++ sz > 0; sz -= 1) ++ *dp++ = 0; ++ ++ dlthis->strm = module; ++ dlthis->mysym = syms; ++ ++ /* read in the doff image and store in our state variable */ ++ dload_headers(dlthis); ++ ++ if (!dlthis->dload_errcount) ++ dload_strings(dlthis, true); ++ ++ /* skip ahead past the unread portion of the string table */ ++ sec_start = sizeof(struct doff_filehdr_t) + ++ sizeof(struct doff_verify_rec_t) + ++ BYTE_TO_HOST(DOFF_ALIGN(dlthis->dfile_hdr.df_strtab_size)); ++ ++ if (dlthis->strm->set_file_posn(dlthis->strm, sec_start) != 0) { ++ dload_error(dlthis, E_SEEK, sec_start); ++ return NULL; ++ } ++ ++ if (!dlthis->dload_errcount) ++ dload_sections(dlthis); ++ ++ if (dlthis->dload_errcount) { ++ DLOAD_module_close(dlthis); /* errors, blow off our state */ ++ dlthis = NULL; ++ return NULL; ++ } ++#if BITS_PER_AU > BITS_PER_BYTE ++ /* Expand all section names from the string table into the */ ++ /* state variable, and convert section names from a relative */ ++ /* string table offset to a pointers to the expanded string. */ ++ expand_sec_names(dlthis); ++#else ++ /* Convert section names from a relative string table offset */ ++ /* to a pointer into the string table. */ ++ for (sec = 0; sec < dlthis->dfile_hdr.df_no_scns; sec++) { ++ struct LDR_SECTION_INFO *shp = ++ DOFFSEC_IS_LDRSEC(&dlthis->sect_hdrs[sec]); ++ shp->name = dlthis->str_head + *(u32 *)&shp->name; ++ } ++#endif ++ ++ return dlthis; ++} ++ ++/*************************************************************************** ++ * Procedure DLOAD_GetSectionInfo ++ * ++ * Parameters: ++ * minfo Handle from DLOAD_module_open for this module ++ * sectionName Pointer to the string name of the section desired ++ * sectionInfo Address of a section info structure pointer to be ++ * initialized ++ * ++ * Effect: ++ * Finds the specified section in the module information, and initializes ++ * the provided struct LDR_SECTION_INFO pointer. ++ * ++ * Returns: ++ * true for success, false for section not found ++ **************************************************************************/ ++int DLOAD_GetSectionInfo(DLOAD_module_info minfo, const char *sectionName, ++ const struct LDR_SECTION_INFO **const sectionInfo) ++{ ++ struct dload_state *dlthis; ++ struct LDR_SECTION_INFO *shp; ++ u16 sec; ++ ++ dlthis = (struct dload_state *)minfo; ++ if (!dlthis) ++ return false; ++ ++ for (sec = 0; sec < dlthis->dfile_hdr.df_no_scns; sec++) { ++ shp = DOFFSEC_IS_LDRSEC(&dlthis->sect_hdrs[sec]); ++ if (strcmp(sectionName, shp->name) == 0) { ++ *sectionInfo = shp; ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++#define IPH_SIZE (sizeof(struct image_packet_t) - sizeof(u32)) ++#define REVERSE_REORDER_MAP(rawmap) ((rawmap) ^ 0x3030303) ++ ++/************************************************************************** ++ * Procedure DLOAD_GetSection ++ * ++ * Parameters: ++ * minfo Handle from DLOAD_module_open for this module ++ * sectionInfo Pointer to a section info structure for the desired ++ * section ++ * sectionData Buffer to contain the section initialized data ++ * ++ * Effect: ++ * Copies the initialized data for the specified section into the ++ * supplied buffer. ++ * ++ * Returns: ++ * true for success, false for section not found ++ **************************************************************************/ ++int DLOAD_GetSection(DLOAD_module_info minfo, ++ const struct LDR_SECTION_INFO *sectionInfo, void *sectionData) ++{ ++ struct dload_state *dlthis; ++ u32 pos; ++ struct doff_scnhdr_t *sptr = NULL; ++ s32 nip; ++ struct image_packet_t ipacket; ++ s32 ipsize; ++ u32 checks; ++ s8 *dest = (s8 *)sectionData; ++ ++ dlthis = (struct dload_state *)minfo; ++ if (!dlthis) ++ return false; ++ sptr = LDRSEC_IS_DOFFSEC(sectionInfo); ++ if (sptr == NULL) ++ return false; ++ ++ /* skip ahead to the start of the first packet */ ++ pos = BYTE_TO_HOST(DOFF_ALIGN((u32) sptr->ds_first_pkt_offset)); ++ if (dlthis->strm->set_file_posn(dlthis->strm, pos) != 0) { ++ dload_error(dlthis, E_SEEK, pos); ++ return false; ++ } ++ ++ nip = sptr->ds_nipacks; ++ while ((nip -= 1) >= 0) { /* for each packet */ ++ /* get the fixed header bits */ ++ if (dlthis->strm-> ++ read_buffer(dlthis->strm, &ipacket, IPH_SIZE) != IPH_SIZE) { ++ dload_error(dlthis, E_READSTRM, "image packet"); ++ return false; ++ } ++ /* reorder the header if need be */ ++ if (dlthis->reorder_map) ++ dload_reorder(&ipacket, IPH_SIZE, dlthis->reorder_map); ++ ++ /* Now read the packet image bits. Note: round the size up to ++ * the next multiple of 4 bytes; this is what checksum ++ * routines want. */ ++ ipsize = BYTE_TO_HOST(DOFF_ALIGN(ipacket.i_packet_size)); ++ if (ipsize > BYTE_TO_HOST(IMAGE_PACKET_SIZE)) { ++ dload_error(dlthis, E_ISIZ, ipsize); ++ return false; ++ } ++ if (dlthis->strm->read_buffer ++ (dlthis->strm, dest, ipsize) != ipsize) { ++ dload_error(dlthis, E_READSTRM, "image packet"); ++ return false; ++ } ++ /* reorder the bytes if need be */ ++#if !defined(_BIG_ENDIAN) || (TARGET_AU_BITS > 16) ++ if (dlthis->reorder_map) ++ dload_reorder(dest, ipsize, dlthis->reorder_map); ++ ++ checks = dload_checksum(dest, ipsize); ++#else ++ if (dlthis->dfile_hdr.df_byte_reshuffle != ++ TARGET_ORDER(REORDER_MAP(BYTE_RESHUFFLE_VALUE))) { ++ /* put image bytes in big-endian order, not PC order */ ++ dload_reorder(dest, ipsize, ++ TARGET_ORDER(dlthis->dfile_hdr. ++ df_byte_reshuffle)); ++ } ++#if TARGET_AU_BITS > 8 ++ checks = dload_reverse_checksum_16(dest, ipsize); ++#else ++ checks = dload_reverse_checksum(dest, ipsize); ++#endif ++#endif ++ checks += dload_checksum(&ipacket, IPH_SIZE); ++ ++ /* NYI: unable to handle relocation entries here. Reloc ++ * entries referring to fields that span the packet boundaries ++ * may result in packets of sizes that are not multiple of ++ * 4 bytes. Our checksum implementation works on 32-bit words ++ * only. */ ++ if (ipacket.i_num_relocs != 0) { ++ dload_error(dlthis, E_RELOC, ipsize); ++ return false; ++ } ++ ++ if (~checks) { ++ dload_error(dlthis, E_CHECKSUM, "image packet"); ++ return false; ++ } ++ ++ /*Advance destination ptr by the size of the just-read packet*/ ++ dest += ipsize; ++ } ++ ++ return true; ++} ++ ++/*************************************************************************** ++ * Procedure DLOAD_module_close ++ * ++ * Parameters: ++ * minfo Handle from DLOAD_module_open for this module ++ * ++ * Effect: ++ * Releases any storage associated with the module handle. On return, ++ * the module handle is invalid. ++ * ++ * Returns: ++ * Zero for success. On error, the number of errors detected is returned. ++ * Individual errors are reported using syms->Error_Report(), where syms was ++ * an argument to DLOAD_module_open ++ **************************************************************************/ ++void DLOAD_module_close(DLOAD_module_info minfo) ++{ ++ struct dload_state *dlthis; ++ ++ dlthis = (struct dload_state *)minfo; ++ if (!dlthis) ++ return; ++ ++ if (dlthis->str_head) ++ dlthis->mysym->Deallocate(dlthis->mysym, dlthis->str_head); ++ ++ if (dlthis->sect_hdrs) ++ dlthis->mysym->Deallocate(dlthis->mysym, dlthis->sect_hdrs); ++ ++#if BITS_PER_AU > BITS_PER_BYTE ++ if (dlthis->xstrings) ++ dlthis->mysym->Deallocate(dlthis->mysym, dlthis->xstrings); ++ ++#endif ++ ++ dlthis->mysym->Deallocate(dlthis->mysym, dlthis); ++} +diff --git a/drivers/dsp/bridge/dynload/header.h b/drivers/dsp/bridge/dynload/header.h +new file mode 100644 +index 0000000..0de744b +--- /dev/null ++++ b/drivers/dsp/bridge/dynload/header.h +@@ -0,0 +1,59 @@ ++/* ++ * header.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++ ++#define TRUE 1 ++#define FALSE 0 ++#ifndef NULL ++#define NULL 0 ++#endif ++ ++#include ++#define DL_STRCMP strcmp ++ ++/* maximum parenthesis nesting in relocation stack expressions */ ++#define STATIC_EXPR_STK_SIZE 10 ++ ++#include ++typedef unsigned int uint_least32_t; ++typedef unsigned short int uint_least16_t; ++ ++#include "doff.h" ++#include ++#include "params.h" ++#include "dload_internal.h" ++#include "reloc_table.h" ++ ++/* ++ * Plausibility limits ++ * ++ * These limits are imposed upon the input DOFF file as a check for validity. ++ * They are hard limits, in that the load will fail if they are exceeded. ++ * The numbers selected are arbitrary, in that the loader implementation does ++ * not require these limits. ++ */ ++ ++/* maximum number of bytes in string table */ ++#define MAX_REASONABLE_STRINGTAB (0x100000) ++/* maximum number of code,data,etc. sections */ ++#define MAX_REASONABLE_SECTIONS (200) ++/* maximum number of linker symbols */ ++#define MAX_REASONABLE_SYMBOLS (100000) ++ ++/* shift count to align F_BIG with DLOAD_LITTLE */ ++#define ALIGN_COFF_ENDIANNESS 7 ++#define ENDIANNESS_MASK (DF_BYTE_ORDER >> ALIGN_COFF_ENDIANNESS) +diff --git a/drivers/dsp/bridge/dynload/module_list.h b/drivers/dsp/bridge/dynload/module_list.h +new file mode 100644 +index 0000000..9c4876a +--- /dev/null ++++ b/drivers/dsp/bridge/dynload/module_list.h +@@ -0,0 +1,161 @@ ++/* ++ * dspbridge/mpu_driver/src/dynload/module_list.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/*============================================================================ ++ Filename: module_list.h ++ ++ Copyright (C) 2002 Texas Instruments Incorporated ++ ++ ++ This C header file gives the layout of the data structure created by the ++ dynamic loader to describe the set of modules loaded into the DSP. ++ ++ Linked List Structure: ++ ---------------------- ++ The data structure defined here is a singly-linked list. The list ++ represents the set of modules which are currently loaded in the DSP memory. ++ The first entry in the list is a header record which contains a flag ++ representing the state of the list. The rest of the entries in the list ++ are module records. ++ ++ Global symbol _DLModules designates the first record in the list (i.e. the ++ header record). This symbol must be defined in any program that wishes to ++ use DLLview plug-in. ++ ++ String Representation: ++ ---------------------- ++ The string names of the module and its sections are stored in a block of ++ memory which follows the module record itself. The strings are ordered: ++ module name first, followed by section names in order from the first ++ section to the last. String names are tightly packed arrays of 8-bit ++ characters (two characters per 16-bit word on the C55x). Strings are ++ zero-byte-terminated. ++ ++ Creating and updating the list: ++------------------------------- ++ Upon loading a new module into the DSP memory the dynamic loader inserts a ++new module record as the first module record in the list. The fields of ++ this module record are initialized to reflect the properties of the module. ++ The dynamic loader does NOT increment the flag/counter in the list's header ++ record. ++ ++ Upon unloading a module from the DSP memory the dynamic loader removes the ++module's record from this list. The dynamic loader also increments the ++ flag/counter in the list's header record to indicate that the list has been ++ changed. ++ ++============================================================================*/ ++ ++#ifndef _MODULE_LIST_H_ ++#define _MODULE_LIST_H_ ++ ++#include ++ ++/* Global pointer to the modules_header structure*/ ++#define MODULES_HEADER "_DLModules" ++#define MODULES_HEADER_NO_UNDERSCORE "DLModules" ++ ++/* Initial version number*/ ++#define INIT_VERSION 1 ++ ++/* Verification number -- to be recorded in each module record */ ++#define VERIFICATION 0x79 ++ ++/* forward declarations */ ++struct dll_module; ++struct dll_sect; ++ ++/* the first entry in the list is the modules_header record; ++ * its address is contained in the global _DLModules pointer */ ++struct modules_header { ++ ++ /* Address of the first dll_module record in the list or NULL. ++ Note: for C55x this is a word address (C55x data is word-addressable)*/ ++ u32 first_module; ++ ++ /* Combined storage size (in target addressable units) of the ++ * dll_module record which follows this header record, or zero ++ * if the list is empty. This size includes the module's string table. ++ * Note: for C55x the unit is a 16-bit word */ ++ u16 first_module_size; ++ ++ /* Counter is incremented whenever a module record is removed from ++ * the list */ ++ u16 update_flag; ++ ++} ; ++ ++/* for each 32-bits in above structure, a bitmap, LSB first, whose bits are: ++ * 0 => a 32-bit value, 1 => 2 16-bit values */ ++#define MODULES_HEADER_BITMAP 0x2 /* swapping bitmap for type modules_header */ ++ ++/* information recorded about each section in a module */ ++struct dll_sect { ++ ++ /* Load-time address of the section. ++ * Note: for C55x this is a byte address for program sections, and ++ * a word address for data sections. C55x program memory is ++ * byte-addressable, while data memory is word-addressable. */ ++ u32 sect_load_adr; ++ ++ /* Run-time address of the section. ++ * Note 1: for C55x this is a byte address for program sections, and ++ * a word address for data sections. ++ * Note 2: for C55x two most significant bits of this field indicate ++ * the section type: '00' for a code section, '11' for a data section ++ * (C55 addresses are really only 24-bits wide). */ ++ u32 sect_run_adr; ++ ++} ; ++ ++/* the rest of the entries in the list are module records */ ++struct dll_module { ++ ++ /* Address of the next dll_module record in the list, or 0 if this is ++ * the last record in the list. ++ * Note: for C55x this is a word address (C55x data is ++ * word-addressable) */ ++ u32 next_module; ++ ++ /* Combined storage size (in target addressable units) of the ++ * dll_module record which follows this one, or zero if this is the ++ * last record in the list. This size includes the module's string ++ * table. ++ * Note: for C55x the unit is a 16-bit word. */ ++ u16 next_module_size; ++ ++ /* version number of the tooling; set to INIT_VERSION for Phase 1 */ ++ u16 version; ++ ++ /* the verification word; set to VERIFICATION */ ++ u16 verification; ++ ++ /* Number of sections in the sects array */ ++ u16 num_sects; ++ ++ /* Module's "unique" id; copy of the timestamp from the host ++ * COFF file */ ++ u32 timestamp; ++ ++ /* Array of num_sects elements of the module's section records */ ++ struct dll_sect sects[1]; ++} ; ++ ++/* for each 32 bits in above structure, a bitmap, LSB first, whose bits are: ++ * 0 => a 32-bit value, 1 => 2 16-bit values */ ++#define DLL_MODULE_BITMAP 0x6 /* swapping bitmap for type dll_module */ ++ ++#endif /* _MODULE_LIST_H_ */ +diff --git a/drivers/dsp/bridge/dynload/params.h b/drivers/dsp/bridge/dynload/params.h +new file mode 100644 +index 0000000..ade430d +--- /dev/null ++++ b/drivers/dsp/bridge/dynload/params.h +@@ -0,0 +1,231 @@ ++/* ++ * params.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++ ++/****************************************************************************** ++ * ++ * This file defines host and target properties for all machines ++ * supported by the dynamic loader. To be tedious... ++ * ++ * host == the machine on which the dynamic loader runs ++ * target == the machine that the dynamic loader is loading ++ * ++ * Host and target may or may not be the same, depending upon the particular ++ * use. ++ *****************************************************************************/ ++ ++/****************************************************************************** ++ * ++ * Host Properties ++ * ++ *****************************************************************************/ ++ ++#define BITS_PER_BYTE 8 /* bits in the standard PC/SUN byte */ ++#define LOG_BITS_PER_BYTE 3 /* log base 2 of same */ ++#define BYTE_MASK ((1U<> 16)) ++#define SWAP16BY8(zz) (((zz) << 8) | ((zz) >> 8)) ++ ++/* !! don't be tempted to insert type definitions here; use !! */ ++ ++/****************************************************************************** ++ * ++ * Target Properties ++ * ++ *****************************************************************************/ ++ ++ ++/*--------------------------------------------------------------------------*/ ++/* TMS320C6x Target Specific Parameters (byte-addressable) */ ++/*--------------------------------------------------------------------------*/ ++#if TMS32060 ++#define MEMORG 0x0L /* Size of configured memory */ ++#define MEMSIZE 0x0L /* (full address space) */ ++ ++#define CINIT_ALIGN 8 /* alignment of cinit record in TDATA AUs */ ++#define CINIT_COUNT 4 /* width of count field in TDATA AUs */ ++#define CINIT_ADDRESS 4 /* width of address field in TDATA AUs */ ++#define CINIT_PAGE_BITS 0 /* Number of LSBs of address that ++ * are page number */ ++ ++#define LENIENT_SIGNED_RELEXPS 0 /* DOES SIGNED ALLOW MAX UNSIGNED */ ++ ++#undef TARGET_ENDIANNESS /* may be big or little endian */ ++ ++/* align a target address to a word boundary */ ++#define TARGET_WORD_ALIGN(zz) (((zz) + 0x3) & -0x4) ++#endif ++ ++ ++/*-------------------------------------------------------------------------- ++ * ++ * DEFAULT SETTINGS and DERIVED PROPERTIES ++ * ++ * This section establishes defaults for values not specified above ++ *--------------------------------------------------------------------------*/ ++#ifndef TARGET_AU_BITS ++#define TARGET_AU_BITS 8 /* width of the target addressable unit */ ++#define LOG_TARGET_AU_BITS 3 /* log2 of same */ ++#endif ++ ++#ifndef CINIT_DEFAULT_PAGE ++#define CINIT_DEFAULT_PAGE 0 /* default .cinit page number */ ++#endif ++ ++#ifndef DATA_RUN2LOAD ++#define DATA_RUN2LOAD(zz) (zz) /* translate data run address to load address */ ++#endif ++ ++#ifndef DBG_LIST_PAGE ++#define DBG_LIST_PAGE 0 /* page number for .dllview section */ ++#endif ++ ++#ifndef TARGET_WORD_ALIGN ++/* align a target address to a word boundary */ ++#define TARGET_WORD_ALIGN(zz) (zz) ++#endif ++ ++#ifndef TDATA_TO_TADDR ++#define TDATA_TO_TADDR(zz) (zz) /* target data address to target AU address */ ++#define TADDR_TO_TDATA(zz) (zz) /* target AU address to target data address */ ++#define TDATA_AU_BITS TARGET_AU_BITS /* bits per data AU */ ++#define LOG_TDATA_AU_BITS LOG_TARGET_AU_BITS ++#endif ++ ++/* ++ * ++ * Useful properties and conversions derived from the above ++ * ++ */ ++ ++/* ++ * Conversions between host and target addresses ++ */ ++#if LOG_BITS_PER_AU == LOG_TARGET_AU_BITS ++/* translate target addressable unit to host address */ ++#define TADDR_TO_HOST(x) (x) ++/* translate host address to target addressable unit */ ++#define HOST_TO_TADDR(x) (x) ++#elif LOG_BITS_PER_AU > LOG_TARGET_AU_BITS ++#define TADDR_TO_HOST(x) ((x) >> (LOG_BITS_PER_AU-LOG_TARGET_AU_BITS)) ++#define HOST_TO_TADDR(x) ((x) << (LOG_BITS_PER_AU-LOG_TARGET_AU_BITS)) ++#else ++#define TADDR_TO_HOST(x) ((x) << (LOG_TARGET_AU_BITS-LOG_BITS_PER_AU)) ++#define HOST_TO_TADDR(x) ((x) >> (LOG_TARGET_AU_BITS-LOG_BITS_PER_AU)) ++#endif ++ ++#if LOG_BITS_PER_AU == LOG_TDATA_AU_BITS ++/* translate target addressable unit to host address */ ++#define TDATA_TO_HOST(x) (x) ++/* translate host address to target addressable unit */ ++#define HOST_TO_TDATA(x) (x) ++/* translate host address to target addressable unit, round up */ ++#define HOST_TO_TDATA_ROUND(x) (x) ++/* byte offset to host offset, rounded up for TDATA size */ ++#define BYTE_TO_HOST_TDATA_ROUND(x) BYTE_TO_HOST_ROUND(x) ++#elif LOG_BITS_PER_AU > LOG_TDATA_AU_BITS ++#define TDATA_TO_HOST(x) ((x) >> (LOG_BITS_PER_AU-LOG_TDATA_AU_BITS)) ++#define HOST_TO_TDATA(x) ((x) << (LOG_BITS_PER_AU-LOG_TDATA_AU_BITS)) ++#define HOST_TO_TDATA_ROUND(x) ((x) << (LOG_BITS_PER_AU-LOG_TDATA_AU_BITS)) ++#define BYTE_TO_HOST_TDATA_ROUND(x) BYTE_TO_HOST_ROUND(x) ++#else ++#define TDATA_TO_HOST(x) ((x) << (LOG_TDATA_AU_BITS-LOG_BITS_PER_AU)) ++#define HOST_TO_TDATA(x) ((x) >> (LOG_TDATA_AU_BITS-LOG_BITS_PER_AU)) ++#define HOST_TO_TDATA_ROUND(x) (((x) +\ ++ (1<<(LOG_TDATA_AU_BITS-LOG_BITS_PER_AU))-1) >>\ ++ (LOG_TDATA_AU_BITS-LOG_BITS_PER_AU)) ++#define BYTE_TO_HOST_TDATA_ROUND(x) (BYTE_TO_HOST((x) +\ ++ (1<<(LOG_TDATA_AU_BITS-LOG_BITS_PER_BYTE))-1) &\ ++ -(TDATA_AU_BITS/BITS_PER_AU)) ++#endif ++ ++/* ++ * Input in DOFF format is always expresed in bytes, regardless of loading host ++ * so we wind up converting from bytes to target and host units even when the ++ * host is not a byte machine. ++ */ ++#if LOG_BITS_PER_AU == LOG_BITS_PER_BYTE ++#define BYTE_TO_HOST(x) (x) ++#define BYTE_TO_HOST_ROUND(x) (x) ++#define HOST_TO_BYTE(x) (x) ++#elif LOG_BITS_PER_AU >= LOG_BITS_PER_BYTE ++#define BYTE_TO_HOST(x) ((x) >> (LOG_BITS_PER_AU - LOG_BITS_PER_BYTE)) ++#define BYTE_TO_HOST_ROUND(x) ((x + (BITS_PER_AU/BITS_PER_BYTE-1)) >>\ ++ (LOG_BITS_PER_AU - LOG_BITS_PER_BYTE)) ++#define HOST_TO_BYTE(x) ((x) << (LOG_BITS_PER_AU - LOG_BITS_PER_BYTE)) ++#else ++/* lets not try to deal with sub-8-bit byte machines */ ++#endif ++ ++#if LOG_TARGET_AU_BITS == LOG_BITS_PER_BYTE ++/* translate target addressable unit to byte address */ ++#define TADDR_TO_BYTE(x) (x) ++/* translate byte address to target addressable unit */ ++#define BYTE_TO_TADDR(x) (x) ++#elif LOG_TARGET_AU_BITS > LOG_BITS_PER_BYTE ++#define TADDR_TO_BYTE(x) ((x) << (LOG_TARGET_AU_BITS-LOG_BITS_PER_BYTE)) ++#define BYTE_TO_TADDR(x) ((x) >> (LOG_TARGET_AU_BITS-LOG_BITS_PER_BYTE)) ++#else ++/* lets not try to deal with sub-8-bit byte machines */ ++#endif ++ ++#ifdef _BIG_ENDIAN ++#define HOST_ENDIANNESS 1 ++#else ++#define HOST_ENDIANNESS 0 ++#endif ++ ++#ifdef TARGET_ENDIANNESS ++#define TARGET_ENDIANNESS_DIFFERS(rtend) (HOST_ENDIANNESS^TARGET_ENDIANNESS) ++#elif HOST_ENDIANNESS ++#define TARGET_ENDIANNESS_DIFFERS(rtend) (!(rtend)) ++#else ++#define TARGET_ENDIANNESS_DIFFERS(rtend) (rtend) ++#endif ++ ++/* the unit in which we process target image data */ ++#if TARGET_AU_BITS <= 8 ++typedef u8 TgtAU_t; ++#elif TARGET_AU_BITS <= 16 ++typedef u16 TgtAU_t; ++#else ++typedef u32 TgtAU_t; ++#endif ++ ++/* size of that unit */ ++#if TARGET_AU_BITS < BITS_PER_AU ++#define TGTAU_BITS BITS_PER_AU ++#define LOG_TGTAU_BITS LOG_BITS_PER_AU ++#else ++#define TGTAU_BITS TARGET_AU_BITS ++#define LOG_TGTAU_BITS LOG_TARGET_AU_BITS ++#endif +diff --git a/drivers/dsp/bridge/dynload/reloc.c b/drivers/dsp/bridge/dynload/reloc.c +new file mode 100644 +index 0000000..54e460e +--- /dev/null ++++ b/drivers/dsp/bridge/dynload/reloc.c +@@ -0,0 +1,425 @@ ++/* ++ * reloc.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#include "header.h" ++ ++#if TMS32060 ++/* the magic symbol for the start of BSS */ ++static const char BSSSYMBOL[] = {".bss"}; ++#endif ++ ++#if TMS32060 ++#include "reloc_table_c6000.c" ++#endif ++ ++#if TMS32060 ++/* From coff.h - ignore these relocation operations */ ++#define R_C60ALIGN 0x76 /* C60: Alignment info for compressor */ ++#define R_C60FPHEAD 0x77 /* C60: Explicit assembly directive */ ++#define R_C60NOCMP 0x100 /* C60: Don't compress this code scn */ ++#endif ++ ++/************************************************************************** ++ * Procedure dload_unpack ++ * ++ * Parameters: ++ * data pointer to storage unit containing lowest host address of ++ * image data ++ * fieldsz Size of bit field, 0 < fieldsz <= sizeof(RVALUE)*BITS_PER_AU ++ * offset Offset from LSB, 0 <= offset < BITS_PER_AU ++ * sgn Signedness of the field (ROP_SGN, ROP_UNS, ROP_MAX, ROP_ANY) ++ * ++ * Effect: ++ * Extracts the specified field and returns it. ++ **************************************************************************/ ++RVALUE dload_unpack(struct dload_state *dlthis, TgtAU_t *data, int fieldsz, ++ int offset, unsigned sgn) ++{ ++ register RVALUE objval; ++ register int shift, direction; ++ register TgtAU_t *dp = data; ++ ++ fieldsz -= 1; /* avoid nastiness with 32-bit shift of 32-bit value*/ ++ /* * collect up enough bits to contain the desired field */ ++ if (TARGET_BIG_ENDIAN) { ++ dp += (fieldsz + offset) >> LOG_TGTAU_BITS; ++ direction = -1; ++ } else ++ direction = 1; ++ objval = *dp >> offset; ++ shift = TGTAU_BITS - offset; ++ while (shift <= fieldsz) { ++ dp += direction; ++ objval += (RVALUE)*dp << shift; ++ shift += TGTAU_BITS; ++ } ++ ++ /* * sign or zero extend the value appropriately */ ++ if (sgn == ROP_UNS) ++ objval &= (2 << fieldsz) - 1; ++ else { ++ shift = sizeof(RVALUE) * BITS_PER_AU-1 - fieldsz; ++ objval = (objval << shift) >> shift; ++ } ++ ++ return objval; ++ ++} /* dload_unpack */ ++ ++ ++/************************************************************************** ++ * Procedure dload_repack ++ * ++ * Parameters: ++ * val Value to insert ++ * data Pointer to storage unit containing lowest host address of ++ * image data ++ * fieldsz Size of bit field, 0 < fieldsz <= sizeof(RVALUE)*BITS_PER_AU ++ * offset Offset from LSB, 0 <= offset < BITS_PER_AU ++ * sgn Signedness of the field (ROP_SGN, ROP_UNS, ROP_MAX, ROP_ANY) ++ * ++ * Effect: ++ * Stuffs the specified value in the specified field. Returns 0 for ++ * success ++ * or 1 if the value will not fit in the specified field according to the ++ * specified signedness rule. ++ **************************************************************************/ ++static const unsigned char ovf_limit[] = {1, 2, 2}; ++int dload_repack(struct dload_state *dlthis, RVALUE val, TgtAU_t *data, ++ int fieldsz, int offset, unsigned sgn) ++{ ++ register URVALUE objval, mask; ++ register int shift, direction; ++ register TgtAU_t *dp = data; ++ ++ ++ fieldsz -= 1; /* avoid nastiness with 32-bit shift of 32-bit value */ ++ /* clip the bits */ ++ mask = ((UINT32_C(2) << fieldsz) - 1); ++ objval = (val & mask); ++ /* * store the bits through the specified mask */ ++ if (TARGET_BIG_ENDIAN) { ++ dp += (fieldsz + offset) >> LOG_TGTAU_BITS; ++ direction = -1; ++ } else ++ direction = 1; ++ ++ /* insert LSBs */ ++ *dp = (*dp & ~(mask << offset)) + (objval << offset); ++ shift = TGTAU_BITS-offset; ++ /* align mask and objval with AU boundary */ ++ objval >>= shift; ++ mask >>= shift; ++ ++ while (mask) { ++ dp += direction; ++ *dp = (*dp & ~mask) + objval; ++ objval >>= TGTAU_BITS; ++ mask >>= TGTAU_BITS; ++ } ++ ++ /* ++ * check for overflow ++ */ ++ if (sgn) { ++ unsigned tmp = (val >> fieldsz) + (sgn & 0x1); ++ if (tmp > ovf_limit[sgn-1]) ++ return 1; ++ } ++ return 0; ++ ++} /* dload_repack */ ++ ++/* lookup table for the scaling amount in a C6x instruction */ ++#if TMS32060 ++#define SCALE_BITS 4 /* there are 4 bits in the scale field */ ++#define SCALE_MASK 0x7 /* we really only use the bottom 3 bits */ ++static const u8 C60_Scale[SCALE_MASK+1] = { ++ 1, 0, 0, 0, 1, 1, 2, 2 ++}; ++#endif ++ ++/************************************************************************** ++ * Procedure dload_relocate ++ * ++ * Parameters: ++ * data Pointer to base of image data ++ * rp Pointer to relocation operation ++ * ++ * Effect: ++ * Performs the specified relocation operation ++ **************************************************************************/ ++void dload_relocate(struct dload_state *dlthis, TgtAU_t *data, ++ struct reloc_record_t *rp) ++{ ++ RVALUE val = 0; ++ RVALUE reloc_amt = 0; ++ unsigned int fieldsz = 0; ++ unsigned int offset = 0; ++ unsigned int reloc_info = 0; ++ unsigned int reloc_action = 0; ++ register int rx = 0; ++ RVALUE *stackp = NULL; ++ int top; ++ struct Local_Symbol *svp = NULL; ++#ifdef RFV_SCALE ++ unsigned int scale = 0; ++#endif ++ ++ rx = HASH_FUNC(rp->r_type); ++ while (rop_map1[rx] != rp->r_type) { ++ rx = HASH_L(rop_map2[rx]); ++ if (rx < 0) { ++#if TMS32060 ++ switch (rp->r_type) { ++ case R_C60ALIGN: ++ case R_C60NOCMP: ++ case R_C60FPHEAD: ++ /* Ignore these reloc types and return */ ++ break; ++ default: ++ /* Unknown reloc type, print error and return */ ++ dload_error(dlthis, "Bad coff operator 0x%x", rp->r_type); ++ } ++#else ++ dload_error(dlthis, "Bad coff operator 0x%x", rp->r_type); ++#endif ++ return; ++ } ++ } ++ rx = HASH_I(rop_map2[rx]); ++ if ((rx < (sizeof(rop_action)/sizeof(uint_least16_t))) ++ && (rx < (sizeof(rop_info)/sizeof(uint_least16_t))) && (rx > 0)) { ++ reloc_action = rop_action[rx]; reloc_info = rop_info[rx]; ++ } else { ++ dload_error(dlthis, "Buffer Overflow - Array Index Out of Bounds"); ++ } ++ ++ /* Compute the relocation amount for the referenced symbol, if any */ ++ reloc_amt = rp->r_uval; ++ if (RFV_SYM(reloc_info)) { /* relocation uses a symbol reference */ ++ if ((u32)rp->r_symndx < dlthis->dfile_hdr.df_no_syms) { ++ /* real symbol reference */ ++ svp = &dlthis->local_symtab[rp->r_symndx]; ++ reloc_amt = (RFV_SYM(reloc_info) == ROP_SYMD) ? ++ svp->delta : svp->value; ++ } ++ /* reloc references current section */ ++ else if (rp->r_symndx == -1) ++ reloc_amt = (RFV_SYM(reloc_info) == ROP_SYMD) ? ++ dlthis->delta_runaddr : dlthis->image_secn->run_addr; ++ } /* relocation uses a symbol reference */ ++ /* Handle stack adjustment */ ++ val = 0; ++ top = RFV_STK(reloc_info); ++ if (top) { ++ top += dlthis->relstkidx - RSTK_UOP; ++ if (top >= STATIC_EXPR_STK_SIZE) { ++ dload_error(dlthis, ++ "Expression stack overflow in %s at offset " ++ FMT_UI32, dlthis->image_secn->name, ++ rp->r_vaddr + dlthis->image_offset); ++ return; ++ } ++ val = dlthis->relstk[dlthis->relstkidx]; ++ dlthis->relstkidx = top; ++ stackp = &dlthis->relstk[top]; ++ } ++ /* Derive field position and size, if we need them */ ++ if (reloc_info & ROP_RW) { /* read or write action in our future */ ++ fieldsz = RFV_WIDTH(reloc_action); ++ if (fieldsz) { /* field info from table */ ++ offset = RFV_POSN(reloc_action); ++ if (TARGET_BIG_ENDIAN) ++ /* make sure r_vaddr is the lowest target ++ * address containing bits */ ++ rp->r_vaddr += RFV_BIGOFF(reloc_info); ++ } else { /* field info from relocation op */ ++ fieldsz = rp->r_fieldsz; offset = rp->r_offset; ++ if (TARGET_BIG_ENDIAN) ++ /* make sure r_vaddr is the lowest target ++ address containing bits */ ++ rp->r_vaddr += (rp->r_wordsz - offset - fieldsz) ++ >> LOG_TARGET_AU_BITS; ++ } ++ data = (TgtAU_t *)((char *)data + TADDR_TO_HOST(rp->r_vaddr)); ++ /* compute lowest host location of referenced data */ ++#if BITS_PER_AU > TARGET_AU_BITS ++ /* conversion from target address to host address may lose ++ address bits; add loss to offset */ ++ if (TARGET_BIG_ENDIAN) { ++ offset += -((rp->r_vaddr << LOG_TARGET_AU_BITS) + ++ offset + fieldsz) & ++ (BITS_PER_AU-TARGET_AU_BITS); ++ } else { ++ offset += (rp->r_vaddr << LOG_TARGET_AU_BITS) & ++ (BITS_PER_AU-1); ++ } ++#endif ++#ifdef RFV_SCALE ++ scale = RFV_SCALE(reloc_info); ++#endif ++ } ++ /* read the object value from the current image, if so ordered */ ++ if (reloc_info & ROP_R) { /* relocation reads current image value */ ++ val = dload_unpack(dlthis, data, fieldsz, offset, ++ RFV_SIGN(reloc_info)); ++#ifdef RFV_SCALE ++ val <<= scale; ++#endif ++ } ++ /* perform the necessary arithmetic */ ++ switch (RFV_ACTION(reloc_action)) { /* relocation actions */ ++ case RACT_VAL: ++ break; ++ case RACT_ASGN: ++ val = reloc_amt; ++ break; ++ case RACT_ADD: ++ val += reloc_amt; ++ break; ++ case RACT_PCR: ++ /*----------------------------------------------------------- ++ * Handle special cases of jumping from absolute sections ++ * (special reloc type) or to absolute destination ++ * (symndx == -1). In either case, set the appropriate ++ * relocation amount to 0. ++ *-----------------------------------------------------------*/ ++ if (rp->r_symndx == -1) ++ reloc_amt = 0; ++ val += reloc_amt - dlthis->delta_runaddr; ++ break; ++ case RACT_ADDISP: ++ val += rp->r_disp + reloc_amt; ++ break; ++ case RACT_ASGPC: ++ val = dlthis->image_secn->run_addr + reloc_amt; ++ break; ++ case RACT_PLUS: ++ if (stackp != NULL) ++ val += *stackp; ++ break; ++ case RACT_SUB: ++ if (stackp != NULL) ++ val = *stackp - val; ++ break; ++ case RACT_NEG: ++ val = -val; ++ break; ++ case RACT_MPY: ++ if (stackp != NULL) ++ val *= *stackp; ++ break; ++ case RACT_DIV: ++ if (stackp != NULL) ++ val = *stackp / val; ++ break; ++ case RACT_MOD: ++ if (stackp != NULL) ++ val = *stackp % val; ++ break; ++ case RACT_SR: ++ if (val >= sizeof(RVALUE) * BITS_PER_AU) ++ val = 0; ++ else if (stackp != NULL) ++ val = (URVALUE)*stackp >> val; ++ break; ++ case RACT_ASR: ++ if (val >= sizeof(RVALUE)*BITS_PER_AU) ++ val = sizeof(RVALUE)*BITS_PER_AU - 1; ++ else if (stackp != NULL) ++ val = *stackp >> val; ++ break; ++ case RACT_SL: ++ if (val >= sizeof(RVALUE)*BITS_PER_AU) ++ val = 0; ++ else if (stackp != NULL) ++ val = *stackp << val; ++ break; ++ case RACT_AND: ++ if (stackp != NULL) ++ val &= *stackp; ++ break; ++ case RACT_OR: ++ if (stackp != NULL) ++ val |= *stackp; ++ break; ++ case RACT_XOR: ++ if (stackp != NULL) ++ val ^= *stackp; ++ break; ++ case RACT_NOT: ++ val = ~val; ++ break; ++#if TMS32060 ++ case RACT_C6SECT: ++ /* actually needed address of secn containing symbol */ ++ if (svp != NULL) { ++ if (rp->r_symndx >= 0) ++ if (svp->secnn > 0) ++ reloc_amt = dlthis->ldr_sections ++ [svp->secnn-1].run_addr; ++ } ++ /* !!! FALL THRU !!! */ ++ case RACT_C6BASE: ++ if (dlthis->bss_run_base == 0) { ++ struct dynload_symbol *symp; ++ symp = dlthis->mysym->Find_Matching_Symbol ++ (dlthis->mysym, BSSSYMBOL); ++ /* lookup value of global BSS base */ ++ if (symp) ++ dlthis->bss_run_base = symp->value; ++ else ++ dload_error(dlthis, ++ "Global BSS base referenced in %s offset"\ ++ FMT_UI32 " but not defined", ++ dlthis->image_secn->name, ++ rp->r_vaddr + dlthis->image_offset); ++ } ++ reloc_amt -= dlthis->bss_run_base; ++ /* !!! FALL THRU !!! */ ++ case RACT_C6DSPL: ++ /* scale factor determined by 3 LSBs of field */ ++ scale = C60_Scale[val & SCALE_MASK]; ++ offset += SCALE_BITS; ++ fieldsz -= SCALE_BITS; ++ val >>= SCALE_BITS; /* ignore the scale field hereafter */ ++ val <<= scale; ++ val += reloc_amt; /* do the usual relocation */ ++ if (((1 << scale)-1) & val) ++ dload_error(dlthis, ++ "Unaligned reference in %s offset " FMT_UI32, ++ dlthis->image_secn->name, ++ rp->r_vaddr + dlthis->image_offset); ++ break; ++#endif ++ } /* relocation actions */ ++ /* * Put back result as required */ ++ if (reloc_info & ROP_W) { /* relocation writes image value */ ++#ifdef RFV_SCALE ++ val >>= scale; ++#endif ++ if (dload_repack(dlthis, val, data, fieldsz, offset, ++ RFV_SIGN(reloc_info))) { ++ dload_error(dlthis, "Relocation value " FMT_UI32 ++ " overflows %d bits in %s offset " FMT_UI32, val, ++ fieldsz, dlthis->image_secn->name, ++ dlthis->image_offset + rp->r_vaddr); ++ } ++ } else if (top) ++ *stackp = val; ++} /* reloc_value */ ++ +diff --git a/drivers/dsp/bridge/dynload/reloc_table.h b/drivers/dsp/bridge/dynload/reloc_table.h +new file mode 100644 +index 0000000..6326146 +--- /dev/null ++++ b/drivers/dsp/bridge/dynload/reloc_table.h +@@ -0,0 +1,102 @@ ++/* ++ * reloc_table.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++ ++#ifndef __RELOC_TABLE_H__ ++#define __RELOC_TABLE_H__ ++/* ++ * Table of relocation operator properties ++ */ ++#include ++ ++/* How does this relocation operation access the program image? */ ++#define ROP_N 0 /* does not access image */ ++#define ROP_R 1 /* read from image */ ++#define ROP_W 2 /* write to image */ ++#define ROP_RW 3 /* read from and write to image */ ++ ++/* For program image access, what are the overflow rules for the bit field? */ ++/* Beware! Procedure repack depends on this encoding */ ++#define ROP_ANY 0 /* no overflow ever, just truncate the value */ ++#define ROP_SGN 1 /* signed field */ ++#define ROP_UNS 2 /* unsigned field */ ++#define ROP_MAX 3 /* allow maximum range of either signed or unsigned */ ++ ++/* How does the relocation operation use the symbol reference */ ++#define ROP_IGN 0 /* no symbol is referenced */ ++#define ROP_LIT 0 /* use rp->r_uval literal field */ ++#define ROP_SYM 1 /* symbol value is used in relocation */ ++#define ROP_SYMD 2 /* delta value vs last link is used */ ++ ++/* How does the reloc op use the stack? */ ++#define RSTK_N 0 /* Does not use */ ++#define RSTK_POP 1 /* Does a POP */ ++#define RSTK_UOP 2 /* Unary op, stack position unaffected */ ++#define RSTK_PSH 3 /* Does a push */ ++ ++/* ++ * Computational actions performed by the dynamic loader ++ */ ++enum Dload_Actions { ++ RACT_VAL, /* don't alter the current val (from stack or mem fetch) */ ++ RACT_ASGN, /* set value to reference amount (from symbol reference) */ ++ RACT_ADD, /* add reference to value */ ++ RACT_PCR, /* add reference minus PC delta to value */ ++ RACT_ADDISP, /* add reference plus r_disp */ ++ RACT_ASGPC, /* set value to section address plus reference */ ++ ++ RACT_PLUS, /* stack + */ ++ RACT_SUB, /* stack - */ ++ RACT_NEG, /* stack unary - */ ++ ++ RACT_MPY, /* stack * */ ++ RACT_DIV, /* stack / */ ++ RACT_MOD, /* stack % */ ++ ++ RACT_SR, /* stack unsigned >> */ ++ RACT_ASR, /* stack signed >> */ ++ RACT_SL, /* stack << */ ++ RACT_AND, /* stack & */ ++ RACT_OR, /* stack | */ ++ RACT_XOR, /* stack ^ */ ++ RACT_NOT, /* stack ~ */ ++ RACT_C6SECT, /* for C60 R_SECT op */ ++ RACT_C6BASE, /* for C60 R_BASE op */ ++ RACT_C6DSPL, /* for C60 scaled 15-bit displacement */ ++ RACT_PCR23T /* for ARM Thumb long branch */ ++}; ++ ++/* ++ * macros used to extract values ++ */ ++#define RFV_POSN(aaa) ((aaa) & 0xF) ++#define RFV_WIDTH(aaa) (((aaa) >> 4) & 0x3F) ++#define RFV_ACTION(aaa) ((aaa) >> 10) ++ ++#define RFV_SIGN(iii) (((iii) >> 2) & 0x3) ++#define RFV_SYM(iii) (((iii) >> 4) & 0x3) ++#define RFV_STK(iii) (((iii) >> 6) & 0x3) ++#define RFV_ACCS(iii) ((iii) & 0x3) ++ ++#if (TMS32060) ++#define RFV_SCALE(iii) ((iii) >> 11) ++#define RFV_BIGOFF(iii) (((iii) >> 8) & 0x7) ++#else ++#define RFV_BIGOFF(iii) ((iii) >> 8) ++#endif ++ ++#endif /* __RELOC_TABLE_H__ */ +diff --git a/drivers/dsp/bridge/dynload/reloc_table_c6000.c b/drivers/dsp/bridge/dynload/reloc_table_c6000.c +new file mode 100644 +index 0000000..978834c +--- /dev/null ++++ b/drivers/dsp/bridge/dynload/reloc_table_c6000.c +@@ -0,0 +1,258 @@ ++/* ++ * reloc_table_c6000.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* Tables generated for c6000 */ ++ ++#define HASH_FUNC(zz) (((((zz) + 1) * UINT32_C(1845)) >> 11) & 63) ++#define HASH_L(zz) ((zz) >> 8) ++#define HASH_I(zz) ((zz) & 0xFF) ++ ++static const u16 rop_map1[] = { ++ 0, ++ 1, ++ 2, ++ 20, ++ 4, ++ 5, ++ 6, ++ 15, ++ 80, ++ 81, ++ 82, ++ 83, ++ 84, ++ 85, ++ 86, ++ 87, ++ 17, ++ 18, ++ 19, ++ 21, ++ 16, ++ 16394, ++ 16404, ++ 65535, ++ 65535, ++ 65535, ++ 65535, ++ 65535, ++ 65535, ++ 32, ++ 65535, ++ 65535, ++ 65535, ++ 65535, ++ 65535, ++ 65535, ++ 40, ++ 112, ++ 113, ++ 65535, ++ 16384, ++ 16385, ++ 16386, ++ 16387, ++ 16388, ++ 16389, ++ 16390, ++ 16391, ++ 16392, ++ 16393, ++ 16395, ++ 16396, ++ 16397, ++ 16398, ++ 16399, ++ 16400, ++ 16401, ++ 16402, ++ 16403, ++ 16405, ++ 16406, ++ 65535, ++ 65535, ++ 65535 ++}; ++ ++static const s16 rop_map2[] = { ++ -256, ++ -255, ++ -254, ++ -245, ++ -253, ++ -252, ++ -251, ++ -250, ++ -241, ++ -240, ++ -239, ++ -238, ++ -237, ++ -236, ++ 1813, ++ 5142, ++ -248, ++ -247, ++ 778, ++ -244, ++ -249, ++ -221, ++ -211, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -243, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -242, ++ -233, ++ -232, ++ -1, ++ -231, ++ -230, ++ -229, ++ -228, ++ -227, ++ -226, ++ -225, ++ -224, ++ -223, ++ 5410, ++ -220, ++ -219, ++ -218, ++ -217, ++ -216, ++ -215, ++ -214, ++ -213, ++ 5676, ++ -210, ++ -209, ++ -1, ++ -1, ++ -1 ++}; ++ ++static const u16 rop_action[] = { ++ 2560, ++ 2304, ++ 2304, ++ 2432, ++ 2432, ++ 2560, ++ 2176, ++ 2304, ++ 2560, ++ 3200, ++ 3328, ++ 3584, ++ 3456, ++ 2304, ++ 4208, ++ 20788, ++ 21812, ++ 3415, ++ 3245, ++ 2311, ++ 4359, ++ 19764, ++ 2311, ++ 3191, ++ 3280, ++ 6656, ++ 7680, ++ 8704, ++ 9728, ++ 10752, ++ 11776, ++ 12800, ++ 13824, ++ 14848, ++ 15872, ++ 16896, ++ 17920, ++ 18944, ++ 0, ++ 0, ++ 0, ++ 0, ++ 1536, ++ 1536, ++ 1536, ++ 5632, ++ 512, ++ 0 ++}; ++ ++static const u16 rop_info[] = { ++ 0, ++ 35, ++ 35, ++ 35, ++ 35, ++ 35, ++ 35, ++ 35, ++ 35, ++ 39, ++ 39, ++ 39, ++ 39, ++ 35, ++ 34, ++ 283, ++ 299, ++ 4135, ++ 4391, ++ 291, ++ 33059, ++ 283, ++ 295, ++ 4647, ++ 4135, ++ 64, ++ 64, ++ 128, ++ 64, ++ 64, ++ 64, ++ 64, ++ 64, ++ 64, ++ 64, ++ 64, ++ 64, ++ 128, ++ 201, ++ 197, ++ 74, ++ 70, ++ 208, ++ 196, ++ 200, ++ 192, ++ 192, ++ 66 ++}; +diff --git a/drivers/dsp/bridge/gen/_gt_para.c b/drivers/dsp/bridge/gen/_gt_para.c +new file mode 100644 +index 0000000..181fe41 +--- /dev/null ++++ b/drivers/dsp/bridge/gen/_gt_para.c +@@ -0,0 +1,107 @@ ++/* ++ * _gt_para.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== _gt_para.c ======== ++ * Description: ++ * Configuration parameters for GT. This file is separated from ++ * gt.c so that GT_assert() can reference the error function without ++ * forcing the linker to include all the code for GT_set(), GT_init(), ++ * etc. into a fully bound image. Thus, GT_assert() can be retained in ++ * a program for which GT_?trace() has been compiled out. ++ * ++ *! Revision History: ++ *! ================ ++ *! 24-Feb-2003 vp: Code Review Updates. ++ *! 18-Oct-2002 sb: Ported to Linux platform. ++ *! 03-Jul-2001 rr: Removed kfuncs.h because of build errors. ++ *! 07-Dec-1999 ag: Fxn error now causes a WinCE DebugBreak; ++ *! 30-Aug-1999 ag: Now uses GP_printf for printf and error. ++ *! ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++ ++/* ----------------------------------- Function Prototypes */ ++static void error(char *msg, ...); ++static s32 GT_nop(void); ++ ++/* ----------------------------------- Defines, Data Structures, Typedefs */ ++ ++struct GT_Config _GT_params = { ++ (Fxn) printk, /* printf */ ++ (Fxn) NULL, /* procid */ ++ (Fxn) GT_nop, /* taskid */ ++ (Fxn) error, /* error */ ++}; ++ ++/* ----------------------------------- Globals */ ++struct GT_Config *GT = &_GT_params; ++ ++/* ++ * ======== GT_nop ======== ++ */ ++static s32 GT_nop(void) ++{ ++ return 0; ++} ++ ++/* ++ * ======== error ======== ++ * purpose: ++ * Prints error onto the standard output. ++ */ ++static void error(char *fmt, ...) ++{ ++ s32 arg1, arg2, arg3, arg4, arg5, arg6; ++ ++ va_list va; ++ ++ va_start(va, fmt); ++ ++ arg1 = va_arg(va, s32); ++ arg2 = va_arg(va, s32); ++ arg3 = va_arg(va, s32); ++ arg4 = va_arg(va, s32); ++ arg5 = va_arg(va, s32); ++ arg6 = va_arg(va, s32); ++ ++ va_end(va); ++ ++ printk("ERROR: "); ++ printk(fmt, arg1, arg2, arg3, arg4, arg5, arg6); ++ ++#if defined(DEBUG) || defined(DDSP_DEBUG_PRODUCT) ++ if (in_interrupt()) { ++ printk(KERN_INFO "Not stopping after error since ISR/DPC " ++ "are disabled\n"); ++ } else { ++ set_current_state(TASK_INTERRUPTIBLE); ++ flush_signals(current); ++ schedule(); ++ flush_signals(current); ++ printk(KERN_INFO "Signaled in error function\n"); ++ } ++#endif ++} +diff --git a/drivers/dsp/bridge/gen/gb.c b/drivers/dsp/bridge/gen/gb.c +new file mode 100644 +index 0000000..1d21e97 +--- /dev/null ++++ b/drivers/dsp/bridge/gen/gb.c +@@ -0,0 +1,182 @@ ++/* ++ * gb.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== gb.c ======== ++ * Description: Generic bitmap operations. ++ * ++ *! Revision History ++ *! ================ ++ *! 24-Feb-2003 vp Code review updates. ++ *! 17-Dec-2002 map Fixed GB_minset(), GB_empty(), and GB_full(), ++ *! to ensure only 'len' bits are considered in the map ++ *! 18-Oct-2002 sb Ported to Linux platform. ++ *! 06-Dec-2001 jeh Fixed bug in GB_minclear(). ++ *! ++ */ ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++/* ----------------------------------- This */ ++#include ++#include ++ ++typedef GB_BitNum GB_WordNum; ++ ++struct GB_TMap { ++ GB_BitNum len; ++ GB_WordNum wcnt; ++ u32 *words; ++}; ++ ++/* ++ * ======== GB_clear ======== ++ * purpose: ++ * Clears a bit in the bit map. ++ */ ++ ++void GB_clear(struct GB_TMap *map, GB_BitNum bitn) ++{ ++ u32 mask; ++ ++ mask = 1L << (bitn % BITS_PER_LONG); ++ map->words[bitn / BITS_PER_LONG] &= ~mask; ++} ++ ++/* ++ * ======== GB_create ======== ++ * purpose: ++ * Creates a bit map. ++ */ ++ ++struct GB_TMap *GB_create(GB_BitNum len) ++{ ++ struct GB_TMap *map; ++ GB_WordNum i; ++ map = (struct GB_TMap *)GS_alloc(sizeof(struct GB_TMap)); ++ if (map != NULL) { ++ map->len = len; ++ map->wcnt = len / BITS_PER_LONG + 1; ++ map->words = (u32 *)GS_alloc(map->wcnt * sizeof(u32)); ++ if (map->words != NULL) { ++ for (i = 0; i < map->wcnt; i++) ++ map->words[i] = 0L; ++ ++ } else { ++ GS_frees(map, sizeof(struct GB_TMap)); ++ map = NULL; ++ } ++ } ++ ++ return map; ++} ++ ++/* ++ * ======== GB_delete ======== ++ * purpose: ++ * Frees a bit map. ++ */ ++ ++void GB_delete(struct GB_TMap *map) ++{ ++ GS_frees(map->words, map->wcnt * sizeof(u32)); ++ GS_frees(map, sizeof(struct GB_TMap)); ++} ++ ++/* ++ * ======== GB_findandset ======== ++ * purpose: ++ * Finds a free bit and sets it. ++ */ ++GB_BitNum GB_findandset(struct GB_TMap *map) ++{ ++ GB_BitNum bitn; ++ ++ bitn = GB_minclear(map); ++ ++ if (bitn != GB_NOBITS) ++ GB_set(map, bitn); ++ ++ return bitn; ++} ++ ++/* ++ * ======== GB_minclear ======== ++ * purpose: ++ * returns the location of the first unset bit in the bit map. ++ */ ++GB_BitNum GB_minclear(struct GB_TMap *map) ++{ ++ GB_BitNum bit_location = 0; ++ GB_BitNum bitAcc = 0; ++ GB_WordNum i; ++ GB_BitNum bit; ++ u32 *word; ++ ++ for (word = map->words, i = 0; i < map->wcnt; word++, i++) { ++ if (~*word) { ++ for (bit = 0; bit < BITS_PER_LONG; bit++, bitAcc++) { ++ if (bitAcc == map->len) ++ return GB_NOBITS; ++ ++ if (~*word & (1L << bit)) { ++ bit_location = i * BITS_PER_LONG + bit; ++ return bit_location; ++ } ++ ++ } ++ } else { ++ bitAcc += BITS_PER_LONG; ++ } ++ } ++ ++ return GB_NOBITS; ++} ++ ++/* ++ * ======== GB_set ======== ++ * purpose: ++ * Sets a bit in the bit map. ++ */ ++ ++void GB_set(struct GB_TMap *map, GB_BitNum bitn) ++{ ++ u32 mask; ++ ++ mask = 1L << (bitn % BITS_PER_LONG); ++ map->words[bitn / BITS_PER_LONG] |= mask; ++} ++ ++/* ++ * ======== GB_test ======== ++ * purpose: ++ * Returns true if the bit is set in the specified location. ++ */ ++ ++bool GB_test(struct GB_TMap *map, GB_BitNum bitn) ++{ ++ bool state; ++ u32 mask; ++ u32 word; ++ ++ mask = 1L << (bitn % BITS_PER_LONG); ++ word = map->words[bitn / BITS_PER_LONG]; ++ state = word & mask ? TRUE : FALSE; ++ ++ return state; ++} +diff --git a/drivers/dsp/bridge/gen/gh.c b/drivers/dsp/bridge/gen/gh.c +new file mode 100644 +index 0000000..a20ae16 +--- /dev/null ++++ b/drivers/dsp/bridge/gen/gh.c +@@ -0,0 +1,191 @@ ++/* ++ * gh.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== gh.c ======== ++ */ ++ ++#include ++ ++#include ++ ++#include ++ ++#include ++ ++struct Elem { ++ struct Elem *next; ++ u8 data[1]; ++}; ++ ++struct GH_THashTab { ++ u16 maxBucket; ++ u16 valSize; ++ struct Elem **buckets; ++ u16(*hash) (void *, u16); ++ bool(*match) (void *, void *); ++ void(*delete) (void *); ++}; ++ ++static void Nop(void *p); ++static s32 curInit; ++static void myfree(void *ptr, s32 size); ++ ++/* ++ * ======== GH_create ======== ++ */ ++ ++struct GH_THashTab *GH_create(u16 maxBucket, u16 valSize, ++ u16(*hash)(void *, u16), bool(*match)(void *, void *), ++ void(*delete)(void *)) ++{ ++ struct GH_THashTab *hashTab; ++ u16 i; ++ hashTab = (struct GH_THashTab *)GS_alloc(sizeof(struct GH_THashTab)); ++ if (hashTab == NULL) ++ return NULL; ++ hashTab->maxBucket = maxBucket; ++ hashTab->valSize = valSize; ++ hashTab->hash = hash; ++ hashTab->match = match; ++ hashTab->delete = delete == NULL ? Nop : delete; ++ ++ hashTab->buckets = (struct Elem **) ++ GS_alloc(sizeof(struct Elem *) * maxBucket); ++ if (hashTab->buckets == NULL) { ++ GH_delete(hashTab); ++ return NULL; ++ } ++ ++ for (i = 0; i < maxBucket; i++) ++ hashTab->buckets[i] = NULL; ++ ++ return hashTab; ++} ++ ++/* ++ * ======== GH_delete ======== ++ */ ++void GH_delete(struct GH_THashTab *hashTab) ++{ ++ struct Elem *elem, *next; ++ u16 i; ++ ++ if (hashTab != NULL) { ++ if (hashTab->buckets != NULL) { ++ for (i = 0; i < hashTab->maxBucket; i++) { ++ for (elem = hashTab->buckets[i]; elem != NULL; ++ elem = next) { ++ next = elem->next; ++ (*hashTab->delete) (elem->data); ++ myfree(elem, sizeof(struct Elem) - 1 + ++ hashTab->valSize); ++ } ++ } ++ ++ myfree(hashTab->buckets, sizeof(struct Elem *) ++ * hashTab->maxBucket); ++ } ++ ++ myfree(hashTab, sizeof(struct GH_THashTab)); ++ } ++} ++ ++/* ++ * ======== GH_exit ======== ++ */ ++ ++void GH_exit(void) ++{ ++ if (curInit-- == 1) ++ GS_exit(); ++ ++} ++ ++/* ++ * ======== GH_find ======== ++ */ ++ ++void *GH_find(struct GH_THashTab *hashTab, void *key) ++{ ++ struct Elem *elem; ++ ++ elem = hashTab->buckets[(*hashTab->hash)(key, hashTab->maxBucket)]; ++ ++ for (; elem; elem = elem->next) { ++ if ((*hashTab->match)(key, elem->data)) ++ return elem->data; ++ } ++ ++ return NULL; ++} ++ ++/* ++ * ======== GH_init ======== ++ */ ++ ++void GH_init(void) ++{ ++ if (curInit++ == 0) ++ GS_init(); ++} ++ ++/* ++ * ======== GH_insert ======== ++ */ ++ ++void *GH_insert(struct GH_THashTab *hashTab, void *key, void *value) ++{ ++ struct Elem *elem; ++ u16 i; ++ char *src, *dst; ++ ++ elem = (struct Elem *)GS_alloc(sizeof(struct Elem) - 1 + ++ hashTab->valSize); ++ if (elem != NULL) { ++ ++ dst = (char *)elem->data; ++ src = (char *)value; ++ for (i = 0; i < hashTab->valSize; i++) ++ *dst++ = *src++; ++ ++ i = (*hashTab->hash)(key, hashTab->maxBucket); ++ elem->next = hashTab->buckets[i]; ++ hashTab->buckets[i] = elem; ++ ++ return elem->data; ++ } ++ ++ return NULL; ++} ++ ++/* ++ * ======== Nop ======== ++ */ ++/* ARGSUSED */ ++static void Nop(void *p) ++{ ++ p = p; /* stifle compiler warning */ ++} ++ ++/* ++ * ======== myfree ======== ++ */ ++static void myfree(void *ptr, s32 size) ++{ ++ GS_free(ptr); ++} +diff --git a/drivers/dsp/bridge/gen/gs.c b/drivers/dsp/bridge/gen/gs.c +new file mode 100644 +index 0000000..ef5f923 +--- /dev/null ++++ b/drivers/dsp/bridge/gen/gs.c +@@ -0,0 +1,108 @@ ++/* ++ * gs.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== gs.c ======== ++ * Description: ++ * General storage memory allocator services. ++ * ++ *! Revision History ++ *! ================ ++ *! 29-Sep-1999 ag: Un-commented MEM_Init in GS_init(). ++ *! 14-May-1997 mg: Modified to use new GS API for GS_free() and GS_frees(). ++ *! 06-Nov-1996 gp: Re-commented MEM_Init in GS_init(). GS needs GS_Exit(). ++ *! 21-Oct-1996 db: Un-commented MEM_Init in GS_init(). ++ *! 21-May-1996 mg: Created from original stdlib implementation. ++ */ ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ----------------------------------- Globals */ ++static u32 cumsize; ++ ++/* ++ * ======== GS_alloc ======== ++ * purpose: ++ * Allocates memory of the specified size. ++ */ ++void *GS_alloc(u32 size) ++{ ++ void *p; ++ ++ p = MEM_Calloc(size, MEM_PAGED); ++ if (p == NULL) ++ return NULL; ++ cumsize += size; ++ return p; ++} ++ ++/* ++ * ======== GS_exit ======== ++ * purpose: ++ * Discontinue the usage of the GS module. ++ */ ++void GS_exit(void) ++{ ++ MEM_Exit(); ++} ++ ++/* ++ * ======== GS_free ======== ++ * purpose: ++ * Frees the memory. ++ */ ++void GS_free(void *ptr) ++{ ++ MEM_Free(ptr); ++ /* ack! no size info */ ++ /* cumsize -= size; */ ++} ++ ++/* ++ * ======== GS_frees ======== ++ * purpose: ++ * Frees the memory. ++ */ ++void GS_frees(void *ptr, u32 size) ++{ ++ MEM_Free(ptr); ++ cumsize -= size; ++} ++ ++/* ++ * ======== GS_init ======== ++ * purpose: ++ * Initializes the GS module. ++ */ ++void GS_init(void) ++{ ++ static bool curInit; ++ ++ if (curInit == false) { ++ curInit = true; ++ ++ MEM_Init(); ++ } ++} +diff --git a/drivers/dsp/bridge/gen/gt.c b/drivers/dsp/bridge/gen/gt.c +new file mode 100644 +index 0000000..452d6e6 +--- /dev/null ++++ b/drivers/dsp/bridge/gen/gt.c +@@ -0,0 +1,348 @@ ++/* ++ * gt.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== gt.c ======== ++ * Description: This module implements the trace mechanism for bridge. ++ * ++ *! Revision History ++ *! ================ ++ *! 16-May-1997 dr Changed GT_Config member names to conform to coding ++ *! standards. ++ *! 23-Apr-1997 ge Check for GT->TIDFXN for NULL before calling it. ++ *! 03-Jan-1997 ge Changed GT_Config structure member names to eliminate ++ *! preprocessor confusion with other macros. ++ */ ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++#define GT_WILD '*' ++ ++#define GT_CLEAR '=' ++#define GT_ON '+' ++#define GT_OFF '-' ++ ++enum GT_State { ++ GT_SEP, ++ GT_FIRST, ++ GT_SECOND, ++ GT_OP, ++ GT_DIGITS ++} ; ++ ++#ifdef CONFIG_BRIDGE_DEBUG ++static char *GT_1format = "%s - %d: "; ++static char *GT_2format = "%s - %d(%d): "; ++#endif /* CONFIG_BRIDGE_DEBUG */ ++ ++static unsigned char *GT_tMask[GT_BOUND]; ++ ++static bool curInit; ++static char *separator; ++static unsigned char tabMem[GT_BOUND][sizeof(unsigned char) * GT_BOUND]; ++ ++static void error(char *string); ++static void setMask(s16 index1, s16 index2, char op, unsigned char mask); ++ ++/* ++ * ======== _GT_create ======== ++ * purpose: ++ * Creates GT mask. ++ */ ++void _GT_create(struct GT_Mask *mask, char *modName) ++{ ++ mask->modName = modName; ++ mask->flags = &(GT_tMask[modName[0] - 'A'][modName[1] - 'A']); ++} ++ ++/* ++ * ======== GT_init ======== ++ * purpose: ++ * Initializes GT module. ++ */ ++#ifdef GT_init ++#undef GT_init ++#endif ++void GT_init(void) ++{ ++ register unsigned char index1; ++ register unsigned char index2; ++ ++ if (!curInit) { ++ curInit = true; ++ ++ separator = " ,;/"; ++ ++ for (index1 = 0; index1 < GT_BOUND; index1++) { ++ GT_tMask[index1] = tabMem[index1]; ++ for (index2 = 0; index2 < GT_BOUND; index2++) { ++ /* no tracing */ ++ GT_tMask[index1][index2] = 0x00; ++ } ++ } ++ } ++} ++ ++/* ++ * ======== _GT_set ======== ++ * purpose: ++ * Sets the trace string format. ++ */ ++ ++void _GT_set(char *str) ++{ ++ enum GT_State state; ++ char *sep; ++ s16 index1 = GT_BOUND; /* indicates all values */ ++ s16 index2 = GT_BOUND; /* indicates all values */ ++ char op = GT_CLEAR; ++ bool maskValid; ++ s16 digit; ++ register unsigned char mask = 0x0; /* no tracing */ ++ ++ if (str == NULL) ++ return; ++ ++ maskValid = false; ++ state = GT_SEP; ++ while (*str != '\0') { ++ switch ((s32) state) { ++ case (s32) GT_SEP: ++ maskValid = false; ++ sep = separator; ++ while (*sep != '\0') { ++ if (*str == *sep) { ++ str++; ++ break; ++ } else { ++ sep++; ++ } ++ } ++ if (*sep == '\0') ++ state = GT_FIRST; ++ ++ break; ++ case (s32) GT_FIRST: ++ if (*str == GT_WILD) { ++ /* indicates all values */ ++ index1 = GT_BOUND; ++ /* indicates all values */ ++ index2 = GT_BOUND; ++ state = GT_OP; ++ } else { ++ if (*str >= 'a') ++ index1 = (s16) (*str - 'a'); ++ else ++ index1 = (s16) (*str - 'A'); ++ if ((index1 >= 0) && (index1 < GT_BOUND)) ++ state = GT_SECOND; ++ else ++ state = GT_SEP; ++ } ++ str++; ++ break; ++ case (s32) GT_SECOND: ++ if (*str == GT_WILD) { ++ index2 = GT_BOUND; /* indicates all values */ ++ state = GT_OP; ++ str++; ++ } else { ++ if (*str >= 'a') ++ index2 = (s16) (*str - 'a'); ++ else ++ index2 = (s16) (*str - 'A'); ++ if ((index2 >= 0) && (index2 < GT_BOUND)) { ++ state = GT_OP; ++ str++; ++ } else { ++ state = GT_SEP; ++ } ++ } ++ break; ++ case (s32) GT_OP: ++ op = *str; ++ mask = 0x0; /* no tracing */ ++ switch (op) { ++ case (s32) GT_CLEAR: ++ maskValid = true; ++ case (s32) GT_ON: ++ case (s32) GT_OFF: ++ state = GT_DIGITS; ++ str++; ++ break; ++ default: ++ state = GT_SEP; ++ break; ++ } ++ break; ++ case (s32) GT_DIGITS: ++ digit = (s16) (*str - '0'); ++ if ((digit >= 0) && (digit <= 7)) { ++ mask |= (0x01 << digit); ++ maskValid = true; ++ str++; ++ } else { ++ if (maskValid == true) { ++ setMask(index1, index2, op, mask); ++ maskValid = false; ++ } ++ state = GT_SEP; ++ } ++ break; ++ default: ++ error("illegal trace mask"); ++ break; ++ } ++ } ++ ++ if (maskValid) ++ setMask(index1, index2, op, mask); ++} ++ ++/* ++ * ======== _GT_trace ======== ++ * purpose: ++ * Prints the input string onto standard output ++ */ ++ ++s32 _GT_trace(struct GT_Mask *mask, char *format, ...) ++{ ++ s32 arg1, arg2, arg3, arg4, arg5, arg6; ++ va_list va; ++ ++ va_start(va, format); ++ ++ arg1 = va_arg(va, s32); ++ arg2 = va_arg(va, s32); ++ arg3 = va_arg(va, s32); ++ arg4 = va_arg(va, s32); ++ arg5 = va_arg(va, s32); ++ arg6 = va_arg(va, s32); ++ ++ va_end(va); ++#ifdef DEBUG ++ if (GT->PIDFXN == NULL) { ++ printk(GT_1format, mask->modName, GT->TIDFXN ? ++ (*GT->TIDFXN)() : 0); ++ } else { ++ printk(GT_2format, mask->modName, (*GT->PIDFXN)(), ++ GT->TIDFXN ? (*GT->TIDFXN)() : 0); ++ } ++#endif ++ printk(format, arg1, arg2, arg3, arg4, arg5, arg6); ++ ++ return 0; ++} ++ ++/* ++ * ======== error ======== ++ * purpose: ++ * Prints errors onto the standard output. ++ */ ++static void error(char *string) ++{ ++ printk("GT: %s", string); ++} ++ ++/* ++ * ======== setmask ======== ++ * purpose: ++ * Sets mask for the GT module. ++ */ ++ ++static void setMask(s16 index1, s16 index2, char op, u8 mask) ++{ ++ register s16 index; ++ ++ if (index1 < GT_BOUND) { ++ if (index2 < GT_BOUND) { ++ switch (op) { ++ case (s32) GT_CLEAR: ++ GT_tMask[index1][index2] = mask; ++ break; ++ case (s32) GT_ON: ++ GT_tMask[index1][index2] |= mask; ++ break; ++ case (s32) GT_OFF: ++ GT_tMask[index1][index2] &= ~mask; ++ break; ++ default: ++ error("illegal trace mask"); ++ break; ++ } ++ } else { ++ for (index2--; index2 >= 0; index2--) { ++ switch (op) { ++ case (s32) GT_CLEAR: ++ GT_tMask[index1][index2] = mask; ++ break; ++ case (s32) GT_ON: ++ GT_tMask[index1][index2] |= mask; ++ break; ++ case (s32) GT_OFF: ++ GT_tMask[index1][index2] &= ~mask; ++ break; ++ default: ++ error("illegal trace mask"); ++ break; ++ } ++ } ++ } ++ } else { ++ for (index1--; index1 >= 0; index1--) { ++ if (index2 < GT_BOUND) { ++ switch (op) { ++ case (s32) GT_CLEAR: ++ GT_tMask[index1][index2] = mask; ++ break; ++ case (s32) GT_ON: ++ GT_tMask[index1][index2] |= mask; ++ break; ++ case (s32) GT_OFF: ++ GT_tMask[index1][index2] &= ~mask; ++ break; ++ default: ++ error("illegal trace mask"); ++ break; ++ } ++ } else { ++ index = GT_BOUND; ++ for (index--; index >= 0; index--) { ++ switch (op) { ++ case (s32) GT_CLEAR: ++ GT_tMask[index1][index] = mask; ++ break; ++ case (s32) GT_ON: ++ GT_tMask[index1][index] |= mask; ++ break; ++ case (s32) GT_OFF: ++ GT_tMask[index1][index] &= ++ ~mask; ++ break; ++ default: ++ error("illegal trace mask"); ++ break; ++ } ++ } ++ } ++ } ++ } ++} +diff --git a/drivers/dsp/bridge/gen/uuidutil.c b/drivers/dsp/bridge/gen/uuidutil.c +new file mode 100644 +index 0000000..a45f448 +--- /dev/null ++++ b/drivers/dsp/bridge/gen/uuidutil.c +@@ -0,0 +1,238 @@ ++/* ++ * uuidutil.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== uuidutil.c ======== ++ * Description: ++ * This file contains the implementation of UUID helper functions. ++ * ++ *! Revision History ++ *! ================ ++ *! 23-Feb-2003 vp: Code review updates. ++ *! 18-Oct-2003 vp: Ported to Linux platform. ++ *! 31-Aug-2000 rr: UUID_UuidFromString bug fixed. ++ *! 29-Aug-2000 rr: Modified UUID_UuidFromString. ++ *! 09-Nov-2000 kc: Modified UUID_UuidFromString to simplify implementation. ++ *! 30-Oct-2000 kc: Modified UUID utility module function prefix. ++ *! 10-Aug-2000 kc: Created. ++ *! ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ++ * ======== UUID_UuidToString ======== ++ * Purpose: ++ * Converts a struct DSP_UUID to a string. ++ * Note: snprintf format specifier is: ++ * %[flags] [width] [.precision] [{h | l | I64 | L}]type ++ */ ++void UUID_UuidToString(IN struct DSP_UUID *pUuid, OUT char *pszUuid, ++ IN s32 size) ++{ ++ s32 i; /* return result from snprintf. */ ++ ++ DBC_Require(pUuid && pszUuid); ++ ++ i = snprintf(pszUuid, size, ++ "%.8X_%.4X_%.4X_%.2X%.2X_%.2X%.2X%.2X%.2X%.2X%.2X", ++ pUuid->ulData1, pUuid->usData2, pUuid->usData3, ++ pUuid->ucData4, pUuid->ucData5, pUuid->ucData6[0], ++ pUuid->ucData6[1], pUuid->ucData6[2], pUuid->ucData6[3], ++ pUuid->ucData6[4], pUuid->ucData6[5]); ++ ++ DBC_Ensure(i != -1); ++} ++ ++/* ++ * ======== htoi ======== ++ * Purpose: ++ * Converts a hex value to a decimal integer. ++ */ ++ ++static int htoi(char c) ++{ ++ switch (c) { ++ case '0': ++ return 0; ++ case '1': ++ return 1; ++ case '2': ++ return 2; ++ case '3': ++ return 3; ++ case '4': ++ return 4; ++ case '5': ++ return 5; ++ case '6': ++ return 6; ++ case '7': ++ return 7; ++ case '8': ++ return 8; ++ case '9': ++ return 9; ++ case 'A': ++ return 10; ++ case 'B': ++ return 11; ++ case 'C': ++ return 12; ++ case 'D': ++ return 13; ++ case 'E': ++ return 14; ++ case 'F': ++ return 15; ++ case 'a': ++ return 10; ++ case 'b': ++ return 11; ++ case 'c': ++ return 12; ++ case 'd': ++ return 13; ++ case 'e': ++ return 14; ++ case 'f': ++ return 15; ++ } ++ return 0; ++} ++ ++/* ++ * ======== UUID_UuidFromString ======== ++ * Purpose: ++ * Converts a string to a struct DSP_UUID. ++ */ ++void UUID_UuidFromString(IN char *pszUuid, OUT struct DSP_UUID *pUuid) ++{ ++ char c; ++ s32 i, j; ++ s32 result; ++ char *temp = pszUuid; ++ ++ result = 0; ++ for (i = 0; i < 8; i++) { ++ /* Get first character in string */ ++ c = *temp; ++ ++ /* Increase the results by new value */ ++ result *= 16; ++ result += htoi(c); ++ ++ /* Go to next character in string */ ++ temp++; ++ } ++ pUuid->ulData1 = result; ++ ++ /* Step over underscore */ ++ temp++; ++ ++ result = 0; ++ for (i = 0; i < 4; i++) { ++ /* Get first character in string */ ++ c = *temp; ++ ++ /* Increase the results by new value */ ++ result *= 16; ++ result += htoi(c); ++ ++ /* Go to next character in string */ ++ temp++; ++ } ++ pUuid->usData2 = (u16)result; ++ ++ /* Step over underscore */ ++ temp++; ++ ++ result = 0; ++ for (i = 0; i < 4; i++) { ++ /* Get first character in string */ ++ c = *temp; ++ ++ /* Increase the results by new value */ ++ result *= 16; ++ result += htoi(c); ++ ++ /* Go to next character in string */ ++ temp++; ++ } ++ pUuid->usData3 = (u16)result; ++ ++ /* Step over underscore */ ++ temp++; ++ ++ result = 0; ++ for (i = 0; i < 2; i++) { ++ /* Get first character in string */ ++ c = *temp; ++ ++ /* Increase the results by new value */ ++ result *= 16; ++ result += htoi(c); ++ ++ /* Go to next character in string */ ++ temp++; ++ } ++ pUuid->ucData4 = (u8)result; ++ ++ result = 0; ++ for (i = 0; i < 2; i++) { ++ /* Get first character in string */ ++ c = *temp; ++ ++ /* Increase the results by new value */ ++ result *= 16; ++ result += htoi(c); ++ ++ /* Go to next character in string */ ++ temp++; ++ } ++ pUuid->ucData5 = (u8)result; ++ ++ /* Step over underscore */ ++ temp++; ++ ++ for (j = 0; j < 6; j++) { ++ result = 0; ++ for (i = 0; i < 2; i++) { ++ /* Get first character in string */ ++ c = *temp; ++ ++ /* Increase the results by new value */ ++ result *= 16; ++ result += htoi(c); ++ ++ /* Go to next character in string */ ++ temp++; ++ } ++ pUuid->ucData6[j] = (u8)result; ++ } ++} +diff --git a/drivers/dsp/bridge/hw/EasiGlobal.h b/drivers/dsp/bridge/hw/EasiGlobal.h +new file mode 100644 +index 0000000..b023826 +--- /dev/null ++++ b/drivers/dsp/bridge/hw/EasiGlobal.h +@@ -0,0 +1,42 @@ ++/* ++ * EasiGlobal.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2007 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef __EASIGLOBAL_H ++#define __EASIGLOBAL_H ++#include ++ ++/* ++ * DEFINE: READ_ONLY, WRITE_ONLY & READ_WRITE ++ * ++ * DESCRIPTION: Defines used to describe register types for EASI-checker tests. ++ */ ++ ++#define READ_ONLY 1 ++#define WRITE_ONLY 2 ++#define READ_WRITE 3 ++ ++/* ++ * MACRO: _DEBUG_LEVEL_1_EASI ++ * ++ * DESCRIPTION: A MACRO which can be used to indicate that a particular beach ++ * register access function was called. ++ * ++ * NOTE: We currently dont use this functionality. ++ */ ++#define _DEBUG_LEVEL_1_EASI(easiNum) ((void)0) ++ ++#endif /* __EASIGLOBAL_H */ ++ +diff --git a/drivers/dsp/bridge/hw/GlobalTypes.h b/drivers/dsp/bridge/hw/GlobalTypes.h +new file mode 100644 +index 0000000..9004a37 +--- /dev/null ++++ b/drivers/dsp/bridge/hw/GlobalTypes.h +@@ -0,0 +1,325 @@ ++/* ++ * GlobalTypes.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2007 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== GlobalTypes.h ======== ++ * Description: ++ * Global HW definitions ++ * ++ *! Revision History: ++ *! ================ ++ *! 16 Feb 2003 sb: Initial version ++ */ ++#ifndef __GLOBALTYPES_H ++#define __GLOBALTYPES_H ++ ++/* ++ * Definition: TRUE, FALSE ++ * ++ * DESCRIPTION: Boolean Definitions ++ */ ++#ifndef TRUE ++#define FALSE 0 ++#define TRUE (!(FALSE)) ++#endif ++ ++/* ++ * Definition: NULL ++ * ++ * DESCRIPTION: Invalid pointer ++ */ ++#ifndef NULL ++#define NULL (void *)0 ++#endif ++ ++/* ++ * Definition: RET_CODE_BASE ++ * ++ * DESCRIPTION: Base value for return code offsets ++ */ ++#define RET_CODE_BASE 0 ++ ++/* ++ * Definition: *BIT_OFFSET ++ * ++ * DESCRIPTION: offset in bytes from start of 32-bit word. ++ */ ++#define LOWER_16BIT_OFFSET 0 ++#define UPPER_16BIT_OFFSET 2 ++ ++#define LOWER_8BIT_OFFSET 0 ++#define LOWER_MIDDLE_8BIT_OFFSET 1 ++#define UPPER_MIDDLE_8BIT_OFFSET 2 ++#define UPPER_8BIT_OFFSET 3 ++ ++#define LOWER_8BIT_OF16_OFFSET 0 ++#define UPPER_8BIT_OF16_OFFSET 1 ++ ++/* ++ * Definition: *BIT_SHIFT ++ * ++ * DESCRIPTION: offset in bits from start of 32-bit word. ++ */ ++#define LOWER_16BIT_SHIFT 0 ++#define UPPER_16BIT_SHIFT 16 ++ ++#define LOWER_8BIT_SHIFT 0 ++#define LOWER_MIDDLE_8BIT_SHIFT 8 ++#define UPPER_MIDDLE_8BIT_SHIFT 16 ++#define UPPER_8BIT_SHIFT 24 ++ ++#define LOWER_8BIT_OF16_SHIFT 0 ++#define UPPER_8BIT_OF16_SHIFT 8 ++ ++ ++/* ++ * Definition: LOWER_16BIT_MASK ++ * ++ * DESCRIPTION: 16 bit mask used for inclusion of lower 16 bits i.e. mask out ++ * the upper 16 bits ++ */ ++#define LOWER_16BIT_MASK 0x0000FFFF ++ ++ ++/* ++ * Definition: LOWER_8BIT_MASK ++ * ++ * DESCRIPTION: 8 bit masks used for inclusion of 8 bits i.e. mask out ++ * the upper 16 bits ++ */ ++#define LOWER_8BIT_MASK 0x000000FF ++ ++/* ++ * Definition: RETURN_32BITS_FROM_16LOWER_AND_16UPPER(lower16Bits, upper16Bits) ++ * ++ * DESCRIPTION: Returns a 32 bit value given a 16 bit lower value and a 16 ++ * bit upper value ++ */ ++#define RETURN_32BITS_FROM_16LOWER_AND_16UPPER(lower16Bits,upper16Bits)\ ++ (((((u32)lower16Bits) & LOWER_16BIT_MASK)) | \ ++ (((((u32)upper16Bits) & LOWER_16BIT_MASK) << UPPER_16BIT_SHIFT))) ++ ++/* ++ * Definition: RETURN_16BITS_FROM_8LOWER_AND_8UPPER(lower16Bits, upper16Bits) ++ * ++ * DESCRIPTION: Returns a 16 bit value given a 8 bit lower value and a 8 ++ * bit upper value ++ */ ++#define RETURN_16BITS_FROM_8LOWER_AND_8UPPER(lower8Bits,upper8Bits)\ ++ (((((u32)lower8Bits) & LOWER_8BIT_MASK)) | \ ++ (((((u32)upper8Bits) & LOWER_8BIT_MASK) << UPPER_8BIT_OF16_SHIFT))) ++ ++/* ++ * Definition: RETURN_32BITS_FROM_4_8BIT_VALUES(lower8Bits, lowerMiddle8Bits, ++ * lowerUpper8Bits, upper8Bits) ++ * ++ * DESCRIPTION: Returns a 32 bit value given four 8 bit values ++ */ ++#define RETURN_32BITS_FROM_4_8BIT_VALUES(lower8Bits, lowerMiddle8Bits,\ ++ lowerUpper8Bits, upper8Bits)\ ++ (((((u32)lower8Bits) & LOWER_8BIT_MASK)) | \ ++ (((((u32)lowerMiddle8Bits) & LOWER_8BIT_MASK) <<\ ++ LOWER_MIDDLE_8BIT_SHIFT)) | \ ++ (((((u32)lowerUpper8Bits) & LOWER_8BIT_MASK) <<\ ++ UPPER_MIDDLE_8BIT_SHIFT)) | \ ++ (((((u32)upper8Bits) & LOWER_8BIT_MASK) <<\ ++ UPPER_8BIT_SHIFT))) ++ ++/* ++ * Definition: READ_LOWER_16BITS_OF_32(value32bits) ++ * ++ * DESCRIPTION: Returns a 16 lower bits of 32bit value ++ */ ++#define READ_LOWER_16BITS_OF_32(value32bits)\ ++ ((u16)((u32)(value32bits) & LOWER_16BIT_MASK)) ++ ++/* ++ * Definition: READ_UPPER_16BITS_OF_32(value32bits) ++ * ++ * DESCRIPTION: Returns a 16 lower bits of 32bit value ++ */ ++#define READ_UPPER_16BITS_OF_32(value32bits)\ ++ (((u16)((u32)(value32bits) >> UPPER_16BIT_SHIFT)) &\ ++ LOWER_16BIT_MASK) ++ ++ ++/* ++ * Definition: READ_LOWER_8BITS_OF_32(value32bits) ++ * ++ * DESCRIPTION: Returns a 8 lower bits of 32bit value ++ */ ++#define READ_LOWER_8BITS_OF_32(value32bits)\ ++ ((u8)((u32)(value32bits) & LOWER_8BIT_MASK)) ++ ++/* ++ * Definition: READ_LOWER_MIDDLE_8BITS_OF_32(value32bits) ++ * ++ * DESCRIPTION: Returns a 8 lower middle bits of 32bit value ++ */ ++#define READ_LOWER_MIDDLE_8BITS_OF_32(value32bits)\ ++ (((u8)((u32)(value32bits) >> LOWER_MIDDLE_8BIT_SHIFT)) &\ ++ LOWER_8BIT_MASK) ++ ++/* ++ * Definition: READ_LOWER_MIDDLE_8BITS_OF_32(value32bits) ++ * ++ * DESCRIPTION: Returns a 8 lower middle bits of 32bit value ++ */ ++#define READ_UPPER_MIDDLE_8BITS_OF_32(value32bits)\ ++ (((u8)((u32)(value32bits) >> LOWER_MIDDLE_8BIT_SHIFT)) &\ ++ LOWER_8BIT_MASK) ++ ++/* ++ * Definition: READ_UPPER_8BITS_OF_32(value32bits) ++ * ++ * DESCRIPTION: Returns a 8 upper bits of 32bit value ++ */ ++#define READ_UPPER_8BITS_OF_32(value32bits)\ ++ (((u8)((u32)(value32bits) >> UPPER_8BIT_SHIFT)) & LOWER_8BIT_MASK) ++ ++ ++/* ++ * Definition: READ_LOWER_8BITS_OF_16(value16bits) ++ * ++ * DESCRIPTION: Returns a 8 lower bits of 16bit value ++ */ ++#define READ_LOWER_8BITS_OF_16(value16bits)\ ++ ((u8)((u16)(value16bits) & LOWER_8BIT_MASK)) ++ ++/* ++ * Definition: READ_UPPER_8BITS_OF_16(value32bits) ++ * ++ * DESCRIPTION: Returns a 8 upper bits of 16bit value ++ */ ++#define READ_UPPER_8BITS_OF_16(value16bits)\ ++ (((u8)((u32)(value16bits) >> UPPER_8BIT_SHIFT)) & LOWER_8BIT_MASK) ++ ++ ++ ++/* UWORD16: 16 bit tpyes */ ++ ++ ++/* REG_UWORD8, REG_WORD8: 8 bit register types */ ++typedef volatile unsigned char REG_UWORD8; ++typedef volatile signed char REG_WORD8; ++ ++/* REG_UWORD16, REG_WORD16: 16 bit register types */ ++#ifndef OMAPBRIDGE_TYPES ++typedef volatile unsigned short REG_UWORD16; ++#endif ++typedef volatile short REG_WORD16; ++ ++/* REG_UWORD32, REG_WORD32: 32 bit register types */ ++typedef volatile unsigned long REG_UWORD32; ++ ++/* FLOAT ++ * ++ * Type to be used for floating point calculation. Note that floating point ++ * calculation is very CPU expensive, and you should only use if you ++ * absolutely need this. */ ++ ++ ++/* boolean_t: Boolean Type True, False */ ++/* ReturnCode_t: Return codes to be returned by all library functions */ ++typedef enum ReturnCode_label { ++ RET_OK = 0, ++ RET_FAIL = -1, ++ RET_BAD_NULL_PARAM = -2, ++ RET_PARAM_OUT_OF_RANGE = -3, ++ RET_INVALID_ID = -4, ++ RET_EMPTY = -5, ++ RET_FULL = -6, ++ RET_TIMEOUT = -7, ++ RET_INVALID_OPERATION = -8, ++ ++ /* Add new error codes at end of above list */ ++ ++ RET_NUM_RET_CODES /* this should ALWAYS be LAST entry */ ++} ReturnCode_t, *pReturnCode_t; ++ ++/* MACRO: RD_MEM_8, WR_MEM_8 ++ * ++ * DESCRIPTION: 32 bit memory access macros ++ */ ++#define RD_MEM_8(addr) ((u8)(*((u8 *)(addr)))) ++#define WR_MEM_8(addr, data) (*((u8 *)(addr)) = (u8)(data)) ++ ++/* MACRO: RD_MEM_8_VOLATILE, WR_MEM_8_VOLATILE ++ * ++ * DESCRIPTION: 8 bit register access macros ++ */ ++#define RD_MEM_8_VOLATILE(addr) ((u8)(*((REG_UWORD8 *)(addr)))) ++#define WR_MEM_8_VOLATILE(addr, data) (*((REG_UWORD8 *)(addr)) = (u8)(data)) ++ ++ ++/* ++ * MACRO: RD_MEM_16, WR_MEM_16 ++ * ++ * DESCRIPTION: 16 bit memory access macros ++ */ ++#define RD_MEM_16(addr) ((u16)(*((u16 *)(addr)))) ++#define WR_MEM_16(addr, data) (*((u16 *)(addr)) = (u16)(data)) ++ ++/* ++ * MACRO: RD_MEM_16_VOLATILE, WR_MEM_16_VOLATILE ++ * ++ * DESCRIPTION: 16 bit register access macros ++ */ ++#define RD_MEM_16_VOLATILE(addr) ((u16)(*((REG_UWORD16 *)(addr)))) ++#define WR_MEM_16_VOLATILE(addr, data) (*((REG_UWORD16 *)(addr)) =\ ++ (u16)(data)) ++ ++/* ++ * MACRO: RD_MEM_32, WR_MEM_32 ++ * ++ * DESCRIPTION: 32 bit memory access macros ++ */ ++#define RD_MEM_32(addr) ((u32)(*((u32 *)(addr)))) ++#define WR_MEM_32(addr, data) (*((u32 *)(addr)) = (u32)(data)) ++ ++/* ++ * MACRO: RD_MEM_32_VOLATILE, WR_MEM_32_VOLATILE ++ * ++ * DESCRIPTION: 32 bit register access macros ++ */ ++#define RD_MEM_32_VOLATILE(addr) ((u32)(*((REG_UWORD32 *)(addr)))) ++#define WR_MEM_32_VOLATILE(addr, data) (*((REG_UWORD32 *)(addr)) =\ ++ (u32)(data)) ++ ++/* Not sure if this all belongs here */ ++ ++#define CHECK_RETURN_VALUE(actualValue, expectedValue, returnCodeIfMismatch,\ ++ spyCodeIfMisMatch) ++#define CHECK_RETURN_VALUE_RET(actualValue, expectedValue, returnCodeIfMismatch) ++#define CHECK_RETURN_VALUE_RES(actualValue, expectedValue, spyCodeIfMisMatch) ++#define CHECK_RETURN_VALUE_RET_VOID(actualValue, expectedValue,\ ++ spyCodeIfMisMatch) ++ ++#define CHECK_INPUT_PARAM(actualValue, invalidValue, returnCodeIfMismatch,\ ++ spyCodeIfMisMatch) ++#define CHECK_INPUT_PARAM_NO_SPY(actualValue, invalidValue,\ ++ returnCodeIfMismatch) ++#define CHECK_INPUT_RANGE(actualValue, minValidValue, maxValidValue,\ ++ returnCodeIfMismatch, spyCodeIfMisMatch) ++#define CHECK_INPUT_RANGE_NO_SPY(actualValue, minValidValue, maxValidValue,\ ++ returnCodeIfMismatch) ++#define CHECK_INPUT_RANGE_MIN0(actualValue, maxValidValue,\ ++ returnCodeIfMismatch, spyCodeIfMisMatch) ++#define CHECK_INPUT_RANGE_NO_SPY_MIN0(actualValue, maxValidValue,\ ++ returnCodeIfMismatch) ++ ++#endif /* __GLOBALTYPES_H */ +diff --git a/drivers/dsp/bridge/hw/IPIAccInt.h b/drivers/dsp/bridge/hw/IPIAccInt.h +new file mode 100644 +index 0000000..b88a58c +--- /dev/null ++++ b/drivers/dsp/bridge/hw/IPIAccInt.h +@@ -0,0 +1,41 @@ ++/* ++ * IPIAccInt.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef _IPI_ACC_INT_H ++#define _IPI_ACC_INT_H ++ ++/* Bitfield mask and offset declarations */ ++#define SYSC_IVA2BOOTMOD_OFFSET 0x404 ++#define SYSC_IVA2BOOTADDR_OFFSET 0x400 ++#define SYSC_IVA2BOOTADDR_MASK 0xfffffc00 ++ ++ ++/* The following represent the enumerated values for each bitfield */ ++ ++enum IPIIPI_SYSCONFIGAutoIdleE { ++ IPIIPI_SYSCONFIGAutoIdleclkfree = 0x0000, ++ IPIIPI_SYSCONFIGAutoIdleautoclkgate = 0x0001 ++} ; ++ ++enum IPIIPI_ENTRYElemSizeValueE { ++ IPIIPI_ENTRYElemSizeValueElemSz8b = 0x0000, ++ IPIIPI_ENTRYElemSizeValueElemSz16b = 0x0001, ++ IPIIPI_ENTRYElemSizeValueElemSz32b = 0x0002, ++ IPIIPI_ENTRYElemSizeValueReserved = 0x0003 ++} ; ++ ++#endif /* _IPI_ACC_INT_H */ ++/* EOF */ +diff --git a/drivers/dsp/bridge/hw/IVA2RegAcM.h b/drivers/dsp/bridge/hw/IVA2RegAcM.h +new file mode 100644 +index 0000000..6c1e300 +--- /dev/null ++++ b/drivers/dsp/bridge/hw/IVA2RegAcM.h +@@ -0,0 +1,28 @@ ++/* ++ * IVA1RegAcM.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++ ++#ifndef _IVA2_REG_ACM_H ++#define _IVA2_REG_ACM_H ++ ++#include ++#include ++ ++#define SYSC_IVA2BOOTMOD_OFFSET 0x404 ++#define SYSC_IVA2BOOTADDR_OFFSET 0x400 ++ ++#endif +diff --git a/drivers/dsp/bridge/hw/MLBAccInt.h b/drivers/dsp/bridge/hw/MLBAccInt.h +new file mode 100644 +index 0000000..7a03c6a +--- /dev/null ++++ b/drivers/dsp/bridge/hw/MLBAccInt.h +@@ -0,0 +1,132 @@ ++/* ++ * MLBAccInt.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2007 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++#ifndef _MLB_ACC_INT_H ++#define _MLB_ACC_INT_H ++ ++/* Mappings of level 1 EASI function numbers to function names */ ++ ++#define EASIL1_MLBMAILBOX_SYSCONFIGReadRegister32 (MLB_BASE_EASIL1 + 3) ++#define EASIL1_MLBMAILBOX_SYSCONFIGWriteRegister32 (MLB_BASE_EASIL1 + 4) ++#define EASIL1_MLBMAILBOX_SYSCONFIGSIdleModeRead32 (MLB_BASE_EASIL1 + 7) ++#define EASIL1_MLBMAILBOX_SYSCONFIGSIdleModeWrite32 (MLB_BASE_EASIL1 + 17) ++#define EASIL1_MLBMAILBOX_SYSCONFIGSoftResetWrite32 (MLB_BASE_EASIL1 + 29) ++#define EASIL1_MLBMAILBOX_SYSCONFIGAutoIdleRead32 \ ++ (MLB_BASE_EASIL1 + 33) ++#define EASIL1_MLBMAILBOX_SYSCONFIGAutoIdleWrite32 (MLB_BASE_EASIL1 + 39) ++#define EASIL1_MLBMAILBOX_SYSSTATUSResetDoneRead32 (MLB_BASE_EASIL1 + 44) ++#define EASIL1_MLBMAILBOX_MESSAGE___0_15ReadRegister32 \ ++ (MLB_BASE_EASIL1 + 50) ++#define EASIL1_MLBMAILBOX_MESSAGE___0_15WriteRegister32 \ ++ (MLB_BASE_EASIL1 + 51) ++#define EASIL1_MLBMAILBOX_FIFOSTATUS___0_15ReadRegister32 \ ++ (MLB_BASE_EASIL1 + 56) ++#define EASIL1_MLBMAILBOX_FIFOSTATUS___0_15FifoFullMBmRead32 \ ++ (MLB_BASE_EASIL1 + 57) ++#define EASIL1_MLBMAILBOX_MSGSTATUS___0_15NbOfMsgMBmRead32 \ ++ (MLB_BASE_EASIL1 + 60) ++#define EASIL1_MLBMAILBOX_IRQSTATUS___0_3ReadRegister32 \ ++ (MLB_BASE_EASIL1 + 62) ++#define EASIL1_MLBMAILBOX_IRQSTATUS___0_3WriteRegister32 \ ++ (MLB_BASE_EASIL1 + 63) ++#define EASIL1_MLBMAILBOX_IRQENABLE___0_3ReadRegister32 \ ++ (MLB_BASE_EASIL1 + 192) ++#define EASIL1_MLBMAILBOX_IRQENABLE___0_3WriteRegister32 \ ++ (MLB_BASE_EASIL1 + 193) ++ ++/* Register set MAILBOX_MESSAGE___REGSET_0_15 address offset, bank address ++ * increment and number of banks */ ++ ++#define MLB_MAILBOX_MESSAGE___REGSET_0_15_OFFSET (u32)(0x0040) ++#define MLB_MAILBOX_MESSAGE___REGSET_0_15_STEP (u32)(0x0004) ++ ++/* Register offset address definitions relative to register set ++ * MAILBOX_MESSAGE___REGSET_0_15 */ ++ ++#define MLB_MAILBOX_MESSAGE___0_15_OFFSET (u32)(0x0) ++ ++ ++/* Register set MAILBOX_FIFOSTATUS___REGSET_0_15 address offset, bank address ++ * increment and number of banks */ ++ ++#define MLB_MAILBOX_FIFOSTATUS___REGSET_0_15_OFFSET (u32)(0x0080) ++#define MLB_MAILBOX_FIFOSTATUS___REGSET_0_15_STEP (u32)(0x0004) ++ ++/* Register offset address definitions relative to register set ++ * MAILBOX_FIFOSTATUS___REGSET_0_15 */ ++ ++#define MLB_MAILBOX_FIFOSTATUS___0_15_OFFSET (u32)(0x0) ++ ++ ++/* Register set MAILBOX_MSGSTATUS___REGSET_0_15 address offset, bank address ++ * increment and number of banks */ ++ ++#define MLB_MAILBOX_MSGSTATUS___REGSET_0_15_OFFSET (u32)(0x00c0) ++#define MLB_MAILBOX_MSGSTATUS___REGSET_0_15_STEP (u32)(0x0004) ++ ++/* Register offset address definitions relative to register set ++ * MAILBOX_MSGSTATUS___REGSET_0_15 */ ++ ++#define MLB_MAILBOX_MSGSTATUS___0_15_OFFSET (u32)(0x0) ++ ++ ++/* Register set MAILBOX_IRQSTATUS___REGSET_0_3 address offset, bank address ++ * increment and number of banks */ ++ ++#define MLB_MAILBOX_IRQSTATUS___REGSET_0_3_OFFSET (u32)(0x0100) ++#define MLB_MAILBOX_IRQSTATUS___REGSET_0_3_STEP (u32)(0x0008) ++ ++/* Register offset address definitions relative to register set ++ * MAILBOX_IRQSTATUS___REGSET_0_3 */ ++ ++#define MLB_MAILBOX_IRQSTATUS___0_3_OFFSET (u32)(0x0) ++ ++ ++/* Register set MAILBOX_IRQENABLE___REGSET_0_3 address offset, bank address ++ * increment and number of banks */ ++ ++#define MLB_MAILBOX_IRQENABLE___REGSET_0_3_OFFSET (u32)(0x0104) ++#define MLB_MAILBOX_IRQENABLE___REGSET_0_3_STEP (u32)(0x0008) ++ ++/* Register offset address definitions relative to register set ++ * MAILBOX_IRQENABLE___REGSET_0_3 */ ++ ++#define MLB_MAILBOX_IRQENABLE___0_3_OFFSET (u32)(0x0) ++ ++ ++/* Register offset address definitions */ ++ ++#define MLB_MAILBOX_SYSCONFIG_OFFSET (u32)(0x10) ++#define MLB_MAILBOX_SYSSTATUS_OFFSET (u32)(0x14) ++ ++ ++/* Bitfield mask and offset declarations */ ++ ++#define MLB_MAILBOX_SYSCONFIG_SIdleMode_MASK (u32)(0x18) ++#define MLB_MAILBOX_SYSCONFIG_SIdleMode_OFFSET (u32)(3) ++#define MLB_MAILBOX_SYSCONFIG_SoftReset_MASK (u32)(0x2) ++#define MLB_MAILBOX_SYSCONFIG_SoftReset_OFFSET (u32)(1) ++#define MLB_MAILBOX_SYSCONFIG_AutoIdle_MASK (u32)(0x1) ++#define MLB_MAILBOX_SYSCONFIG_AutoIdle_OFFSET (u32)(0) ++#define MLB_MAILBOX_SYSSTATUS_ResetDone_MASK (u32)(0x1) ++#define MLB_MAILBOX_SYSSTATUS_ResetDone_OFFSET (u32)(0) ++#define MLB_MAILBOX_FIFOSTATUS___0_15_FifoFullMBm_MASK (u32)(0x1) ++#define MLB_MAILBOX_FIFOSTATUS___0_15_FifoFullMBm_OFFSET (u32)(0) ++#define MLB_MAILBOX_MSGSTATUS___0_15_NbOfMsgMBm_MASK (u32)(0x7f) ++#define MLB_MAILBOX_MSGSTATUS___0_15_NbOfMsgMBm_OFFSET (u32)(0) ++ ++#endif /* _MLB_ACC_INT_H */ +diff --git a/drivers/dsp/bridge/hw/MLBRegAcM.h b/drivers/dsp/bridge/hw/MLBRegAcM.h +new file mode 100644 +index 0000000..747a2e1 +--- /dev/null ++++ b/drivers/dsp/bridge/hw/MLBRegAcM.h +@@ -0,0 +1,200 @@ ++/* ++ * MLBRegAcM.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2007 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef _MLB_REG_ACM_H ++#define _MLB_REG_ACM_H ++ ++#include ++#include ++#include "MLBAccInt.h" ++ ++#if defined(USE_LEVEL_1_MACROS) ++ ++#define MLBMAILBOX_SYSCONFIGReadRegister32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MLBMAILBOX_SYSCONFIGReadRegister32),\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+ \ ++ MLB_MAILBOX_SYSCONFIG_OFFSET)) ++ ++ ++#define MLBMAILBOX_SYSCONFIGWriteRegister32(baseAddress, value)\ ++{\ ++ const u32 offset = MLB_MAILBOX_SYSCONFIG_OFFSET;\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MLBMAILBOX_SYSCONFIGWriteRegister32);\ ++ WR_MEM_32_VOLATILE(((u32)(baseAddress))+offset, newValue);\ ++} ++ ++ ++#define MLBMAILBOX_SYSCONFIGSIdleModeRead32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MLBMAILBOX_SYSCONFIGSIdleModeRead32),\ ++ (((RD_MEM_32_VOLATILE((((u32)(baseAddress))+\ ++ (MLB_MAILBOX_SYSCONFIG_OFFSET)))) &\ ++ MLB_MAILBOX_SYSCONFIG_SIdleMode_MASK) >>\ ++ MLB_MAILBOX_SYSCONFIG_SIdleMode_OFFSET)) ++ ++ ++#define MLBMAILBOX_SYSCONFIGSIdleModeWrite32(baseAddress, value)\ ++{\ ++ const u32 offset = MLB_MAILBOX_SYSCONFIG_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE(((u32)(baseAddress)) +\ ++ offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MLBMAILBOX_SYSCONFIGSIdleModeWrite32);\ ++ data &= ~(MLB_MAILBOX_SYSCONFIG_SIdleMode_MASK);\ ++ newValue <<= MLB_MAILBOX_SYSCONFIG_SIdleMode_OFFSET;\ ++ newValue &= MLB_MAILBOX_SYSCONFIG_SIdleMode_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define MLBMAILBOX_SYSCONFIGSoftResetWrite32(baseAddress, value)\ ++{\ ++ const u32 offset = MLB_MAILBOX_SYSCONFIG_OFFSET;\ ++ register u32 data =\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MLBMAILBOX_SYSCONFIGSoftResetWrite32);\ ++ data &= ~(MLB_MAILBOX_SYSCONFIG_SoftReset_MASK);\ ++ newValue <<= MLB_MAILBOX_SYSCONFIG_SoftReset_OFFSET;\ ++ newValue &= MLB_MAILBOX_SYSCONFIG_SoftReset_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define MLBMAILBOX_SYSCONFIGAutoIdleRead32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MLBMAILBOX_SYSCONFIGAutoIdleRead32),\ ++ (((RD_MEM_32_VOLATILE((((u32)(baseAddress))+\ ++ (MLB_MAILBOX_SYSCONFIG_OFFSET)))) &\ ++ MLB_MAILBOX_SYSCONFIG_AutoIdle_MASK) >>\ ++ MLB_MAILBOX_SYSCONFIG_AutoIdle_OFFSET)) ++ ++ ++#define MLBMAILBOX_SYSCONFIGAutoIdleWrite32(baseAddress, value)\ ++{\ ++ const u32 offset = MLB_MAILBOX_SYSCONFIG_OFFSET;\ ++ register u32 data =\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MLBMAILBOX_SYSCONFIGAutoIdleWrite32);\ ++ data &= ~(MLB_MAILBOX_SYSCONFIG_AutoIdle_MASK);\ ++ newValue <<= MLB_MAILBOX_SYSCONFIG_AutoIdle_OFFSET;\ ++ newValue &= MLB_MAILBOX_SYSCONFIG_AutoIdle_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define MLBMAILBOX_SYSSTATUSResetDoneRead32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MLBMAILBOX_SYSSTATUSResetDoneRead32),\ ++ (((RD_MEM_32_VOLATILE((((u32)(baseAddress))+\ ++ (MLB_MAILBOX_SYSSTATUS_OFFSET)))) &\ ++ MLB_MAILBOX_SYSSTATUS_ResetDone_MASK) >>\ ++ MLB_MAILBOX_SYSSTATUS_ResetDone_OFFSET)) ++ ++ ++#define MLBMAILBOX_MESSAGE___0_15ReadRegister32(baseAddress, bank)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MLBMAILBOX_MESSAGE___0_15ReadRegister32),\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+\ ++ (MLB_MAILBOX_MESSAGE___REGSET_0_15_OFFSET +\ ++ MLB_MAILBOX_MESSAGE___0_15_OFFSET+(\ ++ (bank)*MLB_MAILBOX_MESSAGE___REGSET_0_15_STEP)))) ++ ++ ++#define MLBMAILBOX_MESSAGE___0_15WriteRegister32(baseAddress, bank, value)\ ++{\ ++ const u32 offset = MLB_MAILBOX_MESSAGE___REGSET_0_15_OFFSET +\ ++ MLB_MAILBOX_MESSAGE___0_15_OFFSET +\ ++ ((bank)*MLB_MAILBOX_MESSAGE___REGSET_0_15_STEP);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MLBMAILBOX_MESSAGE___0_15WriteRegister32);\ ++ WR_MEM_32_VOLATILE(((u32)(baseAddress))+offset, newValue);\ ++} ++ ++ ++#define MLBMAILBOX_FIFOSTATUS___0_15ReadRegister32(baseAddress, bank)\ ++ (_DEBUG_LEVEL_1_EASI(\ ++ EASIL1_MLBMAILBOX_FIFOSTATUS___0_15ReadRegister32),\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+\ ++ (MLB_MAILBOX_FIFOSTATUS___REGSET_0_15_OFFSET +\ ++ MLB_MAILBOX_FIFOSTATUS___0_15_OFFSET+\ ++ ((bank)*MLB_MAILBOX_FIFOSTATUS___REGSET_0_15_STEP)))) ++ ++ ++#define MLBMAILBOX_FIFOSTATUS___0_15FifoFullMBmRead32(baseAddress, bank)\ ++ (_DEBUG_LEVEL_1_EASI(\ ++ EASIL1_MLBMAILBOX_FIFOSTATUS___0_15FifoFullMBmRead32),\ ++ (((RD_MEM_32_VOLATILE(((u32)(baseAddress))+\ ++ (MLB_MAILBOX_FIFOSTATUS___REGSET_0_15_OFFSET +\ ++ MLB_MAILBOX_FIFOSTATUS___0_15_OFFSET+\ ++ ((bank)*MLB_MAILBOX_FIFOSTATUS___REGSET_0_15_STEP)))) &\ ++ MLB_MAILBOX_FIFOSTATUS___0_15_FifoFullMBm_MASK) >>\ ++ MLB_MAILBOX_FIFOSTATUS___0_15_FifoFullMBm_OFFSET)) ++ ++ ++#define MLBMAILBOX_MSGSTATUS___0_15NbOfMsgMBmRead32(baseAddress, bank)\ ++ (_DEBUG_LEVEL_1_EASI(\ ++ EASIL1_MLBMAILBOX_MSGSTATUS___0_15NbOfMsgMBmRead32),\ ++ (((RD_MEM_32_VOLATILE(((u32)(baseAddress))+\ ++ (MLB_MAILBOX_MSGSTATUS___REGSET_0_15_OFFSET +\ ++ MLB_MAILBOX_MSGSTATUS___0_15_OFFSET+\ ++ ((bank)*MLB_MAILBOX_MSGSTATUS___REGSET_0_15_STEP)))) &\ ++ MLB_MAILBOX_MSGSTATUS___0_15_NbOfMsgMBm_MASK) >>\ ++ MLB_MAILBOX_MSGSTATUS___0_15_NbOfMsgMBm_OFFSET)) ++ ++ ++#define MLBMAILBOX_IRQSTATUS___0_3ReadRegister32(baseAddress, bank)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MLBMAILBOX_IRQSTATUS___0_3ReadRegister32),\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+\ ++ (MLB_MAILBOX_IRQSTATUS___REGSET_0_3_OFFSET +\ ++ MLB_MAILBOX_IRQSTATUS___0_3_OFFSET+\ ++ ((bank)*MLB_MAILBOX_IRQSTATUS___REGSET_0_3_STEP)))) ++ ++ ++#define MLBMAILBOX_IRQSTATUS___0_3WriteRegister32(baseAddress, bank, value)\ ++{\ ++ const u32 offset = MLB_MAILBOX_IRQSTATUS___REGSET_0_3_OFFSET +\ ++ MLB_MAILBOX_IRQSTATUS___0_3_OFFSET +\ ++ ((bank)*MLB_MAILBOX_IRQSTATUS___REGSET_0_3_STEP);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MLBMAILBOX_IRQSTATUS___0_3WriteRegister32);\ ++ WR_MEM_32_VOLATILE(((u32)(baseAddress))+offset, newValue);\ ++} ++ ++ ++#define MLBMAILBOX_IRQENABLE___0_3ReadRegister32(baseAddress, bank)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MLBMAILBOX_IRQENABLE___0_3ReadRegister32),\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+\ ++ (MLB_MAILBOX_IRQENABLE___REGSET_0_3_OFFSET +\ ++ MLB_MAILBOX_IRQENABLE___0_3_OFFSET+\ ++ ((bank)*MLB_MAILBOX_IRQENABLE___REGSET_0_3_STEP)))) ++ ++ ++#define MLBMAILBOX_IRQENABLE___0_3WriteRegister32(baseAddress, bank, value)\ ++{\ ++ const u32 offset = MLB_MAILBOX_IRQENABLE___REGSET_0_3_OFFSET +\ ++ MLB_MAILBOX_IRQENABLE___0_3_OFFSET +\ ++ ((bank)*MLB_MAILBOX_IRQENABLE___REGSET_0_3_STEP);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MLBMAILBOX_IRQENABLE___0_3WriteRegister32);\ ++ WR_MEM_32_VOLATILE(((u32)(baseAddress))+offset, newValue);\ ++} ++ ++ ++#endif /* USE_LEVEL_1_MACROS */ ++ ++#endif /* _MLB_REG_ACM_H */ +diff --git a/drivers/dsp/bridge/hw/MMUAccInt.h b/drivers/dsp/bridge/hw/MMUAccInt.h +new file mode 100644 +index 0000000..6ca1573 +--- /dev/null ++++ b/drivers/dsp/bridge/hw/MMUAccInt.h +@@ -0,0 +1,76 @@ ++/* ++ * MMUAccInt.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2007 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef _MMU_ACC_INT_H ++#define _MMU_ACC_INT_H ++ ++/* Mappings of level 1 EASI function numbers to function names */ ++ ++#define EASIL1_MMUMMU_SYSCONFIGReadRegister32 (MMU_BASE_EASIL1 + 3) ++#define EASIL1_MMUMMU_SYSCONFIGIdleModeWrite32 (MMU_BASE_EASIL1 + 17) ++#define EASIL1_MMUMMU_SYSCONFIGAutoIdleWrite32 (MMU_BASE_EASIL1 + 39) ++#define EASIL1_MMUMMU_IRQSTATUSWriteRegister32 (MMU_BASE_EASIL1 + 51) ++#define EASIL1_MMUMMU_IRQENABLEReadRegister32 (MMU_BASE_EASIL1 + 102) ++#define EASIL1_MMUMMU_IRQENABLEWriteRegister32 (MMU_BASE_EASIL1 + 103) ++#define EASIL1_MMUMMU_WALKING_STTWLRunningRead32 (MMU_BASE_EASIL1 + 156) ++#define EASIL1_MMUMMU_CNTLTWLEnableRead32 (MMU_BASE_EASIL1 + 174) ++#define EASIL1_MMUMMU_CNTLTWLEnableWrite32 (MMU_BASE_EASIL1 + 180) ++#define EASIL1_MMUMMU_CNTLMMUEnableWrite32 (MMU_BASE_EASIL1 + 190) ++#define EASIL1_MMUMMU_FAULT_ADReadRegister32 (MMU_BASE_EASIL1 + 194) ++#define EASIL1_MMUMMU_TTBWriteRegister32 (MMU_BASE_EASIL1 + 198) ++#define EASIL1_MMUMMU_LOCKReadRegister32 (MMU_BASE_EASIL1 + 203) ++#define EASIL1_MMUMMU_LOCKWriteRegister32 (MMU_BASE_EASIL1 + 204) ++#define EASIL1_MMUMMU_LOCKBaseValueRead32 (MMU_BASE_EASIL1 + 205) ++#define EASIL1_MMUMMU_LOCKCurrentVictimRead32 (MMU_BASE_EASIL1 + 209) ++#define EASIL1_MMUMMU_LOCKCurrentVictimWrite32 (MMU_BASE_EASIL1 + 211) ++#define EASIL1_MMUMMU_LOCKCurrentVictimSet32 (MMU_BASE_EASIL1 + 212) ++#define EASIL1_MMUMMU_LD_TLBReadRegister32 (MMU_BASE_EASIL1 + 213) ++#define EASIL1_MMUMMU_LD_TLBWriteRegister32 (MMU_BASE_EASIL1 + 214) ++#define EASIL1_MMUMMU_CAMWriteRegister32 (MMU_BASE_EASIL1 + 226) ++#define EASIL1_MMUMMU_RAMWriteRegister32 (MMU_BASE_EASIL1 + 268) ++#define EASIL1_MMUMMU_FLUSH_ENTRYWriteRegister32 (MMU_BASE_EASIL1 + 322) ++ ++/* Register offset address definitions */ ++#define MMU_MMU_SYSCONFIG_OFFSET 0x10 ++#define MMU_MMU_IRQSTATUS_OFFSET 0x18 ++#define MMU_MMU_IRQENABLE_OFFSET 0x1c ++#define MMU_MMU_WALKING_ST_OFFSET 0x40 ++#define MMU_MMU_CNTL_OFFSET 0x44 ++#define MMU_MMU_FAULT_AD_OFFSET 0x48 ++#define MMU_MMU_TTB_OFFSET 0x4c ++#define MMU_MMU_LOCK_OFFSET 0x50 ++#define MMU_MMU_LD_TLB_OFFSET 0x54 ++#define MMU_MMU_CAM_OFFSET 0x58 ++#define MMU_MMU_RAM_OFFSET 0x5c ++#define MMU_MMU_GFLUSH_OFFSET 0x60 ++#define MMU_MMU_FLUSH_ENTRY_OFFSET 0x64 ++/* Bitfield mask and offset declarations */ ++#define MMU_MMU_SYSCONFIG_IdleMode_MASK 0x18 ++#define MMU_MMU_SYSCONFIG_IdleMode_OFFSET 3 ++#define MMU_MMU_SYSCONFIG_AutoIdle_MASK 0x1 ++#define MMU_MMU_SYSCONFIG_AutoIdle_OFFSET 0 ++#define MMU_MMU_WALKING_ST_TWLRunning_MASK 0x1 ++#define MMU_MMU_WALKING_ST_TWLRunning_OFFSET 0 ++#define MMU_MMU_CNTL_TWLEnable_MASK 0x4 ++#define MMU_MMU_CNTL_TWLEnable_OFFSET 2 ++#define MMU_MMU_CNTL_MMUEnable_MASK 0x2 ++#define MMU_MMU_CNTL_MMUEnable_OFFSET 1 ++#define MMU_MMU_LOCK_BaseValue_MASK 0xfc00 ++#define MMU_MMU_LOCK_BaseValue_OFFSET 10 ++#define MMU_MMU_LOCK_CurrentVictim_MASK 0x3f0 ++#define MMU_MMU_LOCK_CurrentVictim_OFFSET 4 ++ ++#endif /* _MMU_ACC_INT_H */ +diff --git a/drivers/dsp/bridge/hw/MMURegAcM.h b/drivers/dsp/bridge/hw/MMURegAcM.h +new file mode 100644 +index 0000000..e46fdcb +--- /dev/null ++++ b/drivers/dsp/bridge/hw/MMURegAcM.h +@@ -0,0 +1,253 @@ ++/* ++ * MMURegAcM.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2007 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++#ifndef _MMU_REG_ACM_H ++#define _MMU_REG_ACM_H ++ ++#include ++ ++#include ++ ++#include "MMUAccInt.h" ++ ++#if defined(USE_LEVEL_1_MACROS) ++ ++ ++#define MMUMMU_SYSCONFIGReadRegister32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_SYSCONFIGReadRegister32),\ ++ RD_MEM_32_VOLATILE((baseAddress)+MMU_MMU_SYSCONFIG_OFFSET)) ++ ++ ++#define MMUMMU_SYSCONFIGIdleModeWrite32(baseAddress, value)\ ++{\ ++ const u32 offset = MMU_MMU_SYSCONFIG_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((baseAddress)+offset);\ ++ register u32 newValue = (value);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_SYSCONFIGIdleModeWrite32);\ ++ data &= ~(MMU_MMU_SYSCONFIG_IdleMode_MASK);\ ++ newValue <<= MMU_MMU_SYSCONFIG_IdleMode_OFFSET;\ ++ newValue &= MMU_MMU_SYSCONFIG_IdleMode_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE(baseAddress+offset, newValue);\ ++} ++ ++ ++#define MMUMMU_SYSCONFIGAutoIdleWrite32(baseAddress, value)\ ++{\ ++ const u32 offset = MMU_MMU_SYSCONFIG_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((baseAddress)+offset);\ ++ register u32 newValue = (value);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_SYSCONFIGAutoIdleWrite32);\ ++ data &= ~(MMU_MMU_SYSCONFIG_AutoIdle_MASK);\ ++ newValue <<= MMU_MMU_SYSCONFIG_AutoIdle_OFFSET;\ ++ newValue &= MMU_MMU_SYSCONFIG_AutoIdle_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE(baseAddress+offset, newValue);\ ++} ++ ++ ++#define MMUMMU_IRQSTATUSReadRegister32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_IRQSTATUSReadRegister32),\ ++ RD_MEM_32_VOLATILE((baseAddress)+MMU_MMU_IRQSTATUS_OFFSET)) ++ ++ ++#define MMUMMU_IRQSTATUSWriteRegister32(baseAddress, value)\ ++{\ ++ const u32 offset = MMU_MMU_IRQSTATUS_OFFSET;\ ++ register u32 newValue = (value);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_IRQSTATUSWriteRegister32);\ ++ WR_MEM_32_VOLATILE((baseAddress)+offset, newValue);\ ++} ++ ++ ++#define MMUMMU_IRQENABLEReadRegister32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_IRQENABLEReadRegister32),\ ++ RD_MEM_32_VOLATILE((baseAddress)+MMU_MMU_IRQENABLE_OFFSET)) ++ ++ ++#define MMUMMU_IRQENABLEWriteRegister32(baseAddress, value)\ ++{\ ++ const u32 offset = MMU_MMU_IRQENABLE_OFFSET;\ ++ register u32 newValue = (value);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_IRQENABLEWriteRegister32);\ ++ WR_MEM_32_VOLATILE((baseAddress)+offset, newValue);\ ++} ++ ++ ++#define MMUMMU_WALKING_STTWLRunningRead32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_WALKING_STTWLRunningRead32),\ ++ (((RD_MEM_32_VOLATILE(((baseAddress)+(MMU_MMU_WALKING_ST_OFFSET))))\ ++ & MMU_MMU_WALKING_ST_TWLRunning_MASK) >>\ ++ MMU_MMU_WALKING_ST_TWLRunning_OFFSET)) ++ ++ ++#define MMUMMU_CNTLTWLEnableRead32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_CNTLTWLEnableRead32),\ ++ (((RD_MEM_32_VOLATILE(((baseAddress)+(MMU_MMU_CNTL_OFFSET)))) &\ ++ MMU_MMU_CNTL_TWLEnable_MASK) >>\ ++ MMU_MMU_CNTL_TWLEnable_OFFSET)) ++ ++ ++#define MMUMMU_CNTLTWLEnableWrite32(baseAddress, value)\ ++{\ ++ const u32 offset = MMU_MMU_CNTL_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((baseAddress)+offset);\ ++ register u32 newValue = (value);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_CNTLTWLEnableWrite32);\ ++ data &= ~(MMU_MMU_CNTL_TWLEnable_MASK);\ ++ newValue <<= MMU_MMU_CNTL_TWLEnable_OFFSET;\ ++ newValue &= MMU_MMU_CNTL_TWLEnable_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE(baseAddress+offset, newValue);\ ++} ++ ++ ++#define MMUMMU_CNTLMMUEnableWrite32(baseAddress, value)\ ++{\ ++ const u32 offset = MMU_MMU_CNTL_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((baseAddress)+offset);\ ++ register u32 newValue = (value);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_CNTLMMUEnableWrite32);\ ++ data &= ~(MMU_MMU_CNTL_MMUEnable_MASK);\ ++ newValue <<= MMU_MMU_CNTL_MMUEnable_OFFSET;\ ++ newValue &= MMU_MMU_CNTL_MMUEnable_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE(baseAddress+offset, newValue);\ ++} ++ ++ ++#define MMUMMU_FAULT_ADReadRegister32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_FAULT_ADReadRegister32),\ ++ RD_MEM_32_VOLATILE((baseAddress)+MMU_MMU_FAULT_AD_OFFSET)) ++ ++ ++#define MMUMMU_TTBWriteRegister32(baseAddress, value)\ ++{\ ++ const u32 offset = MMU_MMU_TTB_OFFSET;\ ++ register u32 newValue = (value);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_TTBWriteRegister32);\ ++ WR_MEM_32_VOLATILE((baseAddress)+offset, newValue);\ ++} ++ ++ ++#define MMUMMU_LOCKReadRegister32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_LOCKReadRegister32),\ ++ RD_MEM_32_VOLATILE((baseAddress)+MMU_MMU_LOCK_OFFSET)) ++ ++ ++#define MMUMMU_LOCKWriteRegister32(baseAddress, value)\ ++{\ ++ const u32 offset = MMU_MMU_LOCK_OFFSET;\ ++ register u32 newValue = (value);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_LOCKWriteRegister32);\ ++ WR_MEM_32_VOLATILE((baseAddress)+offset, newValue);\ ++} ++ ++ ++#define MMUMMU_LOCKBaseValueRead32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_LOCKBaseValueRead32),\ ++ (((RD_MEM_32_VOLATILE(((baseAddress)+(MMU_MMU_LOCK_OFFSET)))) &\ ++ MMU_MMU_LOCK_BaseValue_MASK) >>\ ++ MMU_MMU_LOCK_BaseValue_OFFSET)) ++ ++ ++#define MMUMMU_LOCKBaseValueWrite32(baseAddress, value)\ ++{\ ++ const u32 offset = MMU_MMU_LOCK_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((baseAddress)+offset);\ ++ register u32 newValue = (value);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_LOCKBaseValueWrite32);\ ++ data &= ~(MMU_MMU_LOCK_BaseValue_MASK);\ ++ newValue <<= MMU_MMU_LOCK_BaseValue_OFFSET;\ ++ newValue &= MMU_MMU_LOCK_BaseValue_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE(baseAddress+offset, newValue);\ ++} ++ ++ ++#define MMUMMU_LOCKCurrentVictimRead32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_LOCKCurrentVictimRead32),\ ++ (((RD_MEM_32_VOLATILE(((baseAddress)+(MMU_MMU_LOCK_OFFSET)))) &\ ++ MMU_MMU_LOCK_CurrentVictim_MASK) >>\ ++ MMU_MMU_LOCK_CurrentVictim_OFFSET)) ++ ++ ++#define MMUMMU_LOCKCurrentVictimWrite32(baseAddress, value)\ ++{\ ++ const u32 offset = MMU_MMU_LOCK_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((baseAddress)+offset);\ ++ register u32 newValue = (value);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_LOCKCurrentVictimWrite32);\ ++ data &= ~(MMU_MMU_LOCK_CurrentVictim_MASK);\ ++ newValue <<= MMU_MMU_LOCK_CurrentVictim_OFFSET;\ ++ newValue &= MMU_MMU_LOCK_CurrentVictim_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE(baseAddress+offset, newValue);\ ++} ++ ++ ++#define MMUMMU_LOCKCurrentVictimSet32(var, value)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_LOCKCurrentVictimSet32),\ ++ (((var) & ~(MMU_MMU_LOCK_CurrentVictim_MASK)) |\ ++ (((value) << MMU_MMU_LOCK_CurrentVictim_OFFSET) &\ ++ MMU_MMU_LOCK_CurrentVictim_MASK))) ++ ++ ++#define MMUMMU_LD_TLBReadRegister32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_LD_TLBReadRegister32),\ ++ RD_MEM_32_VOLATILE((baseAddress)+MMU_MMU_LD_TLB_OFFSET)) ++ ++ ++#define MMUMMU_LD_TLBWriteRegister32(baseAddress, value)\ ++{\ ++ const u32 offset = MMU_MMU_LD_TLB_OFFSET;\ ++ register u32 newValue = (value);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_LD_TLBWriteRegister32);\ ++ WR_MEM_32_VOLATILE((baseAddress)+offset, newValue);\ ++} ++ ++ ++#define MMUMMU_CAMWriteRegister32(baseAddress, value)\ ++{\ ++ const u32 offset = MMU_MMU_CAM_OFFSET;\ ++ register u32 newValue = (value);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_CAMWriteRegister32);\ ++ WR_MEM_32_VOLATILE((baseAddress)+offset, newValue);\ ++} ++ ++ ++#define MMUMMU_RAMWriteRegister32(baseAddress, value)\ ++{\ ++ const u32 offset = MMU_MMU_RAM_OFFSET;\ ++ register u32 newValue = (value);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_RAMWriteRegister32);\ ++ WR_MEM_32_VOLATILE((baseAddress)+offset, newValue);\ ++} ++ ++ ++#define MMUMMU_FLUSH_ENTRYWriteRegister32(baseAddress, value)\ ++{\ ++ const u32 offset = MMU_MMU_FLUSH_ENTRY_OFFSET;\ ++ register u32 newValue = (value);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_MMUMMU_FLUSH_ENTRYWriteRegister32);\ ++ WR_MEM_32_VOLATILE((baseAddress)+offset, newValue);\ ++} ++ ++ ++#endif /* USE_LEVEL_1_MACROS */ ++ ++#endif /* _MMU_REG_ACM_H */ +diff --git a/drivers/dsp/bridge/hw/PRCMAccInt.h b/drivers/dsp/bridge/hw/PRCMAccInt.h +new file mode 100644 +index 0000000..42baa68 +--- /dev/null ++++ b/drivers/dsp/bridge/hw/PRCMAccInt.h +@@ -0,0 +1,300 @@ ++/* ++ * PRCMAccInt.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2007 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef _PRCM_ACC_INT_H ++#define _PRCM_ACC_INT_H ++ ++/* Mappings of level 1 EASI function numbers to function names */ ++ ++#define EASIL1_PRCMPRCM_CLKCFG_CTRLValid_configWriteClk_valid32 \ ++ (PRCM_BASE_EASIL1 + 349) ++#define EASIL1_PRCMCM_FCLKEN1_COREReadRegister32 (PRCM_BASE_EASIL1 + 743) ++#define EASIL1_PRCMCM_FCLKEN1_COREEN_GPT8Write32 (PRCM_BASE_EASIL1 + 951) ++#define EASIL1_PRCMCM_FCLKEN1_COREEN_GPT7Write32 (PRCM_BASE_EASIL1 + 961) ++#define EASIL1_PRCMCM_ICLKEN1_COREReadRegister32 \ ++ (PRCM_BASE_EASIL1 + 1087) ++#define EASIL1_PRCMCM_ICLKEN1_COREEN_MAILBOXESWrite32 \ ++ (PRCM_BASE_EASIL1 + 1105) ++#define EASIL1_PRCMCM_ICLKEN1_COREEN_GPT8Write32 \ ++ (PRCM_BASE_EASIL1 + 1305) ++#define EASIL1_PRCMCM_ICLKEN1_COREEN_GPT7Write32 \ ++ (PRCM_BASE_EASIL1 + 1315) ++#define EASIL1_PRCMCM_CLKSEL1_CORECLKSEL_L3ReadIssel132 \ ++ (PRCM_BASE_EASIL1 + 2261) ++#define EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT8Write32k32 \ ++ (PRCM_BASE_EASIL1 + 2364) ++#define EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT8WriteSys32 \ ++ (PRCM_BASE_EASIL1 + 2365) ++#define EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT8WriteExt32 \ ++ (PRCM_BASE_EASIL1 + 2366) ++#define EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT7Write32k32 \ ++ (PRCM_BASE_EASIL1 + 2380) ++#define EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT7WriteSys32 \ ++ (PRCM_BASE_EASIL1 + 2381) ++#define EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT7WriteExt32 \ ++ (PRCM_BASE_EASIL1 + 2382) ++#define EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT6WriteSys32 \ ++ (PRCM_BASE_EASIL1 + 2397) ++#define EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT6WriteExt32 \ ++ (PRCM_BASE_EASIL1 + 2398) ++#define EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT5WriteSys32 \ ++ (PRCM_BASE_EASIL1 + 2413) ++#define EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT5WriteExt32 \ ++ (PRCM_BASE_EASIL1 + 2414) ++#define EASIL1_PRCMCM_CLKSEL1_PLLAPLLs_ClkinRead32 \ ++ (PRCM_BASE_EASIL1 + 3747) ++#define EASIL1_PRCMCM_FCLKEN_DSPEN_DSPWrite32 (PRCM_BASE_EASIL1 + 3834) ++#define EASIL1_PRCMCM_ICLKEN_DSPEN_DSP_IPIWrite32 \ ++ (PRCM_BASE_EASIL1 + 3846) ++#define EASIL1_PRCMCM_IDLEST_DSPReadRegister32 (PRCM_BASE_EASIL1 + 3850) ++#define EASIL1_PRCMCM_IDLEST_DSPST_IPIRead32 (PRCM_BASE_EASIL1 + 3857) ++#define EASIL1_PRCMCM_IDLEST_DSPST_DSPRead32 (PRCM_BASE_EASIL1 + 3863) ++#define EASIL1_PRCMCM_AUTOIDLE_DSPAUTO_DSP_IPIWrite32 \ ++ (PRCM_BASE_EASIL1 + 3877) ++#define EASIL1_PRCMCM_CLKSEL_DSPSYNC_DSPWrite32 (PRCM_BASE_EASIL1 + 3927) ++#define EASIL1_PRCMCM_CLKSEL_DSPCLKSEL_DSP_IFWrite32 \ ++ (PRCM_BASE_EASIL1 + 3941) ++#define EASIL1_PRCMCM_CLKSEL_DSPCLKSEL_DSPWrite32 \ ++ (PRCM_BASE_EASIL1 + 3965) ++#define EASIL1_PRCMCM_CLKSTCTRL_DSPAutostate_DSPRead32 \ ++ (PRCM_BASE_EASIL1 + 3987) ++#define EASIL1_PRCMCM_CLKSTCTRL_DSPAutostate_DSPWrite32 \ ++ (PRCM_BASE_EASIL1 + 3993) ++#define EASIL1_PRCMRM_RSTCTRL_DSPReadRegister32 (PRCM_BASE_EASIL1 + 3997) ++#define EASIL1_PRCMRM_RSTCTRL_DSPRST1_DSPWrite32 \ ++ (PRCM_BASE_EASIL1 + 4025) ++#define EASIL1_PRCMRM_RSTST_DSPReadRegister32 (PRCM_BASE_EASIL1 + 4029) ++#define EASIL1_PRCMRM_RSTST_DSPWriteRegister32 (PRCM_BASE_EASIL1 + 4030) ++#define EASIL1_PRCMPM_PWSTCTRL_DSPForceStateWrite32 \ ++ (PRCM_BASE_EASIL1 + 4165) ++#define EASIL1_PRCMPM_PWSTCTRL_DSPPowerStateWriteRET32 \ ++ (PRCM_BASE_EASIL1 + 4193) ++#define EASIL1_PRCMPM_PWSTST_DSPReadRegister32 (PRCM_BASE_EASIL1 + 4197) ++#define EASIL1_PRCMPM_PWSTST_DSPInTransitionRead32 \ ++ (PRCM_BASE_EASIL1 + 4198) ++#define EASIL1_PRCMPM_PWSTST_DSPPowerStateStGet32 \ ++ (PRCM_BASE_EASIL1 + 4235) ++#define EASIL1_CM_FCLKEN_PER_GPT5WriteRegister32 \ ++ (PRCM_BASE_EASIL1 + 4368) ++#define EASIL1_CM_ICLKEN_PER_GPT5WriteRegister32 \ ++ (PRCM_BASE_EASIL1 + 4370) ++#define EASIL1_CM_CLKSEL_PER_GPT5Write32k32 (PRCM_BASE_EASIL1 + 4372) ++#define EASIL1_CM_CLKSEL_PER_GPT6Write32k32 (PRCM_BASE_EASIL1 + 4373) ++#define EASIL1_PRCMCM_CLKSTCTRL_IVA2WriteRegister32 \ ++ (PRCM_BASE_EASIL1 + 4374) ++#define EASIL1_PRCMPM_PWSTCTRL_IVA2PowerStateWriteON32 \ ++ (PRCM_BASE_EASIL1 + 4375) ++#define EASIL1_PRCMPM_PWSTCTRL_IVA2PowerStateWriteOFF32 \ ++ (PRCM_BASE_EASIL1 + 4376) ++#define EASIL1_PRCMPM_PWSTST_IVA2InTransitionRead32 \ ++ (PRCM_BASE_EASIL1 + 4377) ++#define EASIL1_PRCMPM_PWSTST_IVA2PowerStateStGet32 \ ++ (PRCM_BASE_EASIL1 + 4378) ++#define EASIL1_PRCMPM_PWSTST_IVA2ReadRegister32 (PRCM_BASE_EASIL1 + 4379) ++ ++/* Register offset address definitions */ ++ ++#define PRCM_PRCM_CLKCFG_CTRL_OFFSET (u32)(0x80) ++#define PRCM_CM_FCLKEN1_CORE_OFFSET (u32)(0x200) ++#define PRCM_CM_ICLKEN1_CORE_OFFSET (u32)(0x210) ++#define PRCM_CM_CLKSEL2_CORE_OFFSET (u32)(0x244) ++#define PRCM_CM_CLKSEL1_PLL_OFFSET (u32)(0x540) ++#define PRCM_CM_ICLKEN_DSP_OFFSET (u32)(0x810) ++#define PRCM_CM_IDLEST_DSP_OFFSET (u32)(0x820) ++#define PRCM_CM_AUTOIDLE_DSP_OFFSET (u32)(0x830) ++#define PRCM_CM_CLKSEL_DSP_OFFSET (u32)(0x840) ++#define PRCM_CM_CLKSTCTRL_DSP_OFFSET (u32)(0x848) ++#define PRCM_RM_RSTCTRL_DSP_OFFSET (u32)(0x850) ++#define PRCM_RM_RSTST_DSP_OFFSET (u32)(0x858) ++#define PRCM_PM_PWSTCTRL_DSP_OFFSET (u32)(0x8e0) ++#define PRCM_PM_PWSTST_DSP_OFFSET (u32)(0x8e4) ++#define PRCM_PM_PWSTST_IVA2_OFFSET (u32)(0xE4) ++#define PRCM_PM_PWSTCTRL_IVA2_OFFSET (u32)(0xE0) ++#define PRCM_CM_CLKSTCTRL_IVA2_OFFSET (u32)(0x48) ++#define CM_CLKSEL_PER_OFFSET (u32)(0x40) ++ ++/* Bitfield mask and offset declarations */ ++ ++#define PRCM_PRCM_CLKCFG_CTRL_Valid_config_MASK (u32)(0x1) ++#define PRCM_PRCM_CLKCFG_CTRL_Valid_config_OFFSET (u32)(0) ++ ++#define PRCM_CM_FCLKEN1_CORE_EN_GPT8_MASK (u32)(0x400) ++#define PRCM_CM_FCLKEN1_CORE_EN_GPT8_OFFSET (u32)(10) ++ ++#define PRCM_CM_FCLKEN1_CORE_EN_GPT7_MASK (u32)(0x200) ++#define PRCM_CM_FCLKEN1_CORE_EN_GPT7_OFFSET (u32)(9) ++ ++#define PRCM_CM_ICLKEN1_CORE_EN_GPT8_MASK (u32)(0x400) ++#define PRCM_CM_ICLKEN1_CORE_EN_GPT8_OFFSET (u32)(10) ++ ++#define PRCM_CM_ICLKEN1_CORE_EN_GPT7_MASK (u32)(0x200) ++#define PRCM_CM_ICLKEN1_CORE_EN_GPT7_OFFSET (u32)(9) ++ ++#define PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT8_MASK (u32)(0xc000) ++#define PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT8_OFFSET (u32)(14) ++ ++#define PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT7_MASK (u32)(0x3000) ++#define PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT7_OFFSET (u32)(12) ++ ++#define PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT6_MASK (u32)(0xc00) ++#define PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT6_OFFSET (u32)(10) ++ ++#define PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT5_MASK (u32)(0x300) ++#define PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT5_OFFSET (u32)(8) ++ ++#define PRCM_CM_CLKSEL1_PLL_APLLs_Clkin_MASK (u32)(0x3800000) ++#define PRCM_CM_CLKSEL1_PLL_APLLs_Clkin_OFFSET (u32)(23) ++ ++#define PRCM_CM_ICLKEN_DSP_EN_DSP_IPI_MASK (u32)(0x2) ++#define PRCM_CM_ICLKEN_DSP_EN_DSP_IPI_OFFSET (u32)(1) ++ ++#define PRCM_CM_IDLEST_DSP_ST_IPI_MASK (u32)(0x2) ++#define PRCM_CM_IDLEST_DSP_ST_IPI_OFFSET (u32)(1) ++ ++#define PRCM_CM_AUTOIDLE_DSP_AUTO_DSP_IPI_MASK (u32)(0x2) ++#define PRCM_CM_AUTOIDLE_DSP_AUTO_DSP_IPI_OFFSET (u32)(1) ++ ++#define PRCM_CM_CLKSEL_DSP_SYNC_DSP_MASK (u32)(0x80) ++#define PRCM_CM_CLKSEL_DSP_SYNC_DSP_OFFSET (u32)(7) ++ ++#define PRCM_CM_CLKSEL_DSP_CLKSEL_DSP_IF_MASK (u32)(0x60) ++#define PRCM_CM_CLKSEL_DSP_CLKSEL_DSP_IF_OFFSET (u32)(5) ++ ++#define PRCM_CM_CLKSEL_DSP_CLKSEL_DSP_MASK (u32)(0x1f) ++#define PRCM_CM_CLKSEL_DSP_CLKSEL_DSP_OFFSET (u32)(0) ++ ++#define PRCM_CM_CLKSTCTRL_DSP_Autostate_DSP_MASK (u32)(0x1) ++#define PRCM_CM_CLKSTCTRL_DSP_Autostate_DSP_OFFSET (u32)(0) ++ ++#define PRCM_PM_PWSTCTRL_DSP_ForceState_MASK (u32)(0x40000) ++#define PRCM_PM_PWSTCTRL_DSP_ForceState_OFFSET (u32)(18) ++ ++#define PRCM_PM_PWSTCTRL_DSP_PowerState_MASK (u32)(0x3) ++#define PRCM_PM_PWSTCTRL_DSP_PowerState_OFFSET (u32)(0) ++ ++#define PRCM_PM_PWSTCTRL_IVA2_PowerState_MASK (u32)(0x3) ++#define PRCM_PM_PWSTCTRL_IVA2_PowerState_OFFSET (u32)(0) ++ ++#define PRCM_PM_PWSTST_DSP_InTransition_MASK (u32)(0x100000) ++#define PRCM_PM_PWSTST_DSP_InTransition_OFFSET (u32)(20) ++ ++#define PRCM_PM_PWSTST_IVA2_InTransition_MASK (u32)(0x100000) ++#define PRCM_PM_PWSTST_IVA2_InTransition_OFFSET (u32)(20) ++ ++#define PRCM_PM_PWSTST_DSP_PowerStateSt_MASK (u32)(0x3) ++#define PRCM_PM_PWSTST_DSP_PowerStateSt_OFFSET (u32)(0) ++ ++#define PRCM_PM_PWSTST_IVA2_PowerStateSt_MASK (u32)(0x3) ++#define PRCM_PM_PWSTST_IVA2_PowerStateSt_OFFSET (u32)(0) ++ ++#define CM_FCLKEN_PER_OFFSET (u32)(0x0) ++#define CM_FCLKEN_PER_GPT5_OFFSET (u32)(6) ++#define CM_FCLKEN_PER_GPT5_MASK (u32)(0x40) ++ ++#define CM_FCLKEN_PER_GPT6_OFFSET (u32)(7) ++#define CM_FCLKEN_PER_GPT6_MASK (u32)(0x80) ++ ++#define CM_ICLKEN_PER_OFFSET (u32)(0x10) ++#define CM_ICLKEN_PER_GPT5_OFFSET (u32)(6) ++#define CM_ICLKEN_PER_GPT5_MASK (u32)(0x40) ++ ++#define CM_ICLKEN_PER_GPT6_OFFSET (u32)(7) ++#define CM_ICLKEN_PER_GPT6_MASK (u32)(0x80) ++ ++#define CM_CLKSEL_PER_GPT5_OFFSET (u32)(3) ++#define CM_CLKSEL_PER_GPT5_MASK (u32)(0x8) ++ ++#define CM_CLKSEL_PER_GPT6_OFFSET (u32)(4) ++#define CM_CLKSEL_PER_GPT6_MASK (u32)(0x10) ++ ++ ++#define CM_FCLKEN_IVA2_OFFSET (u32)(0x0) ++#define CM_FCLKEN_IVA2_EN_MASK (u32)(0x1) ++#define CM_FCLKEN_IVA2_EN_OFFSET (u32)(0x0) ++ ++#define CM_IDLEST_IVA2_OFFSET (u32)(0x20) ++#define CM_IDLEST_IVA2_ST_IVA2_MASK (u32) (0x01) ++#define CM_IDLEST_IVA2_ST_IVA2_OFFSET (u32) (0x00) ++ ++#define CM_FCLKEN1_CORE_OFFSET (u32)(0xA00) ++ ++#define CM_ICLKEN1_CORE_OFFSET (u32)(0xA10) ++#define CM_ICLKEN1_CORE_EN_MAILBOXES_MASK (u32)(0x00000080) /* bit 7 */ ++#define CM_ICLKEN1_CORE_EN_MAILBOXES_OFFSET (u32)(7) ++ ++#define CM_CLKSTCTRL_IVA2_OFFSET (u32)(0x0) ++#define CM_CLKSTCTRL_IVA2_MASK (u32)(0x3) ++ ++ ++#define PRM_RSTCTRL_IVA2_OFFSET (u32)(0x50) ++#define PRM_RSTCTRL_IVA2_RST1_MASK (u32)(0x1) ++#define PRM_RSTCTRL_IVA2_RST1_OFFSET (u32)(0x0) ++#define PRM_RSTCTRL_IVA2_RST2_MASK (u32)(0x2) ++#define PRM_RSTCTRL_IVA2_RST2_OFFSET (u32)(0x1) ++#define PRM_RSTCTRL_IVA2_RST3_MASK (u32)(0x4) ++#define PRM_RSTCTRL_IVA2_RST3_OFFSET (u32)(0x2) ++ ++ ++/* The following represent the enumerated values for each bitfield */ ++ ++enum PRCMPRCM_CLKCFG_CTRLValid_configE { ++ PRCMPRCM_CLKCFG_CTRLValid_configUpdated = 0x0000, ++ PRCMPRCM_CLKCFG_CTRLValid_configClk_valid = 0x0001 ++} ; ++ ++enum PRCMCM_CLKSEL2_CORECLKSEL_GPT8E { ++ PRCMCM_CLKSEL2_CORECLKSEL_GPT832k = 0x0000, ++ PRCMCM_CLKSEL2_CORECLKSEL_GPT8Sys = 0x0001, ++ PRCMCM_CLKSEL2_CORECLKSEL_GPT8Ext = 0x0002, ++ PRCMCM_CLKSEL2_CORECLKSEL_GPT8Reserved = 0x0003 ++} ; ++ ++enum PRCMCM_CLKSEL2_CORECLKSEL_GPT7E { ++ PRCMCM_CLKSEL2_CORECLKSEL_GPT732k = 0x0000, ++ PRCMCM_CLKSEL2_CORECLKSEL_GPT7Sys = 0x0001, ++ PRCMCM_CLKSEL2_CORECLKSEL_GPT7Ext = 0x0002, ++ PRCMCM_CLKSEL2_CORECLKSEL_GPT7Reserved = 0x0003 ++} ; ++ ++enum PRCMCM_CLKSEL2_CORECLKSEL_GPT6E { ++ PRCMCM_CLKSEL2_CORECLKSEL_GPT632k = 0x0000, ++ PRCMCM_CLKSEL2_CORECLKSEL_GPT6Sys = 0x0001, ++ PRCMCM_CLKSEL2_CORECLKSEL_GPT6Ext = 0x0002, ++ PRCMCM_CLKSEL2_CORECLKSEL_GPT6Reserved = 0x0003 ++} ; ++ ++enum PRCMCM_CLKSEL2_CORECLKSEL_GPT5E { ++ PRCMCM_CLKSEL2_CORECLKSEL_GPT532k = 0x0000, ++ PRCMCM_CLKSEL2_CORECLKSEL_GPT5Sys = 0x0001, ++ PRCMCM_CLKSEL2_CORECLKSEL_GPT5Ext = 0x0002, ++ PRCMCM_CLKSEL2_CORECLKSEL_GPT5Reserved = 0x0003 ++} ; ++ ++enum PRCMPM_PWSTCTRL_DSPPowerStateE { ++ PRCMPM_PWSTCTRL_DSPPowerStateON = 0x0000, ++ PRCMPM_PWSTCTRL_DSPPowerStateRET = 0x0001, ++ PRCMPM_PWSTCTRL_DSPPowerStateReserved = 0x0002, ++ PRCMPM_PWSTCTRL_DSPPowerStateOFF = 0x0003 ++} ; ++ ++enum PRCMPM_PWSTCTRL_IVA2PowerStateE { ++ PRCMPM_PWSTCTRL_IVA2PowerStateON = 0x0003, ++ PRCMPM_PWSTCTRL_IVA2PowerStateRET = 0x0001, ++ PRCMPM_PWSTCTRL_IVA2PowerStateReserved = 0x0002, ++ PRCMPM_PWSTCTRL_IVA2PowerStateOFF = 0x0000 ++} ; ++ ++#endif /* _PRCM_ACC_INT_H */ +diff --git a/drivers/dsp/bridge/hw/PRCMRegAcM.h b/drivers/dsp/bridge/hw/PRCMRegAcM.h +new file mode 100644 +index 0000000..91cb33c +--- /dev/null ++++ b/drivers/dsp/bridge/hw/PRCMRegAcM.h +@@ -0,0 +1,669 @@ ++/* ++ * PRCMRegAcM.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2007 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef _PRCM_REG_ACM_H ++#define _PRCM_REG_ACM_H ++ ++#include ++ ++#include ++ ++#include "PRCMAccInt.h" ++ ++#if defined(USE_LEVEL_1_MACROS) ++ ++#define PRCMPRCM_CLKCFG_CTRLValid_configWriteClk_valid32(baseAddress)\ ++{\ ++ const u32 offset = PRCM_PRCM_CLKCFG_CTRL_OFFSET;\ ++ const u32 newValue = \ ++ (u32)PRCMPRCM_CLKCFG_CTRLValid_configClk_valid <<\ ++ PRCM_PRCM_CLKCFG_CTRL_Valid_config_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((u32)(baseAddress)+offset);\ ++ _DEBUG_LEVEL_1_EASI(\ ++ EASIL1_PRCMPRCM_CLKCFG_CTRLValid_configWriteClk_valid32);\ ++ data &= ~(PRCM_PRCM_CLKCFG_CTRL_Valid_config_MASK);\ ++ data |= newValue;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, data);\ ++} ++ ++ ++#define CM_FCLKEN_PERReadRegister32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_FCLKEN1_COREReadRegister32),\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+CM_FCLKEN_PER_OFFSET)) ++ ++ ++#define CM_ICLKEN_PERReadRegister32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_FCLKEN1_COREReadRegister32),\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+CM_ICLKEN_PER_OFFSET)) ++ ++ ++#define CM_FCLKEN_PER_GPT5WriteRegister32(baseAddress,value)\ ++{\ ++ const u32 offset = CM_FCLKEN_PER_OFFSET;\ ++ register u32 data = \ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_CM_FCLKEN_PER_GPT5WriteRegister32);\ ++ data &= ~(CM_FCLKEN_PER_GPT5_MASK);\ ++ newValue <<= CM_FCLKEN_PER_GPT5_OFFSET;\ ++ newValue &= CM_FCLKEN_PER_GPT5_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE(((u32)(baseAddress))+offset, newValue);\ ++} ++ ++ ++#define CM_FCLKEN_PER_GPT6WriteRegister32(baseAddress,value)\ ++{\ ++ const u32 offset = CM_FCLKEN_PER_OFFSET;\ ++ register u32 data =\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_CM_FCLKEN_PER_GPT5WriteRegister32);\ ++ data &= ~(CM_FCLKEN_PER_GPT6_MASK);\ ++ newValue <<= CM_FCLKEN_PER_GPT6_OFFSET;\ ++ newValue &= CM_FCLKEN_PER_GPT6_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE(((u32)(baseAddress))+offset, newValue);\ ++} ++ ++ ++#define CM_ICLKEN_PER_GPT5WriteRegister32(baseAddress,value)\ ++{\ ++ const u32 offset = CM_ICLKEN_PER_OFFSET;\ ++ register u32 data = \ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_CM_ICLKEN_PER_GPT5WriteRegister32);\ ++ data &= ~(CM_ICLKEN_PER_GPT5_MASK);\ ++ newValue <<= CM_ICLKEN_PER_GPT5_OFFSET;\ ++ newValue &= CM_ICLKEN_PER_GPT5_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE(((u32)(baseAddress))+offset, newValue);\ ++} ++ ++ ++#define CM_ICLKEN_PER_GPT6WriteRegister32(baseAddress,value)\ ++{\ ++ const u32 offset = CM_ICLKEN_PER_OFFSET;\ ++ register u32 data = \ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_CM_ICLKEN_PER_GPT5WriteRegister32);\ ++ data &= ~(CM_ICLKEN_PER_GPT6_MASK);\ ++ newValue <<= CM_ICLKEN_PER_GPT6_OFFSET;\ ++ newValue &= CM_ICLKEN_PER_GPT6_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE(((u32)(baseAddress))+offset, newValue);\ ++} ++ ++ ++#define CM_FCLKEN1_COREReadRegister32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_FCLKEN1_COREReadRegister32),\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+CM_FCLKEN1_CORE_OFFSET)) ++ ++ ++#define PRCMCM_FCLKEN1_COREEN_GPT8Write32(baseAddress,value)\ ++{\ ++ const u32 offset = PRCM_CM_FCLKEN1_CORE_OFFSET;\ ++ register u32 data = \ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_FCLKEN1_COREEN_GPT8Write32);\ ++ data &= ~(PRCM_CM_FCLKEN1_CORE_EN_GPT8_MASK);\ ++ newValue <<= PRCM_CM_FCLKEN1_CORE_EN_GPT8_OFFSET;\ ++ newValue &= PRCM_CM_FCLKEN1_CORE_EN_GPT8_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define PRCMCM_FCLKEN1_COREEN_GPT7Write32(baseAddress,value)\ ++{\ ++ const u32 offset = PRCM_CM_FCLKEN1_CORE_OFFSET;\ ++ register u32 data = \ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_FCLKEN1_COREEN_GPT7Write32);\ ++ data &= ~(PRCM_CM_FCLKEN1_CORE_EN_GPT7_MASK);\ ++ newValue <<= PRCM_CM_FCLKEN1_CORE_EN_GPT7_OFFSET;\ ++ newValue &= PRCM_CM_FCLKEN1_CORE_EN_GPT7_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define CM_ICLKEN1_COREReadRegister32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_ICLKEN1_COREReadRegister32),\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+CM_ICLKEN1_CORE_OFFSET)) ++ ++ ++#define CM_ICLKEN1_COREEN_MAILBOXESWrite32(baseAddress, value)\ ++{\ ++ const u32 offset = CM_ICLKEN1_CORE_OFFSET;\ ++ register u32 data = \ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_ICLKEN1_COREEN_MAILBOXESWrite32);\ ++ data &= ~(CM_ICLKEN1_CORE_EN_MAILBOXES_MASK);\ ++ newValue <<= CM_ICLKEN1_CORE_EN_MAILBOXES_OFFSET;\ ++ newValue &= CM_ICLKEN1_CORE_EN_MAILBOXES_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define PRCMCM_ICLKEN1_COREEN_GPT8Write32(baseAddress, value)\ ++{\ ++ const u32 offset = PRCM_CM_ICLKEN1_CORE_OFFSET;\ ++ register u32 data = \ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_ICLKEN1_COREEN_GPT8Write32);\ ++ data &= ~(PRCM_CM_ICLKEN1_CORE_EN_GPT8_MASK);\ ++ newValue <<= PRCM_CM_ICLKEN1_CORE_EN_GPT8_OFFSET;\ ++ newValue &= PRCM_CM_ICLKEN1_CORE_EN_GPT8_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define PRCMCM_ICLKEN1_COREEN_GPT7Write32(baseAddress, value)\ ++{\ ++ const u32 offset = PRCM_CM_ICLKEN1_CORE_OFFSET;\ ++ register u32 data =\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_ICLKEN1_COREEN_GPT7Write32);\ ++ data &= ~(PRCM_CM_ICLKEN1_CORE_EN_GPT7_MASK);\ ++ newValue <<= PRCM_CM_ICLKEN1_CORE_EN_GPT7_OFFSET;\ ++ newValue &= PRCM_CM_ICLKEN1_CORE_EN_GPT7_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define PRCMCM_CLKSEL2_CORECLKSEL_GPT8Write32k32(baseAddress)\ ++{\ ++ const u32 offset = PRCM_CM_CLKSEL2_CORE_OFFSET;\ ++ const u32 newValue = (u32)PRCMCM_CLKSEL2_CORECLKSEL_GPT832k <<\ ++ PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT8_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((u32)(baseAddress)+offset);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT8Write32k32);\ ++ data &= ~(PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT8_MASK);\ ++ data |= newValue;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, data);\ ++} ++ ++ ++#define PRCMCM_CLKSEL2_CORECLKSEL_GPT8WriteSys32(baseAddress)\ ++{\ ++ const u32 offset = PRCM_CM_CLKSEL2_CORE_OFFSET;\ ++ const u32 newValue = (u32)PRCMCM_CLKSEL2_CORECLKSEL_GPT8Sys <<\ ++ PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT8_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((u32)(baseAddress)+offset);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT8WriteSys32);\ ++ data &= ~(PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT8_MASK);\ ++ data |= newValue;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, data);\ ++} ++ ++ ++#define PRCMCM_CLKSEL2_CORECLKSEL_GPT8WriteExt32(baseAddress)\ ++{\ ++ const u32 offset = PRCM_CM_CLKSEL2_CORE_OFFSET;\ ++ const u32 newValue = (u32)PRCMCM_CLKSEL2_CORECLKSEL_GPT8Ext <<\ ++ PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT8_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((u32)(baseAddress)+offset);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT8WriteExt32);\ ++ data &= ~(PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT8_MASK);\ ++ data |= newValue;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, data);\ ++} ++ ++ ++#define PRCMCM_CLKSEL2_CORECLKSEL_GPT7Write32k32(baseAddress)\ ++{\ ++ const u32 offset = PRCM_CM_CLKSEL2_CORE_OFFSET;\ ++ const u32 newValue = (u32)PRCMCM_CLKSEL2_CORECLKSEL_GPT732k <<\ ++ PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT7_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((u32)(baseAddress)+offset);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT7Write32k32);\ ++ data &= ~(PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT7_MASK);\ ++ data |= newValue;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, data);\ ++} ++ ++ ++#define PRCMCM_CLKSEL2_CORECLKSEL_GPT7WriteSys32(baseAddress)\ ++{\ ++ const u32 offset = PRCM_CM_CLKSEL2_CORE_OFFSET;\ ++ const u32 newValue = (u32)PRCMCM_CLKSEL2_CORECLKSEL_GPT7Sys <<\ ++ PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT7_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((u32)(baseAddress)+offset);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT7WriteSys32);\ ++ data &= ~(PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT7_MASK);\ ++ data |= newValue;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, data);\ ++} ++ ++ ++#define PRCMCM_CLKSEL2_CORECLKSEL_GPT7WriteExt32(baseAddress)\ ++{\ ++ const u32 offset = PRCM_CM_CLKSEL2_CORE_OFFSET;\ ++ const u32 newValue = (u32)PRCMCM_CLKSEL2_CORECLKSEL_GPT7Ext <<\ ++ PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT7_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((u32)(baseAddress)+offset);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT7WriteExt32);\ ++ data &= ~(PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT7_MASK);\ ++ data |= newValue;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, data);\ ++} ++ ++ ++#define PRCMCM_CLKSEL2_CORECLKSEL_GPT6WriteSys32(baseAddress)\ ++{\ ++ const u32 offset = PRCM_CM_CLKSEL2_CORE_OFFSET;\ ++ const u32 newValue = (u32)PRCMCM_CLKSEL2_CORECLKSEL_GPT6Sys <<\ ++ PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT6_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((u32)(baseAddress)+offset);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT6WriteSys32);\ ++ data &= ~(PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT6_MASK);\ ++ data |= newValue;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, data);\ ++} ++ ++ ++#define PRCMCM_CLKSEL2_CORECLKSEL_GPT6WriteExt32(baseAddress)\ ++{\ ++ const u32 offset = PRCM_CM_CLKSEL2_CORE_OFFSET;\ ++ const u32 newValue = (u32)PRCMCM_CLKSEL2_CORECLKSEL_GPT6Ext <<\ ++ PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT6_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((u32)(baseAddress)+offset);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT6WriteExt32);\ ++ data &= ~(PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT6_MASK);\ ++ data |= newValue;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, data);\ ++} ++ ++ ++#define CM_CLKSEL_PER_GPT5Write32k32(baseAddress)\ ++{\ ++ const u32 offset = CM_CLKSEL_PER_OFFSET;\ ++ const u32 newValue = (u32)PRCMCM_CLKSEL2_CORECLKSEL_GPT532k <<\ ++ CM_CLKSEL_PER_GPT5_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((u32)(baseAddress)+offset);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_CM_CLKSEL_PER_GPT5Write32k32);\ ++ data &= ~(CM_CLKSEL_PER_GPT5_MASK);\ ++ data |= newValue;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, data);\ ++} ++ ++ ++#define CM_CLKSEL_PER_GPT6Write32k32(baseAddress)\ ++{\ ++ const u32 offset = CM_CLKSEL_PER_OFFSET;\ ++ const u32 newValue = (u32)PRCMCM_CLKSEL2_CORECLKSEL_GPT532k <<\ ++ CM_CLKSEL_PER_GPT6_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((u32)(baseAddress)+offset);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_CM_CLKSEL_PER_GPT6Write32k32);\ ++ data &= ~(CM_CLKSEL_PER_GPT6_MASK);\ ++ data |= newValue;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, data);\ ++} ++ ++ ++#define PRCMCM_CLKSEL2_CORECLKSEL_GPT5WriteSys32(baseAddress)\ ++{\ ++ const u32 offset = PRCM_CM_CLKSEL2_CORE_OFFSET;\ ++ const u32 newValue = (u32)PRCMCM_CLKSEL2_CORECLKSEL_GPT5Sys <<\ ++ PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT5_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((u32)(baseAddress)+offset);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT5WriteSys32);\ ++ data &= ~(PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT5_MASK);\ ++ data |= newValue;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, data);\ ++} ++ ++ ++#define PRCMCM_CLKSEL2_CORECLKSEL_GPT5WriteExt32(baseAddress)\ ++{\ ++ const u32 offset = PRCM_CM_CLKSEL2_CORE_OFFSET;\ ++ const u32 newValue = (u32)PRCMCM_CLKSEL2_CORECLKSEL_GPT5Ext <<\ ++ PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT5_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((u32)(baseAddress)+offset);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSEL2_CORECLKSEL_GPT5WriteExt32);\ ++ data &= ~(PRCM_CM_CLKSEL2_CORE_CLKSEL_GPT5_MASK);\ ++ data |= newValue;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, data);\ ++} ++ ++ ++#define PRCMCM_CLKSEL1_PLLAPLLs_ClkinRead32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSEL1_PLLAPLLs_ClkinRead32),\ ++ (((RD_MEM_32_VOLATILE((((u32)(baseAddress))+\ ++ (PRCM_CM_CLKSEL1_PLL_OFFSET)))) &\ ++ PRCM_CM_CLKSEL1_PLL_APLLs_Clkin_MASK) >>\ ++ PRCM_CM_CLKSEL1_PLL_APLLs_Clkin_OFFSET)) ++ ++ ++#define CM_FCLKEN_IVA2EN_DSPWrite32(baseAddress,value)\ ++{\ ++ const u32 offset = CM_FCLKEN_IVA2_OFFSET;\ ++ register u32 data = \ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_FCLKEN_DSPEN_DSPWrite32);\ ++ data &= ~(CM_FCLKEN_IVA2_EN_MASK);\ ++ newValue <<= CM_FCLKEN_IVA2_EN_OFFSET;\ ++ newValue &= CM_FCLKEN_IVA2_EN_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define PRCMCM_ICLKEN_DSPEN_DSP_IPIWrite32(baseAddress, value)\ ++{\ ++ const u32 offset = PRCM_CM_ICLKEN_DSP_OFFSET;\ ++ register u32 data = \ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_ICLKEN_DSPEN_DSP_IPIWrite32);\ ++ data &= ~(PRCM_CM_ICLKEN_DSP_EN_DSP_IPI_MASK);\ ++ newValue <<= PRCM_CM_ICLKEN_DSP_EN_DSP_IPI_OFFSET;\ ++ newValue &= PRCM_CM_ICLKEN_DSP_EN_DSP_IPI_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define PRCMCM_IDLEST_DSPReadRegister32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_IDLEST_DSPReadRegister32),\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+PRCM_CM_IDLEST_DSP_OFFSET)) ++ ++ ++#define PRCMCM_IDLEST_DSPST_IPIRead32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_IDLEST_DSPST_IPIRead32),\ ++ (((RD_MEM_32_VOLATILE((((u32)(baseAddress))+\ ++ (PRCM_CM_IDLEST_DSP_OFFSET)))) &\ ++ PRCM_CM_IDLEST_DSP_ST_IPI_MASK) >>\ ++ PRCM_CM_IDLEST_DSP_ST_IPI_OFFSET)) ++ ++ ++#define PRM_IDLEST_IVA2ST_IVA2Read32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_IDLEST_DSPST_DSPRead32),\ ++ (((RD_MEM_32_VOLATILE((((u32)(baseAddress))+\ ++ (CM_IDLEST_IVA2_OFFSET)))) &\ ++ CM_IDLEST_IVA2_ST_IVA2_MASK) >>\ ++ CM_IDLEST_IVA2_ST_IVA2_OFFSET)) ++ ++ ++#define PRCMCM_AUTOIDLE_DSPAUTO_DSP_IPIWrite32(baseAddress, value)\ ++{\ ++ const u32 offset = PRCM_CM_AUTOIDLE_DSP_OFFSET;\ ++ register u32 data =\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_AUTOIDLE_DSPAUTO_DSP_IPIWrite32);\ ++ data &= ~(PRCM_CM_AUTOIDLE_DSP_AUTO_DSP_IPI_MASK);\ ++ newValue <<= PRCM_CM_AUTOIDLE_DSP_AUTO_DSP_IPI_OFFSET;\ ++ newValue &= PRCM_CM_AUTOIDLE_DSP_AUTO_DSP_IPI_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define PRCMCM_CLKSEL_DSPSYNC_DSPWrite32(baseAddress,value)\ ++{\ ++ const u32 offset = PRCM_CM_CLKSEL_DSP_OFFSET;\ ++ register u32 data = \ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSEL_DSPSYNC_DSPWrite32);\ ++ data &= ~(PRCM_CM_CLKSEL_DSP_SYNC_DSP_MASK);\ ++ newValue <<= PRCM_CM_CLKSEL_DSP_SYNC_DSP_OFFSET;\ ++ newValue &= PRCM_CM_CLKSEL_DSP_SYNC_DSP_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define PRCMCM_CLKSEL_DSPCLKSEL_DSP_IFWrite32(baseAddress, value)\ ++{\ ++ const u32 offset = PRCM_CM_CLKSEL_DSP_OFFSET;\ ++ register u32 data = \ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSEL_DSPCLKSEL_DSP_IFWrite32);\ ++ data &= ~(PRCM_CM_CLKSEL_DSP_CLKSEL_DSP_IF_MASK);\ ++ newValue <<= PRCM_CM_CLKSEL_DSP_CLKSEL_DSP_IF_OFFSET;\ ++ newValue &= PRCM_CM_CLKSEL_DSP_CLKSEL_DSP_IF_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define PRCMCM_CLKSEL_DSPCLKSEL_DSPWrite32(baseAddress, value)\ ++{\ ++ const u32 offset = PRCM_CM_CLKSEL_DSP_OFFSET;\ ++ register u32 data = \ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSEL_DSPCLKSEL_DSPWrite32);\ ++ data &= ~(PRCM_CM_CLKSEL_DSP_CLKSEL_DSP_MASK);\ ++ newValue <<= PRCM_CM_CLKSEL_DSP_CLKSEL_DSP_OFFSET;\ ++ newValue &= PRCM_CM_CLKSEL_DSP_CLKSEL_DSP_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define PRCMCM_CLKSTCTRL_IVA2WriteRegister32(baseAddress, value)\ ++{\ ++ const u32 offset = PRCM_CM_CLKSTCTRL_IVA2_OFFSET;\ ++ register u32 data = \ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSTCTRL_IVA2WriteRegister32);\ ++ data &= ~(CM_CLKSTCTRL_IVA2_MASK);\ ++ newValue <<= CM_CLKSTCTRL_IVA2_OFFSET;\ ++ newValue &= CM_CLKSTCTRL_IVA2_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define PRCMCM_CLKSTCTRL_DSPAutostate_DSPRead32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSTCTRL_DSPAutostate_DSPRead32),\ ++ (((RD_MEM_32_VOLATILE((((u32)(baseAddress))+\ ++ (PRCM_CM_CLKSTCTRL_DSP_OFFSET)))) &\ ++ PRCM_CM_CLKSTCTRL_DSP_Autostate_DSP_MASK) >>\ ++ PRCM_CM_CLKSTCTRL_DSP_Autostate_DSP_OFFSET)) ++ ++ ++#define PRCMCM_CLKSTCTRL_DSPAutostate_DSPWrite32(baseAddress, value)\ ++{\ ++ const u32 offset = PRCM_CM_CLKSTCTRL_DSP_OFFSET;\ ++ register u32 data = \ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMCM_CLKSTCTRL_DSPAutostate_DSPWrite32);\ ++ data &= ~(PRCM_CM_CLKSTCTRL_DSP_Autostate_DSP_MASK);\ ++ newValue <<= PRCM_CM_CLKSTCTRL_DSP_Autostate_DSP_OFFSET;\ ++ newValue &= PRCM_CM_CLKSTCTRL_DSP_Autostate_DSP_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define PRCMRM_RSTCTRL_DSPReadRegister32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMRM_RSTCTRL_DSPReadRegister32),\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+PRCM_RM_RSTCTRL_DSP_OFFSET)) ++ ++ ++#define PRM_RSTCTRL_IVA2RST1_DSPWrite32(baseAddress,value)\ ++{\ ++ const u32 offset = PRM_RSTCTRL_IVA2_OFFSET;\ ++ register u32 data =\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMRM_RSTCTRL_DSPRST1_DSPWrite32);\ ++ data &= ~(PRM_RSTCTRL_IVA2_RST1_MASK);\ ++ newValue <<= PRM_RSTCTRL_IVA2_RST1_OFFSET;\ ++ newValue &= PRM_RSTCTRL_IVA2_RST1_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define PRM_RSTCTRL_IVA2RST2_DSPWrite32(baseAddress,value)\ ++{\ ++ const u32 offset = PRM_RSTCTRL_IVA2_OFFSET;\ ++ register u32 data =\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMRM_RSTCTRL_DSPRST1_DSPWrite32);\ ++ data &= ~(PRM_RSTCTRL_IVA2_RST2_MASK);\ ++ newValue <<= PRM_RSTCTRL_IVA2_RST2_OFFSET;\ ++ newValue &= PRM_RSTCTRL_IVA2_RST2_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define PRM_RSTCTRL_IVA2RST3_DSPWrite32(baseAddress,value)\ ++{\ ++ const u32 offset = PRM_RSTCTRL_IVA2_OFFSET;\ ++ register u32 data =\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMRM_RSTCTRL_DSPRST1_DSPWrite32);\ ++ data &= ~(PRM_RSTCTRL_IVA2_RST3_MASK);\ ++ newValue <<= PRM_RSTCTRL_IVA2_RST3_OFFSET;\ ++ newValue &= PRM_RSTCTRL_IVA2_RST3_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define PRCMRM_RSTST_DSPReadRegister32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMRM_RSTST_DSPReadRegister32),\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+PRCM_RM_RSTST_DSP_OFFSET)) ++ ++ ++#define PRCMRM_RSTST_DSPWriteRegister32(baseAddress,value)\ ++{\ ++ const u32 offset = PRCM_RM_RSTST_DSP_OFFSET;\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMRM_RSTST_DSPWriteRegister32);\ ++ WR_MEM_32_VOLATILE(((u32)(baseAddress))+offset, newValue);\ ++} ++ ++ ++#define PRCMPM_PWSTCTRL_DSPForceStateWrite32(baseAddress, value)\ ++{\ ++ const u32 offset = PRCM_PM_PWSTCTRL_DSP_OFFSET;\ ++ register u32 data = \ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+offset);\ ++ register u32 newValue = ((u32)(value));\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMPM_PWSTCTRL_DSPForceStateWrite32);\ ++ data &= ~(PRCM_PM_PWSTCTRL_DSP_ForceState_MASK);\ ++ newValue <<= PRCM_PM_PWSTCTRL_DSP_ForceState_OFFSET;\ ++ newValue &= PRCM_PM_PWSTCTRL_DSP_ForceState_MASK;\ ++ newValue |= data;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, newValue);\ ++} ++ ++ ++#define PRCMPM_PWSTCTRL_IVA2PowerStateWriteON32(baseAddress)\ ++{\ ++ const u32 offset = PRCM_PM_PWSTCTRL_IVA2_OFFSET;\ ++ const u32 newValue = (u32)PRCMPM_PWSTCTRL_IVA2PowerStateON <<\ ++ PRCM_PM_PWSTCTRL_IVA2_PowerState_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((u32)(baseAddress)+offset);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMPM_PWSTCTRL_IVA2PowerStateWriteON32);\ ++ data &= ~(PRCM_PM_PWSTCTRL_IVA2_PowerState_MASK);\ ++ data |= newValue;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, data);\ ++} ++ ++ ++#define PRCMPM_PWSTCTRL_IVA2PowerStateWriteOFF32(baseAddress)\ ++{\ ++ const u32 offset = PRCM_PM_PWSTCTRL_IVA2_OFFSET;\ ++ const u32 newValue = (u32)PRCMPM_PWSTCTRL_IVA2PowerStateOFF <<\ ++ PRCM_PM_PWSTCTRL_IVA2_PowerState_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((u32)(baseAddress)+offset);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMPM_PWSTCTRL_IVA2PowerStateWriteOFF32);\ ++ data &= ~(PRCM_PM_PWSTCTRL_IVA2_PowerState_MASK);\ ++ data |= newValue;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, data);\ ++} ++ ++ ++#define PRCMPM_PWSTCTRL_DSPPowerStateWriteRET32(baseAddress)\ ++{\ ++ const u32 offset = PRCM_PM_PWSTCTRL_DSP_OFFSET;\ ++ const u32 newValue = (u32)PRCMPM_PWSTCTRL_DSPPowerStateRET <<\ ++ PRCM_PM_PWSTCTRL_DSP_PowerState_OFFSET;\ ++ register u32 data = RD_MEM_32_VOLATILE((u32)(baseAddress)+offset);\ ++ _DEBUG_LEVEL_1_EASI(EASIL1_PRCMPM_PWSTCTRL_DSPPowerStateWriteRET32);\ ++ data &= ~(PRCM_PM_PWSTCTRL_DSP_PowerState_MASK);\ ++ data |= newValue;\ ++ WR_MEM_32_VOLATILE((u32)(baseAddress)+offset, data);\ ++} ++ ++ ++#define PRCMPM_PWSTST_DSPReadRegister32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMPM_PWSTST_DSPReadRegister32),\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+PRCM_PM_PWSTST_DSP_OFFSET)) ++ ++ ++#define PRCMPM_PWSTST_IVA2ReadRegister32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMPM_PWSTST_IVA2ReadRegister32),\ ++ RD_MEM_32_VOLATILE(((u32)(baseAddress))+PRCM_PM_PWSTST_IVA2_OFFSET)) ++ ++ ++#define PRCMPM_PWSTST_DSPInTransitionRead32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMPM_PWSTST_DSPInTransitionRead32),\ ++ (((RD_MEM_32_VOLATILE((((u32)(baseAddress))+\ ++ (PRCM_PM_PWSTST_DSP_OFFSET)))) &\ ++ PRCM_PM_PWSTST_DSP_InTransition_MASK) >>\ ++ PRCM_PM_PWSTST_DSP_InTransition_OFFSET)) ++ ++ ++#define PRCMPM_PWSTST_IVA2InTransitionRead32(baseAddress)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMPM_PWSTST_IVA2InTransitionRead32),\ ++ (((RD_MEM_32_VOLATILE((((u32)(baseAddress))+\ ++ (PRCM_PM_PWSTST_IVA2_OFFSET)))) &\ ++ PRCM_PM_PWSTST_IVA2_InTransition_MASK) >>\ ++ PRCM_PM_PWSTST_IVA2_InTransition_OFFSET)) ++ ++ ++#define PRCMPM_PWSTST_DSPPowerStateStGet32(var)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMPM_PWSTST_DSPPowerStateStGet32),\ ++ (u32)((((u32)(var)) & PRCM_PM_PWSTST_DSP_PowerStateSt_MASK) >>\ ++ PRCM_PM_PWSTST_DSP_PowerStateSt_OFFSET)) ++ ++ ++#define PRCMPM_PWSTST_IVA2PowerStateStGet32(var)\ ++ (_DEBUG_LEVEL_1_EASI(EASIL1_PRCMPM_PWSTST_IVA2PowerStateStGet32),\ ++ (u32)((((u32)(var)) & PRCM_PM_PWSTST_IVA2_PowerStateSt_MASK) >>\ ++ PRCM_PM_PWSTST_IVA2_PowerStateSt_OFFSET)) ++ ++ ++#endif /* USE_LEVEL_1_MACROS */ ++ ++#endif /* _PRCM_REG_ACM_H */ +diff --git a/drivers/dsp/bridge/hw/hw_defs.h b/drivers/dsp/bridge/hw/hw_defs.h +new file mode 100644 +index 0000000..a973f5c +--- /dev/null ++++ b/drivers/dsp/bridge/hw/hw_defs.h +@@ -0,0 +1,73 @@ ++/* ++ * hw_defs.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2007 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== hw_defs.h ======== ++ * Description: ++ * Global HW definitions ++ * ++ *! Revision History: ++ *! ================ ++ *! 19 Apr 2004 sb: Added generic page size, endianness and element size defns ++ *! 16 Feb 2003 sb: Initial version ++ */ ++#ifndef __HW_DEFS_H ++#define __HW_DEFS_H ++ ++#include ++ ++/* Page size */ ++#define HW_PAGE_SIZE_4KB 0x1000 ++#define HW_PAGE_SIZE_64KB 0x10000 ++#define HW_PAGE_SIZE_1MB 0x100000 ++#define HW_PAGE_SIZE_16MB 0x1000000 ++ ++/* HW_STATUS: return type for HW API */ ++typedef long HW_STATUS; ++ ++/* HW_SetClear_t: Enumerated Type used to set and clear any bit */ ++enum HW_SetClear_t { ++ HW_CLEAR, ++ HW_SET ++} ; ++ ++/* HW_Endianism_t: Enumerated Type used to specify the endianism ++ * Do NOT change these values. They are used as bit fields. */ ++enum HW_Endianism_t { ++ HW_LITTLE_ENDIAN, ++ HW_BIG_ENDIAN ++ ++} ; ++ ++/* HW_ElementSize_t: Enumerated Type used to specify the element size ++ * Do NOT change these values. They are used as bit fields. */ ++enum HW_ElementSize_t { ++ HW_ELEM_SIZE_8BIT, ++ HW_ELEM_SIZE_16BIT, ++ HW_ELEM_SIZE_32BIT, ++ HW_ELEM_SIZE_64BIT ++ ++} ; ++ ++/* HW_IdleMode_t: Enumerated Type used to specify Idle modes */ ++ enum HW_IdleMode_t { ++ HW_FORCE_IDLE, ++ HW_NO_IDLE, ++ HW_SMART_IDLE ++ } ; ++ ++#endif /* __HW_DEFS_H */ +diff --git a/drivers/dsp/bridge/hw/hw_dspssC64P.c b/drivers/dsp/bridge/hw/hw_dspssC64P.c +new file mode 100644 +index 0000000..a4b7c30 +--- /dev/null ++++ b/drivers/dsp/bridge/hw/hw_dspssC64P.c +@@ -0,0 +1,55 @@ ++/* ++ * hw_dspss64P.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== hw_dspss64P.c ======== ++ * Description: ++ * API definitions to configure DSP Subsystem modules like IPI ++ * ++ *! Revision History: ++ *! ================ ++ *! 19 Apr 2004 sb: Implemented HW_DSPSS_IPIEndianismSet ++ *! 16 Feb 2003 sb: Initial version ++ */ ++ ++/* PROJECT SPECIFIC INCLUDE FILES */ ++#include ++#include ++#include ++#include ++#include ++ ++/* HW FUNCTIONS */ ++HW_STATUS HW_DSPSS_BootModeSet(const u32 baseAddress, ++ enum HW_DSPSYSC_BootMode_t bootMode, ++ const u32 bootAddress) ++{ ++ HW_STATUS status = RET_OK; ++ u32 offset = SYSC_IVA2BOOTMOD_OFFSET; ++ u32 alignedBootAddr; ++ ++ /* if Boot mode it DIRECT BOOT, check that the bootAddress is ++ * aligned to atleast 1K :: TODO */ ++ WR_MEM_32_VOLATILE((baseAddress) + offset, bootMode); ++ ++ offset = SYSC_IVA2BOOTADDR_OFFSET; ++ ++ alignedBootAddr = bootAddress & SYSC_IVA2BOOTADDR_MASK; ++ ++ WR_MEM_32_VOLATILE((baseAddress) + offset, alignedBootAddr); ++ ++ return status; ++} +diff --git a/drivers/dsp/bridge/hw/hw_dspssC64P.h b/drivers/dsp/bridge/hw/hw_dspssC64P.h +new file mode 100644 +index 0000000..493effd +--- /dev/null ++++ b/drivers/dsp/bridge/hw/hw_dspssC64P.h +@@ -0,0 +1,48 @@ ++/* ++ * hw_dspssC64P.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== hw_dspss.h ======== ++ * Description: ++ * DSP Subsystem API declarations ++ * ++ *! Revision History: ++ *! ================ ++ *! 19-Apr-2004 sb: Removed redundant argument from HW_DSPSS_IPIEndianismSet ++ *! Moved endianness and element size to generic hw_defs.h ++ *! 16 Feb 2003 sb: Initial version ++ */ ++ ++#ifndef __HW_DSPSS_H ++#define __HW_DSPSS_H ++#include ++ ++ enum HW_DSPSYSC_BootMode_t { ++ HW_DSPSYSC_DIRECTBOOT = 0x0, ++ HW_DSPSYSC_IDLEBOOT = 0x1, ++ HW_DSPSYSC_SELFLOOPBOOT = 0x2, ++ HW_DSPSYSC_USRBOOTSTRAP = 0x3, ++ HW_DSPSYSC_DEFAULTRESTORE = 0x4 ++ } ; ++ ++#define HW_DSP_IDLEBOOT_ADDR 0x007E0000 ++ ++ extern HW_STATUS HW_DSPSS_BootModeSet(const u32 baseAddress, ++ enum HW_DSPSYSC_BootMode_t bootMode, ++ const u32 bootAddress); ++ ++#endif /* __HW_DSPSS_H */ +diff --git a/drivers/dsp/bridge/hw/hw_mbox.c b/drivers/dsp/bridge/hw/hw_mbox.c +new file mode 100644 +index 0000000..9f14f34 +--- /dev/null ++++ b/drivers/dsp/bridge/hw/hw_mbox.c +@@ -0,0 +1,244 @@ ++/* ++ * hw_mbox.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2007 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== hw_mbox.c ======== ++ * Description: ++ * Mailbox messaging & configuration API definitions ++ * ++ *! Revision History: ++ *! ================ ++ *! 16 Feb 2003 sb: Initial version ++ */ ++ ++#include ++#include "MLBRegAcM.h" ++#include ++#include ++ ++/* width in bits of MBOX Id */ ++#define HW_MBOX_ID_WIDTH 2 ++ ++struct MAILBOX_CONTEXT mboxsetting = {0x4, 0x1, 0x1}; ++ ++/* Saves the mailbox context */ ++HW_STATUS HW_MBOX_saveSettings(u32 baseAddress) ++{ ++ HW_STATUS status = RET_OK; ++ ++ mboxsetting.sysconfig = MLBMAILBOX_SYSCONFIGReadRegister32(baseAddress); ++ /* Get current enable status */ ++ mboxsetting.irqEnable0 = MLBMAILBOX_IRQENABLE___0_3ReadRegister32 ++ (baseAddress, HW_MBOX_U0_ARM); ++ mboxsetting.irqEnable1 = MLBMAILBOX_IRQENABLE___0_3ReadRegister32 ++ (baseAddress, HW_MBOX_U1_DSP1); ++ return status; ++} ++ ++/* Restores the mailbox context */ ++HW_STATUS HW_MBOX_restoreSettings(u32 baseAddress) ++{ ++ HW_STATUS status = RET_OK; ++ /* Restor IRQ enable status */ ++ MLBMAILBOX_IRQENABLE___0_3WriteRegister32(baseAddress, HW_MBOX_U0_ARM, ++ mboxsetting.irqEnable0); ++ MLBMAILBOX_IRQENABLE___0_3WriteRegister32(baseAddress, HW_MBOX_U1_DSP1, ++ mboxsetting.irqEnable1); ++ /* Restore Sysconfig register */ ++ MLBMAILBOX_SYSCONFIGWriteRegister32(baseAddress, mboxsetting.sysconfig); ++ return status; ++} ++ ++/* Reads a u32 from the sub module message box Specified. if there are no ++ * messages in the mailbox then and error is returned. */ ++HW_STATUS HW_MBOX_MsgRead(const u32 baseAddress, const HW_MBOX_Id_t mailBoxId, ++ u32 *const pReadValue) ++{ ++ HW_STATUS status = RET_OK; ++ ++ /* Check input parameters */ ++ CHECK_INPUT_PARAM(baseAddress, 0, RET_BAD_NULL_PARAM, RES_MBOX_BASE + ++ RES_INVALID_INPUT_PARAM); ++ CHECK_INPUT_PARAM(pReadValue, NULL, RET_BAD_NULL_PARAM, RES_MBOX_BASE + ++ RES_INVALID_INPUT_PARAM); ++ CHECK_INPUT_RANGE_MIN0(mailBoxId, HW_MBOX_ID_MAX, RET_INVALID_ID, ++ RES_MBOX_BASE + RES_INVALID_INPUT_PARAM); ++ ++ /* Read 32-bit message in mail box */ ++ *pReadValue = MLBMAILBOX_MESSAGE___0_15ReadRegister32(baseAddress, ++ (u32)mailBoxId); ++ ++ return status; ++} ++ ++/* Writes a u32 from the sub module message box Specified. */ ++HW_STATUS HW_MBOX_MsgWrite(const u32 baseAddress, const HW_MBOX_Id_t mailBoxId, ++ const u32 writeValue) ++{ ++ HW_STATUS status = RET_OK; ++ ++ /* Check input parameters */ ++ CHECK_INPUT_PARAM(baseAddress, 0, RET_BAD_NULL_PARAM, RES_MBOX_BASE + ++ RES_INVALID_INPUT_PARAM); ++ CHECK_INPUT_RANGE_MIN0(mailBoxId, HW_MBOX_ID_MAX, RET_INVALID_ID, ++ RES_MBOX_BASE + RES_INVALID_INPUT_PARAM); ++ ++ /* Write 32-bit value to mailbox */ ++ MLBMAILBOX_MESSAGE___0_15WriteRegister32(baseAddress, (u32)mailBoxId, ++ (u32)writeValue); ++ ++ return status; ++} ++ ++/* Gets number of messages in a specified mailbox. */ ++HW_STATUS HW_MBOX_NumMsgGet(const u32 baseAddress, const HW_MBOX_Id_t mailBoxId, ++ u32 *const pNumMsg) ++{ ++ HW_STATUS status = RET_OK; ++ ++ /* Check input parameters */ ++ CHECK_INPUT_PARAM(baseAddress, 0, RET_BAD_NULL_PARAM, RES_MBOX_BASE + ++ RES_INVALID_INPUT_PARAM); ++ CHECK_INPUT_PARAM(pNumMsg, NULL, RET_BAD_NULL_PARAM, RES_MBOX_BASE + ++ RES_INVALID_INPUT_PARAM); ++ ++ CHECK_INPUT_RANGE_MIN0(mailBoxId, HW_MBOX_ID_MAX, RET_INVALID_ID, ++ RES_MBOX_BASE + RES_INVALID_INPUT_PARAM); ++ ++ /* Get number of messages available for MailBox */ ++ *pNumMsg = MLBMAILBOX_MSGSTATUS___0_15NbOfMsgMBmRead32(baseAddress, ++ (u32)mailBoxId); ++ ++ return status; ++} ++ ++/* Enables the specified IRQ. */ ++HW_STATUS HW_MBOX_EventEnable(const u32 baseAddress, ++ const HW_MBOX_Id_t mailBoxId, ++ const HW_MBOX_UserId_t userId, ++ const u32 events) ++{ ++ HW_STATUS status = RET_OK; ++ u32 irqEnableReg; ++ ++ /* Check input parameters */ ++ CHECK_INPUT_PARAM(baseAddress, 0, RET_BAD_NULL_PARAM, RES_MBOX_BASE + ++ RES_INVALID_INPUT_PARAM); ++ CHECK_INPUT_RANGE_MIN0(mailBoxId, HW_MBOX_ID_MAX, RET_INVALID_ID, ++ RES_MBOX_BASE + RES_INVALID_INPUT_PARAM); ++ CHECK_INPUT_RANGE_MIN0(enableIrq, HW_MBOX_INT_MAX, RET_INVALID_ID, ++ RES_MBOX_BASE + RES_INVALID_INPUT_PARAM); ++ CHECK_INPUT_RANGE_MIN0(userId, HW_MBOX_USER_MAX, RET_INVALID_ID, ++ RES_MBOX_BASE + RES_INVALID_INPUT_PARAM); ++ ++ /* Get current enable status */ ++ irqEnableReg = MLBMAILBOX_IRQENABLE___0_3ReadRegister32(baseAddress, ++ (u32)userId); ++ ++ /* update enable value */ ++ irqEnableReg |= ((u32)(events)) << (((u32)(mailBoxId)) * ++ HW_MBOX_ID_WIDTH); ++ ++ /* write new enable status */ ++ MLBMAILBOX_IRQENABLE___0_3WriteRegister32(baseAddress, (u32)userId, ++ (u32)irqEnableReg); ++ ++ mboxsetting.sysconfig = MLBMAILBOX_SYSCONFIGReadRegister32(baseAddress); ++ /* Get current enable status */ ++ mboxsetting.irqEnable0 = MLBMAILBOX_IRQENABLE___0_3ReadRegister32 ++ (baseAddress, HW_MBOX_U0_ARM); ++ mboxsetting.irqEnable1 = MLBMAILBOX_IRQENABLE___0_3ReadRegister32 ++ (baseAddress, HW_MBOX_U1_DSP1); ++ return status; ++} ++ ++/* Disables the specified IRQ. */ ++HW_STATUS HW_MBOX_EventDisable(const u32 baseAddress, ++ const HW_MBOX_Id_t mailBoxId, ++ const HW_MBOX_UserId_t userId, ++ const u32 events) ++{ ++ HW_STATUS status = RET_OK; ++ u32 irqDisableReg; ++ ++ /* Check input parameters */ ++ CHECK_INPUT_PARAM(baseAddress, 0, RET_BAD_NULL_PARAM, RES_MBOX_BASE + ++ RES_INVALID_INPUT_PARAM); ++ CHECK_INPUT_RANGE_MIN0(mailBoxId, HW_MBOX_ID_MAX, RET_INVALID_ID, ++ RES_MBOX_BASE + RES_INVALID_INPUT_PARAM); ++ CHECK_INPUT_RANGE_MIN0(disableIrq, HW_MBOX_INT_MAX, RET_INVALID_ID, ++ RES_MBOX_BASE + RES_INVALID_INPUT_PARAM); ++ CHECK_INPUT_RANGE_MIN0(userId, HW_MBOX_USER_MAX, RET_INVALID_ID, ++ RES_MBOX_BASE + RES_INVALID_INPUT_PARAM); ++ ++ /* Get current enable status */ ++ irqDisableReg = MLBMAILBOX_IRQENABLE___0_3ReadRegister32(baseAddress, ++ (u32)userId); ++ ++ /* update enable value */ ++ irqDisableReg &= ~(((u32)(events)) << (((u32)(mailBoxId)) * ++ HW_MBOX_ID_WIDTH)); ++ ++ /* write new enable status */ ++ MLBMAILBOX_IRQENABLE___0_3WriteRegister32(baseAddress, (u32)userId, ++ (u32)irqDisableReg); ++ ++ return status; ++} ++ ++/* Sets the status of the specified IRQ. */ ++HW_STATUS HW_MBOX_EventAck(const u32 baseAddress, const HW_MBOX_Id_t mailBoxId, ++ const HW_MBOX_UserId_t userId, const u32 event) ++{ ++ HW_STATUS status = RET_OK; ++ u32 irqStatusReg; ++ ++ /* Check input parameters */ ++ CHECK_INPUT_PARAM(baseAddress, 0, RET_BAD_NULL_PARAM, RES_MBOX_BASE + ++ RES_INVALID_INPUT_PARAM); ++ ++ CHECK_INPUT_RANGE_MIN0(irqStatus, HW_MBOX_INT_MAX, RET_INVALID_ID, ++ RES_MBOX_BASE + RES_INVALID_INPUT_PARAM); ++ CHECK_INPUT_RANGE_MIN0(mailBoxId, HW_MBOX_ID_MAX, RET_INVALID_ID, ++ RES_MBOX_BASE + RES_INVALID_INPUT_PARAM); ++ CHECK_INPUT_RANGE_MIN0(userId, HW_MBOX_USER_MAX, RET_INVALID_ID, ++ RES_MBOX_BASE + RES_INVALID_INPUT_PARAM); ++ ++ /* calculate status to write */ ++ irqStatusReg = ((u32)event) << (((u32)(mailBoxId)) * ++ HW_MBOX_ID_WIDTH); ++ ++ /* clear Irq Status for specified mailbox/User Id */ ++ MLBMAILBOX_IRQSTATUS___0_3WriteRegister32(baseAddress, (u32)userId, ++ (u32)irqStatusReg); ++ ++ /* ++ * FIXME: Replace all this custom register access with standard ++ * __raw_read/write(). ++ * ++ * FIXME: Replace all interrupt handlers with standard linux style ++ * interrupt handlers. ++ * ++ * FIXME: Replace direct access to PRCM registers with omap standard ++ * PRCM register access. ++ * ++ * Flush posted write for the irq status to avoid spurious interrupts. ++ */ ++ MLBMAILBOX_IRQSTATUS___0_3ReadRegister32(baseAddress, (u32)userId); ++ ++ return status; ++} +diff --git a/drivers/dsp/bridge/hw/hw_mbox.h b/drivers/dsp/bridge/hw/hw_mbox.h +new file mode 100644 +index 0000000..d2981d3 +--- /dev/null ++++ b/drivers/dsp/bridge/hw/hw_mbox.h +@@ -0,0 +1,323 @@ ++/* ++ * hw_mbox.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2007 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== hw_mbox.h ======== ++ * Description: ++ * HW Mailbox API and types definitions ++ * ++ *! Revision History: ++ *! ================ ++ *! 16 Feb 2003 sb: Initial version ++ */ ++#ifndef __MBOX_H ++#define __MBOX_H ++ ++/* Bitmasks for Mailbox interrupt sources */ ++#define HW_MBOX_INT_NEW_MSG 0x1 ++#define HW_MBOX_INT_NOT_FULL 0x2 ++#define HW_MBOX_INT_ALL 0x3 ++ ++/* Maximum number of messages that mailbox can hald at a time. */ ++#define HW_MBOX_MAX_NUM_MESSAGES 4 ++ ++/* HW_MBOX_Id_t: Enumerated Type used to specify Mailbox Sub Module Id Number */ ++typedef enum HW_MBOX_Id_label { ++ HW_MBOX_ID_0, ++ HW_MBOX_ID_1, ++ HW_MBOX_ID_2, ++ HW_MBOX_ID_3, ++ HW_MBOX_ID_4, ++ HW_MBOX_ID_5 ++ ++} HW_MBOX_Id_t, *pHW_MBOX_Id_t; ++ ++/* HW_MBOX_UserId_t: Enumerated Type used to specify Mail box User Id */ ++typedef enum HW_MBOX_UserId_label { ++ HW_MBOX_U0_ARM, ++ HW_MBOX_U1_DSP1, ++ HW_MBOX_U2_DSP2, ++ HW_MBOX_U3_ARM ++ ++} HW_MBOX_UserId_t, *pHW_MBOX_UserId_t; ++ ++/* Mailbox context settings */ ++struct MAILBOX_CONTEXT { ++ u32 sysconfig; ++ u32 irqEnable0; ++ u32 irqEnable1; ++}; ++ ++/* ++* FUNCTION : HW_MBOX_MsgRead ++* ++* INPUTS: ++* ++* Identifier : baseAddress ++* Type : const u32 ++* Description : Base Address of instance of Mailbox module ++* ++* Identifier : mailBoxId ++* Type : const HW_MBOX_Id_t ++* Description : Mail Box Sub module Id to read ++* ++* OUTPUTS: ++* ++* Identifier : pReadValue ++* Type : u32 *const ++* Description : Value read from MailBox ++* ++* RETURNS: ++* ++* Type : ReturnCode_t ++* Description : RET_OK No errors occured ++* RET_BAD_NULL_PARAM Address/ptr Paramater was set to 0/NULL ++* RET_INVALID_ID Invalid Id used ++* RET_EMPTY Mailbox empty ++* ++* PURPOSE: : this function reads a u32 from the sub module message ++* box Specified. if there are no messages in the mailbox ++* then and error is returned. ++*/ ++extern HW_STATUS HW_MBOX_MsgRead(const u32 baseAddress, ++ const HW_MBOX_Id_t mailBoxId, ++ u32 *const pReadValue); ++ ++/* ++* FUNCTION : HW_MBOX_MsgWrite ++* ++* INPUTS: ++* ++* Identifier : baseAddress ++* Type : const u32 ++* Description : Base Address of instance of Mailbox module ++* ++* Identifier : mailBoxId ++* Type : const HW_MBOX_Id_t ++* Description : Mail Box Sub module Id to write ++* ++* Identifier : writeValue ++* Type : const u32 ++* Description : Value to write to MailBox ++* ++* RETURNS: ++* ++* Type : ReturnCode_t ++* Description : RET_OK No errors occured ++* RET_BAD_NULL_PARAM Address/pointer Paramater was set to 0/NULL ++* RET_INVALID_ID Invalid Id used ++* ++* PURPOSE: : this function writes a u32 from the sub module message ++* box Specified. ++*/ ++extern HW_STATUS HW_MBOX_MsgWrite( ++ const u32 baseAddress, ++ const HW_MBOX_Id_t mailBoxId, ++ const u32 writeValue ++ ); ++ ++/* ++* FUNCTION : HW_MBOX_NumMsgGet ++* ++* INPUTS: ++* ++* Identifier : baseAddress ++* Type : const u32 ++* Description : Base Address of instance of Mailbox module ++* ++* Identifier : mailBoxId ++* Type : const HW_MBOX_Id_t ++* Description : Mail Box Sub module Id to get num messages ++* ++* OUTPUTS: ++* ++* Identifier : pNumMsg ++* Type : u32 *const ++* Description : Number of messages in mailbox ++* ++* RETURNS: ++* ++* Type : ReturnCode_t ++* Description : RET_OK No errors occured ++* RET_BAD_NULL_PARAM Address/pointer Paramater was set to 0/NULL ++* RET_INVALID_ID Inavlid ID input at parameter ++* ++* PURPOSE: : this function gets number of messages in a specified mailbox. ++*/ ++extern HW_STATUS HW_MBOX_NumMsgGet( ++ const u32 baseAddress, ++ const HW_MBOX_Id_t mailBoxId, ++ u32 *const pNumMsg ++ ); ++ ++/* ++* FUNCTION : HW_MBOX_EventEnable ++* ++* INPUTS: ++* ++* Identifier : baseAddress ++* Type : const u32 ++* RET_BAD_NULL_PARAM Address/pointer Paramater was set to 0/NULL ++* ++* Identifier : mailBoxId ++* Type : const HW_MBOX_Id_t ++* Description : Mail Box Sub module Id to enable ++* ++* Identifier : userId ++* Type : const HW_MBOX_UserId_t ++* Description : Mail box User Id to enable ++* ++* Identifier : enableIrq ++* Type : const u32 ++* Description : Irq value to enable ++* ++* RETURNS: ++* ++* Type : ReturnCode_t ++* Description : RET_OK No errors occured ++* RET_BAD_NULL_PARAM A Pointer Paramater was set to NULL ++* RET_INVALID_ID Invalid Id used ++* ++* PURPOSE: : this function enables the specified IRQ. ++*/ ++extern HW_STATUS HW_MBOX_EventEnable( ++ const u32 baseAddress, ++ const HW_MBOX_Id_t mailBoxId, ++ const HW_MBOX_UserId_t userId, ++ const u32 events ++ ); ++ ++/* ++* FUNCTION : HW_MBOX_EventDisable ++* ++* INPUTS: ++* ++* Identifier : baseAddress ++* Type : const u32 ++* RET_BAD_NULL_PARAM Address/pointer Paramater was set to 0/NULL ++* ++* Identifier : mailBoxId ++* Type : const HW_MBOX_Id_t ++* Description : Mail Box Sub module Id to disable ++* ++* Identifier : userId ++* Type : const HW_MBOX_UserId_t ++* Description : Mail box User Id to disable ++* ++* Identifier : enableIrq ++* Type : const u32 ++* Description : Irq value to disable ++* ++* RETURNS: ++* ++* Type : ReturnCode_t ++* Description : RET_OK No errors occured ++* RET_BAD_NULL_PARAM A Pointer Paramater was set to NULL ++* RET_INVALID_ID Invalid Id used ++* ++* PURPOSE: : this function disables the specified IRQ. ++*/ ++extern HW_STATUS HW_MBOX_EventDisable( ++ const u32 baseAddress, ++ const HW_MBOX_Id_t mailBoxId, ++ const HW_MBOX_UserId_t userId, ++ const u32 events ++ ); ++ ++/* ++* FUNCTION : HW_MBOX_EventAck ++* ++* INPUTS: ++* ++* Identifier : baseAddress ++* Type : const u32 ++* Description : Base Address of instance of Mailbox module ++* ++* Identifier : mailBoxId ++* Type : const HW_MBOX_Id_t ++* Description : Mail Box Sub module Id to set ++* ++* Identifier : userId ++* Type : const HW_MBOX_UserId_t ++* Description : Mail box User Id to set ++* ++* Identifier : irqStatus ++* Type : const u32 ++* Description : The value to write IRQ status ++* ++* OUTPUTS: ++* ++* RETURNS: ++* ++* Type : ReturnCode_t ++* Description : RET_OK No errors occured ++* RET_BAD_NULL_PARAM Address Paramater was set to 0 ++* RET_INVALID_ID Invalid Id used ++* ++* PURPOSE: : this function sets the status of the specified IRQ. ++*/ ++extern HW_STATUS HW_MBOX_EventAck( ++ const u32 baseAddress, ++ const HW_MBOX_Id_t mailBoxId, ++ const HW_MBOX_UserId_t userId, ++ const u32 event ++ ); ++ ++/* ++* FUNCTION : HW_MBOX_saveSettings ++* ++* INPUTS: ++* ++* Identifier : baseAddress ++* Type : const u32 ++* Description : Base Address of instance of Mailbox module ++* ++* ++* RETURNS: ++* ++* Type : ReturnCode_t ++* Description : RET_OK No errors occured ++* RET_BAD_NULL_PARAM Address/pointer Paramater was set to 0/NULL ++* RET_INVALID_ID Invalid Id used ++* RET_EMPTY Mailbox empty ++* ++* PURPOSE: : this function saves the context of mailbox ++*/ ++extern HW_STATUS HW_MBOX_saveSettings(u32 baseAddres); ++ ++/* ++* FUNCTION : HW_MBOX_restoreSettings ++* ++* INPUTS: ++* ++* Identifier : baseAddress ++* Type : const u32 ++* Description : Base Address of instance of Mailbox module ++* ++* ++* RETURNS: ++* ++* Type : ReturnCode_t ++* Description : RET_OK No errors occured ++* RET_BAD_NULL_PARAM Address/pointer Paramater was set to 0/NULL ++* RET_INVALID_ID Invalid Id used ++* RET_EMPTY Mailbox empty ++* ++* PURPOSE: : this function restores the context of mailbox ++*/ ++extern HW_STATUS HW_MBOX_restoreSettings(u32 baseAddres); ++ ++#endif /* __MBOX_H */ +diff --git a/drivers/dsp/bridge/hw/hw_mmu.c b/drivers/dsp/bridge/hw/hw_mmu.c +new file mode 100644 +index 0000000..ab65de0 +--- /dev/null ++++ b/drivers/dsp/bridge/hw/hw_mmu.c +@@ -0,0 +1,598 @@ ++/* ++ * hw_mmu.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2007 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== hw_mmu.c ======== ++ * Description: ++ * API definitions to setup MMU TLB and PTE ++ * ++ *! Revision History: ++ *! ================ ++ *! 19-Apr-2004 sb TLBAdd and TLBFlush input the page size in bytes instead ++ of an enum. TLBAdd inputs mapping attributes struct instead ++ of individual arguments. ++ Removed MMU.h and other cosmetic updates. ++ *! 08-Mar-2004 sb Added the Page Table Management APIs ++ *! 16 Feb 2003 sb: Initial version ++ */ ++ ++#include ++#include "MMURegAcM.h" ++#include ++#include ++#include ++ ++#define MMU_BASE_VAL_MASK 0xFC00 ++#define MMU_PAGE_MAX 3 ++#define MMU_ELEMENTSIZE_MAX 3 ++#define MMU_ADDR_MASK 0xFFFFF000 ++#define MMU_TTB_MASK 0xFFFFC000 ++#define MMU_SECTION_ADDR_MASK 0xFFF00000 ++#define MMU_SSECTION_ADDR_MASK 0xFF000000 ++#define MMU_PAGE_TABLE_MASK 0xFFFFFC00 ++#define MMU_LARGE_PAGE_MASK 0xFFFF0000 ++#define MMU_SMALL_PAGE_MASK 0xFFFFF000 ++ ++#define MMU_LOAD_TLB 0x00000001 ++ ++/* HW_MMUPageSize_t: Enumerated Type used to specify the MMU Page Size(SLSS) */ ++enum HW_MMUPageSize_t { ++ HW_MMU_SECTION, ++ HW_MMU_LARGE_PAGE, ++ HW_MMU_SMALL_PAGE, ++ HW_MMU_SUPERSECTION ++} ; ++ ++/* ++* FUNCTION : MMU_FlushEntry ++* ++* INPUTS: ++* ++* Identifier : baseAddress ++* Type : const u32 ++* Description : Base Address of instance of MMU module ++* ++* RETURNS: ++* ++* Type : HW_STATUS ++* Description : RET_OK -- No errors occured ++* RET_BAD_NULL_PARAM -- A Pointer ++* Paramater was set to NULL ++* ++* PURPOSE: : Flush the TLB entry pointed by the ++* lock counter register ++* even if this entry is set protected ++* ++* METHOD: : Check the Input parameter and Flush a ++* single entry in the TLB. ++*/ ++static HW_STATUS MMU_FlushEntry(const u32 baseAddress); ++ ++/* ++* FUNCTION : MMU_SetCAMEntry ++* ++* INPUTS: ++* ++* Identifier : baseAddress ++* TypE : const u32 ++* Description : Base Address of instance of MMU module ++* ++* Identifier : pageSize ++* TypE : const u32 ++* Description : It indicates the page size ++* ++* Identifier : preservedBit ++* Type : const u32 ++* Description : It indicates the TLB entry is preserved entry ++* or not ++* ++* Identifier : validBit ++* Type : const u32 ++* Description : It indicates the TLB entry is valid entry or not ++* ++* ++* Identifier : virtualAddrTag ++* Type : const u32 ++* Description : virtual Address ++* ++* RETURNS: ++* ++* Type : HW_STATUS ++* Description : RET_OK -- No errors occured ++* RET_BAD_NULL_PARAM -- A Pointer Paramater ++* was set to NULL ++* RET_PARAM_OUT_OF_RANGE -- Input Parameter out ++* of Range ++* ++* PURPOSE: : Set MMU_CAM reg ++* ++* METHOD: : Check the Input parameters and set the CAM entry. ++*/ ++static HW_STATUS MMU_SetCAMEntry(const u32 baseAddress, ++ const u32 pageSize, ++ const u32 preservedBit, ++ const u32 validBit, ++ const u32 virtualAddrTag); ++ ++/* ++* FUNCTION : MMU_SetRAMEntry ++* ++* INPUTS: ++* ++* Identifier : baseAddress ++* Type : const u32 ++* Description : Base Address of instance of MMU module ++* ++* Identifier : physicalAddr ++* Type : const u32 ++* Description : Physical Address to which the corresponding ++* virtual Address shouldpoint ++* ++* Identifier : endianism ++* Type : HW_Endianism_t ++* Description : endianism for the given page ++* ++* Identifier : elementSize ++* Type : HW_ElementSize_t ++* Description : The element size ( 8,16, 32 or 64 bit) ++* ++* Identifier : mixedSize ++* Type : HW_MMUMixedSize_t ++* Description : Element Size to follow CPU or TLB ++* ++* RETURNS: ++* ++* Type : HW_STATUS ++* Description : RET_OK -- No errors occured ++* RET_BAD_NULL_PARAM -- A Pointer Paramater ++* was set to NULL ++* RET_PARAM_OUT_OF_RANGE -- Input Parameter ++* out of Range ++* ++* PURPOSE: : Set MMU_CAM reg ++* ++* METHOD: : Check the Input parameters and set the RAM entry. ++*/ ++static HW_STATUS MMU_SetRAMEntry(const u32 baseAddress, ++ const u32 physicalAddr, ++ enum HW_Endianism_t endianism, ++ enum HW_ElementSize_t elementSize, ++ enum HW_MMUMixedSize_t mixedSize); ++ ++/* HW FUNCTIONS */ ++ ++HW_STATUS HW_MMU_Enable(const u32 baseAddress) ++{ ++ HW_STATUS status = RET_OK; ++ ++ MMUMMU_CNTLMMUEnableWrite32(baseAddress, HW_SET); ++ ++ return status; ++} ++ ++HW_STATUS HW_MMU_Disable(const u32 baseAddress) ++{ ++ HW_STATUS status = RET_OK; ++ ++ MMUMMU_CNTLMMUEnableWrite32(baseAddress, HW_CLEAR); ++ ++ return status; ++} ++ ++HW_STATUS HW_MMU_NumLockedSet(const u32 baseAddress, ++ u32 numLockedEntries) ++{ ++ HW_STATUS status = RET_OK; ++ ++ MMUMMU_LOCKBaseValueWrite32(baseAddress, numLockedEntries); ++ ++ return status; ++} ++ ++HW_STATUS HW_MMU_VictimNumSet(const u32 baseAddress, ++ u32 victimEntryNum) ++{ ++ HW_STATUS status = RET_OK; ++ ++ MMUMMU_LOCKCurrentVictimWrite32(baseAddress, victimEntryNum); ++ ++ return status; ++} ++ ++HW_STATUS HW_MMU_EventAck(const u32 baseAddress, u32 irqMask) ++{ ++ HW_STATUS status = RET_OK; ++ ++ MMUMMU_IRQSTATUSWriteRegister32(baseAddress, irqMask); ++ ++ return status; ++} ++ ++HW_STATUS HW_MMU_EventDisable(const u32 baseAddress, ++ u32 irqMask) ++{ ++ HW_STATUS status = RET_OK; ++ u32 irqReg; ++ ++ irqReg = MMUMMU_IRQENABLEReadRegister32(baseAddress); ++ ++ MMUMMU_IRQENABLEWriteRegister32(baseAddress, irqReg & ~irqMask); ++ ++ return status; ++} ++ ++HW_STATUS HW_MMU_EventEnable(const u32 baseAddress, u32 irqMask) ++{ ++ HW_STATUS status = RET_OK; ++ u32 irqReg; ++ ++ irqReg = MMUMMU_IRQENABLEReadRegister32(baseAddress); ++ ++ MMUMMU_IRQENABLEWriteRegister32(baseAddress, irqReg | irqMask); ++ ++ return status; ++} ++ ++ ++HW_STATUS HW_MMU_EventStatus(const u32 baseAddress, u32 *irqMask) ++{ ++ HW_STATUS status = RET_OK; ++ ++ *irqMask = MMUMMU_IRQSTATUSReadRegister32(baseAddress); ++ ++ return status; ++} ++ ++ ++HW_STATUS HW_MMU_FaultAddrRead(const u32 baseAddress, u32 *addr) ++{ ++ HW_STATUS status = RET_OK; ++ ++ /*Check the input Parameters*/ ++ CHECK_INPUT_PARAM(baseAddress, 0, RET_BAD_NULL_PARAM, ++ RES_MMU_BASE + RES_INVALID_INPUT_PARAM); ++ ++ /* read values from register */ ++ *addr = MMUMMU_FAULT_ADReadRegister32(baseAddress); ++ ++ return status; ++} ++ ++HW_STATUS HW_MMU_TTBSet(const u32 baseAddress, u32 TTBPhysAddr) ++{ ++ HW_STATUS status = RET_OK; ++ u32 loadTTB; ++ ++ /*Check the input Parameters*/ ++ CHECK_INPUT_PARAM(baseAddress, 0, RET_BAD_NULL_PARAM, ++ RES_MMU_BASE + RES_INVALID_INPUT_PARAM); ++ ++ loadTTB = TTBPhysAddr & ~0x7FUL; ++ /* write values to register */ ++ MMUMMU_TTBWriteRegister32(baseAddress, loadTTB); ++ ++ return status; ++} ++ ++HW_STATUS HW_MMU_TWLEnable(const u32 baseAddress) ++{ ++ HW_STATUS status = RET_OK; ++ ++ MMUMMU_CNTLTWLEnableWrite32(baseAddress, HW_SET); ++ ++ return status; ++} ++ ++HW_STATUS HW_MMU_TWLDisable(const u32 baseAddress) ++{ ++ HW_STATUS status = RET_OK; ++ ++ MMUMMU_CNTLTWLEnableWrite32(baseAddress, HW_CLEAR); ++ ++ return status; ++} ++ ++HW_STATUS HW_MMU_TLBFlush(const u32 baseAddress, u32 virtualAddr, ++ u32 pageSize) ++{ ++ HW_STATUS status = RET_OK; ++ u32 virtualAddrTag; ++ enum HW_MMUPageSize_t pgSizeBits; ++ ++ switch (pageSize) { ++ case HW_PAGE_SIZE_4KB: ++ pgSizeBits = HW_MMU_SMALL_PAGE; ++ break; ++ ++ case HW_PAGE_SIZE_64KB: ++ pgSizeBits = HW_MMU_LARGE_PAGE; ++ break; ++ ++ case HW_PAGE_SIZE_1MB: ++ pgSizeBits = HW_MMU_SECTION; ++ break; ++ ++ case HW_PAGE_SIZE_16MB: ++ pgSizeBits = HW_MMU_SUPERSECTION; ++ break; ++ ++ default: ++ return RET_FAIL; ++ } ++ ++ /* Generate the 20-bit tag from virtual address */ ++ virtualAddrTag = ((virtualAddr & MMU_ADDR_MASK) >> 12); ++ ++ MMU_SetCAMEntry(baseAddress, pgSizeBits, 0, 0, virtualAddrTag); ++ ++ MMU_FlushEntry(baseAddress); ++ ++ return status; ++} ++ ++HW_STATUS HW_MMU_TLBAdd(const u32 baseAddress, ++ u32 physicalAddr, ++ u32 virtualAddr, ++ u32 pageSize, ++ u32 entryNum, ++ struct HW_MMUMapAttrs_t *mapAttrs, ++ enum HW_SetClear_t preservedBit, ++ enum HW_SetClear_t validBit) ++{ ++ HW_STATUS status = RET_OK; ++ u32 lockReg; ++ u32 virtualAddrTag; ++ enum HW_MMUPageSize_t mmuPgSize; ++ ++ /*Check the input Parameters*/ ++ CHECK_INPUT_PARAM(baseAddress, 0, RET_BAD_NULL_PARAM, ++ RES_MMU_BASE + RES_INVALID_INPUT_PARAM); ++ CHECK_INPUT_RANGE_MIN0(pageSize, MMU_PAGE_MAX, RET_PARAM_OUT_OF_RANGE, ++ RES_MMU_BASE + RES_INVALID_INPUT_PARAM); ++ CHECK_INPUT_RANGE_MIN0(mapAttrs->elementSize, MMU_ELEMENTSIZE_MAX, ++ RET_PARAM_OUT_OF_RANGE, RES_MMU_BASE + ++ RES_INVALID_INPUT_PARAM); ++ ++ switch (pageSize) { ++ case HW_PAGE_SIZE_4KB: ++ mmuPgSize = HW_MMU_SMALL_PAGE; ++ break; ++ ++ case HW_PAGE_SIZE_64KB: ++ mmuPgSize = HW_MMU_LARGE_PAGE; ++ break; ++ ++ case HW_PAGE_SIZE_1MB: ++ mmuPgSize = HW_MMU_SECTION; ++ break; ++ ++ case HW_PAGE_SIZE_16MB: ++ mmuPgSize = HW_MMU_SUPERSECTION; ++ break; ++ ++ default: ++ return RET_FAIL; ++ } ++ ++ lockReg = MMUMMU_LOCKReadRegister32(baseAddress); ++ ++ /* Generate the 20-bit tag from virtual address */ ++ virtualAddrTag = ((virtualAddr & MMU_ADDR_MASK) >> 12); ++ ++ /* Write the fields in the CAM Entry Register */ ++ MMU_SetCAMEntry(baseAddress, mmuPgSize, preservedBit, validBit, ++ virtualAddrTag); ++ ++ /* Write the different fields of the RAM Entry Register */ ++ /* endianism of the page,Element Size of the page (8, 16, 32, 64 bit)*/ ++ MMU_SetRAMEntry(baseAddress, physicalAddr, mapAttrs->endianism, ++ mapAttrs->elementSize, mapAttrs->mixedSize); ++ ++ /* Update the MMU Lock Register */ ++ /* currentVictim between lockedBaseValue and (MMU_Entries_Number - 1)*/ ++ MMUMMU_LOCKCurrentVictimWrite32(baseAddress, entryNum); ++ ++ /* Enable loading of an entry in TLB by writing 1 ++ into LD_TLB_REG register */ ++ MMUMMU_LD_TLBWriteRegister32(baseAddress, MMU_LOAD_TLB); ++ ++ ++ MMUMMU_LOCKWriteRegister32(baseAddress, lockReg); ++ ++ return status; ++} ++ ++HW_STATUS HW_MMU_PteSet(const u32 pgTblVa, ++ u32 physicalAddr, ++ u32 virtualAddr, ++ u32 pageSize, ++ struct HW_MMUMapAttrs_t *mapAttrs) ++{ ++ HW_STATUS status = RET_OK; ++ u32 pteAddr, pteVal; ++ s32 numEntries = 1; ++ ++ switch (pageSize) { ++ case HW_PAGE_SIZE_4KB: ++ pteAddr = HW_MMU_PteAddrL2(pgTblVa, ++ virtualAddr & MMU_SMALL_PAGE_MASK); ++ pteVal = ((physicalAddr & MMU_SMALL_PAGE_MASK) | ++ (mapAttrs->endianism << 9) | ++ (mapAttrs->elementSize << 4) | ++ (mapAttrs->mixedSize << 11) | 2 ++ ); ++ break; ++ ++ case HW_PAGE_SIZE_64KB: ++ numEntries = 16; ++ pteAddr = HW_MMU_PteAddrL2(pgTblVa, ++ virtualAddr & MMU_LARGE_PAGE_MASK); ++ pteVal = ((physicalAddr & MMU_LARGE_PAGE_MASK) | ++ (mapAttrs->endianism << 9) | ++ (mapAttrs->elementSize << 4) | ++ (mapAttrs->mixedSize << 11) | 1 ++ ); ++ break; ++ ++ case HW_PAGE_SIZE_1MB: ++ pteAddr = HW_MMU_PteAddrL1(pgTblVa, ++ virtualAddr & MMU_SECTION_ADDR_MASK); ++ pteVal = ((((physicalAddr & MMU_SECTION_ADDR_MASK) | ++ (mapAttrs->endianism << 15) | ++ (mapAttrs->elementSize << 10) | ++ (mapAttrs->mixedSize << 17)) & ++ ~0x40000) | 0x2 ++ ); ++ break; ++ ++ case HW_PAGE_SIZE_16MB: ++ numEntries = 16; ++ pteAddr = HW_MMU_PteAddrL1(pgTblVa, ++ virtualAddr & MMU_SSECTION_ADDR_MASK); ++ pteVal = (((physicalAddr & MMU_SSECTION_ADDR_MASK) | ++ (mapAttrs->endianism << 15) | ++ (mapAttrs->elementSize << 10) | ++ (mapAttrs->mixedSize << 17) ++ ) | 0x40000 | 0x2 ++ ); ++ break; ++ ++ case HW_MMU_COARSE_PAGE_SIZE: ++ pteAddr = HW_MMU_PteAddrL1(pgTblVa, ++ virtualAddr & MMU_SECTION_ADDR_MASK); ++ pteVal = (physicalAddr & MMU_PAGE_TABLE_MASK) | 1; ++ break; ++ ++ default: ++ return RET_FAIL; ++ } ++ ++ while (--numEntries >= 0) ++ ((u32 *)pteAddr)[numEntries] = pteVal; ++ ++ return status; ++} ++ ++HW_STATUS HW_MMU_PteClear(const u32 pgTblVa, ++ u32 virtualAddr, ++ u32 pgSize) ++{ ++ HW_STATUS status = RET_OK; ++ u32 pteAddr; ++ s32 numEntries = 1; ++ ++ switch (pgSize) { ++ case HW_PAGE_SIZE_4KB: ++ pteAddr = HW_MMU_PteAddrL2(pgTblVa, ++ virtualAddr & MMU_SMALL_PAGE_MASK); ++ break; ++ ++ case HW_PAGE_SIZE_64KB: ++ numEntries = 16; ++ pteAddr = HW_MMU_PteAddrL2(pgTblVa, ++ virtualAddr & MMU_LARGE_PAGE_MASK); ++ break; ++ ++ case HW_PAGE_SIZE_1MB: ++ case HW_MMU_COARSE_PAGE_SIZE: ++ pteAddr = HW_MMU_PteAddrL1(pgTblVa, ++ virtualAddr & MMU_SECTION_ADDR_MASK); ++ break; ++ ++ case HW_PAGE_SIZE_16MB: ++ numEntries = 16; ++ pteAddr = HW_MMU_PteAddrL1(pgTblVa, ++ virtualAddr & MMU_SSECTION_ADDR_MASK); ++ break; ++ ++ default: ++ return RET_FAIL; ++ } ++ ++ while (--numEntries >= 0) ++ ((u32 *)pteAddr)[numEntries] = 0; ++ ++ return status; ++} ++ ++/* MMU_FlushEntry */ ++static HW_STATUS MMU_FlushEntry(const u32 baseAddress) ++{ ++ HW_STATUS status = RET_OK; ++ u32 flushEntryData = 0x1; ++ ++ /*Check the input Parameters*/ ++ CHECK_INPUT_PARAM(baseAddress, 0, RET_BAD_NULL_PARAM, ++ RES_MMU_BASE + RES_INVALID_INPUT_PARAM); ++ ++ /* write values to register */ ++ MMUMMU_FLUSH_ENTRYWriteRegister32(baseAddress, flushEntryData); ++ ++ return status; ++} ++ ++/* MMU_SetCAMEntry */ ++static HW_STATUS MMU_SetCAMEntry(const u32 baseAddress, ++ const u32 pageSize, ++ const u32 preservedBit, ++ const u32 validBit, ++ const u32 virtualAddrTag) ++{ ++ HW_STATUS status = RET_OK; ++ u32 mmuCamReg; ++ ++ /*Check the input Parameters*/ ++ CHECK_INPUT_PARAM(baseAddress, 0, RET_BAD_NULL_PARAM, ++ RES_MMU_BASE + RES_INVALID_INPUT_PARAM); ++ ++ mmuCamReg = (virtualAddrTag << 12); ++ mmuCamReg = (mmuCamReg) | (pageSize) | (validBit << 2) | ++ (preservedBit << 3) ; ++ ++ /* write values to register */ ++ MMUMMU_CAMWriteRegister32(baseAddress, mmuCamReg); ++ ++ return status; ++} ++ ++/* MMU_SetRAMEntry */ ++static HW_STATUS MMU_SetRAMEntry(const u32 baseAddress, ++ const u32 physicalAddr, ++ enum HW_Endianism_t endianism, ++ enum HW_ElementSize_t elementSize, ++ enum HW_MMUMixedSize_t mixedSize) ++{ ++ HW_STATUS status = RET_OK; ++ u32 mmuRamReg; ++ ++ /*Check the input Parameters*/ ++ CHECK_INPUT_PARAM(baseAddress, 0, RET_BAD_NULL_PARAM, ++ RES_MMU_BASE + RES_INVALID_INPUT_PARAM); ++ CHECK_INPUT_RANGE_MIN0(elementSize, MMU_ELEMENTSIZE_MAX, ++ RET_PARAM_OUT_OF_RANGE, RES_MMU_BASE + ++ RES_INVALID_INPUT_PARAM); ++ ++ ++ mmuRamReg = (physicalAddr & MMU_ADDR_MASK); ++ mmuRamReg = (mmuRamReg) | ((endianism << 9) | (elementSize << 7) | ++ (mixedSize << 6)); ++ ++ /* write values to register */ ++ MMUMMU_RAMWriteRegister32(baseAddress, mmuRamReg); ++ ++ return status; ++ ++} +diff --git a/drivers/dsp/bridge/hw/hw_mmu.h b/drivers/dsp/bridge/hw/hw_mmu.h +new file mode 100644 +index 0000000..a3dd2a8 +--- /dev/null ++++ b/drivers/dsp/bridge/hw/hw_mmu.h +@@ -0,0 +1,177 @@ ++/* ++ * hw_mmu.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2007 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== hw_mmu.h ======== ++ * Description: ++ * MMU types and API declarations ++ * ++ *! Revision History: ++ *! ================ ++ *! 19-Apr-2004 sb Moved & renamed endianness, page size, element size ++ TLBAdd takes in MMUMapAttrs instead of separate arguments ++ *! 08-Mar-2004 sb Added the Page Table management APIs ++ *! 16 Feb 2003 sb: Initial version ++ */ ++#ifndef __HW_MMU_H ++#define __HW_MMU_H ++ ++#include ++ ++/* Bitmasks for interrupt sources */ ++#define HW_MMU_TRANSLATION_FAULT 0x2 ++#define HW_MMU_ALL_INTERRUPTS 0x1F ++ ++#define HW_MMU_COARSE_PAGE_SIZE 0x400 ++ ++/* HW_MMUMixedSize_t: Enumerated Type used to specify whether to follow ++ CPU/TLB Element size */ ++enum HW_MMUMixedSize_t { ++ HW_MMU_TLBES, ++ HW_MMU_CPUES ++ ++} ; ++ ++/* HW_MMUMapAttrs_t: Struct containing MMU mapping attributes */ ++struct HW_MMUMapAttrs_t { ++ enum HW_Endianism_t endianism; ++ enum HW_ElementSize_t elementSize; ++ enum HW_MMUMixedSize_t mixedSize; ++ bool donotlockmpupage; ++} ; ++ ++extern HW_STATUS HW_MMU_Enable(const u32 baseAddress); ++ ++extern HW_STATUS HW_MMU_Disable(const u32 baseAddress); ++ ++extern HW_STATUS HW_MMU_NumLockedSet(const u32 baseAddress, ++ u32 numLockedEntries); ++ ++extern HW_STATUS HW_MMU_VictimNumSet(const u32 baseAddress, ++ u32 victimEntryNum); ++ ++/* For MMU faults */ ++extern HW_STATUS HW_MMU_EventAck(const u32 baseAddress, ++ u32 irqMask); ++ ++extern HW_STATUS HW_MMU_EventDisable(const u32 baseAddress, ++ u32 irqMask); ++ ++extern HW_STATUS HW_MMU_EventEnable(const u32 baseAddress, ++ u32 irqMask); ++ ++extern HW_STATUS HW_MMU_EventStatus(const u32 baseAddress, ++ u32 *irqMask); ++ ++extern HW_STATUS HW_MMU_FaultAddrRead(const u32 baseAddress, ++ u32 *addr); ++ ++/* Set the TT base address */ ++extern HW_STATUS HW_MMU_TTBSet(const u32 baseAddress, ++ u32 TTBPhysAddr); ++ ++extern HW_STATUS HW_MMU_TWLEnable(const u32 baseAddress); ++ ++extern HW_STATUS HW_MMU_TWLDisable(const u32 baseAddress); ++ ++extern HW_STATUS HW_MMU_TLBFlush(const u32 baseAddress, ++ u32 virtualAddr, ++ u32 pageSize); ++ ++extern HW_STATUS HW_MMU_TLBAdd(const u32 baseAddress, ++ u32 physicalAddr, ++ u32 virtualAddr, ++ u32 pageSize, ++ u32 entryNum, ++ struct HW_MMUMapAttrs_t *mapAttrs, ++ enum HW_SetClear_t preservedBit, ++ enum HW_SetClear_t validBit); ++ ++ ++/* For PTEs */ ++extern HW_STATUS HW_MMU_PteSet(const u32 pgTblVa, ++ u32 physicalAddr, ++ u32 virtualAddr, ++ u32 pageSize, ++ struct HW_MMUMapAttrs_t *mapAttrs); ++ ++extern HW_STATUS HW_MMU_PteClear(const u32 pgTblVa, ++ u32 pgSize, ++ u32 virtualAddr); ++ ++static inline u32 HW_MMU_PteAddrL1(u32 L1_base, u32 va) ++{ ++ u32 pteAddr; ++ u32 VA_31_to_20; ++ ++ VA_31_to_20 = va >> (20 - 2); /* Left-shift by 2 here itself */ ++ VA_31_to_20 &= 0xFFFFFFFCUL; ++ pteAddr = L1_base + VA_31_to_20; ++ ++ return pteAddr; ++} ++ ++static inline u32 HW_MMU_PteAddrL2(u32 L2_base, u32 va) ++{ ++ u32 pteAddr; ++ ++ pteAddr = (L2_base & 0xFFFFFC00) | ((va >> 10) & 0x3FC); ++ ++ return pteAddr; ++} ++ ++static inline u32 HW_MMU_PteCoarseL1(u32 pteVal) ++{ ++ u32 pteCoarse; ++ ++ pteCoarse = pteVal & 0xFFFFFC00; ++ ++ return pteCoarse; ++} ++ ++static inline u32 HW_MMU_PteSizeL1(u32 pteVal) ++{ ++ u32 pteSize = 0; ++ ++ if ((pteVal & 0x3) == 0x1) { ++ /* Points to L2 PT */ ++ pteSize = HW_MMU_COARSE_PAGE_SIZE; ++ } ++ ++ if ((pteVal & 0x3) == 0x2) { ++ if (pteVal & (1 << 18)) ++ pteSize = HW_PAGE_SIZE_16MB; ++ else ++ pteSize = HW_PAGE_SIZE_1MB; ++ } ++ ++ return pteSize; ++} ++ ++static inline u32 HW_MMU_PteSizeL2(u32 pteVal) ++{ ++ u32 pteSize = 0; ++ ++ if (pteVal & 0x2) ++ pteSize = HW_PAGE_SIZE_4KB; ++ else if (pteVal & 0x1) ++ pteSize = HW_PAGE_SIZE_64KB; ++ ++ return pteSize; ++} ++ ++#endif /* __HW_MMU_H */ +diff --git a/drivers/dsp/bridge/hw/hw_prcm.c b/drivers/dsp/bridge/hw/hw_prcm.c +new file mode 100644 +index 0000000..61ff08f +--- /dev/null ++++ b/drivers/dsp/bridge/hw/hw_prcm.c +@@ -0,0 +1,167 @@ ++/* ++ * hw_prcm.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2007 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== hw_prcm.c ======== ++ * Description: ++ * API definitions to configure PRCM (Power, Reset & Clocks Manager) ++ * ++ *! Revision History: ++ *! ================ ++ *! 16 Feb 2003 sb: Initial version ++ */ ++ ++#include ++#include "PRCMRegAcM.h" ++#include ++#include ++ ++static HW_STATUS HW_RST_WriteVal(const u32 baseAddress, ++ enum HW_RstModule_t r, ++ enum HW_SetClear_t val); ++ ++HW_STATUS HW_RST_Reset(const u32 baseAddress, enum HW_RstModule_t r) ++{ ++ return HW_RST_WriteVal(baseAddress, r, HW_SET); ++} ++ ++HW_STATUS HW_RST_UnReset(const u32 baseAddress, enum HW_RstModule_t r) ++{ ++ return HW_RST_WriteVal(baseAddress, r, HW_CLEAR); ++} ++ ++static HW_STATUS HW_RST_WriteVal(const u32 baseAddress, ++ enum HW_RstModule_t r, ++ enum HW_SetClear_t val) ++{ ++ HW_STATUS status = RET_OK; ++ ++ switch (r) { ++ case HW_RST1_IVA2: ++ PRM_RSTCTRL_IVA2RST1_DSPWrite32(baseAddress, val); ++ break; ++ case HW_RST2_IVA2: ++ PRM_RSTCTRL_IVA2RST2_DSPWrite32(baseAddress, val); ++ break; ++ case HW_RST3_IVA2: ++ PRM_RSTCTRL_IVA2RST3_DSPWrite32(baseAddress, val); ++ break; ++ default: ++ status = RET_FAIL; ++ break; ++ } ++ return status; ++} ++ ++HW_STATUS HW_PWR_IVA2StateGet(const u32 baseAddress, enum HW_PwrModule_t p, ++ enum HW_PwrState_t *value) ++{ ++ HW_STATUS status = RET_OK; ++ u32 temp; ++ ++ switch (p) { ++ case HW_PWR_DOMAIN_DSP: ++ /* wait until Transition is complete */ ++ do { ++ /* mdelay(1); */ ++ temp = PRCMPM_PWSTST_IVA2InTransitionRead32 ++ (baseAddress); ++ ++ } while (temp); ++ ++ temp = PRCMPM_PWSTST_IVA2ReadRegister32(baseAddress); ++ *value = PRCMPM_PWSTST_IVA2PowerStateStGet32(temp); ++ break; ++ ++ default: ++ status = RET_FAIL; ++ break; ++ } ++ return status; ++} ++ ++HW_STATUS HW_PWRST_IVA2RegGet(const u32 baseAddress, u32 *value) ++{ ++ HW_STATUS status = RET_OK; ++ ++ *value = PRCMPM_PWSTST_IVA2ReadRegister32(baseAddress); ++ ++ return status; ++} ++ ++ ++HW_STATUS HW_PWR_IVA2PowerStateSet(const u32 baseAddress, ++ enum HW_PwrModule_t p, ++ enum HW_PwrState_t value) ++{ ++ HW_STATUS status = RET_OK; ++ ++ switch (p) { ++ case HW_PWR_DOMAIN_DSP: ++ switch (value) { ++ case HW_PWR_STATE_ON: ++ PRCMPM_PWSTCTRL_IVA2PowerStateWriteON32(baseAddress); ++ break; ++ case HW_PWR_STATE_RET: ++ PRCMPM_PWSTCTRL_DSPPowerStateWriteRET32(baseAddress); ++ break; ++ case HW_PWR_STATE_OFF: ++ PRCMPM_PWSTCTRL_IVA2PowerStateWriteOFF32(baseAddress); ++ break; ++ default: ++ status = RET_FAIL; ++ break; ++ } ++ break; ++ ++ default: ++ status = RET_FAIL; ++ break; ++ } ++ ++ return status; ++} ++ ++HW_STATUS HW_PWR_CLKCTRL_IVA2RegSet(const u32 baseAddress, ++ enum HW_TransitionState_t val) ++{ ++ HW_STATUS status = RET_OK; ++ ++ PRCMCM_CLKSTCTRL_IVA2WriteRegister32(baseAddress, val); ++ ++ return status; ++ ++} ++ ++HW_STATUS HW_RSTST_RegGet(const u32 baseAddress, enum HW_RstModule_t m, ++ u32 *value) ++{ ++ HW_STATUS status = RET_OK; ++ ++ *value = PRCMRM_RSTST_DSPReadRegister32(baseAddress); ++ ++ return status; ++} ++ ++HW_STATUS HW_RSTCTRL_RegGet(const u32 baseAddress, enum HW_RstModule_t m, ++ u32 *value) ++{ ++ HW_STATUS status = RET_OK; ++ ++ *value = PRCMRM_RSTCTRL_DSPReadRegister32(baseAddress); ++ ++ return status; ++} +diff --git a/drivers/dsp/bridge/hw/hw_prcm.h b/drivers/dsp/bridge/hw/hw_prcm.h +new file mode 100644 +index 0000000..928486c +--- /dev/null ++++ b/drivers/dsp/bridge/hw/hw_prcm.h +@@ -0,0 +1,168 @@ ++/* ++ * hw_prcm.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2007 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== hw_prcm.h ======== ++ * Description: ++ * PRCM types and API declarations ++ * ++ *! Revision History: ++ *! ================ ++ *! 16 Feb 2003 sb: Initial version ++ */ ++ ++#ifndef __HW_PRCM_H ++#define __HW_PRCM_H ++ ++/* HW_ClkModule: Enumerated Type used to specify the clock domain */ ++ ++enum HW_ClkModule_t { ++/* DSP Domain */ ++ HW_CLK_DSP_CPU, ++ HW_CLK_DSP_IPI_MMU, ++ HW_CLK_IVA_ARM, ++ HW_CLK_IVA_COP, /* IVA Coprocessor */ ++ ++/* Core Domain */ ++ HW_CLK_FN_WDT4, /* Functional Clock */ ++ HW_CLK_FN_WDT3, ++ HW_CLK_FN_UART2, ++ HW_CLK_FN_UART1, ++ HW_CLK_GPT5, ++ HW_CLK_GPT6, ++ HW_CLK_GPT7, ++ HW_CLK_GPT8, ++ ++ HW_CLK_IF_WDT4, /* Interface Clock */ ++ HW_CLK_IF_WDT3, ++ HW_CLK_IF_UART2, ++ HW_CLK_IF_UART1, ++ HW_CLK_IF_MBOX ++ ++} ; ++ ++enum HW_ClkSubsys_t { ++ HW_CLK_DSPSS, ++ HW_CLK_IVASS ++} ; ++ ++/* HW_GPtimers: General purpose timers */ ++enum HW_GPtimer_t { ++ HW_GPT5 = 5, ++ HW_GPT6 = 6, ++ HW_GPT7 = 7, ++ HW_GPT8 = 8 ++} ; ++ ++ ++/* GP timers Input clock type: General purpose timers */ ++enum HW_Clocktype_t { ++ HW_CLK_32KHz = 0, ++ HW_CLK_SYS = 1, ++ HW_CLK_EXT = 2 ++} ; ++ ++/* HW_ClkDiv: Clock divisors */ ++enum HW_ClkDiv_t { ++ HW_CLK_DIV_1 = 0x1, ++ HW_CLK_DIV_2 = 0x2, ++ HW_CLK_DIV_3 = 0x3, ++ HW_CLK_DIV_4 = 0x4, ++ HW_CLK_DIV_6 = 0x6, ++ HW_CLK_DIV_8 = 0x8, ++ HW_CLK_DIV_12 = 0xC ++} ; ++ ++/* HW_RstModule: Enumerated Type used to specify the module to be reset */ ++enum HW_RstModule_t { ++ HW_RST1_IVA2, /* Reset the DSP */ ++ HW_RST2_IVA2, /* Reset MMU and LEON HWa */ ++ HW_RST3_IVA2 /* Reset LEON sequencer */ ++} ; ++ ++/* HW_PwrModule: Enumerated Type used to specify the power domain */ ++enum HW_PwrModule_t { ++/* Domains */ ++ HW_PWR_DOMAIN_CORE, ++ HW_PWR_DOMAIN_MPU, ++ HW_PWR_DOMAIN_WAKEUP, ++ HW_PWR_DOMAIN_DSP, ++ ++/* Sub-domains */ ++ HW_PWR_DSP_IPI, /* IPI = Intrusive Port Interface */ ++ HW_PWR_IVA_ISP /* ISP = Intrusive Slave Port */ ++} ; ++ ++enum HW_PwrState_t { ++ HW_PWR_STATE_OFF, ++ HW_PWR_STATE_RET, ++ HW_PWR_STATE_INACT, ++ HW_PWR_STATE_ON = 3 ++} ; ++ ++enum HW_ForceState_t { ++ HW_FORCE_OFF, ++ HW_FORCE_ON ++} ; ++ ++enum HW_IdleState_t { ++ HW_ACTIVE, ++ HW_STANDBY ++ ++} ; ++ ++enum HW_TransitionState_t { ++ HW_AUTOTRANS_DIS, ++ HW_SW_SUP_SLEEP, ++ HW_SW_SUP_WAKEUP, ++ HW_AUTOTRANS_EN ++} ; ++ ++ ++extern HW_STATUS HW_RST_Reset(const u32 baseAddress, ++ enum HW_RstModule_t r); ++ ++extern HW_STATUS HW_RST_UnReset(const u32 baseAddress, ++ enum HW_RstModule_t r); ++ ++extern HW_STATUS HW_RSTCTRL_RegGet(const u32 baseAddress, ++ enum HW_RstModule_t p, ++ u32 *value); ++extern HW_STATUS HW_RSTST_RegGet(const u32 baseAddress, ++ enum HW_RstModule_t p, u32 *value); ++ ++extern HW_STATUS HW_PWR_PowerStateSet(const u32 baseAddress, ++ enum HW_PwrModule_t p, ++ enum HW_PwrState_t value); ++ ++extern HW_STATUS HW_CLK_SetInputClock(const u32 baseAddress, ++ enum HW_GPtimer_t gpt, ++ enum HW_Clocktype_t c); ++ ++extern HW_STATUS HW_PWR_IVA2StateGet(const u32 baseAddress, ++ enum HW_PwrModule_t p, ++ enum HW_PwrState_t *value); ++ ++extern HW_STATUS HW_PWRST_IVA2RegGet(const u32 baseAddress, u32 *value); ++ ++extern HW_STATUS HW_PWR_IVA2PowerStateSet(const u32 baseAddress, ++ enum HW_PwrModule_t p, ++ enum HW_PwrState_t value); ++ ++extern HW_STATUS HW_PWR_CLKCTRL_IVA2RegSet(const u32 baseAddress, ++ enum HW_TransitionState_t val); ++ ++#endif /* __HW_PRCM_H */ +diff --git a/drivers/dsp/bridge/pmgr/chnl.c b/drivers/dsp/bridge/pmgr/chnl.c +new file mode 100644 +index 0000000..6b5a0d9 +--- /dev/null ++++ b/drivers/dsp/bridge/pmgr/chnl.c +@@ -0,0 +1,260 @@ ++/* ++ * chnl.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== chnl.c ======== ++ * Description: ++ * WCD channel interface: multiplexes data streams through the single ++ * physical link managed by a 'Bridge mini-driver. ++ * ++ * Public Functions: ++ * CHNL_Close ++ * CHNL_CloseOrphans ++ * CHNL_Create ++ * CHNL_Destroy ++ * CHNL_Exit ++ * CHNL_GetHandle ++ * CHNL_GetProcessHandle ++ * CHNL_Init ++ * CHNL_Open ++ * ++ * Notes: ++ * This interface is basically a pass through to the WMD CHNL functions, ++ * except for the CHNL_Get() accessor functions which call ++ * WMD_CHNL_GetInfo(). ++ * ++ *! Revision History: ++ *! ================ ++ *! 24-Feb-2003 swa PMGR Code review comments incorporated. ++ *! 07-Jan-2002 ag CHNL_CloseOrphans() now closes supported # of channels. ++ *! 17-Nov-2000 jeh Removed IRQ, shared memory stuff from CHNL_Create. ++ *! 28-Feb-2000 rr: New GT USage Implementation ++ *! 03-Feb-2000 rr: GT and Module init/exit Changes.(Done up front from ++ *! SERVICES) ++ *! 21-Jan-2000 ag: Added code review comments. ++ *! 13-Jan-2000 rr: CFG_Get/SetPrivateDword renamed to CFG_Get/SetDevObject. ++ *! 08-Dec-1999 ag: CHNL_[Alloc|Free]Buffer bufs taken from client process heap. ++ *! 02-Dec-1999 ag: Implemented CHNL_GetEventHandle(). ++ *! 17-Nov-1999 ag: CHNL_AllocBuffer() allocs extra word for process mapping. ++ *! 28-Oct-1999 ag: WinCE port. Search for "WinCE" for changes(TBR). ++ *! 07-Jan-1998 gp: CHNL_[Alloc|Free]Buffer now call MEM_UMB functions. ++ *! 22-Oct-1997 gp: Removed requirement in CHNL_Open that hReserved1 != NULL. ++ *! 30-Aug-1997 cr: Renamed cfg.h wbwcd.h b/c of WINNT file name collision. ++ *! 10-Mar-1997 gp: Added GT trace. ++ *! 14-Jan-1997 gp: Updated based on code review feedback. ++ *! 03-Jan-1997 gp: Moved CHNL_AllocBuffer/CHNL_FreeBuffer code from udspsys. ++ *! 14-Dec-1996 gp: Added uChnlId parameter to CHNL_Open(). ++ *! 09-Sep-1996 gp: Added CHNL_GetProcessHandle(). ++ *! 15-Jul-1996 gp: Created. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++#include ++ ++/* ----------------------------------- Others */ ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ----------------------------------- Globals */ ++static u32 cRefs; ++#if GT_TRACE ++static struct GT_Mask CHNL_DebugMask = { NULL, NULL }; /* WCD CHNL Mask */ ++#endif ++ ++ ++ ++/* ++ * ======== CHNL_Create ======== ++ * Purpose: ++ * Create a channel manager object, responsible for opening new channels ++ * and closing old ones for a given 'Bridge board. ++ */ ++DSP_STATUS CHNL_Create(OUT struct CHNL_MGR **phChnlMgr, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct CHNL_MGRATTRS *pMgrAttrs) ++{ ++ DSP_STATUS status; ++ struct CHNL_MGR *hChnlMgr; ++ struct CHNL_MGR_ *pChnlMgr = NULL; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phChnlMgr != NULL); ++ DBC_Require(pMgrAttrs != NULL); ++ ++ GT_3trace(CHNL_DebugMask, GT_ENTER, ++ "Entered CHNL_Create: phChnlMgr: 0x%x\t" ++ "hDevObject: 0x%x\tpMgrAttrs:0x%x\n", ++ phChnlMgr, hDevObject, pMgrAttrs); ++ ++ *phChnlMgr = NULL; ++ ++ /* Validate args: */ ++ if ((0 < pMgrAttrs->cChannels) && ++ (pMgrAttrs->cChannels <= CHNL_MAXCHANNELS)) { ++ status = DSP_SOK; ++ } else if (pMgrAttrs->cChannels == 0) { ++ status = DSP_EINVALIDARG; ++ GT_0trace(CHNL_DebugMask, GT_7CLASS, ++ "CHNL_Create:Invalid Args\n"); ++ } else { ++ status = CHNL_E_MAXCHANNELS; ++ GT_0trace(CHNL_DebugMask, GT_7CLASS, ++ "CHNL_Create:Error Max Channels\n"); ++ } ++ if (pMgrAttrs->uWordSize == 0) { ++ status = CHNL_E_INVALIDWORDSIZE; ++ GT_0trace(CHNL_DebugMask, GT_7CLASS, ++ "CHNL_Create:Invalid Word size\n"); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = DEV_GetChnlMgr(hDevObject, &hChnlMgr); ++ if (DSP_SUCCEEDED(status) && hChnlMgr != NULL) ++ status = CHNL_E_MGREXISTS; ++ ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ DEV_GetIntfFxns(hDevObject, &pIntfFxns); ++ /* Let WMD channel module finish the create: */ ++ status = (*pIntfFxns->pfnChnlCreate)(&hChnlMgr, hDevObject, ++ pMgrAttrs); ++ if (DSP_SUCCEEDED(status)) { ++ /* Fill in WCD channel module's fields of the ++ * CHNL_MGR structure */ ++ pChnlMgr = (struct CHNL_MGR_ *)hChnlMgr; ++ pChnlMgr->pIntfFxns = pIntfFxns; ++ /* Finally, return the new channel manager handle: */ ++ *phChnlMgr = hChnlMgr; ++ GT_1trace(CHNL_DebugMask, GT_1CLASS, ++ "CHNL_Create: Success pChnlMgr:" ++ "0x%x\n", pChnlMgr); ++ } ++ } ++ ++ GT_2trace(CHNL_DebugMask, GT_ENTER, ++ "Exiting CHNL_Create: pChnlMgr: 0x%x," ++ "status: 0x%x\n", pChnlMgr, status); ++ DBC_Ensure(DSP_FAILED(status) || CHNL_IsValidMgr(pChnlMgr)); ++ ++ return status; ++} ++ ++/* ++ * ======== CHNL_Destroy ======== ++ * Purpose: ++ * Close all open channels, and destroy the channel manager. ++ */ ++DSP_STATUS CHNL_Destroy(struct CHNL_MGR *hChnlMgr) ++{ ++ struct CHNL_MGR_ *pChnlMgr = (struct CHNL_MGR_ *)hChnlMgr; ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ DSP_STATUS status; ++ ++ DBC_Require(cRefs > 0); ++ ++ GT_1trace(CHNL_DebugMask, GT_ENTER, ++ "Entered CHNL_Destroy: hChnlMgr: 0x%x\n", hChnlMgr); ++ if (CHNL_IsValidMgr(pChnlMgr)) { ++ pIntfFxns = pChnlMgr->pIntfFxns; ++ /* Let WMD channel module destroy the CHNL_MGR: */ ++ status = (*pIntfFxns->pfnChnlDestroy)(hChnlMgr); ++ } else { ++ GT_0trace(CHNL_DebugMask, GT_7CLASS, ++ "CHNL_Destroy:Invalid Handle\n"); ++ status = DSP_EHANDLE; ++ } ++ ++ GT_2trace(CHNL_DebugMask, GT_ENTER, ++ "Exiting CHNL_Destroy: pChnlMgr: 0x%x," ++ " status:0x%x\n", pChnlMgr, status); ++ DBC_Ensure(DSP_FAILED(status) || !CHNL_IsValidMgr(pChnlMgr)); ++ ++ return status; ++} ++ ++/* ++ * ======== CHNL_Exit ======== ++ * Purpose: ++ * Discontinue usage of the CHNL module. ++ */ ++void CHNL_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ cRefs--; ++ ++ GT_1trace(CHNL_DebugMask, GT_5CLASS, ++ "Entered CHNL_Exit, ref count: 0x%x\n", cRefs); ++ ++ DBC_Ensure(cRefs >= 0); ++} ++ ++ ++/* ++ * ======== CHNL_Init ======== ++ * Purpose: ++ * Initialize the CHNL module's private state. ++ */ ++bool CHNL_Init(void) ++{ ++ bool fRetval = true; ++ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++ DBC_Assert(!CHNL_DebugMask.flags); ++ GT_create(&CHNL_DebugMask, "CH"); /* "CH" for CHannel */ ++ } ++ ++ if (fRetval) ++ cRefs++; ++ ++ GT_1trace(CHNL_DebugMask, GT_5CLASS, ++ "Entered CHNL_Init, ref count: 0x%x\n", ++ cRefs); ++ ++ DBC_Ensure((fRetval && (cRefs > 0)) || (!fRetval && (cRefs >= 0))); ++ ++ return fRetval; ++} ++ ++ +diff --git a/drivers/dsp/bridge/pmgr/chnlobj.h b/drivers/dsp/bridge/pmgr/chnlobj.h +new file mode 100644 +index 0000000..da74c96 +--- /dev/null ++++ b/drivers/dsp/bridge/pmgr/chnlobj.h +@@ -0,0 +1,71 @@ ++/* ++ * chnlobj.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== chnlobj.h ======== ++ * Description: ++ * Structure subcomponents of channel class library channel objects which ++ * are exposed to class driver from mini-driver. ++ * ++ * Public Functions: ++ * None. ++ * ++ *! Revision History: ++ *! ================ ++ *! 24-Feb-2003 swa PMGR Code review comments incorporated. ++ *! 17-Nov-2000 jeh Removed some fields from CHNL_MGR_ to match CHNL_MGR ++ *! structure defined in _chnl_sm.h. ++ *! 16-Jan-1997 gp: Created from chnlpriv.h ++ */ ++ ++#ifndef CHNLOBJ_ ++#define CHNLOBJ_ ++ ++#include ++#include ++ ++/* Object validateion macros: */ ++#define CHNL_IsValidMgr(h) \ ++ ((h != NULL) && ((h)->dwSignature == CHNL_MGRSIGNATURE)) ++ ++#define CHNL_IsValidChnl(h)\ ++ ((h != NULL) && ((h)->dwSignature == CHNL_SIGNATURE)) ++ ++/* ++ * This struct is the first field in a CHNL_MGR struct, as implemented in ++ * a WMD channel class library. Other, implementation specific fields ++ * follow this structure in memory. ++ */ ++struct CHNL_MGR_ { ++ /* These must be the first fields in a CHNL_MGR struct: */ ++ u32 dwSignature; /* Used for object validation. */ ++ struct WMD_DRV_INTERFACE *pIntfFxns; /* Function interface to WMD. */ ++} ; ++ ++/* ++ * This struct is the first field in a CHNL_OBJECT struct, as implemented in ++ * a WMD channel class library. Other, implementation specific fields ++ * follow this structure in memory. ++ */ ++struct CHNL_OBJECT_ { ++ /* These must be the first fields in a CHNL_OBJECT struct: */ ++ u32 dwSignature; /* Used for object validation. */ ++ struct CHNL_MGR_ *pChnlMgr; /* Pointer back to channel manager. */ ++} ; ++ ++#endif /* CHNLOBJ_ */ ++ +diff --git a/drivers/dsp/bridge/pmgr/cmm.c b/drivers/dsp/bridge/pmgr/cmm.c +new file mode 100644 +index 0000000..99a2432 +--- /dev/null ++++ b/drivers/dsp/bridge/pmgr/cmm.c +@@ -0,0 +1,1291 @@ ++/* ++ * cmm.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== cmm.c ======== ++ * Purpose: ++ * The Communication(Shared) Memory Management(CMM) module provides ++ * shared memory management services for DSP/BIOS Bridge data streaming ++ * and messaging. ++ * ++ * Multiple shared memory segments can be registered with CMM. ++ * Each registered SM segment is represented by a SM "allocator" that ++ * describes a block of physically contiguous shared memory used for ++ * future allocations by CMM. ++ * ++ * Memory is coelesced back to the appropriate heap when a buffer is ++ * freed. ++ * ++ * Public Functions: ++ * CMM_CallocBuf ++ * CMM_Create ++ * CMM_Destroy ++ * CMM_Exit ++ * CMM_FreeBuf ++ * CMM_GetHandle ++ * CMM_GetInfo ++ * CMM_Init ++ * CMM_RegisterGPPSMSeg ++ * CMM_UnRegisterGPPSMSeg ++ * ++ * The CMM_Xlator[xxx] routines below are used by Node and Stream ++ * to perform SM address translation to the client process address space. ++ * A "translator" object is created by a node/stream for each SM seg used. ++ * ++ * Translator Routines: ++ * CMM_XlatorAllocBuf ++ * CMM_XlatorCreate ++ * CMM_XlatorDelete ++ * CMM_XlatorFreeBuf ++ * CMM_XlatorInfo ++ * CMM_XlatorTranslate ++ * ++ * Private Functions: ++ * AddToFreeList ++ * GetAllocator ++ * GetFreeBlock ++ * GetNode ++ * GetSlot ++ * UnRegisterGPPSMSeg ++ * ++ * Notes: ++ * Va: Virtual address. ++ * Pa: Physical or kernel system address. ++ * ++ *! Revision History: ++ *! ================ ++ *! 24-Feb-2003 swa PMGR Code review comments incorporated. ++ *! 16-Feb-2002 ag Code review cleanup. ++ *! PreOMAP address translation no longner supported. ++ *! 30-Jan-2002 ag Updates to CMM_XlatorTranslate() per TII, ANSI C++ ++ *! warnings. ++ *! 27-Jan-2002 ag Removed unused CMM_[Alloc][Free]Desc() & #ifdef USELOOKUP, ++ *! & unused VALIDATECMM and VaPaConvert(). ++ *! Removed bFastXlate from CMM_XLATOR. Always fast lookup. ++ *! 03-Jan-2002 ag Clear SM in CMM_AllocBuf(). Renamed to CMM_CallocBuf(). ++ *! 13-Nov-2001 ag Now delete pNodeFreeListHead and nodes in CMM_Destroy(). ++ *! 28-Aug-2001 ag CMM_GetHandle() returns CMM Mgr hndle given HPROCESSOR. ++ *! Removed unused CMM_[Un]RegisterDSPSMSeg() & ++ * CMM_[Un}ReserveVirtSpace fxns. Some cleanup. ++ *! 12-Aug-2001 ag Exposed CMM_UnRegisterGPP[DSP]SMSeg. ++ *! 13-Feb-2001 kc DSP/BIOS Bridge name update. ++ *! 21-Dec-2000 rr GetFreeBlock checks for pAllocator. ++ *! 09-Dec-2000 ag Added GPPPA2DSPPA, DSPPA2GPPPA macros. ++ *! 05-Dec-2000 ag CMM_XlatorDelete() optionally frees SM bufs and descriptors. ++ *! 30-Oct-2000 ag Buf size bug fixed in CMM_AllocBuf() causing leak. ++ *! Revamped XlatorTranslate() routine. ++ *! 10-Oct-2000 ag Added CMM_Xlator[xxx] functions. ++ *! 02-Aug-2000 ag Created. ++ *! ++ */ ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ----------------------------------- Defines, Data Structures, Typedefs */ ++/* Object signatures */ ++#define CMMSIGNATURE 0x004d4d43 /* "CMM" (in reverse) */ ++#define SMEMSIGNATURE 0x4D454D53 /* "SMEM" SM space */ ++#define CMMXLATESIGNATURE 0x584d4d43 /* "CMMX" CMM Xlator */ ++ ++#define NEXT_PA(pNode) (pNode->dwPA + pNode->ulSize) ++ ++/* Other bus/platform translations */ ++#define DSPPA2GPPPA(base, x, y) ((x)+(y)) ++#define GPPPA2DSPPA(base, x, y) ((x)-(y)) ++ ++/* ++ * Allocators define a block of contiguous memory used for future allocations. ++ * ++ * sma - shared memory allocator. ++ * vma - virtual memory allocator.(not used). ++ */ ++struct CMM_ALLOCATOR { /* sma */ ++ u32 dwSignature; /* SMA allocator signature SMEMSIGNATURE */ ++ unsigned int dwSmBase; /* Start of physical SM block */ ++ u32 ulSmSize; /* Size of SM block in bytes */ ++ unsigned int dwVmBase; /* Start of VM block. (Dev driver ++ * context for 'sma') */ ++ u32 dwDSPPhysAddrOffset; /* DSP PA to GPP PA offset for this ++ * SM space */ ++ /* CMM_ADDTO[SUBFROM]DSPPA, _POMAPEMIF2DSPBUS */ ++ enum CMM_CNVTTYPE cFactor; ++ unsigned int dwDSPBase; /* DSP virt base byte address */ ++ u32 ulDSPSize; /* DSP seg size in bytes */ ++ struct CMM_OBJECT *hCmmMgr; /* back ref to parent mgr */ ++ struct LST_LIST *pFreeListHead; /* node list of available memory */ ++ struct LST_LIST *pInUseListHead; /* node list of memory in use */ ++} ; ++ ++struct CMM_XLATOR { /* Pa<->Va translator object */ ++ u32 dwSignature; /* "CMMX" */ ++ struct CMM_OBJECT *hCmmMgr; /* CMM object this translator associated */ ++ /* ++ * Client process virtual base address that corresponds to phys SM ++ * base address for translator's ulSegId. ++ * Only 1 segment ID currently supported. ++ */ ++ unsigned int dwVirtBase; /* virtual base address */ ++ u32 ulVirtSize; /* size of virt space in bytes */ ++ u32 ulSegId; /* Segment Id */ ++} ; ++ ++/* CMM Mgr */ ++struct CMM_OBJECT { ++ u32 dwSignature; /* Used for object validation */ ++ /* ++ * Cmm Lock is used to serialize access mem manager for multi-threads. ++ */ ++ struct SYNC_CSOBJECT *hCmmLock; /* Lock to access cmm mgr */ ++ struct LST_LIST *pNodeFreeListHead; /* Free list of memory nodes */ ++ u32 ulMinBlockSize; /* Min SM block; default 16 bytes */ ++ u32 dwPageSize; /* Memory Page size (1k/4k) */ ++ /* GPP SM segment ptrs */ ++ struct CMM_ALLOCATOR *paGPPSMSegTab[CMM_MAXGPPSEGS]; ++} ; ++ ++/* Default CMM Mgr attributes */ ++static struct CMM_MGRATTRS CMM_DFLTMGRATTRS = { ++ 16 /* ulMinBlockSize, min block size(bytes) allocated by cmm mgr */ ++}; ++ ++/* Default allocation attributes */ ++static struct CMM_ATTRS CMM_DFLTALCTATTRS = { ++ 1 /* ulSegId, default segment Id for allocator */ ++}; ++ ++/* Address translator default attrs */ ++static struct CMM_XLATORATTRS CMM_DFLTXLATORATTRS = { ++ 1, /* ulSegId, does not have to match CMM_DFLTALCTATTRS ulSegId */ ++ 0, /* dwDSPBufs */ ++ 0, /* dwDSPBufSize */ ++ NULL, /* pVmBase */ ++ 0, /* dwVmSize */ ++}; ++ ++/* SM node representing a block of memory. */ ++struct CMM_MNODE { ++ struct LST_ELEM link; /* must be 1st element */ ++ u32 dwPA; /* Phys addr */ ++ u32 dwVA; /* Virtual address in device process context */ ++ u32 ulSize; /* SM block size in bytes */ ++ u32 hClientProc; /* Process that allocated this mem block */ ++} ; ++ ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask CMM_debugMask = { NULL, NULL }; /* GT trace variable */ ++#endif ++ ++static u32 cRefs; /* module reference count */ ++ ++/* ----------------------------------- Function Prototypes */ ++static void AddToFreeList(struct CMM_ALLOCATOR *pAllocator, ++ struct CMM_MNODE *pNode); ++static struct CMM_ALLOCATOR *GetAllocator(struct CMM_OBJECT *pCmmMgr, ++ u32 ulSegId); ++static struct CMM_MNODE *GetFreeBlock(struct CMM_ALLOCATOR *pAllocator, ++ u32 uSize); ++static struct CMM_MNODE *GetNode(struct CMM_OBJECT *pCmmMgr, u32 dwPA, ++ u32 dwVA, u32 ulSize); ++/* get available slot for new allocator */ ++static s32 GetSlot(struct CMM_OBJECT *hCmmMgr); ++static void UnRegisterGPPSMSeg(struct CMM_ALLOCATOR *pSMA); ++ ++/* ++ * ======== CMM_CallocBuf ======== ++ * Purpose: ++ * Allocate a SM buffer, zero contents, and return the physical address ++ * and optional driver context virtual address(ppBufVA). ++ * ++ * The freelist is sorted in increasing size order. Get the first ++ * block that satifies the request and sort the remaining back on ++ * the freelist; if large enough. The kept block is placed on the ++ * inUseList. ++ */ ++void *CMM_CallocBuf(struct CMM_OBJECT *hCmmMgr, u32 uSize, ++ struct CMM_ATTRS *pAttrs, OUT void **ppBufVA) ++{ ++ struct CMM_OBJECT *pCmmMgr = (struct CMM_OBJECT *)hCmmMgr; ++ void *pBufPA = NULL; ++ struct CMM_MNODE *pNode = NULL; ++ struct CMM_MNODE *pNewNode = NULL; ++ struct CMM_ALLOCATOR *pAllocator = NULL; ++ u32 uDeltaSize; ++ u8 *pByte = NULL; ++ s32 cnt; ++ ++ if (pAttrs == NULL) ++ pAttrs = &CMM_DFLTALCTATTRS; ++ ++ if (ppBufVA != NULL) ++ *ppBufVA = NULL; ++ ++ if ((MEM_IsValidHandle(pCmmMgr, CMMSIGNATURE)) && (uSize != 0)) { ++ if (pAttrs->ulSegId > 0) { ++ /* SegId > 0 is SM */ ++ /* get the allocator object for this segment id */ ++ pAllocator = GetAllocator(pCmmMgr, pAttrs->ulSegId); ++ /* keep block size a multiple of ulMinBlockSize */ ++ uSize = ((uSize - 1) & ~(pCmmMgr->ulMinBlockSize - 1)) ++ + pCmmMgr->ulMinBlockSize; ++ SYNC_EnterCS(pCmmMgr->hCmmLock); ++ pNode = GetFreeBlock(pAllocator, uSize); ++ } ++ if (pNode) { ++ uDeltaSize = (pNode->ulSize - uSize); ++ if (uDeltaSize >= pCmmMgr->ulMinBlockSize) { ++ /* create a new block with the leftovers and ++ * add to freelist */ ++ pNewNode = GetNode(pCmmMgr, pNode->dwPA + uSize, ++ pNode->dwVA + uSize, ++ (u32)uDeltaSize); ++ /* leftovers go free */ ++ AddToFreeList(pAllocator, pNewNode); ++ /* adjust our node's size */ ++ pNode->ulSize = uSize; ++ } ++ /* Tag node with client process requesting allocation ++ * We'll need to free up a process's alloc'd SM if the ++ * client process goes away. ++ */ ++ /* Return PID instead of process handle */ ++ pNode->hClientProc = current->pid; ++ ++ /* put our node on InUse list */ ++ LST_PutTail(pAllocator->pInUseListHead, ++ (struct LST_ELEM *)pNode); ++ pBufPA = (void *)pNode->dwPA; /* physical address */ ++ /* clear mem */ ++ pByte = (u8 *)pNode->dwVA; ++ for (cnt = 0; cnt < (s32) uSize; cnt++, pByte++) ++ *pByte = 0; ++ ++ if (ppBufVA != NULL) { ++ /* Virtual address */ ++ *ppBufVA = (void *)pNode->dwVA; ++ } ++ } ++ GT_3trace(CMM_debugMask, GT_3CLASS, ++ "CMM_CallocBuf dwPA %x, dwVA %x uSize" ++ "%x\n", pNode->dwPA, pNode->dwVA, uSize); ++ SYNC_LeaveCS(pCmmMgr->hCmmLock); ++ } ++ return pBufPA; ++} ++ ++/* ++ * ======== CMM_Create ======== ++ * Purpose: ++ * Create a communication memory manager object. ++ */ ++DSP_STATUS CMM_Create(OUT struct CMM_OBJECT **phCmmMgr, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct CMM_MGRATTRS *pMgrAttrs) ++{ ++ struct CMM_OBJECT *pCmmObject = NULL; ++ DSP_STATUS status = DSP_SOK; ++ struct UTIL_SYSINFO sysInfo; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phCmmMgr != NULL); ++ ++ GT_3trace(CMM_debugMask, GT_ENTER, ++ "CMM_Create: phCmmMgr: 0x%x\thDevObject: " ++ "0x%x\tpMgrAttrs: 0x%x\n", phCmmMgr, hDevObject, pMgrAttrs); ++ *phCmmMgr = NULL; ++ /* create, zero, and tag a cmm mgr object */ ++ MEM_AllocObject(pCmmObject, struct CMM_OBJECT, CMMSIGNATURE); ++ if (pCmmObject != NULL) { ++ if (pMgrAttrs == NULL) ++ pMgrAttrs = &CMM_DFLTMGRATTRS; /* set defaults */ ++ ++ /* 4 bytes minimum */ ++ DBC_Assert(pMgrAttrs->ulMinBlockSize >= 4); ++ /* save away smallest block allocation for this cmm mgr */ ++ pCmmObject->ulMinBlockSize = pMgrAttrs->ulMinBlockSize; ++ /* save away the systems memory page size */ ++ sysInfo.dwPageSize = PAGE_SIZE; ++ sysInfo.dwAllocationGranularity = PAGE_SIZE; ++ sysInfo.dwNumberOfProcessors = 1; ++ if (DSP_SUCCEEDED(status)) { ++ GT_1trace(CMM_debugMask, GT_5CLASS, ++ "CMM_Create: Got system page size" ++ "= 0x%x\t\n", sysInfo.dwPageSize); ++ pCmmObject->dwPageSize = sysInfo.dwPageSize; ++ } else { ++ GT_0trace(CMM_debugMask, GT_7CLASS, ++ "CMM_Create: failed to get system" ++ "page size\n"); ++ pCmmObject->dwPageSize = 0; ++ status = DSP_EFAIL; ++ } ++ /* Note: DSP SM seg table(aDSPSMSegTab[]) zero'd by ++ * MEM_AllocObject */ ++ if (DSP_SUCCEEDED(status)) { ++ /* create node free list */ ++ pCmmObject->pNodeFreeListHead = LST_Create(); ++ if (pCmmObject->pNodeFreeListHead == NULL) { ++ GT_0trace(CMM_debugMask, GT_7CLASS, ++ "CMM_Create: LST_Create() " ++ "failed \n"); ++ status = DSP_EMEMORY; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) ++ status = SYNC_InitializeCS(&pCmmObject->hCmmLock); ++ ++ if (DSP_SUCCEEDED(status)) ++ *phCmmMgr = pCmmObject; ++ else ++ CMM_Destroy(pCmmObject, true); ++ ++ } else { ++ GT_0trace(CMM_debugMask, GT_6CLASS, ++ "CMM_Create: Object Allocation " ++ "Failure(CMM Object)\n"); ++ status = DSP_EMEMORY; ++ } ++ return status; ++} ++ ++/* ++ * ======== CMM_Destroy ======== ++ * Purpose: ++ * Release the communication memory manager resources. ++ */ ++DSP_STATUS CMM_Destroy(struct CMM_OBJECT *hCmmMgr, bool bForce) ++{ ++ struct CMM_OBJECT *pCmmMgr = (struct CMM_OBJECT *)hCmmMgr; ++ struct CMM_INFO tempInfo; ++ DSP_STATUS status = DSP_SOK; ++ s32 nSlot; ++ struct CMM_MNODE *pNode; ++ ++ DBC_Require(cRefs > 0); ++ if (!MEM_IsValidHandle(hCmmMgr, CMMSIGNATURE)) { ++ status = DSP_EHANDLE; ++ return status; ++ } ++ SYNC_EnterCS(pCmmMgr->hCmmLock); ++ /* If not force then fail if outstanding allocations exist */ ++ if (!bForce) { ++ /* Check for outstanding memory allocations */ ++ status = CMM_GetInfo(hCmmMgr, &tempInfo); ++ if (DSP_SUCCEEDED(status)) { ++ if (tempInfo.ulTotalInUseCnt > 0) { ++ /* outstanding allocations */ ++ status = DSP_EFAIL; ++ } ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* UnRegister SM allocator */ ++ for (nSlot = 0; nSlot < CMM_MAXGPPSEGS; nSlot++) { ++ if (pCmmMgr->paGPPSMSegTab[nSlot] != NULL) { ++ UnRegisterGPPSMSeg(pCmmMgr-> ++ paGPPSMSegTab[nSlot]); ++ /* Set slot to NULL for future reuse */ ++ pCmmMgr->paGPPSMSegTab[nSlot] = NULL; ++ } ++ } ++ } ++ if (pCmmMgr->pNodeFreeListHead != NULL) { ++ /* Free the free nodes */ ++ while (!LST_IsEmpty(pCmmMgr->pNodeFreeListHead)) { ++ /* (struct LST_ELEM*) pNode = ++ * LST_GetHead(pCmmMgr->pNodeFreeListHead);*/ ++ pNode = (struct CMM_MNODE *)LST_GetHead(pCmmMgr-> ++ pNodeFreeListHead); ++ MEM_Free(pNode); ++ } ++ /* delete NodeFreeList list */ ++ LST_Delete(pCmmMgr->pNodeFreeListHead); ++ } ++ SYNC_LeaveCS(pCmmMgr->hCmmLock); ++ if (DSP_SUCCEEDED(status)) { ++ /* delete CS & cmm mgr object */ ++ SYNC_DeleteCS(pCmmMgr->hCmmLock); ++ MEM_FreeObject(pCmmMgr); ++ } ++ return status; ++} ++ ++/* ++ * ======== CMM_Exit ======== ++ * Purpose: ++ * Discontinue usage of module; free resources when reference count ++ * reaches 0. ++ */ ++void CMM_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ cRefs--; ++ ++ GT_1trace(CMM_debugMask, GT_ENTER, ++ "exiting CMM_Exit,ref count:0x%x\n", cRefs); ++} ++ ++/* ++ * ======== CMM_FreeBuf ======== ++ * Purpose: ++ * Free the given buffer. ++ */ ++DSP_STATUS CMM_FreeBuf(struct CMM_OBJECT *hCmmMgr, void *pBufPA, u32 ulSegId) ++{ ++ struct CMM_OBJECT *pCmmMgr = (struct CMM_OBJECT *)hCmmMgr; ++ DSP_STATUS status = DSP_EPOINTER; ++ struct CMM_MNODE *pCurNode = NULL; ++ struct CMM_ALLOCATOR *pAllocator = NULL; ++ struct CMM_ATTRS *pAttrs; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pBufPA != NULL); ++ GT_1trace(CMM_debugMask, GT_ENTER, "CMM_FreeBuf pBufPA %x\n", pBufPA); ++ if (ulSegId == 0) { ++ pAttrs = &CMM_DFLTALCTATTRS; ++ ulSegId = pAttrs->ulSegId; ++ } ++ if (!(MEM_IsValidHandle(hCmmMgr, CMMSIGNATURE)) || !(ulSegId > 0)) { ++ status = DSP_EHANDLE; ++ return status; ++ } ++ /* get the allocator for this segment id */ ++ pAllocator = GetAllocator(pCmmMgr, ulSegId); ++ if (pAllocator != NULL) { ++ SYNC_EnterCS(pCmmMgr->hCmmLock); ++ pCurNode = (struct CMM_MNODE *)LST_First(pAllocator-> ++ pInUseListHead); ++ while (pCurNode) { ++ if ((u32)pBufPA == pCurNode->dwPA) { ++ /* Found it */ ++ LST_RemoveElem(pAllocator->pInUseListHead, ++ (struct LST_ELEM *)pCurNode); ++ /* back to freelist */ ++ AddToFreeList(pAllocator, pCurNode); ++ status = DSP_SOK; /* all right! */ ++ break; ++ } ++ /* next node. */ ++ pCurNode = (struct CMM_MNODE *)LST_Next(pAllocator-> ++ pInUseListHead, (struct LST_ELEM *)pCurNode); ++ } ++ SYNC_LeaveCS(pCmmMgr->hCmmLock); ++ } ++ return status; ++} ++ ++/* ++ * ======== CMM_GetHandle ======== ++ * Purpose: ++ * Return the communication memory manager object for this device. ++ * This is typically called from the client process. ++ */ ++DSP_STATUS CMM_GetHandle(DSP_HPROCESSOR hProcessor, ++ OUT struct CMM_OBJECT **phCmmMgr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *hDevObject; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phCmmMgr != NULL); ++ if (hProcessor != NULL) ++ status = PROC_GetDevObject(hProcessor, &hDevObject); ++ else ++ hDevObject = DEV_GetFirst(); /* default */ ++ ++ if (DSP_SUCCEEDED(status)) ++ status = DEV_GetCmmMgr(hDevObject, phCmmMgr); ++ ++ return status; ++} ++ ++/* ++ * ======== CMM_GetInfo ======== ++ * Purpose: ++ * Return the current memory utilization information. ++ */ ++DSP_STATUS CMM_GetInfo(struct CMM_OBJECT *hCmmMgr, ++ OUT struct CMM_INFO *pCmmInfo) ++{ ++ struct CMM_OBJECT *pCmmMgr = (struct CMM_OBJECT *)hCmmMgr; ++ u32 ulSeg; ++ DSP_STATUS status = DSP_SOK; ++ struct CMM_ALLOCATOR *pAltr; ++ struct CMM_MNODE *pCurNode = NULL; ++ ++ DBC_Require(pCmmInfo != NULL); ++ ++ if (!MEM_IsValidHandle(hCmmMgr, CMMSIGNATURE)) { ++ status = DSP_EHANDLE; ++ return status; ++ } ++ SYNC_EnterCS(pCmmMgr->hCmmLock); ++ pCmmInfo->ulNumGPPSMSegs = 0; /* # of SM segments */ ++ pCmmInfo->ulTotalInUseCnt = 0; /* Total # of outstanding alloc */ ++ pCmmInfo->ulMinBlockSize = pCmmMgr->ulMinBlockSize; /* min block size */ ++ /* check SM memory segments */ ++ for (ulSeg = 1; ulSeg <= CMM_MAXGPPSEGS; ulSeg++) { ++ /* get the allocator object for this segment id */ ++ pAltr = GetAllocator(pCmmMgr, ulSeg); ++ if (pAltr != NULL) { ++ pCmmInfo->ulNumGPPSMSegs++; ++ pCmmInfo->segInfo[ulSeg - 1].dwSegBasePa = ++ pAltr->dwSmBase - pAltr->ulDSPSize; ++ pCmmInfo->segInfo[ulSeg - 1].ulTotalSegSize = ++ pAltr->ulDSPSize + pAltr->ulSmSize; ++ pCmmInfo->segInfo[ulSeg - 1].dwGPPBasePA = ++ pAltr->dwSmBase; ++ pCmmInfo->segInfo[ulSeg - 1].ulGPPSize = ++ pAltr->ulSmSize; ++ pCmmInfo->segInfo[ulSeg - 1].dwDSPBaseVA = ++ pAltr->dwDSPBase; ++ pCmmInfo->segInfo[ulSeg - 1].ulDSPSize = ++ pAltr->ulDSPSize; ++ pCmmInfo->segInfo[ulSeg - 1].dwSegBaseVa = ++ pAltr->dwVmBase - pAltr->ulDSPSize; ++ pCmmInfo->segInfo[ulSeg - 1].ulInUseCnt = 0; ++ pCurNode = (struct CMM_MNODE *)LST_First(pAltr-> ++ pInUseListHead); ++ /* Count inUse blocks */ ++ while (pCurNode) { ++ pCmmInfo->ulTotalInUseCnt++; ++ pCmmInfo->segInfo[ulSeg - 1].ulInUseCnt++; ++ /* next node. */ ++ pCurNode = (struct CMM_MNODE *)LST_Next(pAltr-> ++ pInUseListHead, ++ (struct LST_ELEM *)pCurNode); ++ } ++ } ++ } /* end for */ ++ SYNC_LeaveCS(pCmmMgr->hCmmLock); ++ return status; ++} ++ ++/* ++ * ======== CMM_Init ======== ++ * Purpose: ++ * Initializes private state of CMM module. ++ */ ++bool CMM_Init(void) ++{ ++ bool fRetval = true; ++ ++ DBC_Require(cRefs >= 0); ++ if (cRefs == 0) { ++ /* Set the Trace mask */ ++ /* "CM" for Comm Memory manager */ ++ GT_create(&CMM_debugMask, "CM"); ++ } ++ if (fRetval) ++ cRefs++; ++ ++ GT_1trace(CMM_debugMask, GT_ENTER, ++ "Entered CMM_Init,ref count:0x%x\n", cRefs); ++ ++ DBC_Ensure((fRetval && (cRefs > 0)) || (!fRetval && (cRefs >= 0))); ++ ++ return fRetval; ++} ++ ++/* ++ * ======== CMM_RegisterGPPSMSeg ======== ++ * Purpose: ++ * Register a block of SM with the CMM to be used for later GPP SM ++ * allocations. ++ */ ++DSP_STATUS CMM_RegisterGPPSMSeg(struct CMM_OBJECT *hCmmMgr, u32 dwGPPBasePA, ++ u32 ulSize, u32 dwDSPAddrOffset, ++ enum CMM_CNVTTYPE cFactor, u32 dwDSPBase, ++ u32 ulDSPSize, u32 *pulSegId, ++ u32 dwGPPBaseVA) ++{ ++ struct CMM_OBJECT *pCmmMgr = (struct CMM_OBJECT *)hCmmMgr; ++ struct CMM_ALLOCATOR *pSMA = NULL; ++ DSP_STATUS status = DSP_SOK; ++ struct CMM_MNODE *pNewNode; ++ s32 nSlot; ++ ++ DBC_Require(ulSize > 0); ++ DBC_Require(pulSegId != NULL); ++ DBC_Require(dwGPPBasePA != 0); ++ DBC_Require(dwGPPBaseVA != 0); ++ DBC_Require((cFactor <= CMM_ADDTODSPPA) && ++ (cFactor >= CMM_SUBFROMDSPPA)); ++ GT_6trace(CMM_debugMask, GT_ENTER, ++ "CMM_RegisterGPPSMSeg dwGPPBasePA %x " ++ "ulSize %x dwDSPAddrOffset %x dwDSPBase %x ulDSPSize %x " ++ "dwGPPBaseVA %x\n", dwGPPBasePA, ulSize, dwDSPAddrOffset, ++ dwDSPBase, ulDSPSize, dwGPPBaseVA); ++ if (!MEM_IsValidHandle(hCmmMgr, CMMSIGNATURE)) { ++ status = DSP_EHANDLE; ++ return status; ++ } ++ /* make sure we have room for another allocator */ ++ SYNC_EnterCS(pCmmMgr->hCmmLock); ++ nSlot = GetSlot(pCmmMgr); ++ if (nSlot < 0) { ++ /* get a slot number */ ++ status = DSP_EFAIL; ++ goto func_end; ++ } ++ /* Check if input ulSize is big enough to alloc at least one block */ ++ if (DSP_SUCCEEDED(status)) { ++ if (ulSize < pCmmMgr->ulMinBlockSize) { ++ GT_0trace(CMM_debugMask, GT_7CLASS, ++ "CMM_RegisterGPPSMSeg: " ++ "ulSize too small\n"); ++ status = DSP_EINVALIDARG; ++ goto func_end; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* create, zero, and tag an SM allocator object */ ++ MEM_AllocObject(pSMA, struct CMM_ALLOCATOR, SMEMSIGNATURE); ++ } ++ if (pSMA != NULL) { ++ pSMA->hCmmMgr = hCmmMgr; /* ref to parent */ ++ pSMA->dwSmBase = dwGPPBasePA; /* SM Base phys */ ++ pSMA->ulSmSize = ulSize; /* SM segment size in bytes */ ++ pSMA->dwVmBase = dwGPPBaseVA; ++ pSMA->dwDSPPhysAddrOffset = dwDSPAddrOffset; ++ pSMA->cFactor = cFactor; ++ pSMA->dwDSPBase = dwDSPBase; ++ pSMA->ulDSPSize = ulDSPSize; ++ if (pSMA->dwVmBase == 0) { ++ GT_0trace(CMM_debugMask, GT_7CLASS, ++ "CMM_RegisterGPPSMSeg: Error" ++ "MEM_LinearAddress()\n"); ++ status = DSP_EFAIL; ++ goto func_end; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* return the actual segment identifier */ ++ *pulSegId = (u32) nSlot + 1; ++ /* create memory free list */ ++ pSMA->pFreeListHead = LST_Create(); ++ if (pSMA->pFreeListHead == NULL) { ++ GT_0trace(CMM_debugMask, GT_7CLASS, ++ "CMM_RegisterGPPSMSeg: " ++ "Out Of Memory \n"); ++ status = DSP_EMEMORY; ++ goto func_end; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* create memory in-use list */ ++ pSMA->pInUseListHead = LST_Create(); ++ if (pSMA->pInUseListHead == NULL) { ++ GT_0trace(CMM_debugMask, GT_7CLASS, ++ "CMM_RegisterGPPSMSeg: " ++ "LST_Create failed\n"); ++ status = DSP_EMEMORY; ++ goto func_end; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Get a mem node for this hunk-o-memory */ ++ pNewNode = GetNode(pCmmMgr, dwGPPBasePA, ++ pSMA->dwVmBase, ulSize); ++ /* Place node on the SM allocator's free list */ ++ if (pNewNode) { ++ LST_PutTail(pSMA->pFreeListHead, ++ (struct LST_ELEM *)pNewNode); ++ } else { ++ status = DSP_EMEMORY; ++ goto func_end; ++ } ++ } ++ if (DSP_FAILED(status)) { ++ /* Cleanup allocator */ ++ UnRegisterGPPSMSeg(pSMA); ++ } ++ } else { ++ GT_0trace(CMM_debugMask, GT_6CLASS, ++ "CMM_RegisterGPPSMSeg: SMA Object " ++ "Allocation Failure\n"); ++ status = DSP_EMEMORY; ++ goto func_end; ++ } ++ /* make entry */ ++ if (DSP_SUCCEEDED(status)) ++ pCmmMgr->paGPPSMSegTab[nSlot] = pSMA; ++ ++func_end: ++ SYNC_LeaveCS(pCmmMgr->hCmmLock); ++ return status; ++} ++ ++/* ++ * ======== CMM_UnRegisterGPPSMSeg ======== ++ * Purpose: ++ * UnRegister GPP SM segments with the CMM. ++ */ ++DSP_STATUS CMM_UnRegisterGPPSMSeg(struct CMM_OBJECT *hCmmMgr, u32 ulSegId) ++{ ++ struct CMM_OBJECT *pCmmMgr = (struct CMM_OBJECT *)hCmmMgr; ++ DSP_STATUS status = DSP_SOK; ++ struct CMM_ALLOCATOR *pSMA; ++ u32 ulId = ulSegId; ++ ++ DBC_Require(ulSegId > 0); ++ if (MEM_IsValidHandle(hCmmMgr, CMMSIGNATURE)) { ++ if (ulSegId == CMM_ALLSEGMENTS) ++ ulId = 1; ++ ++ if ((ulId > 0) && (ulId <= CMM_MAXGPPSEGS)) { ++ while (ulId <= CMM_MAXGPPSEGS) { ++ SYNC_EnterCS(pCmmMgr->hCmmLock); ++ /* slot = segId-1 */ ++ pSMA = pCmmMgr->paGPPSMSegTab[ulId - 1]; ++ if (pSMA != NULL) { ++ UnRegisterGPPSMSeg(pSMA); ++ /* Set alctr ptr to NULL for future ++ * reuse */ ++ pCmmMgr->paGPPSMSegTab[ulId - 1] = NULL; ++ } else if (ulSegId != CMM_ALLSEGMENTS) { ++ status = DSP_EFAIL; ++ } ++ SYNC_LeaveCS(pCmmMgr->hCmmLock); ++ if (ulSegId != CMM_ALLSEGMENTS) ++ break; ++ ++ ulId++; ++ } /* end while */ ++ } else { ++ status = DSP_EINVALIDARG; ++ GT_0trace(CMM_debugMask, GT_7CLASS, ++ "CMM_UnRegisterGPPSMSeg: Bad " ++ "segment Id\n"); ++ } ++ } else { ++ status = DSP_EHANDLE; ++ } ++ return status; ++} ++ ++/* ++ * ======== UnRegisterGPPSMSeg ======== ++ * Purpose: ++ * UnRegister the SM allocator by freeing all its resources and ++ * nulling cmm mgr table entry. ++ * Note: ++ * This routine is always called within cmm lock crit sect. ++ */ ++static void UnRegisterGPPSMSeg(struct CMM_ALLOCATOR *pSMA) ++{ ++ struct CMM_MNODE *pCurNode = NULL; ++ struct CMM_MNODE *pNextNode = NULL; ++ ++ DBC_Require(pSMA != NULL); ++ if (pSMA->pFreeListHead != NULL) { ++ /* free nodes on free list */ ++ pCurNode = (struct CMM_MNODE *)LST_First(pSMA->pFreeListHead); ++ while (pCurNode) { ++ pNextNode = (struct CMM_MNODE *)LST_Next(pSMA-> ++ pFreeListHead, ++ (struct LST_ELEM *)pCurNode); ++ LST_RemoveElem(pSMA->pFreeListHead, ++ (struct LST_ELEM *)pCurNode); ++ MEM_Free((void *) pCurNode); ++ /* next node. */ ++ pCurNode = pNextNode; ++ } ++ LST_Delete(pSMA->pFreeListHead); /* delete freelist */ ++ /* free nodes on InUse list */ ++ pCurNode = (struct CMM_MNODE *)LST_First(pSMA->pInUseListHead); ++ while (pCurNode) { ++ pNextNode = (struct CMM_MNODE *)LST_Next(pSMA-> ++ pInUseListHead, ++ (struct LST_ELEM *)pCurNode); ++ LST_RemoveElem(pSMA->pInUseListHead, ++ (struct LST_ELEM *)pCurNode); ++ MEM_Free((void *) pCurNode); ++ /* next node. */ ++ pCurNode = pNextNode; ++ } ++ LST_Delete(pSMA->pInUseListHead); /* delete InUse list */ ++ } ++ if ((void *) pSMA->dwVmBase != NULL) ++ MEM_UnmapLinearAddress((void *) pSMA->dwVmBase); ++ ++ /* Free allocator itself */ ++ MEM_FreeObject(pSMA); ++} ++ ++/* ++ * ======== GetSlot ======== ++ * Purpose: ++ * An available slot # is returned. Returns negative on failure. ++ */ ++static s32 GetSlot(struct CMM_OBJECT *pCmmMgr) ++{ ++ s32 nSlot = -1; /* neg on failure */ ++ DBC_Require(pCmmMgr != NULL); ++ /* get first available slot in cmm mgr SMSegTab[] */ ++ for (nSlot = 0; nSlot < CMM_MAXGPPSEGS; nSlot++) { ++ if (pCmmMgr->paGPPSMSegTab[nSlot] == NULL) ++ break; ++ ++ } ++ if (nSlot == CMM_MAXGPPSEGS) { ++ GT_0trace(CMM_debugMask, GT_7CLASS, ++ "CMM_RegisterGPPSMSeg: Allocator " ++ "entry failure, max exceeded\n"); ++ nSlot = -1; /* failed */ ++ } ++ return nSlot; ++} ++ ++/* ++ * ======== GetNode ======== ++ * Purpose: ++ * Get a memory node from freelist or create a new one. ++ */ ++static struct CMM_MNODE *GetNode(struct CMM_OBJECT *pCmmMgr, u32 dwPA, ++ u32 dwVA, u32 ulSize) ++{ ++ struct CMM_MNODE *pNode = NULL; ++ ++ DBC_Require(pCmmMgr != NULL); ++ DBC_Require(dwPA != 0); ++ DBC_Require(dwVA != 0); ++ DBC_Require(ulSize != 0); ++ /* Check cmm mgr's node freelist */ ++ if (LST_IsEmpty(pCmmMgr->pNodeFreeListHead)) { ++ pNode = (struct CMM_MNODE *)MEM_Calloc(sizeof(struct CMM_MNODE), ++ MEM_PAGED); ++ } else { ++ /* surely a valid element */ ++ /* (struct LST_ELEM*) pNode = LST_GetHead(pCmmMgr-> ++ * pNodeFreeListHead);*/ ++ pNode = (struct CMM_MNODE *)LST_GetHead(pCmmMgr-> ++ pNodeFreeListHead); ++ } ++ if (pNode == NULL) { ++ GT_0trace(CMM_debugMask, GT_7CLASS, "GetNode: Out Of Memory\n"); ++ } else { ++ LST_InitElem((struct LST_ELEM *) pNode); /* set self */ ++ pNode->dwPA = dwPA; /* Physical addr of start of block */ ++ pNode->dwVA = dwVA; /* Virtual " " */ ++ pNode->ulSize = ulSize; /* Size of block */ ++ } ++ return pNode; ++} ++ ++/* ++ * ======== DeleteNode ======== ++ * Purpose: ++ * Put a memory node on the cmm nodelist for later use. ++ * Doesn't actually delete the node. Heap thrashing friendly. ++ */ ++static void DeleteNode(struct CMM_OBJECT *pCmmMgr, struct CMM_MNODE *pNode) ++{ ++ DBC_Require(pNode != NULL); ++ LST_InitElem((struct LST_ELEM *) pNode); /* init .self ptr */ ++ LST_PutTail(pCmmMgr->pNodeFreeListHead, (struct LST_ELEM *) pNode); ++} ++ ++/* ++ * ====== GetFreeBlock ======== ++ * Purpose: ++ * Scan the free block list and return the first block that satisfies ++ * the size. ++ */ ++static struct CMM_MNODE *GetFreeBlock(struct CMM_ALLOCATOR *pAllocator, ++ u32 uSize) ++{ ++ if (pAllocator) { ++ struct CMM_MNODE *pCurNode = (struct CMM_MNODE *) ++ LST_First(pAllocator->pFreeListHead); ++ while (pCurNode) { ++ if (uSize <= (u32) pCurNode->ulSize) { ++ LST_RemoveElem(pAllocator->pFreeListHead, ++ (struct LST_ELEM *)pCurNode); ++ return pCurNode; ++ } ++ /* next node. */ ++ pCurNode = (struct CMM_MNODE *)LST_Next(pAllocator-> ++ pFreeListHead, (struct LST_ELEM *)pCurNode); ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * ======== AddToFreeList ======== ++ * Purpose: ++ * Coelesce node into the freelist in ascending size order. ++ */ ++static void AddToFreeList(struct CMM_ALLOCATOR *pAllocator, ++ struct CMM_MNODE *pNode) ++{ ++ struct CMM_MNODE *pNodePrev = NULL; ++ struct CMM_MNODE *pNodeNext = NULL; ++ struct CMM_MNODE *pCurNode; ++ u32 dwThisPA; ++ u32 dwNextPA; ++ ++ DBC_Require(pNode != NULL); ++ DBC_Require(pAllocator != NULL); ++ dwThisPA = pNode->dwPA; ++ dwNextPA = NEXT_PA(pNode); ++ pCurNode = (struct CMM_MNODE *)LST_First(pAllocator->pFreeListHead); ++ while (pCurNode) { ++ if (dwThisPA == NEXT_PA(pCurNode)) { ++ /* found the block ahead of this one */ ++ pNodePrev = pCurNode; ++ } else if (dwNextPA == pCurNode->dwPA) { ++ pNodeNext = pCurNode; ++ } ++ if ((pNodePrev == NULL) || (pNodeNext == NULL)) { ++ /* next node. */ ++ pCurNode = (struct CMM_MNODE *)LST_Next(pAllocator-> ++ pFreeListHead, (struct LST_ELEM *)pCurNode); ++ } else { ++ /* got 'em */ ++ break; ++ } ++ } /* while */ ++ if (pNodePrev != NULL) { ++ /* combine with previous block */ ++ LST_RemoveElem(pAllocator->pFreeListHead, ++ (struct LST_ELEM *)pNodePrev); ++ /* grow node to hold both */ ++ pNode->ulSize += pNodePrev->ulSize; ++ pNode->dwPA = pNodePrev->dwPA; ++ pNode->dwVA = pNodePrev->dwVA; ++ /* place node on mgr nodeFreeList */ ++ DeleteNode((struct CMM_OBJECT *)pAllocator->hCmmMgr, pNodePrev); ++ } ++ if (pNodeNext != NULL) { ++ /* combine with next block */ ++ LST_RemoveElem(pAllocator->pFreeListHead, ++ (struct LST_ELEM *)pNodeNext); ++ /* grow da node */ ++ pNode->ulSize += pNodeNext->ulSize; ++ /* place node on mgr nodeFreeList */ ++ DeleteNode((struct CMM_OBJECT *)pAllocator->hCmmMgr, pNodeNext); ++ } ++ /* Now, let's add to freelist in increasing size order */ ++ pCurNode = (struct CMM_MNODE *)LST_First(pAllocator->pFreeListHead); ++ while (pCurNode) { ++ if (pNode->ulSize <= pCurNode->ulSize) ++ break; ++ ++ /* next node. */ ++ pCurNode = (struct CMM_MNODE *)LST_Next(pAllocator-> ++ pFreeListHead, (struct LST_ELEM *)pCurNode); ++ } ++ /* if pCurNode is NULL then add our pNode to the end of the freelist */ ++ if (pCurNode == NULL) { ++ LST_PutTail(pAllocator->pFreeListHead, ++ (struct LST_ELEM *)pNode); ++ } else { ++ /* insert our node before the current traversed node */ ++ LST_InsertBefore(pAllocator->pFreeListHead, ++ (struct LST_ELEM *)pNode, ++ (struct LST_ELEM *)pCurNode); ++ } ++} ++ ++/* ++ * ======== GetAllocator ======== ++ * Purpose: ++ * Return the allocator for the given SM Segid. ++ * SegIds: 1,2,3..max. ++ */ ++static struct CMM_ALLOCATOR *GetAllocator(struct CMM_OBJECT *pCmmMgr, ++ u32 ulSegId) ++{ ++ struct CMM_ALLOCATOR *pAllocator = NULL; ++ ++ DBC_Require(pCmmMgr != NULL); ++ DBC_Require((ulSegId > 0) && (ulSegId <= CMM_MAXGPPSEGS)); ++ pAllocator = pCmmMgr->paGPPSMSegTab[ulSegId - 1]; ++ if (pAllocator != NULL) { ++ /* make sure it's for real */ ++ if (!MEM_IsValidHandle(pAllocator, SMEMSIGNATURE)) { ++ pAllocator = NULL; ++ DBC_Assert(false); ++ } ++ } ++ return pAllocator; ++} ++ ++/* ++ * ======== CMM_XlatorCreate ======== ++ * Purpose: ++ * Create an address translator object. ++ */ ++DSP_STATUS CMM_XlatorCreate(OUT struct CMM_XLATOROBJECT **phXlator, ++ struct CMM_OBJECT *hCmmMgr, ++ struct CMM_XLATORATTRS *pXlatorAttrs) ++{ ++ struct CMM_XLATOR *pXlatorObject = NULL; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phXlator != NULL); ++ DBC_Require(hCmmMgr != NULL); ++ GT_3trace(CMM_debugMask, GT_ENTER, ++ "CMM_XlatorCreate: phXlator: 0x%x\t" ++ "phCmmMgr: 0x%x\tpXlAttrs: 0x%x\n", phXlator, ++ hCmmMgr, pXlatorAttrs); ++ *phXlator = NULL; ++ if (pXlatorAttrs == NULL) ++ pXlatorAttrs = &CMM_DFLTXLATORATTRS; /* set defaults */ ++ ++ MEM_AllocObject(pXlatorObject, struct CMM_XLATOR, CMMXLATESIGNATURE); ++ if (pXlatorObject != NULL) { ++ pXlatorObject->hCmmMgr = hCmmMgr; /* ref back to CMM */ ++ pXlatorObject->ulSegId = pXlatorAttrs->ulSegId; /* SM segId */ ++ } else { ++ GT_0trace(CMM_debugMask, GT_6CLASS, ++ "CMM_XlatorCreate: Object Allocation" ++ "Failure(CMM Xlator)\n"); ++ status = DSP_EMEMORY; ++ } ++ if (DSP_SUCCEEDED(status)) ++ *phXlator = (struct CMM_XLATOROBJECT *) pXlatorObject; ++ ++ return status; ++} ++ ++/* ++ * ======== CMM_XlatorDelete ======== ++ * Purpose: ++ * Free the Xlator resources. ++ * VM gets freed later. ++ */ ++DSP_STATUS CMM_XlatorDelete(struct CMM_XLATOROBJECT *hXlator, bool bForce) ++{ ++ struct CMM_XLATOR *pXlator = (struct CMM_XLATOR *)hXlator; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ ++ if (MEM_IsValidHandle(pXlator, CMMXLATESIGNATURE)) { ++ MEM_FreeObject(pXlator); ++ } else { ++ status = DSP_EHANDLE; ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== CMM_XlatorAllocBuf ======== ++ */ ++void *CMM_XlatorAllocBuf(struct CMM_XLATOROBJECT *hXlator, void *pVaBuf, ++ u32 uPaSize) ++{ ++ struct CMM_XLATOR *pXlator = (struct CMM_XLATOR *)hXlator; ++ void *pBuf = NULL; ++ struct CMM_ATTRS attrs; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(hXlator != NULL); ++ DBC_Require(pXlator->hCmmMgr != NULL); ++ DBC_Require(pVaBuf != NULL); ++ DBC_Require(uPaSize > 0); ++ DBC_Require(pXlator->ulSegId > 0); ++ ++ if (MEM_IsValidHandle(pXlator, CMMXLATESIGNATURE)) { ++ attrs.ulSegId = pXlator->ulSegId; ++ *(volatile u32 *)pVaBuf = 0; ++ /* Alloc SM */ ++ pBuf = CMM_CallocBuf(pXlator->hCmmMgr, uPaSize, &attrs, NULL); ++ if (pBuf) { ++ /* convert to translator(node/strm) process Virtual ++ * address */ ++ *(volatile u32 **)pVaBuf = ++ (u32 *)CMM_XlatorTranslate(hXlator, ++ pBuf, CMM_PA2VA); ++ } ++ } ++ return pBuf; ++} ++ ++/* ++ * ======== CMM_XlatorFreeBuf ======== ++ * Purpose: ++ * Free the given SM buffer and descriptor. ++ * Does not free virtual memory. ++ */ ++DSP_STATUS CMM_XlatorFreeBuf(struct CMM_XLATOROBJECT *hXlator, void *pBufVa) ++{ ++ struct CMM_XLATOR *pXlator = (struct CMM_XLATOR *)hXlator; ++ DSP_STATUS status = DSP_EFAIL; ++ void *pBufPa = NULL; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pBufVa != NULL); ++ DBC_Require(pXlator->ulSegId > 0); ++ ++ if (MEM_IsValidHandle(pXlator, CMMXLATESIGNATURE)) { ++ /* convert Va to Pa so we can free it. */ ++ pBufPa = CMM_XlatorTranslate(hXlator, pBufVa, CMM_VA2PA); ++ if (pBufPa) { ++ status = CMM_FreeBuf(pXlator->hCmmMgr, pBufPa, ++ pXlator->ulSegId); ++ if (DSP_FAILED(status)) { ++ /* Uh oh, this shouldn't happen. Descriptor ++ * gone! */ ++ GT_2trace(CMM_debugMask, GT_7CLASS, ++ "Cannot free DMA/ZCPY buffer" ++ "not allocated by MPU. PA %x, VA %x\n", ++ pBufPa, pBufVa); ++ DBC_Assert(false); /* CMM is leaking mem! */ ++ } ++ } ++ } ++ return status; ++} ++ ++/* ++ * ======== CMM_XlatorInfo ======== ++ * Purpose: ++ * Set/Get translator info. ++ */ ++DSP_STATUS CMM_XlatorInfo(struct CMM_XLATOROBJECT *hXlator, IN OUT u8 **pAddr, ++ u32 ulSize, u32 uSegId, bool bSetInfo) ++{ ++ struct CMM_XLATOR *pXlator = (struct CMM_XLATOR *)hXlator; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pAddr != NULL); ++ DBC_Require((uSegId > 0) && (uSegId <= CMM_MAXGPPSEGS)); ++ ++ if (MEM_IsValidHandle(pXlator, CMMXLATESIGNATURE)) { ++ if (bSetInfo) { ++ /* set translators virtual address range */ ++ pXlator->dwVirtBase = (u32)*pAddr; ++ pXlator->ulVirtSize = ulSize; ++ GT_2trace(CMM_debugMask, GT_3CLASS, ++ "pXlator->dwVirtBase %x, " ++ "ulVirtSize %x\n", pXlator->dwVirtBase, ++ pXlator->ulVirtSize); ++ } else { /* return virt base address */ ++ *pAddr = (u8 *)pXlator->dwVirtBase; ++ } ++ } else { ++ status = DSP_EHANDLE; ++ } ++ return status; ++} ++ ++/* ++ * ======== CMM_XlatorTranslate ======== ++ */ ++void *CMM_XlatorTranslate(struct CMM_XLATOROBJECT *hXlator, void *pAddr, ++ enum CMM_XLATETYPE xType) ++{ ++ u32 dwAddrXlate = 0; ++ struct CMM_XLATOR *pXlator = (struct CMM_XLATOR *)hXlator; ++ struct CMM_OBJECT *pCmmMgr = NULL; ++ struct CMM_ALLOCATOR *pAlctr = NULL; ++ u32 dwOffset = 0; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pAddr != NULL); ++ DBC_Require((xType >= CMM_VA2PA) && (xType <= CMM_DSPPA2PA)); ++ ++ if (!MEM_IsValidHandle(pXlator, CMMXLATESIGNATURE)) ++ goto loop_cont; ++ ++ pCmmMgr = (struct CMM_OBJECT *)pXlator->hCmmMgr; ++ /* get this translator's default SM allocator */ ++ DBC_Assert(pXlator->ulSegId > 0); ++ pAlctr = pCmmMgr->paGPPSMSegTab[pXlator->ulSegId - 1]; ++ if (!MEM_IsValidHandle(pAlctr, SMEMSIGNATURE)) ++ goto loop_cont; ++ ++ if ((xType == CMM_VA2DSPPA) || (xType == CMM_VA2PA) || ++ (xType == CMM_PA2VA)) { ++ if (xType == CMM_PA2VA) { ++ /* Gpp Va = Va Base + offset */ ++ dwOffset = (u8 *)pAddr - (u8 *)(pAlctr->dwSmBase - ++ pAlctr->ulDSPSize); ++ dwAddrXlate = pXlator->dwVirtBase + dwOffset; ++ /* Check if translated Va base is in range */ ++ if ((dwAddrXlate < pXlator->dwVirtBase) || ++ (dwAddrXlate >= ++ (pXlator->dwVirtBase + pXlator->ulVirtSize))) { ++ dwAddrXlate = 0; /* bad address */ ++ GT_0trace(CMM_debugMask, GT_7CLASS, ++ "CMM_XlatorTranslate: " ++ "Virt addr out of range\n"); ++ } ++ } else { ++ /* Gpp PA = Gpp Base + offset */ ++ dwOffset = (u8 *)pAddr - (u8 *)pXlator->dwVirtBase; ++ dwAddrXlate = pAlctr->dwSmBase - pAlctr->ulDSPSize + ++ dwOffset; ++ } ++ } else { ++ dwAddrXlate = (u32)pAddr; ++ } ++ /*Now convert address to proper target physical address if needed*/ ++ if ((xType == CMM_VA2DSPPA) || (xType == CMM_PA2DSPPA)) { ++ /* Got Gpp Pa now, convert to DSP Pa */ ++ dwAddrXlate = GPPPA2DSPPA((pAlctr->dwSmBase - pAlctr-> ++ ulDSPSize), dwAddrXlate, ++ pAlctr->dwDSPPhysAddrOffset * ++ pAlctr->cFactor); ++ } else if (xType == CMM_DSPPA2PA) { ++ /* Got DSP Pa, convert to GPP Pa */ ++ dwAddrXlate = DSPPA2GPPPA(pAlctr->dwSmBase - pAlctr->ulDSPSize, ++ dwAddrXlate, ++ pAlctr->dwDSPPhysAddrOffset * ++ pAlctr->cFactor); ++ } ++loop_cont: ++ if (!dwAddrXlate) { ++ GT_2trace(CMM_debugMask, GT_7CLASS, ++ "CMM_XlatorTranslate: Can't translate" ++ " address: 0x%x xType %x\n", pAddr, xType); ++ } else { ++ GT_3trace(CMM_debugMask, GT_3CLASS, ++ "CMM_XlatorTranslate: pAddr %x, xType" ++ " %x, dwAddrXlate %x\n", pAddr, xType, dwAddrXlate); ++ } ++ return (void *)dwAddrXlate; ++} +diff --git a/drivers/dsp/bridge/pmgr/cod.c b/drivers/dsp/bridge/pmgr/cod.c +new file mode 100644 +index 0000000..6363f1e +--- /dev/null ++++ b/drivers/dsp/bridge/pmgr/cod.c +@@ -0,0 +1,683 @@ ++/* ++ * cod.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== cod.c ======== ++ * This module implements DSP code management for the DSP/BIOS Bridge ++ * environment. It is mostly a thin wrapper. ++ * ++ * This module provides an interface for loading both static and ++ * dynamic code objects onto DSP systems. ++ * ++ *! Revision History ++ *! ================ ++ *! 08-Apr-2003 map: Consolidated DBL to DBLL loader name ++ *! 24-Feb-2003 swa: PMGR Code review comments incorporated. ++ *! 18-Apr-2002 jeh: Added DBL function tables. ++ *! 20-Nov-2001 jeh: Removed call to ZL_loadArgs function. ++ *! 19-Oct-2001 jeh: Access DBL as a static library. Added COD_GetBaseLib, ++ *! COD_GetLoader, removed COD_LoadSection, COD_UnloadSection. ++ *! 07-Sep-2001 jeh: Added COD_LoadSection(), COD_UnloadSection(). ++ *! 07-Aug-2001 rr: hMgr->baseLib is updated after zlopen in COD_LoadBase. ++ *! 18-Apr-2001 jeh: Check for fLoaded flag before ZL_unload, to allow ++ *! COD_OpenBase to be used. ++ *! 11-Jan-2001 jeh: Added COD_OpenBase (not used yet, since there is an ++ *! occasional crash). ++ *! 02-Aug-2000 kc: Added COD_ReadSection to COD module. Incorporates use ++ *! of ZL_readSect (new function in ZL module). ++ *! 28-Feb-2000 rr: New GT Usage Implementation ++ *! 08-Dec-1999 ag: Removed x86 specific __asm int 3. ++ *! 02-Oct-1999 ag: Added #ifdef DEBUGINT3COD for debug. ++ *! 20-Sep-1999 ag: Removed call to GT_set(). ++ *! 04-Jun-1997 cr: Added validation of argc/argv pair in COD_LoadBase, as it ++ *! is a requirement to ZL_loadArgs. ++ *! 31-May-1997 cr: Changed COD_LoadBase argc value from u32 to int, added ++ *! DSP_ENOTIMPL return value to COD_Create when attrs != NULL. ++ *! 29-May-1997 cr: Added debugging support. ++ *! 24-Oct-1996 gp: Added COD_GetSection(). ++ *! 18-Jun-1996 gp: Updated GetSymValue() to check for lib; updated E_ codes. ++ *! 12-Jun-1996 gp: Imported CSL_ services for strcpyn(); Added ref counting. ++ *! 20-May-1996 mg: Adapted for new MEM and LDR modules. ++ *! 08-May-1996 mg: Created. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++/* Include appropriate loader header file */ ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* magic number for handle validation */ ++#define MAGIC 0xc001beef ++ ++/* macro to validate COD manager handles */ ++#define IsValid(h) ((h) != NULL && (h)->ulMagic == MAGIC) ++ ++/* ++ * ======== COD_MANAGER ======== ++ */ ++struct COD_MANAGER { ++ struct DBLL_TarObj *target; ++ struct DBLL_LibraryObj *baseLib; ++ bool fLoaded; /* Base library loaded? */ ++ u32 ulEntry; ++ struct LDR_MODULE *hDll; ++ struct DBLL_Fxns fxns; ++ struct DBLL_Attrs attrs; ++ char szZLFile[COD_MAXPATHLENGTH]; ++ u32 ulMagic; ++} ; ++ ++/* ++ * ======== COD_LIBRARYOBJ ======== ++ */ ++struct COD_LIBRARYOBJ { ++ struct DBLL_LibraryObj *dbllLib; ++ struct COD_MANAGER *hCodMgr; ++} ; ++ ++static u32 cRefs = 0L; ++ ++#if GT_TRACE ++static struct GT_Mask COD_debugMask = { NULL, NULL }; ++#endif ++ ++static struct DBLL_Fxns dbllFxns = { ++ (DBLL_CloseFxn) DBLL_close, ++ (DBLL_CreateFxn) DBLL_create, ++ (DBLL_DeleteFxn) DBLL_delete, ++ (DBLL_ExitFxn) DBLL_exit, ++ (DBLL_GetAttrsFxn) DBLL_getAttrs, ++ (DBLL_GetAddrFxn) DBLL_getAddr, ++ (DBLL_GetCAddrFxn) DBLL_getCAddr, ++ (DBLL_GetSectFxn) DBLL_getSect, ++ (DBLL_InitFxn) DBLL_init, ++ (DBLL_LoadFxn) DBLL_load, ++ (DBLL_LoadSectFxn) DBLL_loadSect, ++ (DBLL_OpenFxn) DBLL_open, ++ (DBLL_ReadSectFxn) DBLL_readSect, ++ (DBLL_SetAttrsFxn) DBLL_setAttrs, ++ (DBLL_UnloadFxn) DBLL_unload, ++ (DBLL_UnloadSectFxn) DBLL_unloadSect, ++}; ++ ++static bool NoOp(void); ++ ++/* ++ * ======== COD_Close ======== ++ */ ++void COD_Close(struct COD_LIBRARYOBJ *lib) ++{ ++ struct COD_MANAGER *hMgr; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(lib != NULL); ++ DBC_Require(IsValid(((struct COD_LIBRARYOBJ *)lib)->hCodMgr)); ++ ++ hMgr = lib->hCodMgr; ++ hMgr->fxns.closeFxn(lib->dbllLib); ++ ++ MEM_Free(lib); ++} ++ ++/* ++ * ======== COD_Create ======== ++ * Purpose: ++ * Create an object to manage code on a DSP system. ++ * This object can be used to load an initial program image with ++ * arguments that can later be expanded with ++ * dynamically loaded object files. ++ * ++ */ ++DSP_STATUS COD_Create(OUT struct COD_MANAGER **phMgr, char *pstrDummyFile, ++ IN OPTIONAL CONST struct COD_ATTRS *attrs) ++{ ++ struct COD_MANAGER *hMgrNew; ++ struct DBLL_Attrs zlAttrs; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phMgr != NULL); ++ ++ GT_3trace(COD_debugMask, GT_ENTER, ++ "Entered COD_Create, Args: \t\nphMgr: " ++ "0x%x\t\npstrDummyFile: 0x%x\t\nattr: 0x%x\n", ++ phMgr, pstrDummyFile, attrs); ++ /* assume failure */ ++ *phMgr = NULL; ++ ++ /* we don't support non-default attrs yet */ ++ if (attrs != NULL) ++ return DSP_ENOTIMPL; ++ ++ hMgrNew = MEM_Calloc(sizeof(struct COD_MANAGER), MEM_NONPAGED); ++ if (hMgrNew == NULL) { ++ GT_0trace(COD_debugMask, GT_7CLASS, ++ "COD_Create: Out Of Memory\n"); ++ return DSP_EMEMORY; ++ } ++ ++ hMgrNew->ulMagic = MAGIC; ++ ++ /* Set up loader functions */ ++ hMgrNew->fxns = dbllFxns; ++ ++ /* initialize the ZL module */ ++ hMgrNew->fxns.initFxn(); ++ ++ zlAttrs.alloc = (DBLL_AllocFxn)NoOp; ++ zlAttrs.free = (DBLL_FreeFxn)NoOp; ++ zlAttrs.fread = (DBLL_ReadFxn)KFILE_Read; ++ zlAttrs.fseek = (DBLL_SeekFxn)KFILE_Seek; ++ zlAttrs.ftell = (DBLL_TellFxn)KFILE_Tell; ++ zlAttrs.fclose = (DBLL_FCloseFxn)KFILE_Close; ++ zlAttrs.fopen = (DBLL_FOpenFxn)KFILE_Open; ++ zlAttrs.symLookup = NULL; ++ zlAttrs.baseImage = true; ++ zlAttrs.logWrite = NULL; ++ zlAttrs.logWriteHandle = NULL; ++ zlAttrs.write = NULL; ++ zlAttrs.rmmHandle = NULL; ++ zlAttrs.wHandle = NULL; ++ zlAttrs.symHandle = NULL; ++ zlAttrs.symArg = NULL; ++ ++ hMgrNew->attrs = zlAttrs; ++ ++ status = hMgrNew->fxns.createFxn(&hMgrNew->target, &zlAttrs); ++ ++ if (DSP_FAILED(status)) { ++ COD_Delete(hMgrNew); ++ GT_1trace(COD_debugMask, GT_7CLASS, ++ "COD_Create:ZL Create Failed: 0x%x\n", status); ++ return COD_E_ZLCREATEFAILED; ++ } ++ ++ /* return the new manager */ ++ *phMgr = hMgrNew; ++ GT_1trace(COD_debugMask, GT_1CLASS, ++ "COD_Create: Success CodMgr: 0x%x\n", *phMgr); ++ return DSP_SOK; ++} ++ ++/* ++ * ======== COD_Delete ======== ++ * Purpose: ++ * Delete a code manager object. ++ */ ++void COD_Delete(struct COD_MANAGER *hMgr) ++{ ++ DBC_Require(cRefs > 0); ++ DBC_Require(IsValid(hMgr)); ++ ++ GT_1trace(COD_debugMask, GT_ENTER, "COD_Delete:hMgr 0x%x\n", hMgr); ++ if (hMgr->baseLib) { ++ if (hMgr->fLoaded) ++ hMgr->fxns.unloadFxn(hMgr->baseLib, &hMgr->attrs); ++ ++ hMgr->fxns.closeFxn(hMgr->baseLib); ++ } ++ if (hMgr->target) { ++ hMgr->fxns.deleteFxn(hMgr->target); ++ hMgr->fxns.exitFxn(); ++ } ++ hMgr->ulMagic = ~MAGIC; ++ MEM_Free(hMgr); ++} ++ ++/* ++ * ======== COD_Exit ======== ++ * Purpose: ++ * Discontinue usage of the COD module. ++ * ++ */ ++void COD_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ cRefs--; ++ ++ GT_1trace(COD_debugMask, GT_ENTER, ++ "Entered COD_Exit, ref count: 0x%x\n", cRefs); ++ ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== COD_GetBaseLib ======== ++ * Purpose: ++ * Get handle to the base image DBL library. ++ */ ++DSP_STATUS COD_GetBaseLib(struct COD_MANAGER *hManager, ++ struct DBLL_LibraryObj **plib) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(IsValid(hManager)); ++ DBC_Require(plib != NULL); ++ ++ *plib = (struct DBLL_LibraryObj *) hManager->baseLib; ++ ++ return status; ++} ++ ++/* ++ * ======== COD_GetBaseName ======== ++ */ ++DSP_STATUS COD_GetBaseName(struct COD_MANAGER *hManager, char *pszName, ++ u32 uSize) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(IsValid(hManager)); ++ DBC_Require(pszName != NULL); ++ ++ if (uSize <= COD_MAXPATHLENGTH) ++ strncpy(pszName, hManager->szZLFile, uSize); ++ else ++ status = DSP_EFAIL; ++ ++ return status; ++} ++ ++/* ++ * ======== COD_GetEntry ======== ++ * Purpose: ++ * Retrieve the entry point of a loaded DSP program image ++ * ++ */ ++DSP_STATUS COD_GetEntry(struct COD_MANAGER *hManager, u32 *pulEntry) ++{ ++ DBC_Require(cRefs > 0); ++ DBC_Require(IsValid(hManager)); ++ DBC_Require(pulEntry != NULL); ++ ++ *pulEntry = hManager->ulEntry; ++ ++ GT_1trace(COD_debugMask, GT_ENTER, "COD_GetEntry:ulEntr 0x%x\n", ++ *pulEntry); ++ ++ return DSP_SOK; ++} ++ ++/* ++ * ======== COD_GetLoader ======== ++ * Purpose: ++ * Get handle to the DBLL loader. ++ */ ++DSP_STATUS COD_GetLoader(struct COD_MANAGER *hManager, ++ struct DBLL_TarObj **phLoader) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(IsValid(hManager)); ++ DBC_Require(phLoader != NULL); ++ ++ *phLoader = (struct DBLL_TarObj *)hManager->target; ++ ++ return status; ++} ++ ++/* ++ * ======== COD_GetSection ======== ++ * Purpose: ++ * Retrieve the starting address and length of a section in the COFF file ++ * given the section name. ++ */ ++DSP_STATUS COD_GetSection(struct COD_LIBRARYOBJ *lib, IN char *pstrSect, ++ OUT u32 *puAddr, OUT u32 *puLen) ++{ ++ struct COD_MANAGER *hManager; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(lib != NULL); ++ DBC_Require(IsValid(lib->hCodMgr)); ++ DBC_Require(pstrSect != NULL); ++ DBC_Require(puAddr != NULL); ++ DBC_Require(puLen != NULL); ++ ++ GT_4trace(COD_debugMask, GT_ENTER, ++ "Entered COD_GetSection Args \t\n lib: " ++ "0x%x\t\npstrsect: 0x%x\t\npuAddr: 0x%x\t\npuLen: 0x%x\n", ++ lib, pstrSect, puAddr, puLen); ++ *puAddr = 0; ++ *puLen = 0; ++ if (lib != NULL) { ++ hManager = lib->hCodMgr; ++ status = hManager->fxns.getSectFxn(lib->dbllLib, pstrSect, ++ puAddr, puLen); ++ if (DSP_FAILED(status)) { ++ GT_1trace(COD_debugMask, GT_7CLASS, ++ "COD_GetSection: Section %s not" ++ "found\n", pstrSect); ++ } ++ } else { ++ status = COD_E_NOSYMBOLSLOADED; ++ GT_0trace(COD_debugMask, GT_7CLASS, ++ "COD_GetSection:No Symbols loaded\n"); ++ } ++ ++ DBC_Ensure(DSP_SUCCEEDED(status) || ((*puAddr == 0) && (*puLen == 0))); ++ ++ return status; ++} ++ ++/* ++ * ======== COD_GetSymValue ======== ++ * Purpose: ++ * Retrieve the value for the specified symbol. The symbol is first ++ * searched for literally and then, if not found, searched for as a ++ * C symbol. ++ * ++ */ ++DSP_STATUS COD_GetSymValue(struct COD_MANAGER *hMgr, char *pstrSym, ++ u32 *pulValue) ++{ ++ struct DBLL_Symbol *pSym; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(IsValid(hMgr)); ++ DBC_Require(pstrSym != NULL); ++ DBC_Require(pulValue != NULL); ++ ++ GT_3trace(COD_debugMask, GT_ENTER, "Entered COD_GetSymValue Args \t\n" ++ "hMgr: 0x%x\t\npstrSym: 0x%x\t\npulValue: 0x%x\n", ++ hMgr, pstrSym, pulValue); ++ if (hMgr->baseLib) { ++ if (!hMgr->fxns.getAddrFxn(hMgr->baseLib, pstrSym, &pSym)) { ++ if (!hMgr->fxns.getCAddrFxn(hMgr->baseLib, pstrSym, ++ &pSym)) { ++ GT_0trace(COD_debugMask, GT_7CLASS, ++ "COD_GetSymValue: " ++ "Symbols not found\n"); ++ return COD_E_SYMBOLNOTFOUND; ++ } ++ } ++ } else { ++ GT_0trace(COD_debugMask, GT_7CLASS, "COD_GetSymValue: " ++ "No Symbols loaded\n"); ++ return COD_E_NOSYMBOLSLOADED; ++ } ++ ++ *pulValue = pSym->value; ++ ++ return DSP_SOK; ++} ++ ++/* ++ * ======== COD_Init ======== ++ * Purpose: ++ * Initialize the COD module's private state. ++ * ++ */ ++bool COD_Init(void) ++{ ++ bool fRetVal = true; ++ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++ DBC_Assert(!COD_debugMask.flags); ++ GT_create(&COD_debugMask, "CO"); ++ } ++ ++ if (fRetVal) ++ cRefs++; ++ ++ ++ GT_1trace(COD_debugMask, GT_1CLASS, ++ "Entered COD_Init, ref count: 0x%x\n", cRefs); ++ DBC_Ensure((fRetVal && cRefs > 0) || (!fRetVal && cRefs >= 0)); ++ return fRetVal; ++} ++ ++/* ++ * ======== COD_LoadBase ======== ++ * Purpose: ++ * Load the initial program image, optionally with command-line arguments, ++ * on the DSP system managed by the supplied handle. The program to be ++ * loaded must be the first element of the args array and must be a fully ++ * qualified pathname. ++ * Details: ++ * if nArgc doesn't match the number of arguments in the aArgs array, the ++ * aArgs array is searched for a NULL terminating entry, and argc is ++ * recalculated to reflect this. In this way, we can support NULL ++ * terminating aArgs arrays, if nArgc is very large. ++ */ ++DSP_STATUS COD_LoadBase(struct COD_MANAGER *hMgr, u32 nArgc, char *aArgs[], ++ COD_WRITEFXN pfnWrite, void *pArb, char *envp[]) ++{ ++ DBLL_Flags flags; ++ struct DBLL_Attrs saveAttrs; ++ struct DBLL_Attrs newAttrs; ++ DSP_STATUS status; ++ u32 i; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(IsValid(hMgr)); ++ DBC_Require(nArgc > 0); ++ DBC_Require(aArgs != NULL); ++ DBC_Require(aArgs[0] != NULL); ++ DBC_Require(pfnWrite != NULL); ++ DBC_Require(hMgr->baseLib != NULL); ++ ++ GT_6trace(COD_debugMask, GT_ENTER, ++ "Entered COD_LoadBase, hMgr: 0x%x\n \t" ++ "nArgc: 0x%x\n\taArgs: 0x%x\n\tpfnWrite: 0x%x\n\tpArb:" ++ " 0x%x\n \tenvp: 0x%x\n", hMgr, nArgc, aArgs, pfnWrite, ++ pArb, envp); ++ /* ++ * Make sure every argv[] stated in argc has a value, or change argc to ++ * reflect true number in NULL terminated argv array. ++ */ ++ for (i = 0; i < nArgc; i++) { ++ if (aArgs[i] == NULL) { ++ nArgc = i; ++ break; ++ } ++ } ++ ++ /* set the write function for this operation */ ++ hMgr->fxns.getAttrsFxn(hMgr->target, &saveAttrs); ++ ++ newAttrs = saveAttrs; ++ newAttrs.write = (DBLL_WriteFxn)pfnWrite; ++ newAttrs.wHandle = pArb; ++ newAttrs.alloc = (DBLL_AllocFxn)NoOp; ++ newAttrs.free = (DBLL_FreeFxn)NoOp; ++ newAttrs.logWrite = NULL; ++ newAttrs.logWriteHandle = NULL; ++ ++ /* Load the image */ ++ flags = DBLL_CODE | DBLL_DATA | DBLL_SYMB; ++ status = hMgr->fxns.loadFxn(hMgr->baseLib, flags, &newAttrs, ++ &hMgr->ulEntry); ++ if (DSP_FAILED(status)) { ++ hMgr->fxns.closeFxn(hMgr->baseLib); ++ GT_1trace(COD_debugMask, GT_7CLASS, ++ "COD_LoadBase: COD Load failed: " ++ "0x%x\n", status); ++ } ++ if (DSP_SUCCEEDED(status)) ++ hMgr->fLoaded = true; ++ else ++ hMgr->baseLib = NULL; ++ ++ return status; ++} ++ ++/* ++ * ======== COD_Open ======== ++ * Open library for reading sections. ++ */ ++DSP_STATUS COD_Open(struct COD_MANAGER *hMgr, IN char *pszCoffPath, ++ COD_FLAGS flags, struct COD_LIBRARYOBJ **pLib) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct COD_LIBRARYOBJ *lib = NULL; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(IsValid(hMgr)); ++ DBC_Require(pszCoffPath != NULL); ++ DBC_Require(flags == COD_NOLOAD || flags == COD_SYMB); ++ DBC_Require(pLib != NULL); ++ ++ GT_4trace(COD_debugMask, GT_ENTER, "Entered COD_Open, hMgr: 0x%x\n\t " ++ "pszCoffPath: 0x%x\tflags: 0x%x\tlib: 0x%x\n", hMgr, ++ pszCoffPath, flags, pLib); ++ ++ *pLib = NULL; ++ ++ lib = MEM_Calloc(sizeof(struct COD_LIBRARYOBJ), MEM_NONPAGED); ++ if (lib == NULL) { ++ GT_0trace(COD_debugMask, GT_7CLASS, ++ "COD_Open: Out Of Memory\n"); ++ status = DSP_EMEMORY; ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ lib->hCodMgr = hMgr; ++ status = hMgr->fxns.openFxn(hMgr->target, pszCoffPath, flags, ++ &lib->dbllLib); ++ if (DSP_FAILED(status)) { ++ GT_1trace(COD_debugMask, GT_7CLASS, ++ "COD_Open failed: 0x%x\n", status); ++ } else { ++ *pLib = lib; ++ } ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== COD_OpenBase ======== ++ * Purpose: ++ * Open base image for reading sections. ++ */ ++DSP_STATUS COD_OpenBase(struct COD_MANAGER *hMgr, IN char *pszCoffPath, ++ DBLL_Flags flags) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DBLL_LibraryObj *lib; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(IsValid(hMgr)); ++ DBC_Require(pszCoffPath != NULL); ++ ++ GT_2trace(COD_debugMask, GT_ENTER, ++ "Entered COD_OpenBase, hMgr: 0x%x\n\t" ++ "pszCoffPath: 0x%x\n", hMgr, pszCoffPath); ++ ++ /* if we previously opened a base image, close it now */ ++ if (hMgr->baseLib) { ++ if (hMgr->fLoaded) { ++ GT_0trace(COD_debugMask, GT_7CLASS, ++ "Base Image is already loaded. " ++ "Unloading it...\n"); ++ hMgr->fxns.unloadFxn(hMgr->baseLib, &hMgr->attrs); ++ hMgr->fLoaded = false; ++ } ++ hMgr->fxns.closeFxn(hMgr->baseLib); ++ hMgr->baseLib = NULL; ++ } else { ++ GT_0trace(COD_debugMask, GT_1CLASS, ++ "COD_OpenBase: Opening the base image ...\n"); ++ } ++ status = hMgr->fxns.openFxn(hMgr->target, pszCoffPath, flags, &lib); ++ if (DSP_FAILED(status)) { ++ GT_0trace(COD_debugMask, GT_7CLASS, ++ "COD_OpenBase: COD Open failed\n"); ++ } else { ++ /* hang onto the library for subsequent sym table usage */ ++ hMgr->baseLib = lib; ++ strncpy(hMgr->szZLFile, pszCoffPath, COD_MAXPATHLENGTH); ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== COD_ReadSection ======== ++ * Purpose: ++ * Retrieve the content of a code section given the section name. ++ */ ++DSP_STATUS COD_ReadSection(struct COD_LIBRARYOBJ *lib, IN char *pstrSect, ++ OUT char *pstrContent, IN u32 cContentSize) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(lib != NULL); ++ DBC_Require(IsValid(lib->hCodMgr)); ++ DBC_Require(pstrSect != NULL); ++ DBC_Require(pstrContent != NULL); ++ ++ GT_4trace(COD_debugMask, GT_ENTER, "Entered COD_ReadSection Args: 0x%x," ++ " 0x%x, 0x%x, 0x%x\n", lib, pstrSect, pstrContent, ++ cContentSize); ++ ++ if (lib != NULL) { ++ status = lib->hCodMgr->fxns.readSectFxn(lib->dbllLib, pstrSect, ++ pstrContent, ++ cContentSize); ++ if (DSP_FAILED(status)) { ++ GT_1trace(COD_debugMask, GT_7CLASS, ++ "COD_ReadSection failed: 0x%lx\n", status); ++ } ++ } else { ++ status = COD_E_NOSYMBOLSLOADED; ++ GT_0trace(COD_debugMask, GT_7CLASS, ++ "COD_ReadSection: No Symbols loaded\n"); ++ } ++ return status; ++} ++ ++/* ++ * ======== NoOp ======== ++ * Purpose: ++ * No Operation. ++ * ++ */ ++static bool NoOp(void) ++{ ++ return true; ++} ++ +diff --git a/drivers/dsp/bridge/pmgr/dbl.c b/drivers/dsp/bridge/pmgr/dbl.c +new file mode 100644 +index 0000000..641b011 +--- /dev/null ++++ b/drivers/dsp/bridge/pmgr/dbl.c +@@ -0,0 +1,1385 @@ ++/* ++ * dbl.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dbl.c ======== ++ * Dynamic BOF Loader library. Contains functions related to ++ * loading and unloading symbols/code/data on DSP. ++ * Also contains other support functions. ++ * ++ *! Revision History ++ *! ================ ++ *! 24-Feb-2003 swa PMGR Code review comments incorporated. ++ *! 24-May-2002 jeh Free DCD sects in DBL_close(). ++ *! 19-Mar-2002 jeh Changes made to match dynamic loader (dbll.c): Pass ++ *! DBL_Library to DBL_getAddr() instead of DBL_Target, ++ *! eliminate scope param, use DBL_Symbol. Pass attrs to ++ *! DBL_load(), DBL_unload(). ++ *! 20-Nov-2001 jeh Removed DBL_loadArgs(). ++ *! 07-Sep-2001 jeh Added overlay support. ++ *! 31-Jul-2001 jeh Include windows.h. ++ *! 06-Jun-2001 jeh Created. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++#include ++ ++#define DBL_TARGSIGNATURE 0x544c4244 /* "TLBD" */ ++#define DBL_LIBSIGNATURE 0x4c4c4244 /* "LLBD" */ ++ ++#define C54TARG 0 ++#define C55TARG 1 ++#define NUMTARGS 2 ++ ++#define C54MAGIC 0x98 /* Magic number for TI C54 COF */ ++#define C55MAGIC 0x9c /* Magic number for LEAD3 (C55) COF */ ++ ++/* Three task phases */ ++#define CREATEPHASE 0 ++#define DELETEPHASE 1 ++#define EXECUTEPHASE 2 ++#define NONE 3 /* For overlay section with phase not specified */ ++ ++/* Default load buffer size */ ++#define LOADBUFSIZE 0x800 ++ ++#define SWAPLONG(x) ((((x) << 24) & 0xFF000000) | (((x) << 8) & 0xFF0000L) | \ ++ (((x) >> 8) & 0xFF00L) | (((x) >> 24) & 0xFF)) ++ ++#define SWAPWORD(x) ((((x) << 8) & 0xFF00) | (((x) >> 8) & 0xFF)) ++ ++/* ++ * Macros for accessing the following types of overlay data within a ++ * structure of type OvlyData: ++ * - Overlay data not associated with a particular phase ++ * - Create phase overlay data ++ * - Delete phase overlay data ++ * - Execute phase overlay data ++ */ ++#define numOtherSects(pOvlyData) ((pOvlyData)->hdr.dbofHdr.numOtherSects) ++#define numCreateSects(pOvlyData) ((pOvlyData)->hdr.dbofHdr.numCreateSects) ++#define numDeleteSects(pOvlyData) ((pOvlyData)->hdr.dbofHdr.numDeleteSects) ++#define numExecuteSects(pOvlyData) ((pOvlyData)->hdr.dbofHdr.numExecuteSects) ++#define otherOffset(pOvlyData) 0 ++#define createOffset(pOvlyData) ((pOvlyData)->hdr.dbofHdr.numOtherSects) ++#define deleteOffset(pOvlyData) (createOffset(pOvlyData) + \ ++ (pOvlyData->hdr.dbofHdr.numCreateSects)) ++#define executeOffset(pOvlyData) (deleteOffset(pOvlyData) + \ ++ (pOvlyData->hdr.dbofHdr.numDeleteSects)) ++/* ++ * ======== OvlyHdr ======== ++ */ ++struct OvlyHdr { ++ struct DBOF_OvlySectHdr dbofHdr; ++ char *pName; /* Name of overlay section */ ++ u16 createRef; /* Reference count for create phase */ ++ u16 deleteRef; /* Reference count for delete phase */ ++ u16 executeRef; /* Execute phase ref count */ ++ u16 otherRef; /* Unspecified phase ref count */ ++} ; ++ ++/* ++ * ======== OvlyData ======== ++ */ ++struct OvlyData { ++ struct OvlyHdr hdr; ++ struct DBOF_OvlySectData data[1]; ++} ; ++ ++/* ++ * ======== Symbol ======== ++ */ ++struct Symbol { ++ struct DBL_Symbol sym; ++ char *pSymName; ++}; ++ ++/* ++ * ======== DCDSect ======== ++ */ ++struct DCDSect { ++ struct DBOF_DCDSectHdr sectHdr; ++ char *pData; ++} ; ++ ++/* ++ * ======== DBL_TargetObj ======== ++ */ ++struct DBL_TargetObj { ++ u32 dwSignature; /* For object validation */ ++ struct DBL_Attrs dblAttrs; /* file read, write, etc. functions */ ++ char *pBuf; /* Load buffer */ ++}; ++ ++/* ++ * ======== TargetInfo ======== ++ */ ++struct TargetInfo { ++ u16 dspType; /* eg, C54TARG, C55TARG */ ++ u32 magic; /* COFF magic number, identifies target type */ ++ u16 wordSize; /* Size of a DSP word */ ++ u16 mauSize; /* Size of minimum addressable unit */ ++ u16 charSize; /* For C55x, mausize = 1, but charsize = 2 */ ++} ; ++ ++/* ++ * ======== DBL_LibraryObj ======== ++ * Represents a library loaded on a target. ++ */ ++struct DBL_LibraryObj { ++ u32 dwSignature; /* For object validation */ ++ struct DBL_TargetObj *pTarget; /* Target for this library */ ++ struct KFILE_FileObj *file; /* DBOF file handle */ ++ bool byteSwapped; /* Are bytes swapped? */ ++ struct DBOF_FileHdr fileHdr; /* Header of DBOF file */ ++ u16 nSymbols; /* Number of DSP/Bridge symbols */ ++ struct Symbol *symbols; /* Table of DSP/Bridge symbols */ ++ u16 nDCDSects; /* Number of DCD sections */ ++ u16 nOvlySects; /* Number of overlay nodes */ ++ struct DCDSect *dcdSects; /* DCD section data */ ++ struct OvlyData **ppOvlyData; /* Array of overlay section data */ ++ struct TargetInfo *pTargetInfo; /* Entry in targetTab[] below */ ++} ; ++ ++#if GT_TRACE ++static struct GT_Mask DBL_debugMask = { NULL, NULL }; /* GT trace variable */ ++#endif ++ ++static u32 cRefs; /* module reference count */ ++ ++static u32 magicTab[NUMTARGS] = { C54MAGIC, C55MAGIC }; ++ ++static struct TargetInfo targetTab[] = { ++ /* targ magic wordsize mausize charsize */ ++ {C54TARG, C54MAGIC, 2, 2, 2}, /* C54 */ ++ {C55TARG, C55MAGIC, 2, 1, 2}, /* C55 */ ++}; ++ ++static void freeSects(struct DBL_TargetObj *dbl, struct OvlyData *pOvlyData, ++ s32 offset, s32 nSects); ++static DSP_STATUS loadSect(struct DBL_TargetObj *dbl, ++ struct DBL_LibraryObj *pdblLib); ++static DSP_STATUS readDCDSects(struct DBL_TargetObj *dbl, ++ struct DBL_LibraryObj *pdblLib); ++static DSP_STATUS readHeader(struct DBL_TargetObj *dbl, ++ struct DBL_LibraryObj *pdblLib); ++static DSP_STATUS readOvlySects(struct DBL_TargetObj *dbl, ++ struct DBL_LibraryObj *pdblLib); ++static DSP_STATUS readSymbols(struct DBL_TargetObj *dbl, ++ struct DBL_LibraryObj *pdblLib); ++ ++/* ++ * ======== DBL_close ======== ++ * Purpose: ++ * Close library opened with DBL_open. ++ */ ++void DBL_close(struct DBL_LibraryObj *lib) ++{ ++ struct DBL_LibraryObj *pdblLib = (struct DBL_LibraryObj *)lib; ++ u16 i; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(pdblLib, DBL_LIBSIGNATURE)); ++ ++ GT_1trace(DBL_debugMask, GT_ENTER, "DBL_close: lib: 0x%x\n", lib); ++ ++ /* Free symbols */ ++ if (pdblLib->symbols) { ++ for (i = 0; i < pdblLib->nSymbols; i++) { ++ if (pdblLib->symbols[i].pSymName) ++ MEM_Free(pdblLib->symbols[i].pSymName); ++ ++ } ++ MEM_Free(pdblLib->symbols); ++ } ++ ++ /* Free DCD sects */ ++ if (pdblLib->dcdSects) { ++ for (i = 0; i < pdblLib->nDCDSects; i++) { ++ if (pdblLib->dcdSects[i].pData) ++ MEM_Free(pdblLib->dcdSects[i].pData); ++ ++ } ++ MEM_Free(pdblLib->dcdSects); ++ } ++ ++ /* Free overlay sects */ ++ if (pdblLib->ppOvlyData) { ++ for (i = 0; i < pdblLib->nOvlySects; i++) { ++ if (pdblLib->ppOvlyData[i]) { ++ if (pdblLib->ppOvlyData[i]->hdr.pName) { ++ MEM_Free(pdblLib->ppOvlyData[i]-> ++ hdr.pName); ++ } ++ MEM_Free(pdblLib->ppOvlyData[i]); ++ } ++ } ++ MEM_Free(pdblLib->ppOvlyData); ++ } ++ ++ /* Close the file */ ++ if (pdblLib->file) ++ (*pdblLib->pTarget->dblAttrs.fclose) (pdblLib->file); ++ ++ ++ MEM_FreeObject(pdblLib); ++} ++ ++/* ++ * ======== DBL_create ======== ++ * Purpose: ++ * Create a target object by specifying the alloc, free, and ++ * write functions for the target. ++ */ ++DSP_STATUS DBL_create(struct DBL_TargetObj **pTarget, struct DBL_Attrs *pAttrs) ++{ ++ struct DBL_TargetObj *pdblTarget = NULL; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pAttrs != NULL); ++ DBC_Require(pTarget != NULL); ++ ++ GT_2trace(DBL_debugMask, GT_ENTER, ++ "DBL_create: pTarget: 0x%x pAttrs: 0x%x\n", ++ pTarget, pAttrs); ++ /* Allocate DBL target object */ ++ MEM_AllocObject(pdblTarget, struct DBL_TargetObj, DBL_TARGSIGNATURE); ++ if (pdblTarget == NULL) { ++ GT_0trace(DBL_debugMask, GT_6CLASS, ++ "DBL_create: Memory allocation failed\n"); ++ status = DSP_EMEMORY; ++ } else { ++ pdblTarget->dblAttrs = *pAttrs; ++ /* Allocate buffer for loading target */ ++ pdblTarget->pBuf = MEM_Calloc(LOADBUFSIZE, MEM_PAGED); ++ if (pdblTarget->pBuf == NULL) ++ status = DSP_EMEMORY; ++ ++ } ++ if (DSP_SUCCEEDED(status)) { ++ *pTarget = pdblTarget; ++ } else { ++ *pTarget = NULL; ++ if (pdblTarget) ++ DBL_delete(pdblTarget); ++ ++ } ++ DBC_Ensure(DSP_SUCCEEDED(status) && ++ ((MEM_IsValidHandle((*pTarget), DBL_TARGSIGNATURE)) || ++ (DSP_FAILED(status) && *pTarget == NULL))); ++ return status; ++} ++ ++/* ++ * ======== DBL_delete ======== ++ * Purpose: ++ * Delete target object and free resources for any loaded libraries. ++ */ ++void DBL_delete(struct DBL_TargetObj *target) ++{ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(target, DBL_TARGSIGNATURE)); ++ ++ GT_1trace(DBL_debugMask, GT_ENTER, ++ "DBL_delete: target: 0x%x\n", target); ++ ++ if (target->pBuf) ++ MEM_Free(target->pBuf); ++ ++ MEM_FreeObject(target); ++} ++ ++/* ++ * ======== DBL_exit ======== ++ * Purpose ++ * Discontinue usage of DBL module. ++ */ ++void DBL_exit() ++{ ++ DBC_Require(cRefs > 0); ++ cRefs--; ++ GT_1trace(DBL_debugMask, GT_5CLASS, ++ "DBL_exit() ref count: 0x%x\n", cRefs); ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== DBL_getAddr ======== ++ * Purpose: ++ * Get address of name in the specified library. ++ */ ++bool DBL_getAddr(struct DBL_LibraryObj *lib, char *name, ++ struct DBL_Symbol **ppSym) ++{ ++ bool retVal = false; ++ struct Symbol *symbol; ++ u16 i; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(lib, DBL_LIBSIGNATURE)); ++ DBC_Require(name != NULL); ++ DBC_Require(ppSym != NULL); ++ ++ GT_3trace(DBL_debugMask, GT_ENTER, ++ "DBL_getAddr: libt: 0x%x name: %s pAddr: " ++ "0x%x\n", lib, name, ppSym); ++ for (i = 0; i < lib->nSymbols; i++) { ++ symbol = &lib->symbols[i]; ++ if (CSL_Strcmp(name, symbol->pSymName) == 0) { ++ /* Found it */ ++ *ppSym = &lib->symbols[i].sym; ++ retVal = true; ++ break; ++ } ++ } ++ return retVal; ++} ++ ++/* ++ * ======== DBL_getAttrs ======== ++ * Purpose: ++ * Retrieve the attributes of the target. ++ */ ++void DBL_getAttrs(struct DBL_TargetObj *target, struct DBL_Attrs *pAttrs) ++{ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(target, DBL_TARGSIGNATURE)); ++ DBC_Require(pAttrs != NULL); ++ GT_2trace(DBL_debugMask, GT_ENTER, "DBL_getAttrs: target: 0x%x pAttrs: " ++ "0x%x\n", target, pAttrs); ++ *pAttrs = target->dblAttrs; ++} ++ ++/* ++ * ======== DBL_getCAddr ======== ++ * Purpose: ++ * Get address of "C" name in the specified library. ++ */ ++bool DBL_getCAddr(struct DBL_LibraryObj *lib, char *name, ++ struct DBL_Symbol **ppSym) ++{ ++ bool retVal = false; ++ struct Symbol *symbol; ++ u16 i; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(lib, DBL_LIBSIGNATURE)); ++ DBC_Require(name != NULL); ++ DBC_Require(ppSym != NULL); ++ ++ GT_3trace(DBL_debugMask, GT_ENTER, ++ "DBL_getCAddr: target: 0x%x name:%s pAddr:" ++ " 0x%x\n", lib, name, ppSym); ++ for (i = 0; i < lib->nSymbols; i++) { ++ symbol = &lib->symbols[i]; ++ if ((CSL_Strcmp(name, symbol->pSymName) == 0) || ++ (CSL_Strcmp(name, symbol->pSymName + 1) == 0 && ++ symbol->pSymName[0] == '_')) { ++ /* Found it */ ++ *ppSym = &lib->symbols[i].sym; ++ retVal = true; ++ break; ++ } ++ } ++ return retVal; ++} ++ ++/* ++ * ======== DBL_getEntry ======== ++ * Purpose: ++ * Get program entry point. ++ * ++ */ ++bool DBL_getEntry(struct DBL_LibraryObj *lib, u32 *pEntry) ++{ ++ struct DBL_LibraryObj *pdblLib = (struct DBL_LibraryObj *)lib; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(pdblLib, DBL_LIBSIGNATURE)); ++ DBC_Require(pEntry != NULL); ++ ++ GT_2trace(DBL_debugMask, GT_ENTER, ++ "DBL_getEntry: lib: 0x%x pEntry: 0x%x\n", lib, pEntry); ++ *pEntry = pdblLib->fileHdr.entry; ++ ++ return true; ++} ++ ++/* ++ * ======== DBL_getSect ======== ++ * Purpose: ++ * Get address and size of a named section. ++ */ ++DSP_STATUS DBL_getSect(struct DBL_LibraryObj *lib, char *name, u32 *pAddr, ++ u32 *pSize) ++{ ++ struct DBL_LibraryObj *pdblLib = (struct DBL_LibraryObj *)lib; ++ u16 i; ++ DSP_STATUS status = DSP_ENOSECT; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(name != NULL); ++ DBC_Require(pAddr != NULL); ++ DBC_Require(pSize != NULL); ++ DBC_Require(MEM_IsValidHandle(pdblLib, DBL_LIBSIGNATURE)); ++ ++ GT_4trace(DBL_debugMask, GT_ENTER, ++ "DBL_getSect: lib: 0x%x name: %s pAddr:" ++ " 0x%x pSize: 0x%x\n", lib, name, pAddr, pSize); ++ ++ /* ++ * Check for DCD and overlay sections. Overlay loader uses DBL_getSect ++ * to determine whether or not a node has overlay sections. ++ * DCD section names begin with '.' ++ */ ++ if (name[0] == '.') { ++ /* Get DCD section size (address is 0, since it's a NOLOAD). */ ++ for (i = 0; i < pdblLib->nDCDSects; i++) { ++ if (CSL_Strcmp(pdblLib->dcdSects[i].sectHdr.name, ++ name) == 0) { ++ *pAddr = 0; ++ *pSize = pdblLib->dcdSects[i].sectHdr.size * ++ pdblLib->pTargetInfo->mauSize; ++ status = DSP_SOK; ++ break; ++ } ++ } ++ } else { ++ /* Check for overlay section */ ++ for (i = 0; i < pdblLib->nOvlySects; i++) { ++ if (CSL_Strcmp(pdblLib->ppOvlyData[i]->hdr.pName, ++ name) == 0) { ++ /* Address and size are meaningless */ ++ *pAddr = 0; ++ *pSize = 0; ++ status = DSP_SOK; ++ break; ++ } ++ } ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== DBL_init ======== ++ * Purpose: ++ * Initialize DBL module. ++ */ ++bool DBL_init(void) ++{ ++ bool retVal = true; ++ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++ DBC_Assert(!DBL_debugMask.flags); ++ GT_create(&DBL_debugMask, "BL"); /* "BL" for dBL */ ++ ++ } ++ ++ if (retVal) ++ cRefs++; ++ ++ ++ GT_1trace(DBL_debugMask, GT_5CLASS, "DBL_init(), ref count: 0x%x\n", ++ cRefs); ++ ++ DBC_Ensure((retVal && (cRefs > 0)) || (!retVal && (cRefs >= 0))); ++ ++ return retVal; ++} ++ ++/* ++ * ======== DBL_load ======== ++ * Purpose: ++ * Add symbols/code/data defined in file to that already present ++ * on the target. ++ */ ++DSP_STATUS DBL_load(struct DBL_LibraryObj *lib, DBL_Flags flags, ++ struct DBL_Attrs *attrs, u32 *pEntry) ++{ ++ struct DBL_LibraryObj *pdblLib = (struct DBL_LibraryObj *)lib; ++ struct DBL_TargetObj *dbl; ++ u16 i; ++ u16 nSects; ++ DSP_STATUS status = DSP_EFAIL; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(pdblLib, DBL_LIBSIGNATURE)); ++ DBC_Require(pEntry != NULL); ++ DBC_Require(attrs != NULL); ++ ++ GT_4trace(DBL_debugMask, GT_ENTER, "DBL_load: lib: 0x%x flags: " ++ "0x%x attrs: 0x%x pEntry: 0x%x\n", lib, flags, attrs, pEntry); ++ ++ dbl = pdblLib->pTarget; ++ *pEntry = pdblLib->fileHdr.entry; ++ nSects = pdblLib->fileHdr.numSects; ++ dbl->dblAttrs = *attrs; ++ ++ for (i = 0; i < nSects; i++) { ++ /* Load the section at the current file offset */ ++ status = loadSect(dbl, lib); ++ if (DSP_FAILED(status)) ++ break; ++ ++ } ++ ++ /* Done with file, we can close it */ ++ if (pdblLib->file) { ++ (*pdblLib->pTarget->dblAttrs.fclose) (pdblLib->file); ++ pdblLib->file = NULL; ++ } ++ return status; ++} ++ ++/* ++ * ======== DBL_loadSect ======== ++ * Purpose: ++ * Load a named section from an library (for overlay support). ++ */ ++DSP_STATUS DBL_loadSect(struct DBL_LibraryObj *lib, char *sectName, ++ struct DBL_Attrs *attrs) ++{ ++ struct DBL_TargetObj *dbl; ++ s32 i; ++ s32 phase; ++ s32 offset = -1; ++ s32 nSects = -1; ++ s32 allocdSects = 0; ++ u32 loadAddr; ++ u32 runAddr; ++ u32 size; ++ u32 space; ++ u32 ulBytes; ++ u16 mauSize; ++ u16 wordSize; ++ u16 *phaseRef = NULL; ++ u16 *otherRef = NULL; ++ char *name = NULL; ++ struct OvlyData *pOvlyData; ++ DSP_STATUS status = DSP_ENOSECT; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(lib, DBL_LIBSIGNATURE)); ++ DBC_Require(sectName != NULL); ++ DBC_Require(attrs != NULL); ++ DBC_Require(attrs->write != NULL); ++ GT_3trace(DBL_debugMask, GT_ENTER, ++ "DBL_loadSect: lib: 0x%x sectName: %s " ++ "attrs: 0x%x\n", lib, sectName, attrs); ++ dbl = lib->pTarget; ++ mauSize = lib->pTargetInfo->mauSize; ++ wordSize = lib->pTargetInfo->wordSize; ++ /* Check for match of sect name in overlay table */ ++ for (i = 0; i < lib->nOvlySects; i++) { ++ name = lib->ppOvlyData[i]->hdr.pName; ++ if (!CSL_Strncmp(name, sectName, CSL_Strlen(name))) { ++ /* Match found */ ++ status = DSP_SOK; ++ break; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ DBC_Assert(i < lib->nOvlySects); ++ pOvlyData = lib->ppOvlyData[i]; ++ /* ++ * If node overlay, phase will be encoded in name. If not node ++ * overlay, set phase to NONE. ++ */ ++ phase = (CSL_Strcmp(name, sectName)) ? ++ CSL_Atoi(sectName + CSL_Strlen(sectName) - 1) : NONE; ++ /* Get reference count of node phase to be loaded, offset into ++ * overlay data array, and number of sections to overlay. */ ++ switch (phase) { ++ case NONE: ++ /* Not a node overlay */ ++ phaseRef = &pOvlyData->hdr.otherRef; ++ nSects = numOtherSects(pOvlyData); ++ offset = otherOffset(pOvlyData); ++ break; ++ case CREATEPHASE: ++ phaseRef = &pOvlyData->hdr.createRef; ++ otherRef = &pOvlyData->hdr.otherRef; ++ if (*otherRef) { ++ /* The overlay sections where node phase was ++ * not specified, have already been loaded. */ ++ nSects = numCreateSects(pOvlyData); ++ offset = createOffset(pOvlyData); ++ } else { ++ /* Overlay sections where node phase was not ++ * specified get loaded at create time, along ++ * with create sects. */ ++ nSects = numCreateSects(pOvlyData) + ++ numOtherSects(pOvlyData); ++ offset = otherOffset(pOvlyData); ++ } ++ break; ++ case DELETEPHASE: ++ phaseRef = &pOvlyData->hdr.deleteRef; ++ nSects = numDeleteSects(pOvlyData); ++ offset = deleteOffset(pOvlyData); ++ break; ++ case EXECUTEPHASE: ++ phaseRef = &pOvlyData->hdr.executeRef; ++ nSects = numExecuteSects(pOvlyData); ++ offset = executeOffset(pOvlyData); ++ break; ++ default: ++ /* ERROR */ ++ DBC_Assert(false); ++ break; ++ } ++ /* Do overlay if reference count is 0 */ ++ if (!(*phaseRef)) { ++ /* "Allocate" all sections */ ++ for (i = 0; i < nSects; i++) { ++ runAddr = pOvlyData->data[offset + i].runAddr; ++ size = pOvlyData->data[offset + i].size; ++ space = pOvlyData->data[offset + i].page; ++ status = (dbl->dblAttrs.alloc)(dbl->dblAttrs. ++ rmmHandle, space, size, 0, ++ &runAddr, true); ++ if (DSP_FAILED(status)) ++ break; ++ ++ allocdSects++; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Load sections */ ++ for (i = 0; i < nSects; i++) { ++ loadAddr = pOvlyData->data[offset + i]. ++ loadAddr; ++ runAddr = pOvlyData->data[offset + i]. ++ runAddr; ++ size = pOvlyData->data[offset + i]. ++ size; ++ space = pOvlyData->data[offset + i]. ++ page; ++ /* Convert to word address, call ++ * write function */ ++ loadAddr /= (wordSize / mauSize); ++ runAddr /= (wordSize / mauSize); ++ ulBytes = size * mauSize; ++ if ((*attrs->write)(attrs->wHandle, ++ runAddr, (void *)loadAddr, ulBytes, ++ space) != ulBytes) { ++ GT_0trace(DBL_debugMask, ++ GT_6CLASS, ++ "DBL_loadSect: write" ++ " failed\n"); ++ status = DSP_EFWRITE; ++ break; ++ } ++ } ++ } ++ /* Free sections on failure */ ++ if (DSP_FAILED(status)) ++ freeSects(dbl, pOvlyData, offset, allocdSects); ++ ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Increment reference counts */ ++ if (otherRef) ++ *otherRef = *otherRef + 1; ++ ++ *phaseRef = *phaseRef + 1; ++ } ++ return status; ++} ++ ++/* ++ * ======== DBL_open ======== ++ * Purpose: ++ * DBL_open() returns a library handle that can be used to ++ * load/unload the symbols/code/data via DBL_load()/DBL_unload(). ++ */ ++DSP_STATUS DBL_open(struct DBL_TargetObj *target, char *file, DBL_Flags flags, ++ struct DBL_LibraryObj **pLib) ++{ ++ struct DBL_LibraryObj *pdblLib = NULL; ++ u16 nSymbols; ++ u16 nDCDSects; ++ DSP_STATUS status = DSP_SOK; ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(target, DBL_TARGSIGNATURE)); ++ DBC_Require(target->dblAttrs.fopen != NULL); ++ DBC_Require(file != NULL); ++ DBC_Require(pLib != NULL); ++ ++ GT_3trace(DBL_debugMask, GT_ENTER, "DBL_open: target: 0x%x file: %s " ++ "pLib: 0x%x\n", target, file, pLib); ++ /* Allocate DBL library object */ ++ MEM_AllocObject(pdblLib, struct DBL_LibraryObj, DBL_LIBSIGNATURE); ++ if (pdblLib == NULL) ++ status = DSP_EMEMORY; ++ ++ /* Open the file */ ++ if (DSP_SUCCEEDED(status)) { ++ pdblLib->pTarget = target; ++ pdblLib->file = (*target->dblAttrs.fopen)(file, "rb"); ++ if (pdblLib->file == NULL) ++ status = DSP_EFOPEN; ++ ++ } ++ /* Read file header */ ++ if (DSP_SUCCEEDED(status)) { ++ status = readHeader(target, pdblLib); ++ if (DSP_FAILED(status)) { ++ GT_0trace(DBL_debugMask, GT_6CLASS, ++ "DBL_open(): Failed to read file header\n"); ++ } ++ } ++ /* Allocate symbol table */ ++ if (DSP_SUCCEEDED(status)) { ++ nSymbols = pdblLib->nSymbols = pdblLib->fileHdr.numSymbols; ++ pdblLib->symbols = MEM_Calloc(nSymbols * sizeof(struct Symbol), ++ MEM_PAGED); ++ if (pdblLib->symbols == NULL) ++ status = DSP_EMEMORY; ++ ++ } ++ /* Read all the symbols */ ++ if (DSP_SUCCEEDED(status)) { ++ status = readSymbols(target, pdblLib); ++ if (DSP_FAILED(status)) { ++ GT_0trace(DBL_debugMask, GT_6CLASS, ++ "DBL_open(): Failed to read symbols\n"); ++ } ++ } ++ /* Allocate DCD sect table */ ++ if (DSP_SUCCEEDED(status)) { ++ nDCDSects = pdblLib->nDCDSects = pdblLib->fileHdr.numDCDSects; ++ pdblLib->dcdSects = MEM_Calloc(nDCDSects * ++ sizeof(struct DCDSect), MEM_PAGED); ++ if (pdblLib->dcdSects == NULL) ++ status = DSP_EMEMORY; ++ ++ } ++ /* Read DCD sections */ ++ if (DSP_SUCCEEDED(status)) { ++ status = readDCDSects(target, pdblLib); ++ if (DSP_FAILED(status)) { ++ GT_0trace(DBL_debugMask, GT_6CLASS, ++ "DBL_open(): Failed to read DCD sections\n"); ++ } ++ } ++ /* Read overlay sections */ ++ if (DSP_SUCCEEDED(status)) { ++ status = readOvlySects(target, pdblLib); ++ if (DSP_FAILED(status)) { ++ GT_0trace(DBL_debugMask, GT_6CLASS, ++ "DBL_open(): Failed to read " ++ "overlay sections\n"); ++ } ++ } ++ if (DSP_FAILED(status)) { ++ *pLib = NULL; ++ if (pdblLib != NULL) ++ DBL_close((struct DBL_LibraryObj *) pdblLib); ++ ++ } else { ++ *pLib = pdblLib; ++ } ++ DBC_Ensure((DSP_SUCCEEDED(status) && ++ (MEM_IsValidHandle((*pLib), DBL_LIBSIGNATURE))) || ++ (DSP_FAILED(status) && *pLib == NULL)); ++ return status; ++} ++ ++/* ++ * ======== DBL_readSect ======== ++ * Purpose: ++ * Read COFF section into a character buffer. ++ */ ++DSP_STATUS DBL_readSect(struct DBL_LibraryObj *lib, char *name, char *pContent, ++ u32 size) ++{ ++ struct DBL_LibraryObj *pdblLib = (struct DBL_LibraryObj *)lib; ++ u16 i; ++ u32 mauSize; ++ u32 max; ++ DSP_STATUS status = DSP_ENOSECT; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(pdblLib, DBL_LIBSIGNATURE)); ++ DBC_Require(name != NULL); ++ DBC_Require(pContent != NULL); ++ DBC_Require(size != 0); ++ GT_4trace(DBL_debugMask, GT_ENTER, "DBL_readSect: lib: 0x%x name: %s " ++ "pContent: 0x%x size: 0x%x\n", lib, name, pContent, size); ++ ++ mauSize = pdblLib->pTargetInfo->mauSize; ++ ++ /* Attempt to find match with DCD section names. */ ++ for (i = 0; i < pdblLib->nDCDSects; i++) { ++ if (CSL_Strcmp(pdblLib->dcdSects[i].sectHdr.name, name) == 0) { ++ /* Match found */ ++ max = pdblLib->dcdSects[i].sectHdr.size * mauSize; ++ max = (max > size) ? size : max; ++ memcpy(pContent, pdblLib->dcdSects[i].pData, max); ++ status = DSP_SOK; ++ break; ++ } ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== DBL_setAttrs ======== ++ * Purpose: ++ * Set the attributes of the target. ++ */ ++void DBL_setAttrs(struct DBL_TargetObj *target, struct DBL_Attrs *pAttrs) ++{ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(target, DBL_TARGSIGNATURE)); ++ DBC_Require(pAttrs != NULL); ++ ++ GT_2trace(DBL_debugMask, GT_ENTER, "DBL_setAttrs: target: 0x%x pAttrs: " ++ "0x%x\n", target, pAttrs); ++ ++ target->dblAttrs = *pAttrs; ++} ++ ++/* ++ * ======== DBL_unload ======== ++ * Purpose: ++ * Remove the symbols/code/data corresponding to the library lib. ++ */ ++void DBL_unload(struct DBL_LibraryObj *lib, struct DBL_Attrs *attrs) ++{ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(lib, DBL_LIBSIGNATURE)); ++ ++ GT_1trace(DBL_debugMask, GT_ENTER, "DBL_unload: lib: 0x%x\n", lib); ++ ++ /* Nothing to do for static loading */ ++} ++ ++/* ++ * ======== DBL_unloadSect ======== ++ * Purpose: ++ * Unload a named section from an library (for overlay support). ++ */ ++DSP_STATUS DBL_unloadSect(struct DBL_LibraryObj *lib, char *sectName, ++ struct DBL_Attrs *attrs) ++{ ++ struct DBL_TargetObj *dbl; ++ s32 i; ++ s32 phase; ++ s32 offset = -1; ++ s32 nSects = -1; ++ u16 *phaseRef = NULL; ++ u16 *otherRef = NULL; ++ char *pName = NULL; ++ struct OvlyData *pOvlyData; ++ DSP_STATUS status = DSP_ENOSECT; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(lib, DBL_LIBSIGNATURE)); ++ DBC_Require(sectName != NULL); ++ ++ GT_2trace(DBL_debugMask, GT_ENTER, ++ "DBL_unloadSect: lib: 0x%x sectName: %s\n", lib, sectName); ++ dbl = lib->pTarget; ++ /* Check for match of sect name in overlay table */ ++ for (i = 0; i < lib->nOvlySects; i++) { ++ pName = lib->ppOvlyData[i]->hdr.pName; ++ if (!CSL_Strncmp(pName, sectName, CSL_Strlen(pName))) { ++ /* Match found */ ++ status = DSP_SOK; ++ break; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ DBC_Assert(i < lib->nOvlySects); ++ pOvlyData = lib->ppOvlyData[i]; ++ /* If node overlay, phase will be encoded in name. */ ++ phase = (CSL_Strcmp(pName, sectName)) ? ++ CSL_Atoi(sectName + CSL_Strlen(sectName) - 1) : NONE; ++ switch (phase) { ++ case NONE: ++ nSects = numOtherSects(pOvlyData); ++ phaseRef = &pOvlyData->hdr.otherRef; ++ offset = otherOffset(pOvlyData); ++ break; ++ case CREATEPHASE: ++ nSects = numCreateSects(pOvlyData); ++ offset = createOffset(pOvlyData); ++ phaseRef = &pOvlyData->hdr.createRef; ++ break; ++ case DELETEPHASE: ++ nSects = numDeleteSects(pOvlyData); ++ offset = deleteOffset(pOvlyData); ++ phaseRef = &pOvlyData->hdr.deleteRef; ++ otherRef = &pOvlyData->hdr.otherRef; ++ break; ++ case EXECUTEPHASE: ++ nSects = numExecuteSects(pOvlyData); ++ offset = executeOffset(pOvlyData); ++ phaseRef = &pOvlyData->hdr.executeRef; ++ break; ++ default: ++ /* ERROR */ ++ DBC_Assert(false); ++ break; ++ } ++ if (*phaseRef) { ++ *phaseRef = *phaseRef - 1; ++ if (*phaseRef == 0) { ++ /* Unload overlay sections for phase */ ++ freeSects(dbl, pOvlyData, offset, nSects); ++ } ++ if (phase == DELETEPHASE) { ++ DBC_Assert(*otherRef); ++ *otherRef = *otherRef - 1; ++ if (*otherRef == 0) { ++ /* Unload other overlay sections */ ++ nSects = numOtherSects(pOvlyData); ++ offset = otherOffset(pOvlyData); ++ freeSects(dbl, pOvlyData, offset, ++ nSects); ++ } ++ } ++ } ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== freeSects ======== ++ * Purpose: ++ * Free section ++ */ ++static void freeSects(struct DBL_TargetObj *dbl, struct OvlyData *pOvlyData, ++ s32 offset, s32 nSects) ++{ ++ u32 runAddr; ++ u32 size; ++ u32 space; ++ s32 i; ++ ++ for (i = 0; i < nSects; i++) { ++ runAddr = pOvlyData->data[offset + i].runAddr; ++ size = pOvlyData->data[offset + i].size; ++ space = pOvlyData->data[offset + i].page; ++ if (!(dbl->dblAttrs.free) ++ (dbl->dblAttrs.rmmHandle, space, runAddr, size, true)) { ++ /* ++ * Free function will not fail for overlay, unless ++ * address passed in is bad. ++ */ ++ DBC_Assert(false); ++ } ++ } ++} ++ ++/* ++ * ======== loadSect ======== ++ * Purpose: ++ * Load section to target ++ */ ++static DSP_STATUS loadSect(struct DBL_TargetObj *dbl, ++ struct DBL_LibraryObj *pdblLib) ++{ ++ struct DBOF_SectHdr sectHdr; ++ char *pBuf; ++ struct KFILE_FileObj *file; ++ u32 space; ++ u32 addr; ++ u32 total; ++ u32 nWords = 0; ++ u32 nBytes = 0; ++ u16 mauSize; ++ u32 bufSize; ++ DSP_STATUS status = DSP_SOK; ++ ++ file = pdblLib->file; ++ mauSize = pdblLib->pTargetInfo->mauSize; ++ bufSize = LOADBUFSIZE / mauSize; ++ pBuf = dbl->pBuf; ++ ++ /* Read the section header */ ++ if ((*dbl->dblAttrs.fread)(§Hdr, sizeof(struct DBOF_SectHdr), ++ 1, file) != 1) { ++ GT_0trace(DBL_debugMask, GT_6CLASS, ++ "Failed to read DCD sect header\n"); ++ status = DSP_EFREAD; ++ } else { ++ if (pdblLib->byteSwapped) { ++ sectHdr.size = SWAPLONG(sectHdr.size); ++ sectHdr.addr = SWAPLONG(sectHdr.addr); ++ sectHdr.page = SWAPWORD(sectHdr.page); ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ addr = sectHdr.addr; ++ space = sectHdr.page; ++ for (total = sectHdr.size; total > 0; total -= nWords) { ++ nWords = min(total, bufSize); ++ nBytes = nWords * mauSize; ++ /* Read section data */ ++ if ((*dbl->dblAttrs.fread)(pBuf, nBytes, 1, ++ file) != 1) { ++ GT_0trace(DBL_debugMask, GT_6CLASS, ++ "Failed to read DCD sect header\n"); ++ status = DSP_EFREAD; ++ break; ++ } ++ /* Write section to target */ ++ if (!(*dbl->dblAttrs.write)(dbl->dblAttrs.wHandle, ++ addr, pBuf, nBytes, space)) { ++ GT_0trace(DBL_debugMask, GT_6CLASS, ++ "Failed to write section data\n"); ++ status = DSP_EFWRITE; ++ break; ++ } ++ addr += nWords; ++ } ++ } ++ return status; ++} ++ ++/* ++ * ======== readDCDSects ======== ++ * Purpose: ++ * Read DCD sections. ++ */ ++static DSP_STATUS readDCDSects(struct DBL_TargetObj *dbl, ++ struct DBL_LibraryObj *pdblLib) ++{ ++ struct DBOF_DCDSectHdr *pSectHdr; ++ struct DCDSect *pSect; ++ struct KFILE_FileObj *file; ++ u16 nSects; ++ u16 i; ++ u16 mauSize; ++ DSP_STATUS status = DSP_SOK; ++ ++ file = pdblLib->file; ++ mauSize = pdblLib->pTargetInfo->mauSize; ++ nSects = pdblLib->fileHdr.numDCDSects; ++ for (i = 0; i < nSects; i++) { ++ pSect = &pdblLib->dcdSects[i]; ++ pSectHdr = &pdblLib->dcdSects[i].sectHdr; ++ /* Read sect header */ ++ if ((*dbl->dblAttrs.fread)(pSectHdr, ++ sizeof(struct DBOF_DCDSectHdr), 1, file) != 1) { ++ GT_0trace(DBL_debugMask, GT_6CLASS, ++ "Failed to read DCD sect header\n"); ++ status = DSP_EFREAD; ++ break; ++ } ++ if (pdblLib->byteSwapped) ++ pSectHdr->size = SWAPLONG(pSectHdr->size); ++ ++ pSect->pData = (char *)MEM_Calloc(pSectHdr->size * ++ mauSize, MEM_PAGED); ++ if (pSect->pData == NULL) { ++ GT_2trace(DBL_debugMask, GT_6CLASS, ++ "Memory allocation for sect %s " ++ "data failed: Size: 0x%lx\n", pSectHdr->name, ++ pSectHdr->size); ++ status = DSP_EMEMORY; ++ break; ++ } ++ /* Read DCD sect data */ ++ if ((*dbl->dblAttrs.fread)(pSect->pData, mauSize, ++ pSectHdr->size, file) != pSectHdr->size) { ++ GT_0trace(DBL_debugMask, GT_6CLASS, ++ "Failed to read DCD sect data\n"); ++ status = DSP_EFREAD; ++ break; ++ } ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== readHeader ======== ++ * Purpose: ++ * Read Header. ++ */ ++static DSP_STATUS readHeader(struct DBL_TargetObj *dbl, ++ struct DBL_LibraryObj *pdblLib) ++{ ++ struct KFILE_FileObj *file; ++ s32 i; ++ struct DBOF_FileHdr *pHdr; ++ u32 swapMagic; ++ DSP_STATUS status = DSP_SOK; ++ ++ pdblLib->byteSwapped = false; ++ file = pdblLib->file; ++ pHdr = &pdblLib->fileHdr; ++ if ((*dbl->dblAttrs.fread)(pHdr, sizeof(struct DBOF_FileHdr), 1, ++ file) != 1) { ++ GT_0trace(DBL_debugMask, GT_6CLASS, ++ "readHeader: Failed to read file header\n"); ++ status = DSP_EFREAD; ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Determine if byte swapped */ ++ for (i = 0; i < NUMTARGS; i++) { ++ swapMagic = SWAPLONG(pHdr->magic); ++ if (pHdr->magic == magicTab[i] || swapMagic == ++ magicTab[i]) { ++ if (swapMagic == magicTab[i]) { ++ pdblLib->byteSwapped = true; ++ pHdr->magic = SWAPLONG(pHdr->magic); ++ pHdr->entry = SWAPLONG(pHdr->entry); ++ pHdr->symOffset = SWAPLONG(pHdr-> ++ symOffset); ++ pHdr->dcdSectOffset = SWAPLONG(pHdr-> ++ dcdSectOffset); ++ pHdr->loadSectOffset = SWAPLONG(pHdr-> ++ loadSectOffset); ++ pHdr->ovlySectOffset = SWAPLONG(pHdr-> ++ ovlySectOffset); ++ pHdr->numSymbols = SWAPWORD(pHdr-> ++ numSymbols); ++ pHdr->numDCDSects = SWAPWORD(pHdr-> ++ numDCDSects); ++ pHdr->numSects = SWAPWORD(pHdr-> ++ numSects); ++ pHdr->numOvlySects = SWAPWORD(pHdr-> ++ numOvlySects); ++ } ++ break; ++ } ++ } ++ if (i == NUMTARGS) { ++ GT_0trace(DBL_debugMask, GT_6CLASS, ++ "readHeader: Failed to determine" ++ " target type\n"); ++ status = DSP_ECORRUPTFILE; ++ } else { ++ pdblLib->pTargetInfo = &targetTab[i]; ++ GT_1trace(DBL_debugMask, GT_ENTER, ++ "COF type: 0x%lx\n", pHdr->magic); ++ GT_1trace(DBL_debugMask, GT_ENTER, ++ "Entry point:0x%lx\n", pHdr->entry); ++ } ++ } ++ return status; ++} ++ ++/* ++ * ======== readOvlySects ======== ++ * Purpose: ++ * Read Overlay Sections ++ */ ++static DSP_STATUS readOvlySects(struct DBL_TargetObj *dbl, ++ struct DBL_LibraryObj *pdblLib) ++{ ++ struct DBOF_OvlySectHdr hdr; ++ struct DBOF_OvlySectData *pData; ++ struct OvlyData *pOvlyData; ++ char *pName; ++ struct KFILE_FileObj *file; ++ u16 i, j; ++ u16 nSects; ++ u16 n; ++ DSP_STATUS status = DSP_SOK; ++ ++ pdblLib->nOvlySects = nSects = pdblLib->fileHdr.numOvlySects; ++ file = pdblLib->file; ++ if (nSects > 0) { ++ pdblLib->ppOvlyData = MEM_Calloc(nSects * sizeof(OvlyData *), ++ MEM_PAGED); ++ if (pdblLib->ppOvlyData == NULL) { ++ GT_0trace(DBL_debugMask, GT_7CLASS, ++ "Failed to allocatate overlay " ++ "data memory\n"); ++ status = DSP_EMEMORY; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Read overlay data for each node */ ++ for (i = 0; i < nSects; i++) { ++ /* Read overlay section header */ ++ if ((*dbl->dblAttrs.fread)(&hdr, ++ sizeof(struct DBOF_OvlySectHdr), 1, file) != 1) { ++ GT_0trace(DBL_debugMask, GT_6CLASS, ++ "Failed to read overlay sect" ++ " header\n"); ++ status = DSP_EFREAD; ++ break; ++ } ++ if (pdblLib->byteSwapped) { ++ hdr.nameLen = SWAPWORD(hdr.nameLen); ++ hdr.numCreateSects = ++ SWAPWORD(hdr.numCreateSects); ++ hdr.numDeleteSects = ++ SWAPWORD(hdr.numDeleteSects); ++ hdr.numExecuteSects = ++ SWAPWORD(hdr.numExecuteSects); ++ hdr.numOtherSects = ++ SWAPWORD(hdr.numOtherSects); ++ hdr.resvd = SWAPWORD(hdr.resvd); ++ } ++ n = hdr.numCreateSects + hdr.numDeleteSects + ++ hdr.numExecuteSects + hdr.numOtherSects; ++ ++ /* Allocate memory for node's overlay data */ ++ pOvlyData = (struct OvlyData *)MEM_Calloc ++ (sizeof(struct OvlyHdr) + ++ n * sizeof(struct DBOF_OvlySectData), ++ MEM_PAGED); ++ if (pOvlyData == NULL) { ++ GT_0trace(DBL_debugMask, GT_7CLASS, ++ "Failed to allocatate ovlyay" ++ " data memory\n"); ++ status = DSP_EMEMORY; ++ break; ++ } ++ pOvlyData->hdr.dbofHdr = hdr; ++ pdblLib->ppOvlyData[i] = pOvlyData; ++ /* Allocate memory for section name */ ++ pName = (char *)MEM_Calloc(hdr.nameLen + 1, MEM_PAGED); ++ if (pName == NULL) { ++ GT_0trace(DBL_debugMask, GT_7CLASS, ++ "Failed to allocatate ovlyay" ++ " section name\n"); ++ status = DSP_EMEMORY; ++ break; ++ } ++ pOvlyData->hdr.pName = pName; ++ /* Read the overlay section name */ ++ if ((*dbl->dblAttrs.fread)(pName, sizeof(char), ++ hdr.nameLen, file) != hdr.nameLen) { ++ GT_0trace(DBL_debugMask, GT_7CLASS, ++ "readOvlySects: Unable to " ++ "read overlay name.\n"); ++ status = DSP_EFREAD; ++ break; ++ } ++ /* Read the overlay section data */ ++ pData = pOvlyData->data; ++ if ((*dbl->dblAttrs.fread)(pData, ++ sizeof(struct DBOF_OvlySectData), n, file) != n) { ++ GT_0trace(DBL_debugMask, GT_7CLASS, ++ "readOvlySects: Unable to " ++ "read overlay data.\n"); ++ status = DSP_EFREAD; ++ break; ++ } ++ /* Swap overlay data, if necessary */ ++ if (pdblLib->byteSwapped) { ++ for (j = 0; j < n; j++) { ++ pData[j].loadAddr = ++ SWAPLONG(pData[j].loadAddr); ++ pData[j].runAddr = ++ SWAPLONG(pData[j].runAddr); ++ pData[j].size = ++ SWAPLONG(pData[j].size); ++ pData[j].page = ++ SWAPWORD(pData[j].page); ++ } ++ } ++ } ++ } ++ return status; ++} ++ ++/* ++ * ======== readSymbols ======== ++ * Purpose: ++ * Read Symbols ++ */ ++static DSP_STATUS readSymbols(struct DBL_TargetObj *dbl, ++ struct DBL_LibraryObj *pdblLib) ++{ ++ struct DBOF_SymbolHdr symHdr; ++ struct KFILE_FileObj *file; ++ u16 i; ++ u16 nSymbols; ++ u16 len; ++ char *pName = NULL; ++ DSP_STATUS status = DSP_SOK; ++ ++ file = pdblLib->file; ++ ++ nSymbols = pdblLib->fileHdr.numSymbols; ++ ++ for (i = 0; i < nSymbols; i++) { ++ /* Read symbol value */ ++ if ((*dbl->dblAttrs.fread)(&symHdr, ++ sizeof(struct DBOF_SymbolHdr), 1, file) != 1) { ++ GT_0trace(DBL_debugMask, GT_6CLASS, ++ "Failed to read symbol value\n"); ++ status = DSP_EFREAD; ++ break; ++ } ++ if (pdblLib->byteSwapped) { ++ symHdr.nameLen = SWAPWORD(symHdr.nameLen); ++ symHdr.value = SWAPLONG(symHdr.value); ++ } ++ /* Allocate buffer for symbol name */ ++ len = symHdr.nameLen; ++ pName = (char *)MEM_Calloc(len + 1, MEM_PAGED); ++ if (pName == NULL) { ++ GT_0trace(DBL_debugMask, GT_6CLASS, ++ "Memory allocation failed\n"); ++ status = DSP_EMEMORY; ++ break; ++ } ++ pdblLib->symbols[i].pSymName = pName; ++ pdblLib->symbols[i].sym.value = symHdr.value; ++ /* Read symbol name */ ++ if ((*dbl->dblAttrs.fread) (pName, sizeof(char), len, file) != ++ len) { ++ GT_0trace(DBL_debugMask, GT_6CLASS, ++ "Failed to read symbol value\n"); ++ status = DSP_EFREAD; ++ break; ++ } else { ++ pName[len] = '\0'; ++ GT_2trace(DBL_debugMask, GT_ENTER, ++ "Symbol: %s Value: 0x%lx\n", ++ pName, symHdr.value); ++ } ++ } ++ return status; ++} ++ +diff --git a/drivers/dsp/bridge/pmgr/dbll.c b/drivers/dsp/bridge/pmgr/dbll.c +new file mode 100644 +index 0000000..82430a3 +--- /dev/null ++++ b/drivers/dsp/bridge/pmgr/dbll.c +@@ -0,0 +1,1564 @@ ++/* ++ * dbll.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== dbll.c ======== ++ * ++ *! Revision History ++ *! ================ ++ *! 25-Apr-2030 map: Fixed symbol redefinition bug + unload and return error ++ *! 08-Apr-2003 map: Consolidated DBL with DBLL loader name ++ *! 24-Mar-2003 map: Updated findSymbol to support dllview update ++ *! 23-Jan-2003 map: Updated rmmAlloc to support memory granularity ++ *! 21-Nov-2002 map: Combine fopen and DLOAD_module_open to increase ++ *! performance on start. ++ *! 04-Oct-2002 map: Integrated new TIP dynamic loader w/ DOF api. ++ *! 27-Sep-2002 map: Changed handle passed to RemoteFree, instead of ++ *! RMM_free; added GT_trace to rmmDealloc ++ *! 20-Sep-2002 map: Updated from Code Review ++ *! 08-Aug-2002 jeh: Updated to support overlays. ++ *! 25-Jun-2002 jeh: Pass RMM_Addr object to alloc function in rmmAlloc(). ++ *! 20-Mar-2002 jeh: Created. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++ ++/* Dynamic loader library interface */ ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++#include ++ ++#define DBLL_TARGSIGNATURE 0x544c4c44 /* "TLLD" */ ++#define DBLL_LIBSIGNATURE 0x4c4c4c44 /* "LLLD" */ ++ ++/* Number of buckets for symbol hash table */ ++#define MAXBUCKETS 211 ++ ++/* Max buffer length */ ++#define MAXEXPR 128 ++ ++#ifndef UINT32_C ++#define UINT32_C(zzz) ((uint32_t)zzz) ++#endif ++#define DOFF_ALIGN(x) (((x) + 3) & ~UINT32_C(3)) ++ ++/* ++ * ======== struct DBLL_TarObj* ======== ++ * A target may have one or more libraries of symbols/code/data loaded ++ * onto it, where a library is simply the symbols/code/data contained ++ * in a DOFF file. ++ */ ++/* ++ * ======== DBLL_TarObj ======== ++ */ ++struct DBLL_TarObj { ++ u32 dwSignature; /* For object validation */ ++ struct DBLL_Attrs attrs; ++ struct DBLL_LibraryObj *head; /* List of all opened libraries */ ++} ; ++ ++/* ++ * The following 4 typedefs are "super classes" of the dynamic loader ++ * library types used in dynamic loader functions (dynamic_loader.h). ++ */ ++/* ++ * ======== DBLLStream ======== ++ * Contains Dynamic_Loader_Stream ++ */ ++struct DBLLStream { ++ struct Dynamic_Loader_Stream dlStream; ++ struct DBLL_LibraryObj *lib; ++} ; ++ ++/* ++ * ======== DBLLSymbol ======== ++ */ ++struct DBLLSymbol { ++ struct Dynamic_Loader_Sym dlSymbol; ++ struct DBLL_LibraryObj *lib; ++} ; ++ ++/* ++ * ======== DBLLAlloc ======== ++ */ ++ struct DBLLAlloc { ++ struct Dynamic_Loader_Allocate dlAlloc; ++ struct DBLL_LibraryObj *lib; ++} ; ++ ++/* ++ * ======== DBLLInit ======== ++ */ ++struct DBLLInit { ++ struct Dynamic_Loader_Initialize dlInit; ++ struct DBLL_LibraryObj *lib; ++}; ++ ++/* ++ * ======== DBLL_Library ======== ++ * A library handle is returned by DBLL_Open() and is passed to DBLL_load() ++ * to load symbols/code/data, and to DBLL_unload(), to remove the ++ * symbols/code/data loaded by DBLL_load(). ++ */ ++ ++/* ++ * ======== DBLL_LibraryObj ======== ++ */ ++ struct DBLL_LibraryObj { ++ u32 dwSignature; /* For object validation */ ++ struct DBLL_LibraryObj *next; /* Next library in target's list */ ++ struct DBLL_LibraryObj *prev; /* Previous in the list */ ++ struct DBLL_TarObj *pTarget; /* target for this library */ ++ ++ /* Objects needed by dynamic loader */ ++ struct DBLLStream stream; ++ struct DBLLSymbol symbol; ++ struct DBLLAlloc allocate; ++ struct DBLLInit init; ++ DLOAD_mhandle mHandle; ++ ++ char *fileName; /* COFF file name */ ++ void *fp; /* Opaque file handle */ ++ u32 entry; /* Entry point */ ++ DLOAD_mhandle desc; /* desc of DOFF file loaded */ ++ u32 openRef; /* Number of times opened */ ++ u32 loadRef; /* Number of times loaded */ ++ struct GH_THashTab *symTab; /* Hash table of symbols */ ++ u32 ulPos; ++} ; ++ ++/* ++ * ======== Symbol ======== ++ */ ++struct Symbol { ++ struct DBLL_Symbol value; ++ char *name; ++} ; ++extern bool bSymbolsReloaded; ++ ++static void dofClose(struct DBLL_LibraryObj *zlLib); ++static DSP_STATUS dofOpen(struct DBLL_LibraryObj *zlLib); ++static s32 NoOp(struct Dynamic_Loader_Initialize *thisptr, void *bufr, ++ LDR_ADDR locn, struct LDR_SECTION_INFO *info, unsigned bytsiz); ++ ++/* ++ * Functions called by dynamic loader ++ * ++ */ ++/* Dynamic_Loader_Stream */ ++static int readBuffer(struct Dynamic_Loader_Stream *this, void *buffer, ++ unsigned bufsize); ++static int setFilePosn(struct Dynamic_Loader_Stream *this, unsigned int pos); ++/* Dynamic_Loader_Sym */ ++static struct dynload_symbol *findSymbol(struct Dynamic_Loader_Sym *this, ++ const char *name); ++static struct dynload_symbol *addToSymbolTable(struct Dynamic_Loader_Sym *this, ++ const char *name, ++ unsigned moduleId); ++static struct dynload_symbol *findInSymbolTable(struct Dynamic_Loader_Sym *this, ++ const char *name, ++ unsigned moduleid); ++static void purgeSymbolTable(struct Dynamic_Loader_Sym *this, ++ unsigned moduleId); ++static void *allocate(struct Dynamic_Loader_Sym *this, unsigned memsize); ++static void deallocate(struct Dynamic_Loader_Sym *this, void *memPtr); ++static void errorReport(struct Dynamic_Loader_Sym *this, const char *errstr, ++ va_list args); ++/* Dynamic_Loader_Allocate */ ++static int rmmAlloc(struct Dynamic_Loader_Allocate *this, ++ struct LDR_SECTION_INFO *info, unsigned align); ++static void rmmDealloc(struct Dynamic_Loader_Allocate *this, ++ struct LDR_SECTION_INFO *info); ++ ++/* Dynamic_Loader_Initialize */ ++static int connect(struct Dynamic_Loader_Initialize *this); ++static int readMem(struct Dynamic_Loader_Initialize *this, void *buf, ++ LDR_ADDR addr, struct LDR_SECTION_INFO *info, ++ unsigned nbytes); ++static int writeMem(struct Dynamic_Loader_Initialize *this, void *buf, ++ LDR_ADDR addr, struct LDR_SECTION_INFO *info, ++ unsigned nbytes); ++static int fillMem(struct Dynamic_Loader_Initialize *this, LDR_ADDR addr, ++ struct LDR_SECTION_INFO *info, unsigned nbytes, ++ unsigned val); ++static int execute(struct Dynamic_Loader_Initialize *this, LDR_ADDR start); ++static void release(struct Dynamic_Loader_Initialize *this); ++ ++/* symbol table hash functions */ ++static u16 nameHash(void *name, u16 maxBucket); ++static bool nameMatch(void *name, void *sp); ++static void symDelete(void *sp); ++ ++#if GT_TRACE ++static struct GT_Mask DBLL_debugMask = { NULL, NULL }; /* GT trace variable */ ++#endif ++ ++static u32 cRefs; /* module reference count */ ++ ++/* Symbol Redefinition */ ++static int bRedefinedSymbol; ++static int bGblSearch = 1; ++ ++/* ++ * ======== DBLL_close ======== ++ */ ++void DBLL_close(struct DBLL_LibraryObj *zlLib) ++{ ++ struct DBLL_TarObj *zlTarget; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(zlLib, DBLL_LIBSIGNATURE)); ++ DBC_Require(zlLib->openRef > 0); ++ zlTarget = zlLib->pTarget; ++ GT_1trace(DBLL_debugMask, GT_ENTER, "DBLL_close: lib: 0x%x\n", zlLib); ++ zlLib->openRef--; ++ if (zlLib->openRef == 0) { ++ /* Remove library from list */ ++ if (zlTarget->head == zlLib) ++ zlTarget->head = zlLib->next; ++ ++ if (zlLib->prev) ++ (zlLib->prev)->next = zlLib->next; ++ ++ if (zlLib->next) ++ (zlLib->next)->prev = zlLib->prev; ++ ++ /* Free DOF resources */ ++ dofClose(zlLib); ++ if (zlLib->fileName) ++ MEM_Free(zlLib->fileName); ++ ++ /* remove symbols from symbol table */ ++ if (zlLib->symTab) ++ GH_delete(zlLib->symTab); ++ ++ /* remove the library object itself */ ++ MEM_FreeObject(zlLib); ++ zlLib = NULL; ++ } ++} ++ ++/* ++ * ======== DBLL_create ======== ++ */ ++DSP_STATUS DBLL_create(struct DBLL_TarObj **pTarget, struct DBLL_Attrs *pAttrs) ++{ ++ struct DBLL_TarObj *pzlTarget; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pAttrs != NULL); ++ DBC_Require(pTarget != NULL); ++ ++ GT_2trace(DBLL_debugMask, GT_ENTER, ++ "DBLL_create: pTarget: 0x%x pAttrs: " ++ "0x%x\n", pTarget, pAttrs); ++ /* Allocate DBL target object */ ++ MEM_AllocObject(pzlTarget, struct DBLL_TarObj, DBLL_TARGSIGNATURE); ++ if (pTarget != NULL) { ++ if (pzlTarget == NULL) { ++ GT_0trace(DBLL_debugMask, GT_6CLASS, ++ "DBLL_create: Memory allocation" ++ " failed\n"); ++ *pTarget = NULL; ++ status = DSP_EMEMORY; ++ } else { ++ pzlTarget->attrs = *pAttrs; ++ *pTarget = (struct DBLL_TarObj *)pzlTarget; ++ } ++ DBC_Ensure((DSP_SUCCEEDED(status) && ++ MEM_IsValidHandle(((struct DBLL_TarObj *)(*pTarget)), ++ DBLL_TARGSIGNATURE)) || (DSP_FAILED(status) && ++ *pTarget == NULL)); ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== DBLL_delete ======== ++ */ ++void DBLL_delete(struct DBLL_TarObj *target) ++{ ++ struct DBLL_TarObj *zlTarget = (struct DBLL_TarObj *)target; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(zlTarget, DBLL_TARGSIGNATURE)); ++ ++ GT_1trace(DBLL_debugMask, GT_ENTER, "DBLL_delete: target: 0x%x\n", ++ target); ++ ++ if (zlTarget != NULL) ++ MEM_FreeObject(zlTarget); ++ ++} ++ ++/* ++ * ======== DBLL_exit ======== ++ * Discontinue usage of DBL module. ++ */ ++void DBLL_exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ cRefs--; ++ ++ GT_1trace(DBLL_debugMask, GT_5CLASS, "DBLL_exit() ref count: 0x%x\n", ++ cRefs); ++ ++ if (cRefs == 0) { ++ MEM_Exit(); ++ CSL_Exit(); ++ GH_exit(); ++#if GT_TRACE ++ DBLL_debugMask.flags = NULL; ++#endif ++ } ++ ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== DBLL_getAddr ======== ++ * Get address of name in the specified library. ++ */ ++bool DBLL_getAddr(struct DBLL_LibraryObj *zlLib, char *name, ++ struct DBLL_Symbol **ppSym) ++{ ++ struct Symbol *sym; ++ bool status = false; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(zlLib, DBLL_LIBSIGNATURE)); ++ DBC_Require(name != NULL); ++ DBC_Require(ppSym != NULL); ++ DBC_Require(zlLib->symTab != NULL); ++ ++ GT_3trace(DBLL_debugMask, GT_ENTER, ++ "DBLL_getAddr: lib: 0x%x name: %s pAddr:" ++ " 0x%x\n", zlLib, name, ppSym); ++ sym = (struct Symbol *)GH_find(zlLib->symTab, name); ++ if (sym != NULL) { ++ *ppSym = &sym->value; ++ status = true; ++ } ++ return status; ++} ++ ++/* ++ * ======== DBLL_getAttrs ======== ++ * Retrieve the attributes of the target. ++ */ ++void DBLL_getAttrs(struct DBLL_TarObj *target, struct DBLL_Attrs *pAttrs) ++{ ++ struct DBLL_TarObj *zlTarget = (struct DBLL_TarObj *)target; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(zlTarget, DBLL_TARGSIGNATURE)); ++ DBC_Require(pAttrs != NULL); ++ ++ if ((pAttrs != NULL) && (zlTarget != NULL)) ++ *pAttrs = zlTarget->attrs; ++ ++} ++ ++/* ++ * ======== DBLL_getCAddr ======== ++ * Get address of a "C" name in the specified library. ++ */ ++bool DBLL_getCAddr(struct DBLL_LibraryObj *zlLib, char *name, ++ struct DBLL_Symbol **ppSym) ++{ ++ struct Symbol *sym; ++ char cname[MAXEXPR + 1]; ++ bool status = false; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(zlLib, DBLL_LIBSIGNATURE)); ++ DBC_Require(ppSym != NULL); ++ DBC_Require(zlLib->symTab != NULL); ++ DBC_Require(name != NULL); ++ ++ cname[0] = '_'; ++ ++ strncpy(cname + 1, name, sizeof(cname) - 2); ++ cname[MAXEXPR] = '\0'; /* insure '\0' string termination */ ++ ++ /* Check for C name, if not found */ ++ sym = (struct Symbol *)GH_find(zlLib->symTab, cname); ++ ++ if (sym != NULL) { ++ *ppSym = &sym->value; ++ status = true; ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== DBLL_getSect ======== ++ * Get the base address and size (in bytes) of a COFF section. ++ */ ++DSP_STATUS DBLL_getSect(struct DBLL_LibraryObj *lib, char *name, u32 *pAddr, ++ u32 *pSize) ++{ ++ u32 uByteSize; ++ bool fOpenedDoff = false; ++ const struct LDR_SECTION_INFO *sect = NULL; ++ struct DBLL_LibraryObj *zlLib = (struct DBLL_LibraryObj *)lib; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(name != NULL); ++ DBC_Require(pAddr != NULL); ++ DBC_Require(pSize != NULL); ++ DBC_Require(MEM_IsValidHandle(zlLib, DBLL_LIBSIGNATURE)); ++ ++ GT_4trace(DBLL_debugMask, GT_ENTER, ++ "DBLL_getSect: lib: 0x%x name: %s pAddr:" ++ " 0x%x pSize: 0x%x\n", lib, name, pAddr, pSize); ++ /* If DOFF file is not open, we open it. */ ++ if (zlLib != NULL) { ++ if (zlLib->fp == NULL) { ++ status = dofOpen(zlLib); ++ if (DSP_SUCCEEDED(status)) ++ fOpenedDoff = true; ++ ++ } else { ++ (*(zlLib->pTarget->attrs.fseek))(zlLib->fp, ++ zlLib->ulPos, SEEK_SET); ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ uByteSize = 1; ++ if (DLOAD_GetSectionInfo(zlLib->desc, name, §)) { ++ *pAddr = sect->load_addr; ++ *pSize = sect->size * uByteSize; ++ /* Make sure size is even for good swap */ ++ if (*pSize % 2) ++ (*pSize)++; ++ ++ /* Align size */ ++ *pSize = DOFF_ALIGN(*pSize); ++ } else { ++ status = DSP_ENOSECT; ++ } ++ } ++ if (fOpenedDoff) { ++ dofClose(zlLib); ++ fOpenedDoff = false; ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== DBLL_init ======== ++ */ ++bool DBLL_init(void) ++{ ++ bool retVal = true; ++ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++ DBC_Assert(!DBLL_debugMask.flags); ++ GT_create(&DBLL_debugMask, "DL"); /* "DL" for dbDL */ ++ GH_init(); ++ CSL_Init(); ++ retVal = MEM_Init(); ++ if (!retVal) ++ MEM_Exit(); ++ ++ } ++ ++ if (retVal) ++ cRefs++; ++ ++ ++ GT_1trace(DBLL_debugMask, GT_5CLASS, "DBLL_init(), ref count: 0x%x\n", ++ cRefs); ++ ++ DBC_Ensure((retVal && (cRefs > 0)) || (!retVal && (cRefs >= 0))); ++ ++ return retVal; ++} ++ ++/* ++ * ======== DBLL_load ======== ++ */ ++DSP_STATUS DBLL_load(struct DBLL_LibraryObj *lib, DBLL_Flags flags, ++ struct DBLL_Attrs *attrs, u32 *pEntry) ++{ ++ struct DBLL_LibraryObj *zlLib = (struct DBLL_LibraryObj *)lib; ++ struct DBLL_TarObj *dbzl; ++ bool gotSymbols = true; ++ s32 err; ++ DSP_STATUS status = DSP_SOK; ++ bool fOpenedDoff = false; ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(zlLib, DBLL_LIBSIGNATURE)); ++ DBC_Require(pEntry != NULL); ++ DBC_Require(attrs != NULL); ++ ++ GT_4trace(DBLL_debugMask, GT_ENTER, ++ "DBLL_load: lib: 0x%x flags: 0x%x pEntry:" ++ " 0x%x\n", lib, flags, attrs, pEntry); ++ /* ++ * Load if not already loaded. ++ */ ++ if (zlLib->loadRef == 0 || !(flags & DBLL_DYNAMIC)) { ++ dbzl = zlLib->pTarget; ++ dbzl->attrs = *attrs; ++ /* Create a hash table for symbols if not already created */ ++ if (zlLib->symTab == NULL) { ++ gotSymbols = false; ++ zlLib->symTab = GH_create(MAXBUCKETS, ++ sizeof(struct Symbol), ++ nameHash, ++ nameMatch, symDelete); ++ if (zlLib->symTab == NULL) ++ status = DSP_EMEMORY; ++ ++ } ++ /* ++ * Set up objects needed by the dynamic loader ++ */ ++ /* Stream */ ++ zlLib->stream.dlStream.read_buffer = readBuffer; ++ zlLib->stream.dlStream.set_file_posn = setFilePosn; ++ zlLib->stream.lib = zlLib; ++ /* Symbol */ ++ zlLib->symbol.dlSymbol.Find_Matching_Symbol = findSymbol; ++ if (gotSymbols) { ++ zlLib->symbol.dlSymbol.Add_To_Symbol_Table = ++ findInSymbolTable; ++ } else { ++ zlLib->symbol.dlSymbol.Add_To_Symbol_Table = ++ addToSymbolTable; ++ } ++ zlLib->symbol.dlSymbol.Purge_Symbol_Table = purgeSymbolTable; ++ zlLib->symbol.dlSymbol.Allocate = allocate; ++ zlLib->symbol.dlSymbol.Deallocate = deallocate; ++ zlLib->symbol.dlSymbol.Error_Report = errorReport; ++ zlLib->symbol.lib = zlLib; ++ /* Allocate */ ++ zlLib->allocate.dlAlloc.Allocate = rmmAlloc; ++ zlLib->allocate.dlAlloc.Deallocate = rmmDealloc; ++ zlLib->allocate.lib = zlLib; ++ /* Init */ ++ zlLib->init.dlInit.connect = connect; ++ zlLib->init.dlInit.readmem = readMem; ++ zlLib->init.dlInit.writemem = writeMem; ++ zlLib->init.dlInit.fillmem = fillMem; ++ zlLib->init.dlInit.execute = execute; ++ zlLib->init.dlInit.release = release; ++ zlLib->init.lib = zlLib; ++ /* If COFF file is not open, we open it. */ ++ if (zlLib->fp == NULL) { ++ status = dofOpen(zlLib); ++ if (DSP_SUCCEEDED(status)) ++ fOpenedDoff = true; ++ ++ } ++ if (DSP_SUCCEEDED(status)) { ++ zlLib->ulPos = (*(zlLib->pTarget->attrs.ftell)) ++ (zlLib->fp); ++ /* Reset file cursor */ ++ (*(zlLib->pTarget->attrs.fseek))(zlLib->fp, (long)0, ++ SEEK_SET); ++ bSymbolsReloaded = true; ++ /* The 5th argument, DLOAD_INITBSS, tells the DLL ++ * module to zero-init all BSS sections. In general, ++ * this is not necessary and also increases load time. ++ * We may want to make this configurable by the user */ ++ err = Dynamic_Load_Module(&zlLib->stream.dlStream, ++ &zlLib->symbol.dlSymbol, &zlLib->allocate.dlAlloc, ++ &zlLib->init.dlInit, DLOAD_INITBSS, ++ &zlLib->mHandle); ++ ++ if (err != 0) { ++ GT_1trace(DBLL_debugMask, GT_6CLASS, ++ "DBLL_load: " ++ "Dynamic_Load_Module failed: 0x%lx\n", ++ err); ++ status = DSP_EDYNLOAD; ++ } else if (bRedefinedSymbol) { ++ zlLib->loadRef++; ++ DBLL_unload(zlLib, (struct DBLL_Attrs *) attrs); ++ bRedefinedSymbol = false; ++ status = DSP_EDYNLOAD; ++ } else { ++ *pEntry = zlLib->entry; ++ } ++ } ++ } ++ if (DSP_SUCCEEDED(status)) ++ zlLib->loadRef++; ++ ++ /* Clean up DOFF resources */ ++ if (fOpenedDoff) ++ dofClose(zlLib); ++ ++ DBC_Ensure(DSP_FAILED(status) || zlLib->loadRef > 0); ++ return status; ++} ++ ++/* ++ * ======== DBLL_loadSect ======== ++ * Not supported for COFF. ++ */ ++DSP_STATUS DBLL_loadSect(struct DBLL_LibraryObj *zlLib, char *sectName, ++ struct DBLL_Attrs *attrs) ++{ ++ DBC_Require(MEM_IsValidHandle(zlLib, DBLL_LIBSIGNATURE)); ++ ++ return DSP_ENOTIMPL; ++} ++ ++/* ++ * ======== DBLL_open ======== ++ */ ++DSP_STATUS DBLL_open(struct DBLL_TarObj *target, char *file, DBLL_Flags flags, ++ struct DBLL_LibraryObj **pLib) ++{ ++ struct DBLL_TarObj *zlTarget = (struct DBLL_TarObj *)target; ++ struct DBLL_LibraryObj *zlLib = NULL; ++ s32 err; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(zlTarget, DBLL_TARGSIGNATURE)); ++ DBC_Require(zlTarget->attrs.fopen != NULL); ++ DBC_Require(file != NULL); ++ DBC_Require(pLib != NULL); ++ ++ GT_3trace(DBLL_debugMask, GT_ENTER, ++ "DBLL_open: target: 0x%x file: %s pLib:" ++ " 0x%x\n", target, file, pLib); ++ zlLib = zlTarget->head; ++ while (zlLib != NULL) { ++ if (strcmp(zlLib->fileName, file) == 0) { ++ /* Library is already opened */ ++ zlLib->openRef++; ++ break; ++ } ++ zlLib = zlLib->next; ++ } ++ if (zlLib == NULL) { ++ /* Allocate DBL library object */ ++ MEM_AllocObject(zlLib, struct DBLL_LibraryObj, ++ DBLL_LIBSIGNATURE); ++ if (zlLib == NULL) { ++ GT_0trace(DBLL_debugMask, GT_6CLASS, ++ "DBLL_open: Memory allocation failed\n"); ++ status = DSP_EMEMORY; ++ } else { ++ zlLib->ulPos = 0; ++ /* Increment ref count to allow close on failure ++ * later on */ ++ zlLib->openRef++; ++ zlLib->pTarget = zlTarget; ++ /* Keep a copy of the file name */ ++ zlLib->fileName = MEM_Calloc(strlen(file) + 1, ++ MEM_PAGED); ++ if (zlLib->fileName == NULL) { ++ GT_0trace(DBLL_debugMask, GT_6CLASS, ++ "DBLL_open: Memory " ++ "allocation failed\n"); ++ status = DSP_EMEMORY; ++ } else { ++ strncpy(zlLib->fileName, file, ++ strlen(file) + 1); ++ } ++ zlLib->symTab = NULL; ++ } ++ } ++ /* ++ * Set up objects needed by the dynamic loader ++ */ ++ if (DSP_FAILED(status)) ++ goto func_cont; ++ ++ /* Stream */ ++ zlLib->stream.dlStream.read_buffer = readBuffer; ++ zlLib->stream.dlStream.set_file_posn = setFilePosn; ++ zlLib->stream.lib = zlLib; ++ /* Symbol */ ++ zlLib->symbol.dlSymbol.Add_To_Symbol_Table = addToSymbolTable; ++ zlLib->symbol.dlSymbol.Find_Matching_Symbol = findSymbol; ++ zlLib->symbol.dlSymbol.Purge_Symbol_Table = purgeSymbolTable; ++ zlLib->symbol.dlSymbol.Allocate = allocate; ++ zlLib->symbol.dlSymbol.Deallocate = deallocate; ++ zlLib->symbol.dlSymbol.Error_Report = errorReport; ++ zlLib->symbol.lib = zlLib; ++ /* Allocate */ ++ zlLib->allocate.dlAlloc.Allocate = rmmAlloc; ++ zlLib->allocate.dlAlloc.Deallocate = rmmDealloc; ++ zlLib->allocate.lib = zlLib; ++ /* Init */ ++ zlLib->init.dlInit.connect = connect; ++ zlLib->init.dlInit.readmem = readMem; ++ zlLib->init.dlInit.writemem = writeMem; ++ zlLib->init.dlInit.fillmem = fillMem; ++ zlLib->init.dlInit.execute = execute; ++ zlLib->init.dlInit.release = release; ++ zlLib->init.lib = zlLib; ++ if (DSP_SUCCEEDED(status) && zlLib->fp == NULL) ++ status = dofOpen(zlLib); ++ ++ zlLib->ulPos = (*(zlLib->pTarget->attrs.ftell)) (zlLib->fp); ++ (*(zlLib->pTarget->attrs.fseek))(zlLib->fp, (long) 0, SEEK_SET); ++ /* Create a hash table for symbols if flag is set */ ++ if (zlLib->symTab != NULL || !(flags & DBLL_SYMB)) ++ goto func_cont; ++ ++ zlLib->symTab = GH_create(MAXBUCKETS, sizeof(struct Symbol), nameHash, ++ nameMatch, symDelete); ++ if (zlLib->symTab == NULL) { ++ status = DSP_EMEMORY; ++ } else { ++ /* Do a fake load to get symbols - set write function to NoOp */ ++ zlLib->init.dlInit.writemem = NoOp; ++ err = Dynamic_Open_Module(&zlLib->stream.dlStream, ++ &zlLib->symbol.dlSymbol, ++ &zlLib->allocate.dlAlloc, ++ &zlLib->init.dlInit, 0, ++ &zlLib->mHandle); ++ if (err != 0) { ++ GT_1trace(DBLL_debugMask, GT_6CLASS, "DBLL_open: " ++ "Dynamic_Load_Module failed: 0x%lx\n", err); ++ status = DSP_EDYNLOAD; ++ } else { ++ /* Now that we have the symbol table, we can unload */ ++ err = Dynamic_Unload_Module(zlLib->mHandle, ++ &zlLib->symbol.dlSymbol, ++ &zlLib->allocate.dlAlloc, ++ &zlLib->init.dlInit); ++ if (err != 0) { ++ GT_1trace(DBLL_debugMask, GT_6CLASS, ++ "DBLL_open: " ++ "Dynamic_Unload_Module failed: 0x%lx\n", ++ err); ++ status = DSP_EDYNLOAD; ++ } ++ zlLib->mHandle = NULL; ++ } ++ } ++func_cont: ++ if (DSP_SUCCEEDED(status)) { ++ if (zlLib->openRef == 1) { ++ /* First time opened - insert in list */ ++ if (zlTarget->head) ++ (zlTarget->head)->prev = zlLib; ++ ++ zlLib->prev = NULL; ++ zlLib->next = zlTarget->head; ++ zlTarget->head = zlLib; ++ } ++ *pLib = (struct DBLL_LibraryObj *)zlLib; ++ } else { ++ *pLib = NULL; ++ if (zlLib != NULL) ++ DBLL_close((struct DBLL_LibraryObj *)zlLib); ++ ++ } ++ DBC_Ensure((DSP_SUCCEEDED(status) && (zlLib->openRef > 0) && ++ MEM_IsValidHandle(((struct DBLL_LibraryObj *)(*pLib)), ++ DBLL_LIBSIGNATURE)) || (DSP_FAILED(status) && *pLib == NULL)); ++ return status; ++} ++ ++/* ++ * ======== DBLL_readSect ======== ++ * Get the content of a COFF section. ++ */ ++DSP_STATUS DBLL_readSect(struct DBLL_LibraryObj *lib, char *name, ++ char *pContent, u32 size) ++{ ++ struct DBLL_LibraryObj *zlLib = (struct DBLL_LibraryObj *)lib; ++ bool fOpenedDoff = false; ++ u32 uByteSize; /* size of bytes */ ++ u32 ulSectSize; /* size of section */ ++ const struct LDR_SECTION_INFO *sect = NULL; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(zlLib, DBLL_LIBSIGNATURE)); ++ DBC_Require(name != NULL); ++ DBC_Require(pContent != NULL); ++ DBC_Require(size != 0); ++ ++ GT_4trace(DBLL_debugMask, GT_ENTER, ++ "DBLL_readSect: lib: 0x%x name: %s " ++ "pContent: 0x%x size: 0x%x\n", lib, name, pContent, size); ++ /* If DOFF file is not open, we open it. */ ++ if (zlLib != NULL) { ++ if (zlLib->fp == NULL) { ++ status = dofOpen(zlLib); ++ if (DSP_SUCCEEDED(status)) ++ fOpenedDoff = true; ++ ++ } else { ++ (*(zlLib->pTarget->attrs.fseek))(zlLib->fp, ++ zlLib->ulPos, SEEK_SET); ++ } ++ } ++ ++ if (DSP_FAILED(status)) ++ goto func_cont; ++ ++ uByteSize = 1; ++ if (!DLOAD_GetSectionInfo(zlLib->desc, name, §)) { ++ status = DSP_ENOSECT; ++ goto func_cont; ++ } ++ /* ++ * Ensure the supplied buffer size is sufficient to store ++ * the section content to be read. ++ */ ++ ulSectSize = sect->size * uByteSize; ++ /* Make sure size is even for good swap */ ++ if (ulSectSize % 2) ++ ulSectSize++; ++ ++ /* Align size */ ++ ulSectSize = DOFF_ALIGN(ulSectSize); ++ if (ulSectSize > size) { ++ status = DSP_EFAIL; ++ } else { ++ if (!DLOAD_GetSection(zlLib->desc, sect, pContent)) ++ status = DSP_EFREAD; ++ ++ } ++func_cont: ++ if (fOpenedDoff) { ++ dofClose(zlLib); ++ fOpenedDoff = false; ++ } ++ return status; ++} ++ ++/* ++ * ======== DBLL_setAttrs ======== ++ * Set the attributes of the target. ++ */ ++void DBLL_setAttrs(struct DBLL_TarObj *target, struct DBLL_Attrs *pAttrs) ++{ ++ struct DBLL_TarObj *zlTarget = (struct DBLL_TarObj *)target; ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(zlTarget, DBLL_TARGSIGNATURE)); ++ DBC_Require(pAttrs != NULL); ++ GT_2trace(DBLL_debugMask, GT_ENTER, ++ "DBLL_setAttrs: target: 0x%x pAttrs: " ++ "0x%x\n", target, pAttrs); ++ if ((pAttrs != NULL) && (zlTarget != NULL)) ++ zlTarget->attrs = *pAttrs; ++ ++} ++ ++/* ++ * ======== DBLL_unload ======== ++ */ ++void DBLL_unload(struct DBLL_LibraryObj *lib, struct DBLL_Attrs *attrs) ++{ ++ struct DBLL_LibraryObj *zlLib = (struct DBLL_LibraryObj *)lib; ++ s32 err = 0; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(zlLib, DBLL_LIBSIGNATURE)); ++ DBC_Require(zlLib->loadRef > 0); ++ GT_1trace(DBLL_debugMask, GT_ENTER, "DBLL_unload: lib: 0x%x\n", lib); ++ zlLib->loadRef--; ++ /* Unload only if reference count is 0 */ ++ if (zlLib->loadRef != 0) ++ goto func_end; ++ ++ zlLib->pTarget->attrs = *attrs; ++ if (zlLib != NULL) { ++ if (zlLib->mHandle) { ++ err = Dynamic_Unload_Module(zlLib->mHandle, ++ &zlLib->symbol.dlSymbol, ++ &zlLib->allocate.dlAlloc, &zlLib->init.dlInit); ++ if (err != 0) { ++ GT_1trace(DBLL_debugMask, GT_5CLASS, ++ "Dynamic_Unload_Module " ++ "failed: 0x%x\n", err); ++ } ++ } ++ /* remove symbols from symbol table */ ++ if (zlLib->symTab != NULL) { ++ GH_delete(zlLib->symTab); ++ zlLib->symTab = NULL; ++ } ++ /* delete DOFF desc since it holds *lots* of host OS ++ * resources */ ++ dofClose(zlLib); ++ } ++func_end: ++ DBC_Ensure(zlLib->loadRef >= 0); ++} ++ ++/* ++ * ======== DBLL_unloadSect ======== ++ * Not supported for COFF. ++ */ ++DSP_STATUS DBLL_unloadSect(struct DBLL_LibraryObj *lib, char *sectName, ++ struct DBLL_Attrs *attrs) ++{ ++ DBC_Require(cRefs > 0); ++ DBC_Require(sectName != NULL); ++ GT_2trace(DBLL_debugMask, GT_ENTER, ++ "DBLL_unloadSect: lib: 0x%x sectName: " ++ "%s\n", lib, sectName); ++ return DSP_ENOTIMPL; ++} ++ ++/* ++ * ======== dofClose ======== ++ */ ++static void dofClose(struct DBLL_LibraryObj *zlLib) ++{ ++ if (zlLib->desc) { ++ DLOAD_module_close(zlLib->desc); ++ zlLib->desc = NULL; ++ } ++ /* close file */ ++ if (zlLib->fp) { ++ (zlLib->pTarget->attrs.fclose) (zlLib->fp); ++ zlLib->fp = NULL; ++ } ++} ++ ++/* ++ * ======== dofOpen ======== ++ */ ++static DSP_STATUS dofOpen(struct DBLL_LibraryObj *zlLib) ++{ ++ void *open = *(zlLib->pTarget->attrs.fopen); ++ DSP_STATUS status = DSP_SOK; ++ ++ /* First open the file for the dynamic loader, then open COF */ ++ zlLib->fp = (void *)((DBLL_FOpenFxn)(open))(zlLib->fileName, "rb"); ++ ++ /* Open DOFF module */ ++ if (zlLib->fp && zlLib->desc == NULL) { ++ (*(zlLib->pTarget->attrs.fseek))(zlLib->fp, (long)0, SEEK_SET); ++ zlLib->desc = DLOAD_module_open(&zlLib->stream.dlStream, ++ &zlLib->symbol.dlSymbol); ++ if (zlLib->desc == NULL) { ++ (zlLib->pTarget->attrs.fclose)(zlLib->fp); ++ zlLib->fp = NULL; ++ status = DSP_EFOPEN; ++ } ++ } else { ++ status = DSP_EFOPEN; ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== nameHash ======== ++ */ ++static u16 nameHash(void *key, u16 maxBucket) ++{ ++ u16 ret; ++ u16 hash; ++ char *name = (char *)key; ++ ++ DBC_Require(name != NULL); ++ ++ hash = 0; ++ ++ while (*name) { ++ hash <<= 1; ++ hash ^= *name++; ++ } ++ ++ ret = hash % maxBucket; ++ ++ return ret; ++} ++ ++/* ++ * ======== nameMatch ======== ++ */ ++static bool nameMatch(void *key, void *value) ++{ ++ DBC_Require(key != NULL); ++ DBC_Require(value != NULL); ++ ++ if ((key != NULL) && (value != NULL)) { ++ if (strcmp((char *)key, ((struct Symbol *)value)->name) == 0) ++ return true; ++ } ++ return false; ++} ++ ++/* ++ * ======== NoOp ======== ++ */ ++static int NoOp(struct Dynamic_Loader_Initialize *thisptr, void *bufr, ++ LDR_ADDR locn, struct LDR_SECTION_INFO *info, unsigned bytsize) ++{ ++ return 1; ++} ++ ++/* ++ * ======== symDelete ======== ++ */ ++static void symDelete(void *value) ++{ ++ struct Symbol *sp = (struct Symbol *)value; ++ ++ MEM_Free(sp->name); ++} ++ ++/* ++ * Dynamic Loader Functions ++ */ ++ ++/* Dynamic_Loader_Stream */ ++/* ++ * ======== readBuffer ======== ++ */ ++static int readBuffer(struct Dynamic_Loader_Stream *this, void *buffer, ++ unsigned bufsize) ++{ ++ struct DBLLStream *pStream = (struct DBLLStream *)this; ++ struct DBLL_LibraryObj *lib; ++ int bytesRead = 0; ++ ++ DBC_Require(this != NULL); ++ lib = pStream->lib; ++ DBC_Require(MEM_IsValidHandle(lib, DBLL_LIBSIGNATURE)); ++ ++ if (lib != NULL) { ++ bytesRead = (*(lib->pTarget->attrs.fread))(buffer, 1, bufsize, ++ lib->fp); ++ } ++ return bytesRead; ++} ++ ++/* ++ * ======== setFilePosn ======== ++ */ ++static int setFilePosn(struct Dynamic_Loader_Stream *this, unsigned int pos) ++{ ++ struct DBLLStream *pStream = (struct DBLLStream *)this; ++ struct DBLL_LibraryObj *lib; ++ int status = 0; /* Success */ ++ ++ DBC_Require(this != NULL); ++ lib = pStream->lib; ++ DBC_Require(MEM_IsValidHandle(lib, DBLL_LIBSIGNATURE)); ++ ++ if (lib != NULL) { ++ status = (*(lib->pTarget->attrs.fseek))(lib->fp, (long)pos, ++ SEEK_SET); ++ } ++ ++ return status; ++} ++ ++/* Dynamic_Loader_Sym */ ++ ++/* ++ * ======== findSymbol ======== ++ */ ++static struct dynload_symbol *findSymbol(struct Dynamic_Loader_Sym *this, ++ const char *name) ++{ ++ struct dynload_symbol *retSym; ++ struct DBLLSymbol *pSymbol = (struct DBLLSymbol *)this; ++ struct DBLL_LibraryObj *lib; ++ struct DBLL_Symbol *pSym = NULL; ++ bool status = false; /* Symbol not found yet */ ++ ++ DBC_Require(this != NULL); ++ lib = pSymbol->lib; ++ DBC_Require(MEM_IsValidHandle(lib, DBLL_LIBSIGNATURE)); ++ ++ if (lib != NULL) { ++ if (lib->pTarget->attrs.symLookup) { ++ /* Check current lib + base lib + dep lib + ++ * persistent lib */ ++ status = (*(lib->pTarget->attrs.symLookup)) ++ (lib->pTarget->attrs.symHandle, ++ lib->pTarget->attrs.symArg, ++ lib->pTarget->attrs.rmmHandle, name, &pSym); ++ } else { ++ /* Just check current lib for symbol */ ++ status = DBLL_getAddr((struct DBLL_LibraryObj *)lib, ++ (char *)name, &pSym); ++ if (!status) { ++ status = ++ DBLL_getCAddr((struct DBLL_LibraryObj *)lib, ++ (char *)name, &pSym); ++ } ++ } ++ } ++ ++ if (!status && bGblSearch) { ++ GT_1trace(DBLL_debugMask, GT_6CLASS, ++ "findSymbol: Symbol not found: %s\n", name); ++ } ++ ++ DBC_Assert((status && (pSym != NULL)) || (!status && (pSym == NULL))); ++ ++ retSym = (struct dynload_symbol *)pSym; ++ return retSym; ++} ++ ++/* ++ * ======== findInSymbolTable ======== ++ */ ++static struct dynload_symbol *findInSymbolTable(struct Dynamic_Loader_Sym *this, ++ const char *name, ++ unsigned moduleid) ++{ ++ struct dynload_symbol *retSym; ++ struct DBLLSymbol *pSymbol = (struct DBLLSymbol *)this; ++ struct DBLL_LibraryObj *lib; ++ struct Symbol *sym; ++ ++ DBC_Require(this != NULL); ++ lib = pSymbol->lib; ++ DBC_Require(MEM_IsValidHandle(lib, DBLL_LIBSIGNATURE)); ++ DBC_Require(lib->symTab != NULL); ++ ++ sym = (struct Symbol *)GH_find(lib->symTab, (char *) name); ++ ++ retSym = (struct dynload_symbol *)&sym->value; ++ return retSym; ++} ++ ++/* ++ * ======== addToSymbolTable ======== ++ */ ++static struct dynload_symbol *addToSymbolTable(struct Dynamic_Loader_Sym *this, ++ const char *name, ++ unsigned moduleId) ++{ ++ struct Symbol *symPtr = NULL; ++ struct Symbol symbol; ++ struct dynload_symbol *pSym = NULL; ++ struct DBLLSymbol *pSymbol = (struct DBLLSymbol *)this; ++ struct DBLL_LibraryObj *lib; ++ struct dynload_symbol *retVal; ++ ++ DBC_Require(this != NULL); ++ DBC_Require(name); ++ lib = pSymbol->lib; ++ DBC_Require(MEM_IsValidHandle(lib, DBLL_LIBSIGNATURE)); ++ ++ /* Check to see if symbol is already defined in symbol table */ ++ if (!(lib->pTarget->attrs.baseImage)) { ++ bGblSearch = false; ++ pSym = findSymbol(this, name); ++ bGblSearch = true; ++ if (pSym) { ++ bRedefinedSymbol = true; ++ GT_1trace(DBLL_debugMask, GT_6CLASS, ++ "Symbol already defined in " ++ "symbol table: %s\n", name); ++ return NULL; ++ } ++ } ++ /* Allocate string to copy symbol name */ ++ symbol.name = (char *)MEM_Calloc(strlen((char *const)name) + 1, ++ MEM_PAGED); ++ if (symbol.name == NULL) ++ return NULL; ++ ++ if (symbol.name != NULL) { ++ /* Just copy name (value will be filled in by dynamic loader) */ ++ strncpy(symbol.name, (char *const)name, ++ strlen((char *const)name) + 1); ++ ++ /* Add symbol to symbol table */ ++ symPtr = (struct Symbol *)GH_insert(lib->symTab, (void *)name, ++ (void *)&symbol); ++ if (symPtr == NULL) ++ MEM_Free(symbol.name); ++ ++ } ++ if (symPtr != NULL) ++ retVal = (struct dynload_symbol *)&symPtr->value; ++ else ++ retVal = NULL; ++ ++ return retVal; ++} ++ ++/* ++ * ======== purgeSymbolTable ======== ++ */ ++static void purgeSymbolTable(struct Dynamic_Loader_Sym *this, unsigned moduleId) ++{ ++ struct DBLLSymbol *pSymbol = (struct DBLLSymbol *)this; ++ struct DBLL_LibraryObj *lib; ++ ++ DBC_Require(this != NULL); ++ lib = pSymbol->lib; ++ DBC_Require(MEM_IsValidHandle(lib, DBLL_LIBSIGNATURE)); ++ ++ /* May not need to do anything */ ++} ++ ++/* ++ * ======== allocate ======== ++ */ ++static void *allocate(struct Dynamic_Loader_Sym *this, unsigned memsize) ++{ ++ struct DBLLSymbol *pSymbol = (struct DBLLSymbol *)this; ++ struct DBLL_LibraryObj *lib; ++ void *buf; ++ ++ DBC_Require(this != NULL); ++ lib = pSymbol->lib; ++ DBC_Require(MEM_IsValidHandle(lib, DBLL_LIBSIGNATURE)); ++ ++ buf = MEM_Calloc(memsize, MEM_PAGED); ++ ++ return buf; ++} ++ ++/* ++ * ======== deallocate ======== ++ */ ++static void deallocate(struct Dynamic_Loader_Sym *this, void *memPtr) ++{ ++ struct DBLLSymbol *pSymbol = (struct DBLLSymbol *)this; ++ struct DBLL_LibraryObj *lib; ++ ++ DBC_Require(this != NULL); ++ lib = pSymbol->lib; ++ DBC_Require(MEM_IsValidHandle(lib, DBLL_LIBSIGNATURE)); ++ ++ MEM_Free(memPtr); ++} ++ ++/* ++ * ======== errorReport ======== ++ */ ++static void errorReport(struct Dynamic_Loader_Sym *this, const char *errstr, ++ va_list args) ++{ ++ struct DBLLSymbol *pSymbol = (struct DBLLSymbol *)this; ++ struct DBLL_LibraryObj *lib; ++ char tempBuf[MAXEXPR]; ++ ++ DBC_Require(this != NULL); ++ lib = pSymbol->lib; ++ DBC_Require(MEM_IsValidHandle(lib, DBLL_LIBSIGNATURE)); ++ vsnprintf((char *)tempBuf, MAXEXPR, (char *)errstr, args); ++ GT_1trace(DBLL_debugMask, GT_5CLASS, "%s\n", tempBuf); ++} ++ ++/* Dynamic_Loader_Allocate */ ++ ++/* ++ * ======== rmmAlloc ======== ++ */ ++static int rmmAlloc(struct Dynamic_Loader_Allocate *this, ++ struct LDR_SECTION_INFO *info, unsigned align) ++{ ++ struct DBLLAlloc *pAlloc = (struct DBLLAlloc *)this; ++ struct DBLL_LibraryObj *lib; ++ DSP_STATUS status = DSP_SOK; ++ u32 memType; ++ struct RMM_Addr rmmAddr; ++ s32 retVal = TRUE; ++ unsigned stype = DLOAD_SECTION_TYPE(info->type); ++ char *pToken = NULL; ++ char *szSecLastToken = NULL; ++ char *szLastToken = NULL; ++ char *szSectName = NULL; ++ char *pszCur; ++ s32 tokenLen = 0; ++ s32 segId = -1; ++ s32 req = -1; ++ s32 count = 0; ++ u32 allocSize = 0; ++ ++ DBC_Require(this != NULL); ++ lib = pAlloc->lib; ++ DBC_Require(MEM_IsValidHandle(lib, DBLL_LIBSIGNATURE)); ++ ++ memType = (stype == DLOAD_TEXT) ? DBLL_CODE : (stype == DLOAD_BSS) ? ++ DBLL_BSS : DBLL_DATA; ++ ++ /* Attempt to extract the segment ID and requirement information from ++ the name of the section */ ++ DBC_Require(info->name); ++ tokenLen = strlen((char *)(info->name)) + 1; ++ ++ szSectName = MEM_Calloc(tokenLen, MEM_PAGED); ++ szLastToken = MEM_Calloc(tokenLen, MEM_PAGED); ++ szSecLastToken = MEM_Calloc(tokenLen, MEM_PAGED); ++ ++ if (szSectName == NULL || szSecLastToken == NULL || ++ szLastToken == NULL) { ++ status = DSP_EMEMORY; ++ goto func_cont; ++ } ++ strncpy(szSectName, (char *)(info->name), tokenLen); ++ pszCur = szSectName; ++ while ((pToken = strsep(&pszCur, ":")) && *pToken != '\0') { ++ strncpy(szSecLastToken, szLastToken, strlen(szLastToken) + 1); ++ strncpy(szLastToken, pToken, strlen(pToken) + 1); ++ pToken = strsep(&pszCur, ":"); ++ count++; /* optimizes processing*/ ++ } ++ /* If pToken is 0 or 1, and szSecLastToken is DYN_DARAM or DYN_SARAM, ++ or DYN_EXTERNAL, then mem granularity information is present ++ within the section name - only process if there are at least three ++ tokens within the section name (just a minor optimization)*/ ++ if (count >= 3) ++ strict_strtol(szLastToken, 10, (long *)&req); ++ ++ if ((req == 0) || (req == 1)) { ++ if (strcmp(szSecLastToken, "DYN_DARAM") == 0) { ++ segId = 0; ++ } else { ++ if (strcmp(szSecLastToken, "DYN_SARAM") == 0) { ++ segId = 1; ++ } else { ++ if (strcmp(szSecLastToken, ++ "DYN_EXTERNAL") == 0) { ++ segId = 2; ++ } ++ } ++ } ++ if (segId != -1) { ++ GT_2trace(DBLL_debugMask, GT_5CLASS, ++ "Extracted values for memory" ++ " granularity req [%d] segId [%d]\n", ++ req, segId); ++ } ++ } ++ MEM_Free(szSectName); ++ szSectName = NULL; ++ MEM_Free(szLastToken); ++ szLastToken = NULL; ++ MEM_Free(szSecLastToken); ++ szSecLastToken = NULL; ++func_cont: ++ if (memType == DBLL_CODE) ++ allocSize = info->size + GEM_L1P_PREFETCH_SIZE; ++ else ++ allocSize = info->size; ++ /* TODO - ideally, we can pass the alignment requirement also ++ * from here */ ++ if (lib != NULL) { ++ status = (lib->pTarget->attrs.alloc)(lib->pTarget-> ++ attrs.rmmHandle, memType, allocSize, align, ++ (u32 *)&rmmAddr, segId, req, FALSE); ++ } ++ if (DSP_FAILED(status)) { ++ retVal = false; ++ } else { ++ /* RMM gives word address. Need to convert to byte address */ ++ info->load_addr = rmmAddr.addr * DSPWORDSIZE; ++ info->run_addr = info->load_addr; ++ info->context = (u32)rmmAddr.segid; ++ GT_3trace(DBLL_debugMask, GT_5CLASS, ++ "Remote alloc: %s base = 0x%lx len" ++ "= 0x%lx\n", info->name, info->load_addr / DSPWORDSIZE, ++ info->size / DSPWORDSIZE); ++ } ++ return retVal; ++} ++ ++/* ++ * ======== rmmDealloc ======== ++ */ ++static void rmmDealloc(struct Dynamic_Loader_Allocate *this, ++ struct LDR_SECTION_INFO *info) ++{ ++ struct DBLLAlloc *pAlloc = (struct DBLLAlloc *)this; ++ struct DBLL_LibraryObj *lib; ++ u32 segid; ++ DSP_STATUS status = DSP_SOK; ++ unsigned stype = DLOAD_SECTION_TYPE(info->type); ++ u32 memType; ++ u32 freeSize = 0; ++ ++ memType = (stype == DLOAD_TEXT) ? DBLL_CODE : (stype == DLOAD_BSS) ? ++ DBLL_BSS : DBLL_DATA; ++ DBC_Require(this != NULL); ++ lib = pAlloc->lib; ++ DBC_Require(MEM_IsValidHandle(lib, DBLL_LIBSIGNATURE)); ++ /* segid was set by alloc function */ ++ segid = (u32)info->context; ++ if (memType == DBLL_CODE) ++ freeSize = info->size + GEM_L1P_PREFETCH_SIZE; ++ else ++ freeSize = info->size; ++ if (lib != NULL) { ++ status = (lib->pTarget->attrs.free)(lib->pTarget-> ++ attrs.symHandle, segid, info->load_addr / DSPWORDSIZE, ++ freeSize, false); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ GT_2trace(DBLL_debugMask, GT_5CLASS, ++ "Remote dealloc: base = 0x%lx len =" ++ "0x%lx\n", info->load_addr / DSPWORDSIZE, ++ freeSize / DSPWORDSIZE); ++ } ++} ++ ++/* Dynamic_Loader_Initialize */ ++/* ++ * ======== connect ======== ++ */ ++static int connect(struct Dynamic_Loader_Initialize *this) ++{ ++ return true; ++} ++ ++/* ++ * ======== readMem ======== ++ * This function does not need to be implemented. ++ */ ++static int readMem(struct Dynamic_Loader_Initialize *this, void *buf, ++ LDR_ADDR addr, struct LDR_SECTION_INFO *info, ++ unsigned nbytes) ++{ ++ struct DBLLInit *pInit = (struct DBLLInit *)this; ++ struct DBLL_LibraryObj *lib; ++ int bytesRead = 0; ++ ++ DBC_Require(this != NULL); ++ lib = pInit->lib; ++ DBC_Require(MEM_IsValidHandle(lib, DBLL_LIBSIGNATURE)); ++ /* Need WMD_BRD_Read function */ ++ return bytesRead; ++} ++ ++/* ++ * ======== writeMem ======== ++ */ ++static int writeMem(struct Dynamic_Loader_Initialize *this, void *buf, ++ LDR_ADDR addr, struct LDR_SECTION_INFO *info, ++ unsigned nBytes) ++{ ++ struct DBLLInit *pInit = (struct DBLLInit *)this; ++ struct DBLL_LibraryObj *lib; ++ struct DBLL_SectInfo sectInfo; ++ u32 memType; ++ bool retVal = true; ++ ++ DBC_Require(this != NULL); ++ lib = pInit->lib; ++ DBC_Require(MEM_IsValidHandle(lib, DBLL_LIBSIGNATURE)); ++ ++ memType = (DLOAD_SECTION_TYPE(info->type) == DLOAD_TEXT) ? DBLL_CODE : ++ DBLL_DATA; ++ if (lib != NULL) { ++ retVal = (*lib->pTarget->attrs.write)(lib->pTarget-> ++ attrs.wHandle, addr, buf, nBytes, memType); ++ } ++ if (lib->pTarget->attrs.logWrite) { ++ sectInfo.name = info->name; ++ sectInfo.runAddr = info->run_addr; ++ sectInfo.loadAddr = info->load_addr; ++ sectInfo.size = info->size; ++ sectInfo.type = memType; ++ /* Pass the information about what we've written to ++ * another module */ ++ (*lib->pTarget->attrs.logWrite)(lib->pTarget-> ++ attrs.logWriteHandle, §Info, addr, nBytes); ++ } ++ return retVal; ++} ++ ++/* ++ * ======== fillMem ======== ++ * Fill nBytes of memory at a given address with a given value by ++ * writing from a buffer containing the given value. Write in ++ * sets of MAXEXPR (128) bytes to avoid large stack buffer issues. ++ */ ++static int fillMem(struct Dynamic_Loader_Initialize *this, LDR_ADDR addr, ++ struct LDR_SECTION_INFO *info, unsigned nBytes, ++ unsigned val) ++{ ++ bool retVal = true; ++ char *pBuf; ++ struct DBLL_LibraryObj *lib; ++ struct DBLLInit *pInit = (struct DBLLInit *)this; ++ ++ DBC_Require(this != NULL); ++ lib = pInit->lib; ++ pBuf = NULL; ++ /* Pass the NULL pointer to writeMem to get the start address of Shared ++ memory. This is a trick to just get the start address, there is no ++ writing taking place with this Writemem ++ */ ++ if ((lib->pTarget->attrs.write) != (DBLL_WriteFxn)NoOp) ++ writeMem(this, &pBuf, addr, info, 0); ++ if (pBuf) ++ memset(pBuf, val, nBytes); ++ ++ return retVal; ++} ++ ++/* ++ * ======== execute ======== ++ */ ++static int execute(struct Dynamic_Loader_Initialize *this, LDR_ADDR start) ++{ ++ struct DBLLInit *pInit = (struct DBLLInit *)this; ++ struct DBLL_LibraryObj *lib; ++ bool retVal = true; ++ ++ DBC_Require(this != NULL); ++ lib = pInit->lib; ++ DBC_Require(MEM_IsValidHandle(lib, DBLL_LIBSIGNATURE)); ++ /* Save entry point */ ++ if (lib != NULL) ++ lib->entry = (u32)start; ++ ++ return retVal; ++} ++ ++/* ++ * ======== release ======== ++ */ ++static void release(struct Dynamic_Loader_Initialize *this) ++{ ++} ++ +diff --git a/drivers/dsp/bridge/pmgr/dev.c b/drivers/dsp/bridge/pmgr/dev.c +new file mode 100644 +index 0000000..1c2f7d5 +--- /dev/null ++++ b/drivers/dsp/bridge/pmgr/dev.c +@@ -0,0 +1,1476 @@ ++/* ++ * dev.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dev.c ======== ++ * Description: ++ * Implementation of 'Bridge Mini-driver device operations. ++ * ++ * Public Functions: ++ * DEV_BrdWriteFxn ++ * DEV_CreateDevice ++ * DEV_Create2 ++ * DEV_Destroy2 ++ * DEV_DestroyDevice ++ * DEV_GetChnlMgr ++ * DEV_GetCmmMgr ++ * DEV_GetCodMgr ++ * DEV_GetDehMgr ++ * DEV_GetDevNode ++ * DEV_GetDSPWordSize ++ * DEV_GetFirst ++ * DEV_GetIntfFxns ++ * DEV_GetIOMgr ++ * DEV_GetNext ++ * DEV_GetNodeManager ++ * DEV_GetSymbol ++ * DEV_GetWMDContext ++ * DEV_Exit ++ * DEV_Init ++ * DEV_InsertProcObject ++ * DEV_IsLocked ++ * DEV_NotifyClient ++ * DEV_RegisterNotify ++ * DEV_ReleaseCodMgr ++ * DEV_RemoveDevice ++ * DEV_RemoveProcObject ++ * DEV_SetChnlMgr ++ * DEV_SetMsgMgr ++ * DEV_SetLockOwner ++ * DEV_StartDevice ++ * ++ * Private Functions: ++ * FxnNotImplemented ++ * InitCodMgr ++ * InsertDevObject ++ * IsValidHandle ++ * RemoveDevObject ++ * StoreInterfaceFxns ++ * ++ *! Revision History: ++ *! ================ ++ *! 03-Jan-2005 hn Support for IVA DEH ++ *! 08-Mar-2004 sb Added the Dynamic Memory Mapping feature ++ *! 09-Feb-2004 vp Updated to support IVA. ++ *! 24-Feb-2003 swa PMGR Code review comments incorporated. ++ *! 29-Nov-2001 jeh Check for DSP_ENOTIMPL status of DEH create function. ++ *! 05-Nov-2001 kc Added support for DEH module. ++ *! 05-Aug-2001 ag Shared memory registration moved to WMD_IO_OnLoaded(). ++ *! 11-Jul-2001 jeh Moved MGR_Create() from DSP_Init() to DEV_StartDevice(). ++ *! 11-Apr-2001 rr: Removed CMM_RegisterGPPSMSeg. ++ *! 02-Apr-2001 rr: CHNL_Create failure is printed out. ++ *! 15-Jan-2001 jeh Removed call to IO_OnLoaded() from DEV_Create2(). ++ *! 13-Feb-2001 kc: DSP/BIOS Bridge name update. ++ *! 15-Dec-2000 rr: Dev_Create2 returns error if NODE_CreateMgr fails. ++ *! 05-Dec-2000 jeh Moved IO_OnLoaded() to PROC_Load. Added DEV_SetMsgMgr. ++ *! 05-Dev-2000 ag SM Heap for messaging registered via CMM_RegisterGPPSMSeg(). ++ *! SM heap base and size currently taken from registry. ++ *! 29-Nov-2000 rr: Incorporated code review changes. ++ *! 17-Nov-2000 jeh Added calls to get IO manager (IO_Create), IO_OnLoaded(). ++ *! 06-Oct-2000 rr: DEV_Destroy2 and DEV_Create2 added. ++ *! 02-Oct-2000 rr: DEV_GetNodeManager added. ++ *! 11-Aug-2000 ag: Added DEV_GetCmmMgr(), CMM_Init() & CMM_Exit(). ++ *! Removed & , added ++ *! 10-Aug-2000 rr: DEV_InsertProcObject/RemoveProcObject added. ++ *! DEV_Cleanup calls PROC_Detach if it is a matching process. ++ *! 27-Jul-2000 rr: DEV is in new directoy DEV and produces devlib.lib ++ *! 17-Jul-2000 rr: DRV Object holds the list of Dev Objects. DEV gets ++ *! the List and Next devices through DRV. ++ *! DEV object has a back pointer to DRV Object. ++ *! 06-Jun-2000 jeh Added DEV_GetSymbol(). ++ *! 09-May-2000 rr: dwMemBase has index for multiple windows need. ++ *! 28-Feb-2000 rr: New GT Usage implemented. ++ *! 03-Feb-2000 rr: GT and Module init/exit Changes.(Done up front from ++ *! SERVICES) ++ *! 31-Jan-2000 rr: Comments changed after code review. ++ *! 21-Jan-2000 rr: windows.h, tchar.h, HMODULE removed. FreeLibrary replaced ++ *! with LDR_FreeModule ++ *! 17-Jan-2000 rr: CFG_Get/SetPrivateDword renamed to CFG_Get/SetDevObject. ++ *! StoreInterfaceFxns stores the new fxn WMD_BRD_SETSTATE. ++ *! 20-Nov-1999 ag: Actual uSMLength = total - monitor offset. ++ *! 12-Nov-1999 rr: bIRQ and IRQAttrib taken from the struct CFG_HOSTRES. ++ *! dMemBase is added with offset for monitor taken from ++ *! registry. ++ *! 31-Oct-1999 ag: Added CHNL support. ++ *! 10-Sep-1999 rr: GT Enabled. DEV_Create will Load the Mini Driver and will ++ *! find its fxn table. Right now lot of things are hardcoded ++ *! as the REG is not ready. ++ *! 10-Jun-1996 rr: Created from WSX ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Resource Manager */ ++#include ++#include ++ ++/* ----------------------------------- Others */ ++#include ++#include /* WCD version info. */ ++ ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ----------------------------------- Defines, Data Structures, Typedefs */ ++ ++#define SIGNATURE 0x5f564544 /* "DEV_" (in reverse) */ ++#define MAKEVERSION(major, minor) (major * 10 + minor) ++#define WCDVERSION MAKEVERSION(WCD_MAJOR_VERSION, WCD_MINOR_VERSION) ++ ++/* The WMD device object: */ ++struct DEV_OBJECT { ++ /* LST requires "link" to be first field! */ ++ struct LST_ELEM link; /* Link to next DEV_OBJECT. */ ++ u32 devType; /* Device Type */ ++ u32 dwSignature; /* Used for object validation. */ ++ struct CFG_DEVNODE *hDevNode; /* Platform specific device id */ ++ struct WMD_DEV_CONTEXT *hWmdContext; /* WMD Context Handle */ ++ struct WMD_DRV_INTERFACE intfFxns; /* Function interface to WMD. */ ++ struct BRD_OBJECT *lockOwner; /* Client with exclusive access. */ ++ struct COD_MANAGER *hCodMgr; /* Code manager handle. */ ++ struct CHNL_MGR *hChnlMgr; /* Channel manager. */ ++ struct DEH_MGR *hDehMgr; /* DEH manager. */ ++ struct MSG_MGR *hMsgMgr; /* Message manager. */ ++ struct IO_MGR *hIOMgr; /* IO manager (CHNL, MSG) */ ++ struct CMM_OBJECT *hCmmMgr; /* SM memory manager. */ ++ struct DMM_OBJECT *hDmmMgr; /* Dynamic memory manager. */ ++ struct LDR_MODULE *hModule; /* WMD Module handle. */ ++ u32 uWordSize; /* DSP word size: quick access. */ ++ struct DRV_OBJECT *hDrvObject; /* Driver Object */ ++ struct LST_LIST *procList; /* List of Proceeosr attached to ++ * this device */ ++ struct NODE_MGR *hNodeMgr; ++} ; ++ ++/* ----------------------------------- Globals */ ++static u32 cRefs; /* Module reference count */ ++#if GT_TRACE ++static struct GT_Mask debugMask = { NULL, NULL }; /* For debugging */ ++#endif ++ ++/* ----------------------------------- Function Prototypes */ ++static DSP_STATUS FxnNotImplemented(int arg, ...); ++static DSP_STATUS InitCodMgr(struct DEV_OBJECT *pDevObject); ++static bool IsValidHandle(struct DEV_OBJECT *hObj); ++static void StoreInterfaceFxns(struct WMD_DRV_INTERFACE *pDrvFxns, ++ OUT struct WMD_DRV_INTERFACE *pIntfFxns); ++/* ++ * ======== DEV_BrdWriteFxn ======== ++ * Purpose: ++ * Exported function to be used as the COD write function. This function ++ * is passed a handle to a DEV_hObject, then calls the ++ * device's WMD_BRD_Write() function. ++ */ ++u32 DEV_BrdWriteFxn(void *pArb, u32 ulDspAddr, void *pHostBuf, ++ u32 ulNumBytes, u32 nMemSpace) ++{ ++ struct DEV_OBJECT *pDevObject = (struct DEV_OBJECT *)pArb; ++ u32 ulWritten = 0; ++ DSP_STATUS status; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pHostBuf != NULL); /* Required of BrdWrite(). */ ++ GT_5trace(debugMask, GT_ENTER, ++ "Entered DEV_BrdWriteFxn, pArb: 0x%x\n\t\t" ++ "ulDspAddr: 0x%x\n\t\tpHostBuf: 0x%x\n \t\tulNumBytes: 0x%x\n" ++ "\t\tnMemSpace: 0x%x\n", pArb, ulDspAddr, pHostBuf, ++ ulNumBytes, nMemSpace); ++ if (IsValidHandle(pDevObject)) { ++ /* Require of BrdWrite() */ ++ DBC_Assert(pDevObject->hWmdContext != NULL); ++ status = (*pDevObject->intfFxns.pfnBrdWrite)(pDevObject-> ++ hWmdContext, pHostBuf, ulDspAddr, ulNumBytes, ++ nMemSpace); ++ /* Special case of getting the address only */ ++ if (ulNumBytes == 0) ++ ulNumBytes = 1; ++ if (DSP_SUCCEEDED(status)) ++ ulWritten = ulNumBytes; ++ ++ } ++ GT_1trace(debugMask, GT_ENTER, "Exit DEV_BrdWriteFxn ulWritten: 0x%x\n", ++ ulWritten); ++ return ulWritten; ++} ++ ++/* ++ * ======== DEV_CreateDevice ======== ++ * Purpose: ++ * Called by the operating system to load the PM Mini Driver for a ++ * PM board (device). ++ */ ++DSP_STATUS DEV_CreateDevice(OUT struct DEV_OBJECT **phDevObject, ++ IN CONST char *pstrWMDFileName, ++ IN CONST struct CFG_HOSTRES *pHostConfig, ++ IN CONST struct CFG_DSPRES *pDspConfig, ++ struct CFG_DEVNODE *hDevNode) ++{ ++ struct LDR_MODULE *hModule = NULL; ++ struct WMD_DRV_INTERFACE *pDrvFxns = NULL; ++ struct DEV_OBJECT *pDevObject = NULL; ++ struct CHNL_MGRATTRS mgrAttrs; ++ struct IO_ATTRS ioMgrAttrs; ++ u32 uNumWindows; ++ struct DRV_OBJECT *hDrvObject = NULL; ++ DSP_STATUS status = DSP_SOK; ++ DBC_Require(cRefs > 0); ++ DBC_Require(phDevObject != NULL); ++ DBC_Require(pstrWMDFileName != NULL); ++ DBC_Require(pHostConfig != NULL); ++ DBC_Require(pDspConfig != NULL); ++ ++ GT_5trace(debugMask, GT_ENTER, ++ "Entered DEV_CreateDevice, phDevObject: 0x%x\n" ++ "\t\tpstrWMDFileName: 0x%x\n\t\tpHostConfig:0x%x\n\t\t" ++ "pDspConfig: 0x%x\n\t\tnhDevNode: 0x%x\n", phDevObject, ++ pstrWMDFileName, pHostConfig, pDspConfig, hDevNode); ++ /* Get the WMD interface functions*/ ++ WMD_DRV_Entry(&pDrvFxns, pstrWMDFileName); ++ if (DSP_FAILED(CFG_GetObject((u32 *) &hDrvObject, REG_DRV_OBJECT))) { ++ /* don't propogate CFG errors from this PROC function */ ++ GT_0trace(debugMask, GT_7CLASS, ++ "Failed to get the DRV Object \n"); ++ status = DSP_EFAIL; ++ } ++ /* Create the device object, and pass a handle to the WMD for ++ * storage. */ ++ if (DSP_SUCCEEDED(status)) { ++ DBC_Assert(pDrvFxns); ++ MEM_AllocObject(pDevObject, struct DEV_OBJECT, SIGNATURE); ++ if (pDevObject) { ++ /* Fill out the rest of the Dev Object structure: */ ++ pDevObject->hDevNode = hDevNode; ++ pDevObject->hModule = hModule; ++ pDevObject->hCodMgr = NULL; ++ pDevObject->hChnlMgr = NULL; ++ pDevObject->hDehMgr = NULL; ++ pDevObject->lockOwner = NULL; ++ pDevObject->uWordSize = pDspConfig->uWordSize; ++ pDevObject->hDrvObject = hDrvObject; ++ pDevObject->devType = DSP_UNIT; ++ /* Store this WMD's interface functions, based on its ++ * version. */ ++ StoreInterfaceFxns(pDrvFxns, &pDevObject->intfFxns); ++ /* Call WMD_DEV_CREATE() to get the WMD's device ++ * context handle. */ ++ status = (pDevObject->intfFxns.pfnDevCreate) ++ (&pDevObject->hWmdContext, pDevObject, ++ pHostConfig, pDspConfig); ++ /* Assert WMD_DEV_Create()'s ensure clause: */ ++ DBC_Assert(DSP_FAILED(status) || (pDevObject-> ++ hWmdContext != NULL)); ++ } else { ++ GT_0trace(debugMask, GT_7CLASS, ++ "DEV_Create: Out Of Memory"); ++ status = DSP_EMEMORY; ++ } ++ } ++ /* Attempt to create the COD manager for this device: */ ++ if (DSP_SUCCEEDED(status)) ++ status = InitCodMgr(pDevObject); ++ ++ /* Attempt to create the channel manager for this device: */ ++ if (DSP_SUCCEEDED(status)) { ++ mgrAttrs.cChannels = CHNL_MAXCHANNELS; ++ ioMgrAttrs.bIRQ = pHostConfig->bIRQRegisters; ++ ioMgrAttrs.fShared = (pHostConfig->bIRQAttrib & CFG_IRQSHARED); ++ ioMgrAttrs.uWordSize = pDspConfig->uWordSize; ++ mgrAttrs.uWordSize = pDspConfig->uWordSize; ++ uNumWindows = pHostConfig->wNumMemWindows; ++ if (uNumWindows) { ++ /* Assume last memory window is for CHNL */ ++ ioMgrAttrs.dwSMBase = pHostConfig->dwMemBase[1] + ++ pHostConfig->dwOffsetForMonitor; ++ ioMgrAttrs.uSMLength = pHostConfig->dwMemLength[1] - ++ pHostConfig->dwOffsetForMonitor; ++ } else { ++ ioMgrAttrs.dwSMBase = 0; ++ ioMgrAttrs.uSMLength = 0; ++ GT_0trace(debugMask, GT_7CLASS, ++ "**There is no memory reserved for " ++ "shared structures**\n"); ++ } ++ status = CHNL_Create(&pDevObject->hChnlMgr, pDevObject, ++ &mgrAttrs); ++ if (status == DSP_ENOTIMPL) { ++ /* It's OK for a device not to have a channel ++ * manager: */ ++ status = DSP_SOK; ++ } ++ /* Create CMM mgr even if Msg Mgr not impl. */ ++ status = CMM_Create(&pDevObject->hCmmMgr, ++ (struct DEV_OBJECT *)pDevObject, NULL); ++ if (DSP_FAILED(status)) { ++ GT_0trace(debugMask, GT_7CLASS, ++ "DEV_Create: Failed to Create SM " ++ "Manager\n"); ++ } ++ /* Only create IO manager if we have a channel manager */ ++ if (DSP_SUCCEEDED(status) && pDevObject->hChnlMgr) { ++ status = IO_Create(&pDevObject->hIOMgr, pDevObject, ++ &ioMgrAttrs); ++ } ++ /* Only create DEH manager if we have an IO manager */ ++ if (DSP_SUCCEEDED(status)) { ++ /* Instantiate the DEH module */ ++ status = (*pDevObject->intfFxns.pfnDehCreate) ++ (&pDevObject->hDehMgr, pDevObject); ++ } ++ /* Create DMM mgr . */ ++ status = DMM_Create(&pDevObject->hDmmMgr, ++ (struct DEV_OBJECT *)pDevObject, NULL); ++ if (DSP_FAILED(status)) { ++ GT_0trace(debugMask, GT_7CLASS, ++ "DEV_Create: Failed to Create DMM " ++ "Manager\n"); ++ } ++ } ++ /* Add the new DEV_Object to the global list: */ ++ if (DSP_SUCCEEDED(status)) { ++ LST_InitElem(&pDevObject->link); ++ status = DRV_InsertDevObject(hDrvObject, pDevObject); ++ } ++ /* Create the Processor List */ ++ if (DSP_SUCCEEDED(status)) { ++ pDevObject->procList = LST_Create(); ++ if (!(pDevObject->procList)) { ++ status = DSP_EFAIL; ++ GT_0trace(debugMask, GT_7CLASS, "DEV_Create: " ++ "Failed to Create Proc List"); ++ } ++ } ++ /* If all went well, return a handle to the dev object; ++ * else, cleanup and return NULL in the OUT parameter. */ ++ if (DSP_SUCCEEDED(status)) { ++ *phDevObject = pDevObject; ++ GT_1trace(debugMask, GT_1CLASS, ++ "DEV_CreateDevice Succeeded \nDevObject " ++ "0x%x\n", pDevObject); ++ } else { ++ if (pDevObject && pDevObject->procList) ++ LST_Delete(pDevObject->procList); ++ ++ if (pDevObject && pDevObject->hCodMgr) ++ COD_Delete(pDevObject->hCodMgr); ++ ++ if (pDevObject && pDevObject->hDmmMgr) ++ DMM_Destroy(pDevObject->hDmmMgr); ++ ++ if (pDevObject) ++ MEM_FreeObject(pDevObject); ++ ++ *phDevObject = NULL; ++ GT_0trace(debugMask, GT_7CLASS, "DEV_CreateDevice Failed\n"); ++ } ++ GT_1trace(debugMask, GT_1CLASS, "Exiting DEV_Create: DevObject 0x%x\n", ++ *phDevObject); ++ DBC_Ensure((DSP_SUCCEEDED(status) && IsValidHandle(*phDevObject)) || ++ (DSP_FAILED(status) && !*phDevObject)); ++ return status; ++} ++ ++/* ++ * ======== DEV_Create2 ======== ++ * Purpose: ++ * After successful loading of the image from WCD_InitComplete2 ++ * (PROC Auto_Start) or PROC_Load this fxn is called. This creates ++ * the Node Manager and updates the DEV Object. ++ */ ++DSP_STATUS DEV_Create2(struct DEV_OBJECT *hDevObject) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *pDevObject = hDevObject; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(IsValidHandle(hDevObject)); ++ ++ GT_1trace(debugMask, GT_ENTER, ++ "Entered DEV_Create2, hDevObject: 0x%x\n", hDevObject); ++ /* There can be only one Node Manager per DEV object */ ++ DBC_Assert(!pDevObject->hNodeMgr); ++ status = NODE_CreateMgr(&pDevObject->hNodeMgr, hDevObject); ++ if (DSP_FAILED(status)) { ++ GT_1trace(debugMask, GT_7CLASS, ++ "DEV_Create2: NODE_CreateMgr failed, " ++ "0x%x!\n", status); ++ pDevObject->hNodeMgr = NULL; ++ GT_0trace(debugMask, GT_7CLASS, "DEV_Create2: Failed!!\n"); ++ } ++ DBC_Ensure((DSP_SUCCEEDED(status) && pDevObject->hNodeMgr != NULL) ++ || (DSP_FAILED(status) && pDevObject->hNodeMgr == NULL)); ++ GT_2trace(debugMask, GT_ENTER, ++ "Exiting DEV_Create2, hNodeMgr: 0x%x, status:" ++ " 0x%x\n", pDevObject->hNodeMgr, status); ++ return status; ++} ++ ++/* ++ * ======== DEV_Destroy2 ======== ++ * Purpose: ++ * Destroys the Node manager for this device. ++ */ ++DSP_STATUS DEV_Destroy2(struct DEV_OBJECT *hDevObject) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *pDevObject = hDevObject; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(IsValidHandle(hDevObject)); ++ ++ GT_1trace(debugMask, GT_ENTER, ++ "Entered DEV_Destroy2, hDevObject: 0x%x\n", ++ hDevObject); ++ if (pDevObject->hNodeMgr) { ++ if (DSP_FAILED(NODE_DeleteMgr(pDevObject->hNodeMgr))) ++ status = DSP_EFAIL; ++ else ++ pDevObject->hNodeMgr = NULL; ++ ++ } ++ if (DSP_FAILED(status)) ++ GT_0trace(debugMask, GT_7CLASS, "DEV_Destroy2 failed!!\n"); ++ ++ DBC_Ensure((DSP_SUCCEEDED(status) && pDevObject->hNodeMgr == NULL) || ++ DSP_FAILED(status)); ++ GT_2trace(debugMask, GT_ENTER, ++ "Exiting DEV_Destroy2, hNodeMgr: 0x%x, status" ++ " = 0x%x\n", pDevObject->hNodeMgr, status); ++ return status; ++} ++ ++/* ++ * ======== DEV_DestroyDevice ======== ++ * Purpose: ++ * Destroys the channel manager for this device, if any, calls ++ * WMD_DEV_Destroy(), and then attempts to unload the WMD module. ++ */ ++DSP_STATUS DEV_DestroyDevice(struct DEV_OBJECT *hDevObject) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *pDevObject = hDevObject; ++ ++ DBC_Require(cRefs > 0); ++ ++ GT_1trace(debugMask, GT_ENTER, "Entered DEV_DestroyDevice, hDevObject: " ++ "0x%x\n", hDevObject); ++ if (IsValidHandle(hDevObject)) { ++ if (pDevObject->hCodMgr) ++ COD_Delete(pDevObject->hCodMgr); ++ ++ if (pDevObject->hNodeMgr) ++ NODE_DeleteMgr(pDevObject->hNodeMgr); ++ ++ /* Free the io, channel, and message managers for this board: */ ++ if (pDevObject->hIOMgr) { ++ IO_Destroy(pDevObject->hIOMgr); ++ pDevObject->hIOMgr = NULL; ++ } ++ if (pDevObject->hChnlMgr) { ++ CHNL_Destroy(pDevObject->hChnlMgr); ++ pDevObject->hChnlMgr = NULL; ++ } ++ if (pDevObject->hMsgMgr) ++ MSG_Delete(pDevObject->hMsgMgr); ++ ++ if (pDevObject->hDehMgr) { ++ /* Uninitialize DEH module. */ ++ (*pDevObject->intfFxns.pfnDehDestroy) ++ (pDevObject->hDehMgr); ++ } ++ if (pDevObject->hCmmMgr) ++ CMM_Destroy(pDevObject->hCmmMgr, true); ++ ++ if (pDevObject->hDmmMgr) ++ DMM_Destroy(pDevObject->hDmmMgr); ++ ++ /* Call the driver's WMD_DEV_Destroy() function: */ ++ /* Require of DevDestroy */ ++ DBC_Assert(pDevObject->hWmdContext != NULL); ++ status = (*pDevObject->intfFxns.pfnDevDestroy) ++ (pDevObject->hWmdContext); ++ if (DSP_SUCCEEDED(status)) { ++ if (pDevObject->procList) ++ LST_Delete(pDevObject->procList); ++ ++ /* Remove this DEV_Object from the global list: */ ++ DRV_RemoveDevObject(pDevObject->hDrvObject, pDevObject); ++ /* Free The library * LDR_FreeModule ++ * (pDevObject->hModule);*/ ++ /* Free this dev object: */ ++ MEM_FreeObject(pDevObject); ++ } ++ } else { ++ GT_0trace(debugMask, GT_7CLASS, "DEV_Destroy: Invlaid handle"); ++ status = DSP_EHANDLE; ++ } ++ GT_1trace(debugMask, GT_ENTER, "Exit DEV_destroy: status 0x%x\n", ++ status); ++ return status; ++} ++ ++/* ++ * ======== DEV_GetChnlMgr ======== ++ * Purpose: ++ * Retrieve the handle to the channel manager handle created for this ++ * device. ++ */ ++DSP_STATUS DEV_GetChnlMgr(struct DEV_OBJECT *hDevObject, ++ OUT struct CHNL_MGR **phMgr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *pDevObject = hDevObject; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phMgr != NULL); ++ ++ GT_2trace(debugMask, GT_ENTER, ++ "Entered DEV_GetChnlMgr, hDevObject: 0x%x\n\t" ++ "\tphMgr: 0x%x\n", hDevObject, phMgr); ++ if (IsValidHandle(hDevObject)) { ++ *phMgr = pDevObject->hChnlMgr; ++ } else { ++ *phMgr = NULL; ++ status = DSP_EHANDLE; ++ GT_0trace(debugMask, GT_7CLASS, ++ "DEV_GetChnlMgr: Invalid handle"); ++ } ++ GT_2trace(debugMask, GT_ENTER, ++ "Exit DEV_GetChnlMgr: status 0x%x\t\n hMgr: " ++ "0x%x\n", status, *phMgr); ++ DBC_Ensure(DSP_SUCCEEDED(status) || ((phMgr != NULL) && ++ (*phMgr == NULL))); ++ return status; ++} ++ ++/* ++ * ======== DEV_GetCmmMgr ======== ++ * Purpose: ++ * Retrieve the handle to the shared memory manager created for this ++ * device. ++ */ ++DSP_STATUS DEV_GetCmmMgr(struct DEV_OBJECT *hDevObject, ++ OUT struct CMM_OBJECT **phMgr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *pDevObject = hDevObject; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phMgr != NULL); ++ GT_2trace(debugMask, GT_ENTER, ++ "Entered DEV_GetCmmMgr, hDevObject: 0x%x\n\t" ++ "\tphMgr: 0x%x\n", hDevObject, phMgr); ++ if (IsValidHandle(hDevObject)) { ++ *phMgr = pDevObject->hCmmMgr; ++ } else { ++ *phMgr = NULL; ++ status = DSP_EHANDLE; ++ GT_0trace(debugMask, GT_7CLASS, ++ "DEV_GetCmmMgr: Invalid handle"); ++ } ++ GT_2trace(debugMask, GT_ENTER, ++ "Exit DEV_GetCmmMgr: status 0x%x\t\nhMgr: " ++ "0x%x\n", status, *phMgr); ++ DBC_Ensure(DSP_SUCCEEDED(status) || ((phMgr != NULL) && ++ (*phMgr == NULL))); ++ return status; ++} ++ ++/* ++ * ======== DEV_GetDmmMgr ======== ++ * Purpose: ++ * Retrieve the handle to the dynamic memory manager created for this ++ * device. ++ */ ++DSP_STATUS DEV_GetDmmMgr(struct DEV_OBJECT *hDevObject, ++ OUT struct DMM_OBJECT **phMgr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *pDevObject = hDevObject; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phMgr != NULL); ++ ++ GT_2trace(debugMask, GT_ENTER, "Entered DEV_GetDmmMgr, hDevObject: " ++ "0x%x\n\t\tphMgr: 0x%x\n", hDevObject, phMgr); ++ if (IsValidHandle(hDevObject)) { ++ *phMgr = pDevObject->hDmmMgr; ++ } else { ++ *phMgr = NULL; ++ status = DSP_EHANDLE; ++ GT_0trace(debugMask, GT_7CLASS, ++ "DEV_GetDmmMgr: Invalid handle"); ++ } ++ GT_2trace(debugMask, GT_ENTER, ++ "Exit DEV_GetDmmMgr: status 0x%x\t\n hMgr: " ++ "0x%x\n", status, *phMgr); ++ DBC_Ensure(DSP_SUCCEEDED(status) || ((phMgr != NULL) && ++ (*phMgr == NULL))); ++ return status; ++} ++ ++/* ++ * ======== DEV_GetCodMgr ======== ++ * Purpose: ++ * Retrieve the COD manager create for this device. ++ */ ++DSP_STATUS DEV_GetCodMgr(struct DEV_OBJECT *hDevObject, ++ OUT struct COD_MANAGER **phCodMgr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *pDevObject = hDevObject; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phCodMgr != NULL); ++ ++ GT_2trace(debugMask, GT_ENTER, ++ "Entered DEV_GetCodMgr, hDevObject: 0x%x\n\t\t" ++ "phCodMgr: 0x%x\n", hDevObject, phCodMgr); ++ if (IsValidHandle(hDevObject)) { ++ *phCodMgr = pDevObject->hCodMgr; ++ } else { ++ *phCodMgr = NULL; ++ status = DSP_EHANDLE; ++ GT_1trace(debugMask, GT_7CLASS, ++ "DEV_GetCodMgr, invalid handle: 0x%x\n", ++ hDevObject); ++ } ++ GT_2trace(debugMask, GT_ENTER, ++ "Exit DEV_GetCodMgr: status 0x%x\t\n hCodMgr:" ++ " 0x%x\n", status, *phCodMgr); ++ DBC_Ensure(DSP_SUCCEEDED(status) || ((phCodMgr != NULL) && ++ (*phCodMgr == NULL))); ++ return status; ++} ++ ++/* ++ * ========= DEV_GetDehMgr ======== ++ */ ++DSP_STATUS DEV_GetDehMgr(struct DEV_OBJECT *hDevObject, ++ OUT struct DEH_MGR **phDehMgr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phDehMgr != NULL); ++ DBC_Require(MEM_IsValidHandle(hDevObject, SIGNATURE)); ++ if (IsValidHandle(hDevObject)) { ++ *phDehMgr = hDevObject->hDehMgr; ++ } else { ++ *phDehMgr = NULL; ++ status = DSP_EHANDLE; ++ GT_0trace(debugMask, GT_7CLASS, ++ "DEV_GetDehMgr: Invalid handle"); ++ } ++ return status; ++} ++ ++/* ++ * ======== DEV_GetDevNode ======== ++ * Purpose: ++ * Retrieve the platform specific device ID for this device. ++ */ ++DSP_STATUS DEV_GetDevNode(struct DEV_OBJECT *hDevObject, ++ OUT struct CFG_DEVNODE **phDevNode) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *pDevObject = hDevObject; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phDevNode != NULL); ++ ++ GT_2trace(debugMask, GT_ENTER, ++ "Entered DEV_GetDevNode, hDevObject: 0x%x\n\t" ++ "\tphDevNode: 0x%x\n", hDevObject, phDevNode); ++ if (IsValidHandle(hDevObject)) { ++ *phDevNode = pDevObject->hDevNode; ++ } else { ++ *phDevNode = NULL; ++ status = DSP_EHANDLE; ++ GT_0trace(debugMask, GT_7CLASS, ++ "DEV_GetDevNode: Invalid handle"); ++ } ++ GT_2trace(debugMask, GT_ENTER, ++ "Exit DEV_GetDevNode: status 0x%x\t\nhDevNode:" ++ "0x%x\n", status, *phDevNode); ++ DBC_Ensure(DSP_SUCCEEDED(status) || ((phDevNode != NULL) && ++ (*phDevNode == NULL))); ++ return status; ++} ++ ++/* ++ * ======== DEV_GetFirst ======== ++ * Purpose: ++ * Retrieve the first Device Object handle from an internal linked list ++ * DEV_OBJECTs maintained by DEV. ++ */ ++struct DEV_OBJECT *DEV_GetFirst(void) ++{ ++ struct DEV_OBJECT *pDevObject = NULL; ++ ++ pDevObject = (struct DEV_OBJECT *)DRV_GetFirstDevObject(); ++ ++ DBC_Ensure((pDevObject == NULL) || IsValidHandle(pDevObject)); ++ ++ return pDevObject; ++} ++ ++/* ++ * ======== DEV_GetIntfFxns ======== ++ * Purpose: ++ * Retrieve the WMD interface function structure for the loaded WMD. ++ * ppIntfFxns != NULL. ++ */ ++DSP_STATUS DEV_GetIntfFxns(struct DEV_OBJECT *hDevObject, ++ OUT struct WMD_DRV_INTERFACE **ppIntfFxns) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *pDevObject = hDevObject; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(ppIntfFxns != NULL); ++ ++ GT_2trace(debugMask, GT_ENTER, ++ "Entered DEV_GetIntfFxns, hDevObject: 0x%x\n\t" ++ "\tppIntfFxns: 0x%x\n", hDevObject, ppIntfFxns); ++ if (IsValidHandle(hDevObject)) { ++ *ppIntfFxns = &pDevObject->intfFxns; ++ } else { ++ *ppIntfFxns = NULL; ++ status = DSP_EHANDLE; ++ GT_0trace(debugMask, GT_7CLASS, ++ "DEV_GetIntDxns: Invalid handle"); ++ } ++ GT_2trace(debugMask, GT_ENTER, "Exit DEV_GetIntFxns: status 0x%x\t\n" ++ "ppIntFxns: 0x%x\n", status, *ppIntfFxns); ++ DBC_Ensure(DSP_SUCCEEDED(status) || ((ppIntfFxns != NULL) && ++ (*ppIntfFxns == NULL))); ++ return status; ++} ++ ++/* ++ * ========= DEV_GetIOMgr ======== ++ */ ++DSP_STATUS DEV_GetIOMgr(struct DEV_OBJECT *hDevObject, ++ OUT struct IO_MGR **phIOMgr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phIOMgr != NULL); ++ DBC_Require(MEM_IsValidHandle(hDevObject, SIGNATURE)); ++ ++ if (IsValidHandle(hDevObject)) { ++ *phIOMgr = hDevObject->hIOMgr; ++ } else { ++ *phIOMgr = NULL; ++ status = DSP_EHANDLE; ++ GT_0trace(debugMask, GT_7CLASS, "DEV_GetIOMgr: Invalid handle"); ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== DEV_GetNext ======== ++ * Purpose: ++ * Retrieve the next Device Object handle from an internal linked list ++ * of DEV_OBJECTs maintained by DEV, after having previously called ++ * DEV_GetFirst() and zero or more DEV_GetNext ++ */ ++struct DEV_OBJECT *DEV_GetNext(struct DEV_OBJECT *hDevObject) ++{ ++ struct DEV_OBJECT *pNextDevObject = NULL; ++ ++ if (IsValidHandle(hDevObject)) { ++ pNextDevObject = (struct DEV_OBJECT *) ++ DRV_GetNextDevObject((u32)hDevObject); ++ } ++ DBC_Ensure((pNextDevObject == NULL) || IsValidHandle(pNextDevObject)); ++ return pNextDevObject; ++} ++ ++/* ++ * ========= DEV_GetMsgMgr ======== ++ */ ++void DEV_GetMsgMgr(struct DEV_OBJECT *hDevObject, ++ OUT struct MSG_MGR **phMsgMgr) ++{ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phMsgMgr != NULL); ++ DBC_Require(MEM_IsValidHandle(hDevObject, SIGNATURE)); ++ ++ *phMsgMgr = hDevObject->hMsgMgr; ++} ++ ++/* ++ * ======== DEV_GetNodeManager ======== ++ * Purpose: ++ * Retrieve the Node Manager Handle ++ */ ++DSP_STATUS DEV_GetNodeManager(struct DEV_OBJECT *hDevObject, ++ OUT struct NODE_MGR **phNodeMgr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *pDevObject = hDevObject; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phNodeMgr != NULL); ++ ++ GT_2trace(debugMask, GT_ENTER, ++ "Entered DEV_GetNodeManager, hDevObject: 0x%x" ++ "\n\t\tphNodeMgr: 0x%x\n", hDevObject, phNodeMgr); ++ if (IsValidHandle(hDevObject)) { ++ *phNodeMgr = pDevObject->hNodeMgr; ++ } else { ++ *phNodeMgr = NULL; ++ status = DSP_EHANDLE; ++ GT_1trace(debugMask, GT_7CLASS, ++ "DEV_GetNodeManager, invalid handle: 0x" ++ "%x\n", hDevObject); ++ } ++ GT_2trace(debugMask, GT_ENTER, ++ "Exit DEV_GetNodeManager: status 0x%x\t\nhMgr:" ++ " 0x%x\n", status, *phNodeMgr); ++ DBC_Ensure(DSP_SUCCEEDED(status) || ((phNodeMgr != NULL) && ++ (*phNodeMgr == NULL))); ++ return status; ++} ++ ++/* ++ * ======== DEV_GetSymbol ======== ++ */ ++DSP_STATUS DEV_GetSymbol(struct DEV_OBJECT *hDevObject, ++ IN CONST char *pstrSym, OUT u32 *pulValue) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct COD_MANAGER *hCodMgr; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pstrSym != NULL && pulValue != NULL); ++ ++ GT_3trace(debugMask, GT_ENTER, ++ "Entered DEV_GetSymbol, hDevObject: 0x%x\n\t\t" ++ "pstrSym: 0x%x\n\t\tpulValue: 0x%x\n", hDevObject, pstrSym, ++ pulValue); ++ if (IsValidHandle(hDevObject)) { ++ status = DEV_GetCodMgr(hDevObject, &hCodMgr); ++ if (DSP_SUCCEEDED(status)) { ++ DBC_Assert(hCodMgr != NULL); ++ status = COD_GetSymValue(hCodMgr, (char *)pstrSym, ++ pulValue); ++ } ++ } else { ++ status = DSP_EHANDLE; ++ GT_0trace(debugMask, GT_7CLASS, ++ "DEV_GetSymbol: Invalid handle"); ++ } ++ GT_2trace(debugMask, GT_ENTER, "Exit DEV_GetSymbol: status 0x%x\t\n" ++ "hWmdContext: 0x%x\n", status, *pulValue); ++ return status; ++} ++ ++/* ++ * ======== DEV_GetWMDContext ======== ++ * Purpose: ++ * Retrieve the WMD Context handle, as returned by the WMD_Create fxn. ++ */ ++DSP_STATUS DEV_GetWMDContext(struct DEV_OBJECT *hDevObject, ++ OUT struct WMD_DEV_CONTEXT **phWmdContext) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *pDevObject = hDevObject; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phWmdContext != NULL); ++ GT_2trace(debugMask, GT_ENTER, ++ "Entered DEV_GetWMDContext, hDevObject: 0x%x\n" ++ "\t\tphWmdContext: 0x%x\n", hDevObject, phWmdContext); ++ if (IsValidHandle(hDevObject)) { ++ *phWmdContext = pDevObject->hWmdContext; ++ } else { ++ *phWmdContext = NULL; ++ status = DSP_EHANDLE; ++ GT_0trace(debugMask, GT_7CLASS, ++ "DEV_GetWMDContext: Invalid handle"); ++ } ++ ++ GT_2trace(debugMask, GT_ENTER, ++ "Exit DEV_GetWMDContext: status 0x%x\t\n" ++ "hWmdContext: 0x%x\n", status, *phWmdContext); ++ DBC_Ensure(DSP_SUCCEEDED(status) || ((phWmdContext != NULL) && ++ (*phWmdContext == NULL))); ++ return status; ++} ++ ++/* ++ * ======== DEV_Exit ======== ++ * Purpose: ++ * Decrement reference count, and free resources when reference count is ++ * 0. ++ */ ++void DEV_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ cRefs--; ++ ++ if (cRefs == 0) { ++ CMM_Exit(); ++ DMM_Exit(); ++ } ++ ++ GT_1trace(debugMask, GT_5CLASS, "Entered DEV_Exit, ref count: 0x%x\n", ++ cRefs); ++ ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== DEV_Init ======== ++ * Purpose: ++ * Initialize DEV's private state, keeping a reference count on each call. ++ */ ++bool DEV_Init(void) ++{ ++ bool fCmm, fDmm, fRetval = true; ++ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++ /* Set the Trace mask */ ++ DBC_Assert(!debugMask.flags); ++ GT_create(&debugMask, "DV"); /* "DV" for DeVice */ ++ fCmm = CMM_Init(); ++ fDmm = DMM_Init(); ++ ++ fRetval = fCmm && fDmm; ++ ++ if (!fRetval) { ++ if (fCmm) ++ CMM_Exit(); ++ ++ ++ if (fDmm) ++ DMM_Exit(); ++ ++ } ++ } ++ ++ if (fRetval) ++ cRefs++; ++ ++ ++ GT_1trace(debugMask, GT_5CLASS, "Entered DEV_Init, ref count: 0x%x\n", ++ cRefs); ++ ++ DBC_Ensure((fRetval && (cRefs > 0)) || (!fRetval && (cRefs >= 0))); ++ ++ return fRetval; ++} ++ ++/* ++ * ======== DEV_NotifyClients ======== ++ * Purpose: ++ * Notify all clients of this device of a change in device status. ++ */ ++DSP_STATUS DEV_NotifyClients(struct DEV_OBJECT *hDevObject, u32 ulStatus) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++ struct DEV_OBJECT *pDevObject = hDevObject; ++ DSP_HPROCESSOR hProcObject; ++ ++ GT_2trace(debugMask, GT_ENTER, ++ "Entered DEV_NotifyClients, hDevObject: 0x%x\n" ++ "\t\tulStatus: 0x%x\n", hDevObject, ulStatus); ++ for (hProcObject = (DSP_HPROCESSOR)LST_First(pDevObject->procList); ++ hProcObject != NULL; ++ hProcObject = (DSP_HPROCESSOR)LST_Next(pDevObject->procList, ++ (struct LST_ELEM *)hProcObject)) ++ PROC_NotifyClients(hProcObject, (u32) ulStatus); ++ ++ return status; ++} ++ ++/* ++ * ======== DEV_RemoveDevice ======== ++ */ ++DSP_STATUS DEV_RemoveDevice(struct CFG_DEVNODE *hDevNode) ++{ ++ struct DEV_OBJECT *hDevObject; /* handle to device object */ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *pDevObject; ++ ++ GT_1trace(debugMask, GT_ENTER, ++ "Entered DEV_RemoveDevice, hDevNode: 0x%x\n", hDevNode); ++ /* Retrieve the device object handle originaly stored with ++ * the DevNode: */ ++ status = CFG_GetDevObject(hDevNode, (u32 *)&hDevObject); ++ if (DSP_SUCCEEDED(status)) { ++ /* Remove the Processor List */ ++ pDevObject = (struct DEV_OBJECT *)hDevObject; ++ /* Destroy the device object. */ ++ status = DEV_DestroyDevice(hDevObject); ++ if (DSP_SUCCEEDED(status)) { ++ /* Null out the handle stored with the DevNode. */ ++ GT_0trace(debugMask, GT_1CLASS, ++ "DEV_RemoveDevice, success"); ++ } ++ } ++ GT_1trace(debugMask, GT_ENTER, "Exit DEV_RemoveDevice, status: 0x%x\n", ++ status); ++ return status; ++} ++ ++/* ++ * ======== DEV_SetChnlMgr ======== ++ * Purpose: ++ * Set the channel manager for this device. ++ */ ++DSP_STATUS DEV_SetChnlMgr(struct DEV_OBJECT *hDevObject, struct CHNL_MGR *hMgr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *pDevObject = hDevObject; ++ ++ DBC_Require(cRefs > 0); ++ ++ GT_2trace(debugMask, GT_ENTER, ++ "Entered DEV_SetChnlMgr, hDevObject: 0x%x\n\t" ++ "\thMgr:0x%x\n", hDevObject, hMgr); ++ if (IsValidHandle(hDevObject)) { ++ pDevObject->hChnlMgr = hMgr; ++ } else { ++ status = DSP_EHANDLE; ++ GT_0trace(debugMask, GT_7CLASS, ++ "DEV_SetChnlMgr, Invalid handle\n"); ++ } ++ DBC_Ensure(DSP_FAILED(status) || (pDevObject->hChnlMgr == hMgr)); ++ return status; ++} ++ ++/* ++ * ======== DEV_SetMsgMgr ======== ++ * Purpose: ++ * Set the message manager for this device. ++ */ ++void DEV_SetMsgMgr(struct DEV_OBJECT *hDevObject, struct MSG_MGR *hMgr) ++{ ++ DBC_Require(cRefs > 0); ++ DBC_Require(IsValidHandle(hDevObject)); ++ GT_2trace(debugMask, GT_ENTER, ++ "Entered DEV_SetMsgMgr, hDevObject: 0x%x\n\t\t" ++ "hMgr: 0x%x\n", hDevObject, hMgr); ++ hDevObject->hMsgMgr = hMgr; ++} ++ ++/* ++ * ======== DEV_StartDevice ======== ++ * Purpose: ++ * Initializes the new device with the BRIDGE environment. ++ */ ++DSP_STATUS DEV_StartDevice(struct CFG_DEVNODE *hDevNode) ++{ ++ struct DEV_OBJECT *hDevObject = NULL; /* handle to 'Bridge Device */ ++ struct CFG_HOSTRES hostRes; /* resources struct. */ ++ struct CFG_DSPRES dspRes; /* DSP resources struct */ ++ char szWMDFileName[CFG_MAXSEARCHPATHLEN] = "UMA"; /* wmd filename */ ++ DSP_STATUS status; ++ struct MGR_OBJECT *hMgrObject = NULL; ++ ++ DBC_Require(cRefs > 0); ++ ++ GT_1trace(debugMask, GT_ENTER, ++ "Entered DEV_StartDevice, hDevObject: 0x%x\n", hDevNode); ++ status = CFG_GetHostResources(hDevNode, &hostRes); ++ if (DSP_SUCCEEDED(status)) { ++ /* Get DSP resources of device from Registry: */ ++ status = CFG_GetDSPResources(hDevNode, &dspRes); ++ if (DSP_FAILED(status)) { ++ GT_1trace(debugMask, GT_7CLASS, ++ "Failed to get WMD DSP resources" ++ " from registry: 0x%x ", status); ++ } ++ } else { ++ GT_1trace(debugMask, GT_7CLASS, ++ "Failed to get WMD Host resources " ++ "from registry: 0x%x ", status); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Given all resources, create a device object. */ ++ status = DEV_CreateDevice(&hDevObject, szWMDFileName, &hostRes, ++ &dspRes, hDevNode); ++ if (DSP_SUCCEEDED(status)) { ++ /* Store away the hDevObject with the DEVNODE */ ++ status = CFG_SetDevObject(hDevNode, (u32)hDevObject); ++ if (DSP_FAILED(status)) { ++ /* Clean up */ ++ GT_1trace(debugMask, GT_7CLASS, ++ "Failed to set DevObject in the " ++ "Registry: 0x%x", status); ++ DEV_DestroyDevice(hDevObject); ++ hDevObject = NULL; ++ } ++ } else { ++ GT_1trace(debugMask, GT_7CLASS, ++ "Failed to Create Device: 0x%x", ++ status); ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Create the Manager Object */ ++ status = MGR_Create(&hMgrObject, hDevNode); ++ } ++ if (DSP_FAILED(status)) { ++ GT_1trace(debugMask, GT_7CLASS, "Failed to MGR object: 0x%x", ++ status); ++ status = DSP_EFAIL; ++ } ++ if (DSP_FAILED(status)) { ++ if (hDevObject) ++ DEV_DestroyDevice(hDevObject); ++ ++ /* Ensure the device extension is NULL */ ++ CFG_SetDevObject(hDevNode, 0L); ++ } ++ GT_1trace(debugMask, GT_ENTER, "Exiting DEV_StartDevice status 0x%x\n", ++ status); ++ return status; ++} ++ ++/* ++ * ======== FxnNotImplemented ======== ++ * Purpose: ++ * Takes the place of a WMD Null Function. ++ * Parameters: ++ * Multiple, optional. ++ * Returns: ++ * DSP_ENOTIMPL: Always. ++ */ ++static DSP_STATUS FxnNotImplemented(int arg, ...) ++{ ++ DBG_Trace(DBG_LEVEL1, ++ "WARNING: Calling a non-implemented WMD function.\n"); ++ ++ return DSP_ENOTIMPL; ++} ++ ++/* ++ * ======== IsValidHandle ======== ++ * Purpose: ++ * Validate the device object handle. ++ * Parameters: ++ * hDevObject: Handle to device object created with ++ * DEV_CreateDevice(). ++ * Returns: ++ * true if handle is valid; false otherwise. ++ * Requires: ++ * Ensures: ++ */ ++static bool IsValidHandle(struct DEV_OBJECT *hObj) ++{ ++ bool retVal; ++ ++ retVal = (hObj != NULL) && (hObj->dwSignature == SIGNATURE); ++ ++ return retVal; ++} ++ ++/* ++ * ======== InitCodMgr ======== ++ * Purpose: ++ * Create a COD manager for this device. ++ * Parameters: ++ * pDevObject: Pointer to device object created with ++ * DEV_CreateDevice() ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EHANDLE: Invalid hDevObject. ++ * Requires: ++ * Should only be called once by DEV_CreateDevice() for a given DevObject. ++ * Ensures: ++ */ ++static DSP_STATUS InitCodMgr(struct DEV_OBJECT *pDevObject) ++{ ++ DSP_STATUS status = DSP_SOK; ++ char *szDummyFile = "dummy"; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(!IsValidHandle(pDevObject) || ++ (pDevObject->hCodMgr == NULL)); ++ GT_1trace(debugMask, GT_ENTER, "Entering InitCodMgr pDevObject: 0x%x", ++ pDevObject); ++ status = COD_Create(&pDevObject->hCodMgr, szDummyFile, NULL); ++ GT_1trace(debugMask, GT_ENTER, "Exiting InitCodMgr status 0x%x\n ", ++ status); ++ return status; ++} ++ ++/* ++ * ======== DEV_InsertProcObject ======== ++ * Purpose: ++ * Insert a ProcObject into the list maintained by DEV. ++ * Parameters: ++ * pProcObject: Ptr to ProcObject to insert. ++ * pDevObject: Ptr to Dev Object where the list is. ++ * pbAlreadyAttached: Ptr to return the bool ++ * Returns: ++ * DSP_SOK: If successful. ++ * Requires: ++ * List Exists ++ * hDevObject is Valid handle ++ * DEV Initialized ++ * pbAlreadyAttached != NULL ++ * hProcObject != 0 ++ * Ensures: ++ * DSP_SOK and List is not Empty. ++ */ ++DSP_STATUS DEV_InsertProcObject(struct DEV_OBJECT *hDevObject, ++ u32 hProcObject, ++ OUT bool *pbAlreadyAttached) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *pDevObject = (struct DEV_OBJECT *)hDevObject; ++ ++ GT_2trace(debugMask, GT_ENTER, ++ "Entering DEV_InsetProcObject pProcObject 0x%x" ++ "pDevObject 0x%x\n", hProcObject, hDevObject); ++ DBC_Require(cRefs > 0); ++ DBC_Require(IsValidHandle(pDevObject)); ++ DBC_Require(hProcObject != 0); ++ DBC_Require(pDevObject->procList != NULL); ++ DBC_Require(pbAlreadyAttached != NULL); ++ if (!LST_IsEmpty(pDevObject->procList)) ++ *pbAlreadyAttached = true; ++ ++ /* Add DevObject to tail. */ ++ LST_PutTail(pDevObject->procList, (struct LST_ELEM *)hProcObject); ++ ++ GT_1trace(debugMask, GT_ENTER, ++ "Exiting DEV_InsetProcObject status 0x%x\n", status); ++ DBC_Ensure(DSP_SUCCEEDED(status) && !LST_IsEmpty(pDevObject->procList)); ++ ++ return status; ++} ++ ++/* ++ * ======== DEV_RemoveProcObject ======== ++ * Purpose: ++ * Search for and remove a Proc object from the given list maintained ++ * by the DEV ++ * Parameters: ++ * pProcObject: Ptr to ProcObject to insert. ++ * pDevObject Ptr to Dev Object where the list is. ++ * Returns: ++ * DSP_SOK: If successful. ++ * Requires: ++ * List exists and is not empty ++ * hProcObject != 0 ++ * hDevObject is a valid Dev handle. ++ * Ensures: ++ * Details: ++ * List will be deleted when the DEV is destroyed. ++ */ ++DSP_STATUS DEV_RemoveProcObject(struct DEV_OBJECT *hDevObject, ++ u32 hProcObject) ++{ ++ DSP_STATUS status = DSP_EFAIL; ++ struct LST_ELEM *pCurElem; ++ struct DEV_OBJECT *pDevObject = (struct DEV_OBJECT *)hDevObject; ++ ++ DBC_Require(IsValidHandle(pDevObject)); ++ DBC_Require(hProcObject != 0); ++ DBC_Require(pDevObject->procList != NULL); ++ DBC_Require(!LST_IsEmpty(pDevObject->procList)); ++ ++ GT_1trace(debugMask, GT_ENTER, ++ "Entering DEV_RemoveProcObject hDevObject " ++ "0x%x\n", hDevObject); ++ /* Search list for pDevObject: */ ++ for (pCurElem = LST_First(pDevObject->procList); pCurElem != NULL; ++ pCurElem = LST_Next(pDevObject->procList, pCurElem)) { ++ /* If found, remove it. */ ++ if ((u32)pCurElem == hProcObject) { ++ LST_RemoveElem(pDevObject->procList, pCurElem); ++ status = DSP_SOK; ++ break; ++ } ++ } ++ GT_1trace(debugMask, GT_ENTER, "DEV_RemoveProcObject returning 0x%x\n", ++ status); ++ return status; ++} ++ ++DSP_STATUS DEV_GetDevType(struct DEV_OBJECT *hdevObject, u32 *devType) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *pDevObject = (struct DEV_OBJECT *)hdevObject; ++ ++ *devType = pDevObject->devType; ++ ++ return status; ++} ++ ++/* ++ * ======== StoreInterfaceFxns ======== ++ * Purpose: ++ * Copy the WMD's interface functions into the device object, ++ * ensuring that FxnNotImplemented() is set for: ++ * ++ * 1. All WMD function pointers which are NULL; and ++ * 2. All function slots in the struct DEV_OBJECT structure which have no ++ * corresponding slots in the the WMD's interface, because the WMD ++ * is of an *older* version. ++ * Parameters: ++ * pIntfFxns: Interface Fxn Structure of the WCD's Dev Object. ++ * pDrvFxns: Interface Fxns offered by the WMD during DEV_Create(). ++ * Returns: ++ * Requires: ++ * Input pointers are valid. ++ * WMD is *not* written for a newer WCD. ++ * Ensures: ++ * All function pointers in the dev object's Fxn interface are not NULL. ++ */ ++static void StoreInterfaceFxns(struct WMD_DRV_INTERFACE *pDrvFxns, ++ OUT struct WMD_DRV_INTERFACE *pIntfFxns) ++{ ++ u32 dwWMDVersion; ++ ++ /* Local helper macro: */ ++#define StoreFxn(cast, pfn) \ ++ (pIntfFxns->pfn = ((pDrvFxns->pfn != NULL) ? pDrvFxns->pfn : \ ++ (cast)FxnNotImplemented)) ++ ++ DBC_Require(pIntfFxns != NULL); ++ DBC_Require(pDrvFxns != NULL); ++ DBC_Require(MAKEVERSION(pDrvFxns->dwWCDMajorVersion, ++ pDrvFxns->dwWCDMinorVersion) <= WCDVERSION); ++ dwWMDVersion = MAKEVERSION(pDrvFxns->dwWCDMajorVersion, ++ pDrvFxns->dwWCDMinorVersion); ++ pIntfFxns->dwWCDMajorVersion = pDrvFxns->dwWCDMajorVersion; ++ pIntfFxns->dwWCDMinorVersion = pDrvFxns->dwWCDMinorVersion; ++ /* Install functions up to WCD version .80 (first alpha): */ ++ if (dwWMDVersion > 0) { ++ StoreFxn(WMD_DEV_CREATE, pfnDevCreate); ++ StoreFxn(WMD_DEV_DESTROY, pfnDevDestroy); ++ StoreFxn(WMD_DEV_CTRL, pfnDevCntrl); ++ StoreFxn(WMD_BRD_MONITOR, pfnBrdMonitor); ++ StoreFxn(WMD_BRD_START, pfnBrdStart); ++ StoreFxn(WMD_BRD_STOP, pfnBrdStop); ++ StoreFxn(WMD_BRD_STATUS, pfnBrdStatus); ++ StoreFxn(WMD_BRD_READ, pfnBrdRead); ++ StoreFxn(WMD_BRD_WRITE, pfnBrdWrite); ++ StoreFxn(WMD_BRD_SETSTATE, pfnBrdSetState); ++ StoreFxn(WMD_BRD_MEMCOPY, pfnBrdMemCopy); ++ StoreFxn(WMD_BRD_MEMWRITE, pfnBrdMemWrite); ++ StoreFxn(WMD_BRD_MEMMAP, pfnBrdMemMap); ++ StoreFxn(WMD_BRD_MEMUNMAP, pfnBrdMemUnMap); ++ StoreFxn(WMD_CHNL_CREATE, pfnChnlCreate); ++ StoreFxn(WMD_CHNL_DESTROY, pfnChnlDestroy); ++ StoreFxn(WMD_CHNL_OPEN, pfnChnlOpen); ++ StoreFxn(WMD_CHNL_CLOSE, pfnChnlClose); ++ StoreFxn(WMD_CHNL_ADDIOREQ, pfnChnlAddIOReq); ++ StoreFxn(WMD_CHNL_GETIOC, pfnChnlGetIOC); ++ StoreFxn(WMD_CHNL_CANCELIO, pfnChnlCancelIO); ++ StoreFxn(WMD_CHNL_FLUSHIO, pfnChnlFlushIO); ++ StoreFxn(WMD_CHNL_GETINFO, pfnChnlGetInfo); ++ StoreFxn(WMD_CHNL_GETMGRINFO, pfnChnlGetMgrInfo); ++ StoreFxn(WMD_CHNL_IDLE, pfnChnlIdle); ++ StoreFxn(WMD_CHNL_REGISTERNOTIFY, pfnChnlRegisterNotify); ++ StoreFxn(WMD_DEH_CREATE, pfnDehCreate); ++ StoreFxn(WMD_DEH_DESTROY, pfnDehDestroy); ++ StoreFxn(WMD_DEH_NOTIFY, pfnDehNotify); ++ StoreFxn(WMD_DEH_REGISTERNOTIFY, pfnDehRegisterNotify); ++ StoreFxn(WMD_DEH_GETINFO, pfnDehGetInfo); ++ StoreFxn(WMD_IO_CREATE, pfnIOCreate); ++ StoreFxn(WMD_IO_DESTROY, pfnIODestroy); ++ StoreFxn(WMD_IO_ONLOADED, pfnIOOnLoaded); ++ StoreFxn(WMD_IO_GETPROCLOAD, pfnIOGetProcLoad); ++ StoreFxn(WMD_MSG_CREATE, pfnMsgCreate); ++ StoreFxn(WMD_MSG_CREATEQUEUE, pfnMsgCreateQueue); ++ StoreFxn(WMD_MSG_DELETE, pfnMsgDelete); ++ StoreFxn(WMD_MSG_DELETEQUEUE, pfnMsgDeleteQueue); ++ StoreFxn(WMD_MSG_GET, pfnMsgGet); ++ StoreFxn(WMD_MSG_PUT, pfnMsgPut); ++ StoreFxn(WMD_MSG_REGISTERNOTIFY, pfnMsgRegisterNotify); ++ StoreFxn(WMD_MSG_SETQUEUEID, pfnMsgSetQueueId); ++ } ++ /* Add code for any additional functions in newer WMD versions here: */ ++ /* Ensure postcondition: */ ++ DBC_Ensure(pIntfFxns->pfnDevCreate != NULL); ++ DBC_Ensure(pIntfFxns->pfnDevDestroy != NULL); ++ DBC_Ensure(pIntfFxns->pfnDevCntrl != NULL); ++ DBC_Ensure(pIntfFxns->pfnBrdMonitor != NULL); ++ DBC_Ensure(pIntfFxns->pfnBrdStart != NULL); ++ DBC_Ensure(pIntfFxns->pfnBrdStop != NULL); ++ DBC_Ensure(pIntfFxns->pfnBrdStatus != NULL); ++ DBC_Ensure(pIntfFxns->pfnBrdRead != NULL); ++ DBC_Ensure(pIntfFxns->pfnBrdWrite != NULL); ++ DBC_Ensure(pIntfFxns->pfnChnlCreate != NULL); ++ DBC_Ensure(pIntfFxns->pfnChnlDestroy != NULL); ++ DBC_Ensure(pIntfFxns->pfnChnlOpen != NULL); ++ DBC_Ensure(pIntfFxns->pfnChnlClose != NULL); ++ DBC_Ensure(pIntfFxns->pfnChnlAddIOReq != NULL); ++ DBC_Ensure(pIntfFxns->pfnChnlGetIOC != NULL); ++ DBC_Ensure(pIntfFxns->pfnChnlCancelIO != NULL); ++ DBC_Ensure(pIntfFxns->pfnChnlFlushIO != NULL); ++ DBC_Ensure(pIntfFxns->pfnChnlGetInfo != NULL); ++ DBC_Ensure(pIntfFxns->pfnChnlGetMgrInfo != NULL); ++ DBC_Ensure(pIntfFxns->pfnChnlIdle != NULL); ++ DBC_Ensure(pIntfFxns->pfnChnlRegisterNotify != NULL); ++ DBC_Ensure(pIntfFxns->pfnDehCreate != NULL); ++ DBC_Ensure(pIntfFxns->pfnDehDestroy != NULL); ++ DBC_Ensure(pIntfFxns->pfnDehNotify != NULL); ++ DBC_Ensure(pIntfFxns->pfnDehRegisterNotify != NULL); ++ DBC_Ensure(pIntfFxns->pfnDehGetInfo != NULL); ++ DBC_Ensure(pIntfFxns->pfnIOCreate != NULL); ++ DBC_Ensure(pIntfFxns->pfnIODestroy != NULL); ++ DBC_Ensure(pIntfFxns->pfnIOOnLoaded != NULL); ++ DBC_Ensure(pIntfFxns->pfnIOGetProcLoad != NULL); ++ DBC_Ensure(pIntfFxns->pfnMsgSetQueueId != NULL); ++ ++#undef StoreFxn ++} ++ +diff --git a/drivers/dsp/bridge/pmgr/dmm.c b/drivers/dsp/bridge/pmgr/dmm.c +new file mode 100644 +index 0000000..803de93 +--- /dev/null ++++ b/drivers/dsp/bridge/pmgr/dmm.c +@@ -0,0 +1,692 @@ ++/* ++ * dmm.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== dmm.c ======== ++ * Purpose: ++ * The Dynamic Memory Manager (DMM) module manages the DSP Virtual address ++ * space that can be directly mapped to any MPU buffer or memory region ++ * ++ * Public Functions: ++ * DMM_CreateTables ++ * DMM_Create ++ * DMM_Destroy ++ * DMM_Exit ++ * DMM_Init ++ * DMM_MapMemory ++ * DMM_Reset ++ * DMM_ReserveMemory ++ * DMM_UnMapMemory ++ * DMM_UnReserveMemory ++ * ++ * Private Functions: ++ * AddRegion ++ * CreateRegion ++ * GetRegion ++ * GetFreeRegion ++ * GetMappedRegion ++ * ++ * Notes: ++ * Region: Generic memory entitiy having a start address and a size ++ * Chunk: Reserved region ++ * ++ * ++ *! Revision History: ++ *! ================ ++ *! 04-Jun-2008 Hari K : Optimized DMM implementation. Removed linked list ++ *! and instead used Table approach. ++ *! 19-Apr-2004 sb: Integrated Alan's code review updates. ++ *! 17-Mar-2004 ap: Fixed GetRegion for size=0 using tighter bound. ++ *! 20-Feb-2004 sb: Created. ++ *! ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ----------------------------------- Defines, Data Structures, Typedefs */ ++/* Object signatures */ ++#define DMMSIGNATURE 0x004d4d44 /* "DMM" (in reverse) */ ++ ++#define DMM_ADDR_VIRTUAL(a) \ ++ (((struct MapPage *)(a) - pVirtualMappingTable) * PG_SIZE_4K +\ ++ dynMemMapBeg) ++#define DMM_ADDR_TO_INDEX(a) (((a) - dynMemMapBeg) / PG_SIZE_4K) ++ ++/* DMM Mgr */ ++struct DMM_OBJECT { ++ u32 dwSignature; /* Used for object validation */ ++ /* Dmm Lock is used to serialize access mem manager for ++ * multi-threads. */ ++ struct SYNC_CSOBJECT *hDmmLock; /* Lock to access dmm mgr */ ++}; ++ ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask DMM_debugMask = { NULL, NULL }; /* GT trace variable */ ++#endif ++ ++static u32 cRefs; /* module reference count */ ++struct MapPage { ++ u32 RegionSize:15; ++ u32 MappedSize:15; ++ u32 bReserved:1; ++ u32 bMapped:1; ++}; ++ ++/* Create the free list */ ++static struct MapPage *pVirtualMappingTable; ++static u32 iFreeRegion; /* The index of free region */ ++static u32 iFreeSize; ++static u32 *pPhysicalAddrTable; /* Physical address of MPU buffer */ ++static u32 dynMemMapBeg; /* The Beginning of dynamic memory mapping */ ++static u32 TableSize;/* The size of virtual and physical pages tables */ ++ ++/* ----------------------------------- Function Prototypes */ ++static struct MapPage *GetRegion(u32 addr); ++static struct MapPage *GetFreeRegion(u32 aSize); ++static struct MapPage *GetMappedRegion(u32 aAddr); ++#ifdef DSP_DMM_DEBUG ++u32 DMM_MemMapDump(struct DMM_OBJECT *hDmmMgr); ++#endif ++ ++/* ======== DMM_CreateTables ======== ++ * Purpose: ++ * Create table to hold the information of physical address ++ * the buffer pages that is passed by the user, and the table ++ * to hold the information of the virtual memory that is reserved ++ * for DSP. ++ */ ++DSP_STATUS DMM_CreateTables(struct DMM_OBJECT *hDmmMgr, u32 addr, u32 size) ++{ ++ struct DMM_OBJECT *pDmmObj = (struct DMM_OBJECT *)hDmmMgr; ++ DSP_STATUS status = DSP_SOK; ++ ++ GT_3trace(DMM_debugMask, GT_ENTER, ++ "Entered DMM_CreateTables () hDmmMgr %x, addr" ++ " %x, size %x\n", hDmmMgr, addr, size); ++ status = DMM_DeleteTables(pDmmObj); ++ if (DSP_SUCCEEDED(status)) { ++ SYNC_EnterCS(pDmmObj->hDmmLock); ++ dynMemMapBeg = addr; ++ TableSize = (size/PG_SIZE_4K) + 1; ++ /* Create the free list */ ++ pVirtualMappingTable = (struct MapPage *) MEM_Calloc ++ (TableSize*sizeof(struct MapPage), MEM_NONPAGED); ++ if (pVirtualMappingTable == NULL) ++ status = DSP_EMEMORY; ++ else { ++ /* This table will be used ++ * to store the virtual to physical ++ * address translations ++ */ ++ pPhysicalAddrTable = (u32 *)MEM_Calloc ++ (TableSize*sizeof(u32), MEM_NONPAGED); ++ GT_1trace(DMM_debugMask, GT_4CLASS, ++ "DMM_CreateTables: Allocate" ++ "memory for pPhysicalAddrTable=%d entries\n", ++ TableSize); ++ if (pPhysicalAddrTable == NULL) { ++ status = DSP_EMEMORY; ++ GT_0trace(DMM_debugMask, GT_7CLASS, ++ "DMM_CreateTables: Memory allocation for " ++ "pPhysicalAddrTable failed\n"); ++ } else { ++ /* On successful allocation, ++ * all entries are zero ('free') */ ++ iFreeRegion = 0; ++ iFreeSize = TableSize*PG_SIZE_4K; ++ pVirtualMappingTable[0].RegionSize = TableSize; ++ } ++ } ++ SYNC_LeaveCS(pDmmObj->hDmmLock); ++ } else ++ GT_0trace(DMM_debugMask, GT_7CLASS, ++ "DMM_CreateTables: DMM_DeleteTables" ++ "Failure\n"); ++ ++ GT_1trace(DMM_debugMask, GT_4CLASS, "Leaving DMM_CreateTables status" ++ "0x%x\n", status); ++ return status; ++} ++ ++/* ++ * ======== DMM_Create ======== ++ * Purpose: ++ * Create a dynamic memory manager object. ++ */ ++DSP_STATUS DMM_Create(OUT struct DMM_OBJECT **phDmmMgr, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct DMM_MGRATTRS *pMgrAttrs) ++{ ++ struct DMM_OBJECT *pDmmObject = NULL; ++ DSP_STATUS status = DSP_SOK; ++ DBC_Require(cRefs > 0); ++ DBC_Require(phDmmMgr != NULL); ++ ++ GT_3trace(DMM_debugMask, GT_ENTER, ++ "DMM_Create: phDmmMgr: 0x%x hDevObject: " ++ "0x%x pMgrAttrs: 0x%x\n", phDmmMgr, hDevObject, pMgrAttrs); ++ *phDmmMgr = NULL; ++ /* create, zero, and tag a cmm mgr object */ ++ MEM_AllocObject(pDmmObject, struct DMM_OBJECT, DMMSIGNATURE); ++ if (pDmmObject != NULL) { ++ status = SYNC_InitializeCS(&pDmmObject->hDmmLock); ++ if (DSP_SUCCEEDED(status)) ++ *phDmmMgr = pDmmObject; ++ else ++ DMM_Destroy(pDmmObject); ++ } else { ++ GT_0trace(DMM_debugMask, GT_7CLASS, ++ "DMM_Create: Object Allocation " ++ "Failure(DMM Object)\n"); ++ status = DSP_EMEMORY; ++ } ++ GT_2trace(DMM_debugMask, GT_4CLASS, ++ "Leaving DMM_Create status %x pDmmObject %x\n", ++ status, pDmmObject); ++ ++ return status; ++} ++ ++/* ++ * ======== DMM_Destroy ======== ++ * Purpose: ++ * Release the communication memory manager resources. ++ */ ++DSP_STATUS DMM_Destroy(struct DMM_OBJECT *hDmmMgr) ++{ ++ struct DMM_OBJECT *pDmmObj = (struct DMM_OBJECT *)hDmmMgr; ++ DSP_STATUS status = DSP_SOK; ++ ++ GT_1trace(DMM_debugMask, GT_ENTER, ++ "Entered DMM_Destroy () hDmmMgr %x\n", hDmmMgr); ++ DBC_Require(cRefs > 0); ++ if (MEM_IsValidHandle(hDmmMgr, DMMSIGNATURE)) { ++ status = DMM_DeleteTables(pDmmObj); ++ if (DSP_SUCCEEDED(status)) { ++ /* Delete CS & dmm mgr object */ ++ SYNC_DeleteCS(pDmmObj->hDmmLock); ++ MEM_FreeObject(pDmmObj); ++ } else ++ GT_0trace(DMM_debugMask, GT_7CLASS, ++ "DMM_Destroy: DMM_DeleteTables " ++ "Failure\n"); ++ } else ++ status = DSP_EHANDLE; ++ GT_1trace(DMM_debugMask, GT_4CLASS, "Leaving DMM_Destroy status %x\n", ++ status); ++ return status; ++} ++ ++ ++/* ++ * ======== DMM_DeleteTables ======== ++ * Purpose: ++ * Delete DMM Tables. ++ */ ++DSP_STATUS DMM_DeleteTables(struct DMM_OBJECT *hDmmMgr) ++{ ++ struct DMM_OBJECT *pDmmObj = (struct DMM_OBJECT *)hDmmMgr; ++ DSP_STATUS status = DSP_SOK; ++ ++ GT_1trace(DMM_debugMask, GT_ENTER, ++ "Entered DMM_DeleteTables () hDmmMgr %x\n", hDmmMgr); ++ DBC_Require(cRefs > 0); ++ if (MEM_IsValidHandle(hDmmMgr, DMMSIGNATURE)) { ++ /* Delete all DMM tables */ ++ SYNC_EnterCS(pDmmObj->hDmmLock); ++ ++ if (pVirtualMappingTable != NULL) ++ MEM_Free(pVirtualMappingTable); ++ ++ if (pPhysicalAddrTable != NULL) ++ MEM_Free(pPhysicalAddrTable); ++ ++ SYNC_LeaveCS(pDmmObj->hDmmLock); ++ } else ++ status = DSP_EHANDLE; ++ GT_1trace(DMM_debugMask, GT_4CLASS, ++ "Leaving DMM_DeleteTables status %x\n", status); ++ return status; ++} ++ ++ ++ ++ ++/* ++ * ======== DMM_Exit ======== ++ * Purpose: ++ * Discontinue usage of module; free resources when reference count ++ * reaches 0. ++ */ ++void DMM_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ cRefs--; ++ ++ GT_1trace(DMM_debugMask, GT_ENTER, ++ "exiting DMM_Exit, ref count:0x%x\n", cRefs); ++} ++ ++/* ++ * ======== DMM_GetHandle ======== ++ * Purpose: ++ * Return the dynamic memory manager object for this device. ++ * This is typically called from the client process. ++ */ ++DSP_STATUS DMM_GetHandle(DSP_HPROCESSOR hProcessor, ++ OUT struct DMM_OBJECT **phDmmMgr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *hDevObject; ++ ++ GT_2trace(DMM_debugMask, GT_ENTER, ++ "DMM_GetHandle: hProcessor %x, phDmmMgr" ++ "%x\n", hProcessor, phDmmMgr); ++ DBC_Require(cRefs > 0); ++ DBC_Require(phDmmMgr != NULL); ++ if (hProcessor != NULL) ++ status = PROC_GetDevObject(hProcessor, &hDevObject); ++ else ++ hDevObject = DEV_GetFirst(); /* default */ ++ ++ if (DSP_SUCCEEDED(status)) ++ status = DEV_GetDmmMgr(hDevObject, phDmmMgr); ++ ++ GT_2trace(DMM_debugMask, GT_4CLASS, "Leaving DMM_GetHandle status %x, " ++ "*phDmmMgr %x\n", status, phDmmMgr ? *phDmmMgr : NULL); ++ return status; ++} ++ ++/* ++ * ======== DMM_Init ======== ++ * Purpose: ++ * Initializes private state of DMM module. ++ */ ++bool DMM_Init(void) ++{ ++ bool fRetval = true; ++ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++ /* Set the Trace mask */ ++ /*"DM" for Dymanic Memory Manager */ ++ GT_create(&DMM_debugMask, "DM"); ++ } ++ ++ if (fRetval) ++ cRefs++; ++ ++ GT_1trace(DMM_debugMask, GT_ENTER, ++ "Entered DMM_Init, ref count:0x%x\n", cRefs); ++ ++ DBC_Ensure((fRetval && (cRefs > 0)) || (!fRetval && (cRefs >= 0))); ++ ++ pVirtualMappingTable = NULL ; ++ pPhysicalAddrTable = NULL ; ++ TableSize = 0; ++ ++ return fRetval; ++} ++ ++/* ++ * ======== DMM_MapMemory ======== ++ * Purpose: ++ * Add a mapping block to the reserved chunk. DMM assumes that this block ++ * will be mapped in the DSP/IVA's address space. DMM returns an error if a ++ * mapping overlaps another one. This function stores the info that will be ++ * required later while unmapping the block. ++ */ ++DSP_STATUS DMM_MapMemory(struct DMM_OBJECT *hDmmMgr, u32 addr, u32 size) ++{ ++ struct DMM_OBJECT *pDmmObj = (struct DMM_OBJECT *)hDmmMgr; ++ struct MapPage *chunk; ++ DSP_STATUS status = DSP_SOK; ++ ++ GT_3trace(DMM_debugMask, GT_ENTER, ++ "Entered DMM_MapMemory () hDmmMgr %x, " ++ "addr %x, size %x\n", hDmmMgr, addr, size); ++ SYNC_EnterCS(pDmmObj->hDmmLock); ++ /* Find the Reserved memory chunk containing the DSP block to ++ * be mapped */ ++ chunk = (struct MapPage *)GetRegion(addr); ++ if (chunk != NULL) { ++ /* Mark the region 'mapped', leave the 'reserved' info as-is */ ++ chunk->bMapped = true; ++ chunk->MappedSize = (size/PG_SIZE_4K); ++ } else ++ status = DSP_ENOTFOUND; ++ SYNC_LeaveCS(pDmmObj->hDmmLock); ++ GT_2trace(DMM_debugMask, GT_4CLASS, ++ "Leaving DMM_MapMemory status %x, chunk %x\n", ++ status, chunk); ++ return status; ++} ++ ++/* ++ * ======== DMM_ReserveMemory ======== ++ * Purpose: ++ * Reserve a chunk of virtually contiguous DSP/IVA address space. ++ */ ++DSP_STATUS DMM_ReserveMemory(struct DMM_OBJECT *hDmmMgr, u32 size, ++ u32 *pRsvAddr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DMM_OBJECT *pDmmObj = (struct DMM_OBJECT *)hDmmMgr; ++ struct MapPage *node; ++ u32 rsvAddr = 0; ++ u32 rsvSize = 0; ++ ++ GT_3trace(DMM_debugMask, GT_ENTER, ++ "Entered DMM_ReserveMemory () hDmmMgr %x, " ++ "size %x, pRsvAddr %x\n", hDmmMgr, size, pRsvAddr); ++ SYNC_EnterCS(pDmmObj->hDmmLock); ++ ++ /* Try to get a DSP chunk from the free list */ ++ node = GetFreeRegion(size); ++ if (node != NULL) { ++ /* DSP chunk of given size is available. */ ++ rsvAddr = DMM_ADDR_VIRTUAL(node); ++ /* Calculate the number entries to use */ ++ rsvSize = size/PG_SIZE_4K; ++ if (rsvSize < node->RegionSize) { ++ /* Mark remainder of free region */ ++ node[rsvSize].bMapped = false; ++ node[rsvSize].bReserved = false; ++ node[rsvSize].RegionSize = node->RegionSize - rsvSize; ++ node[rsvSize].MappedSize = 0; ++ } ++ /* GetRegion will return first fit chunk. But we only use what ++ is requested. */ ++ node->bMapped = false; ++ node->bReserved = true; ++ node->RegionSize = rsvSize; ++ node->MappedSize = 0; ++ /* Return the chunk's starting address */ ++ *pRsvAddr = rsvAddr; ++ } else ++ /*dSP chunk of given size is not available */ ++ status = DSP_EMEMORY; ++ ++ SYNC_LeaveCS(pDmmObj->hDmmLock); ++ GT_3trace(DMM_debugMask, GT_4CLASS, ++ "Leaving ReserveMemory status %x, rsvAddr" ++ " %x, rsvSize %x\n", status, rsvAddr, rsvSize); ++ return status; ++} ++ ++ ++/* ++ * ======== DMM_UnMapMemory ======== ++ * Purpose: ++ * Remove the mapped block from the reserved chunk. ++ */ ++DSP_STATUS DMM_UnMapMemory(struct DMM_OBJECT *hDmmMgr, u32 addr, u32 *pSize) ++{ ++ struct DMM_OBJECT *pDmmObj = (struct DMM_OBJECT *)hDmmMgr; ++ struct MapPage *chunk; ++ DSP_STATUS status = DSP_SOK; ++ ++ GT_3trace(DMM_debugMask, GT_ENTER, ++ "Entered DMM_UnMapMemory () hDmmMgr %x, " ++ "addr %x, pSize %x\n", hDmmMgr, addr, pSize); ++ SYNC_EnterCS(pDmmObj->hDmmLock); ++ chunk = GetMappedRegion(addr) ; ++ if (chunk == NULL) ++ status = DSP_ENOTFOUND ; ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Unmap the region */ ++ *pSize = chunk->MappedSize * PG_SIZE_4K; ++ chunk->bMapped = false; ++ chunk->MappedSize = 0; ++ } ++ SYNC_LeaveCS(pDmmObj->hDmmLock); ++ GT_3trace(DMM_debugMask, GT_ENTER, ++ "Leaving DMM_UnMapMemory status %x, chunk" ++ " %x, *pSize %x\n", status, chunk, *pSize); ++ ++ return status; ++} ++ ++/* ++ * ======== DMM_UnReserveMemory ======== ++ * Purpose: ++ * Free a chunk of reserved DSP/IVA address space. ++ */ ++DSP_STATUS DMM_UnReserveMemory(struct DMM_OBJECT *hDmmMgr, u32 rsvAddr) ++{ ++ struct DMM_OBJECT *pDmmObj = (struct DMM_OBJECT *)hDmmMgr; ++ struct MapPage *chunk; ++ u32 i; ++ DSP_STATUS status = DSP_SOK; ++ u32 chunkSize; ++ ++ GT_2trace(DMM_debugMask, GT_ENTER, ++ "Entered DMM_UnReserveMemory () hDmmMgr " ++ "%x, rsvAddr %x\n", hDmmMgr, rsvAddr); ++ ++ SYNC_EnterCS(pDmmObj->hDmmLock); ++ ++ /* Find the chunk containing the reserved address */ ++ chunk = GetMappedRegion(rsvAddr); ++ if (chunk == NULL) ++ status = DSP_ENOTFOUND; ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Free all the mapped pages for this reserved region */ ++ i = 0; ++ while (i < chunk->RegionSize) { ++ if (chunk[i].bMapped) { ++ /* Remove mapping from the page tables. */ ++ chunkSize = chunk[i].MappedSize; ++ /* Clear the mapping flags */ ++ chunk[i].bMapped = false; ++ chunk[i].MappedSize = 0; ++ i += chunkSize; ++ } else ++ i++; ++ } ++ /* Clear the flags (mark the region 'free') */ ++ chunk->bReserved = false; ++ /* NOTE: We do NOT coalesce free regions here. ++ * Free regions are coalesced in GetRegion(), as it traverses ++ *the whole mapping table ++ */ ++ } ++ SYNC_LeaveCS(pDmmObj->hDmmLock); ++ GT_2trace(DMM_debugMask, GT_ENTER, ++ "Leaving DMM_UnReserveMemory status %x" ++ " chunk %x\n", status, chunk); ++ return status; ++} ++ ++ ++/* ++ * ======== GetRegion ======== ++ * Purpose: ++ * Returns a region containing the specified memory region ++ */ ++static struct MapPage *GetRegion(u32 aAddr) ++{ ++ struct MapPage *currRegion = NULL; ++ u32 i = 0; ++ ++ GT_1trace(DMM_debugMask, GT_ENTER, "Entered GetRegion () " ++ " aAddr %x\n", aAddr); ++ ++ if (pVirtualMappingTable != NULL) { ++ /* find page mapped by this address */ ++ i = DMM_ADDR_TO_INDEX(aAddr); ++ if (i < TableSize) ++ currRegion = pVirtualMappingTable + i; ++ } ++ GT_3trace(DMM_debugMask, GT_4CLASS, ++ "Leaving GetRegion currRegion %x, iFreeRegion %d\n," ++ "iFreeSize %d\n", currRegion, iFreeRegion, iFreeSize) ; ++ return currRegion; ++} ++ ++/* ++ * ======== GetFreeRegion ======== ++ * Purpose: ++ * Returns the requested free region ++ */ ++static struct MapPage *GetFreeRegion(u32 aSize) ++{ ++ struct MapPage *currRegion = NULL; ++ u32 i = 0; ++ u32 RegionSize = 0; ++ u32 nextI = 0; ++ GT_1trace(DMM_debugMask, GT_ENTER, "Entered GetFreeRegion () " ++ "aSize 0x%x\n", aSize); ++ ++ if (pVirtualMappingTable == NULL) ++ return currRegion; ++ if (aSize > iFreeSize) { ++ /* Find the largest free region ++ * (coalesce during the traversal) */ ++ while (i < TableSize) { ++ RegionSize = pVirtualMappingTable[i].RegionSize; ++ nextI = i+RegionSize; ++ if (pVirtualMappingTable[i].bReserved == false) { ++ /* Coalesce, if possible */ ++ if (nextI < TableSize && ++ pVirtualMappingTable[nextI].bReserved ++ == false) { ++ pVirtualMappingTable[i].RegionSize += ++ pVirtualMappingTable[nextI].RegionSize; ++ continue; ++ } ++ RegionSize *= PG_SIZE_4K; ++ if (RegionSize > iFreeSize) { ++ iFreeRegion = i; ++ iFreeSize = RegionSize; ++ } ++ } ++ i = nextI; ++ } ++ } ++ if (aSize <= iFreeSize) { ++ currRegion = pVirtualMappingTable + iFreeRegion; ++ iFreeRegion += (aSize / PG_SIZE_4K); ++ iFreeSize -= aSize; ++ } ++ return currRegion; ++} ++ ++/* ++ * ======== GetMappedRegion ======== ++ * Purpose: ++ * Returns the requestedmapped region ++ */ ++static struct MapPage *GetMappedRegion(u32 aAddr) ++{ ++ u32 i = 0; ++ struct MapPage *currRegion = NULL; ++ GT_1trace(DMM_debugMask, GT_ENTER, "Entered GetMappedRegion () " ++ "aAddr 0x%x\n", aAddr); ++ ++ if (pVirtualMappingTable == NULL) ++ return currRegion; ++ ++ i = DMM_ADDR_TO_INDEX(aAddr); ++ if (i < TableSize && (pVirtualMappingTable[i].bMapped || ++ pVirtualMappingTable[i].bReserved)) ++ currRegion = pVirtualMappingTable + i; ++ return currRegion; ++} ++ ++/* ++ * ======== DMM_GetPhysicalAddrTable ======== ++ * Purpose: ++ * Returns the physical table address ++ */ ++u32 *DMM_GetPhysicalAddrTable(void) ++{ ++ GT_1trace(DMM_debugMask, GT_ENTER, "Entered " ++ "DMM_GetPhysicalAddrTable()- pPhysicalAddrTable 0x%x\n", ++ pPhysicalAddrTable); ++ return pPhysicalAddrTable; ++} ++ ++#ifdef DSP_DMM_DEBUG ++u32 DMM_MemMapDump(struct DMM_OBJECT *hDmmMgr) ++{ ++ struct MapPage *curNode = NULL; ++ u32 i; ++ u32 freemem = 0; ++ u32 bigsize = 0; ++ ++ SYNC_EnterCS(hDmmMgr->hDmmLock); ++ ++ if (pVirtualMappingTable != NULL) { ++ for (i = 0; i < TableSize; i += ++ pVirtualMappingTable[i].RegionSize) { ++ curNode = pVirtualMappingTable + i; ++ if (curNode->bReserved == TRUE) { ++ /*printk("RESERVED size = 0x%x, " ++ "Map size = 0x%x\n", ++ (curNode->RegionSize * PG_SIZE_4K), ++ (curNode->bMapped == false) ? 0 : ++ (curNode->MappedSize * PG_SIZE_4K)); ++*/ ++ } else { ++/* printk("UNRESERVED size = 0x%x\n", ++ (curNode->RegionSize * PG_SIZE_4K)); ++*/ ++ freemem += (curNode->RegionSize * PG_SIZE_4K); ++ if (curNode->RegionSize > bigsize) ++ bigsize = curNode->RegionSize; ++ } ++ } ++ } ++ printk(KERN_INFO "Total DSP VA FREE memory = %d Mbytes\n", ++ freemem/(1024*1024)); ++ printk(KERN_INFO "Total DSP VA USED memory= %d Mbytes \n", ++ (((TableSize * PG_SIZE_4K)-freemem))/(1024*1024)); ++ printk(KERN_INFO "DSP VA - Biggest FREE block = %d Mbytes \n\n", ++ (bigsize*PG_SIZE_4K/(1024*1024))); ++ SYNC_LeaveCS(hDmmMgr->hDmmLock); ++ ++ return 0; ++} ++#endif +diff --git a/drivers/dsp/bridge/pmgr/io.c b/drivers/dsp/bridge/pmgr/io.c +new file mode 100644 +index 0000000..cdfe0dc +--- /dev/null ++++ b/drivers/dsp/bridge/pmgr/io.c +@@ -0,0 +1,205 @@ ++/* ++ * io.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== io.c ======== ++ * Description: ++ * IO manager interface: Manages IO between CHNL and MSG. ++ * ++ * Public Functions: ++ * IO_Create ++ * IO_Destroy ++ * IO_Exit ++ * IO_Init ++ * IO_OnLoaded ++ * ++ * Notes: ++ * This interface is basically a pass through to the WMD IO functions. ++ * ++ *! Revision History: ++ *! ================ ++ *! 24-Feb-2003 swa PMGR Code review comments incorporated. ++ *! 04-Apr-2001 rr WSX_STATUS initialized in IO_Create. ++ *! 07-Nov-2000 jeh Created. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++ ++/* ----------------------------------- This */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Globals */ ++static u32 cRefs; ++ ++#if GT_TRACE ++static struct GT_Mask IO_DebugMask = { NULL, NULL }; /* WCD IO Mask */ ++#endif ++ ++/* ++ * ======== IO_Create ======== ++ * Purpose: ++ * Create an IO manager object, responsible for managing IO between ++ * CHNL and MSG ++ */ ++DSP_STATUS IO_Create(OUT struct IO_MGR **phIOMgr, struct DEV_OBJECT *hDevObject, ++ IN CONST struct IO_ATTRS *pMgrAttrs) ++{ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ struct IO_MGR *hIOMgr = NULL; ++ struct IO_MGR_ *pIOMgr = NULL; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phIOMgr != NULL); ++ DBC_Require(pMgrAttrs != NULL); ++ ++ GT_3trace(IO_DebugMask, GT_ENTER, "Entered IO_Create: phIOMgr: 0x%x\t " ++ "hDevObject: 0x%x\tpMgrAttrs: 0x%x\n", ++ phIOMgr, hDevObject, pMgrAttrs); ++ ++ *phIOMgr = NULL; ++ ++ /* A memory base of 0 implies no memory base: */ ++ if ((pMgrAttrs->dwSMBase != 0) && (pMgrAttrs->uSMLength == 0)) { ++ status = CHNL_E_INVALIDMEMBASE; ++ GT_0trace(IO_DebugMask, GT_7CLASS, ++ "IO_Create:Invalid Mem Base\n"); ++ } ++ ++ if (pMgrAttrs->uWordSize == 0) { ++ status = CHNL_E_INVALIDWORDSIZE; ++ GT_0trace(IO_DebugMask, GT_7CLASS, ++ "IO_Create:Invalid Word size\n"); ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ DEV_GetIntfFxns(hDevObject, &pIntfFxns); ++ ++ /* Let WMD channel module finish the create: */ ++ status = (*pIntfFxns->pfnIOCreate)(&hIOMgr, hDevObject, ++ pMgrAttrs); ++ ++ if (DSP_SUCCEEDED(status)) { ++ pIOMgr = (struct IO_MGR_ *) hIOMgr; ++ pIOMgr->pIntfFxns = pIntfFxns; ++ pIOMgr->hDevObject = hDevObject; ++ ++ /* Return the new channel manager handle: */ ++ *phIOMgr = hIOMgr; ++ GT_1trace(IO_DebugMask, GT_1CLASS, ++ "IO_Create: Success hIOMgr: 0x%x\n", ++ hIOMgr); ++ } ++ } ++ ++ GT_2trace(IO_DebugMask, GT_ENTER, ++ "Exiting IO_Create: hIOMgr: 0x%x, status:" ++ " 0x%x\n", hIOMgr, status); ++ ++ return status; ++} ++ ++/* ++ * ======== IO_Destroy ======== ++ * Purpose: ++ * Delete IO manager. ++ */ ++DSP_STATUS IO_Destroy(struct IO_MGR *hIOMgr) ++{ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ struct IO_MGR_ *pIOMgr = (struct IO_MGR_ *)hIOMgr; ++ DSP_STATUS status; ++ ++ DBC_Require(cRefs > 0); ++ ++ GT_1trace(IO_DebugMask, GT_ENTER, "Entered IO_Destroy: hIOMgr: 0x%x\n", ++ hIOMgr); ++ ++ pIntfFxns = pIOMgr->pIntfFxns; ++ ++ /* Let WMD channel module destroy the IO_MGR: */ ++ status = (*pIntfFxns->pfnIODestroy) (hIOMgr); ++ ++ GT_2trace(IO_DebugMask, GT_ENTER, ++ "Exiting IO_Destroy: pIOMgr: 0x%x, status:" ++ " 0x%x\n", pIOMgr, status); ++ return status; ++} ++ ++/* ++ * ======== IO_Exit ======== ++ * Purpose: ++ * Discontinue usage of the IO module. ++ */ ++void IO_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ cRefs--; ++ ++ GT_1trace(IO_DebugMask, GT_5CLASS, ++ "Entered IO_Exit, ref count: 0x%x\n", cRefs); ++ ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== IO_Init ======== ++ * Purpose: ++ * Initialize the IO module's private state. ++ */ ++bool IO_Init(void) ++{ ++ bool fRetval = true; ++ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++ DBC_Assert(!IO_DebugMask.flags); ++ GT_create(&IO_DebugMask, "IO"); /* "IO" for IO */ ++ } ++ ++ if (fRetval) ++ cRefs++; ++ ++ ++ GT_1trace(IO_DebugMask, GT_5CLASS, ++ "Entered IO_Init, ref count: 0x%x\n", cRefs); ++ ++ DBC_Ensure((fRetval && (cRefs > 0)) || (!fRetval && (cRefs >= 0))); ++ ++ return fRetval; ++} +diff --git a/drivers/dsp/bridge/pmgr/ioobj.h b/drivers/dsp/bridge/pmgr/ioobj.h +new file mode 100644 +index 0000000..f473a63 +--- /dev/null ++++ b/drivers/dsp/bridge/pmgr/ioobj.h +@@ -0,0 +1,52 @@ ++/* ++ * ioobj.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== ioobj.h ======== ++ * Description: ++ * Structure subcomponents of channel class library IO objects which ++ * are exposed to class driver from mini-driver. ++ * ++ * Public Functions: ++ * None. ++ * ++ *! Revision History: ++ *! ================ ++ *! 24-Feb-2003 swa PMGR Code review comments incorporated. ++ *! 01/16/97 gp: Created from chnlpriv.h ++ */ ++ ++#ifndef IOOBJ_ ++#define IOOBJ_ ++ ++#include ++#include ++ ++/* ++ * This struct is the first field in a IO_MGR struct, as implemented in ++ * a WMD channel class library. Other, implementation specific fields ++ * follow this structure in memory. ++ */ ++struct IO_MGR_ { ++ /* These must be the first fields in a IO_MGR struct: */ ++ u32 dwSignature; /* Used for object validation. */ ++ struct WMD_DEV_CONTEXT *hWmdContext; /* WMD device context. */ ++ struct WMD_DRV_INTERFACE *pIntfFxns; /* Function interface to WMD. */ ++ struct DEV_OBJECT *hDevObject; /* Device this board represents. */ ++} ; ++ ++#endif /* IOOBJ_ */ +diff --git a/drivers/dsp/bridge/pmgr/msg.c b/drivers/dsp/bridge/pmgr/msg.c +new file mode 100644 +index 0000000..bbf5174 +--- /dev/null ++++ b/drivers/dsp/bridge/pmgr/msg.c +@@ -0,0 +1,173 @@ ++/* ++ * msg.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== msg.c ======== ++ * Description: ++ * DSP/BIOS Bridge MSG Module. ++ * ++ * Public Functions: ++ * MSG_Create ++ * MSG_Delete ++ * MSG_Exit ++ * MSG_Init ++ * ++ *! Revision History: ++ *! ================= ++ *! 24-Feb-2003 swa PMGR Code review comments incorporated. ++ *! 15-May-2001 ag Changed SUCCEEDED to DSP_SUCCEEDED. ++ *! 16-Feb-2001 jeh Fixed some comments. ++ *! 15-Dec-2000 rr MSG_Create returns DSP_EFAIL if pfnMsgCreate fails. ++ *! 12-Sep-2000 jeh Created. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++ ++/* ----------------------------------- Mini Driver */ ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++ ++/* ----------------------------------- This */ ++#include ++#include ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask MSG_debugMask = { NULL, NULL }; /* GT trace variable */ ++#endif ++static u32 cRefs; /* module reference count */ ++ ++/* ++ * ======== MSG_Create ======== ++ * Purpose: ++ * Create an object to manage message queues. Only one of these objects ++ * can exist per device object. ++ */ ++DSP_STATUS MSG_Create(OUT struct MSG_MGR **phMsgMgr, ++ struct DEV_OBJECT *hDevObject, MSG_ONEXIT msgCallback) ++{ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ struct MSG_MGR_ *pMsgMgr; ++ struct MSG_MGR *hMsgMgr; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phMsgMgr != NULL); ++ DBC_Require(msgCallback != NULL); ++ DBC_Require(hDevObject != NULL); ++ ++ GT_3trace(MSG_debugMask, GT_ENTER, "MSG_Create: phMsgMgr: 0x%x\t" ++ "hDevObject: 0x%x\tmsgCallback: 0x%x\n", ++ phMsgMgr, hDevObject, msgCallback); ++ ++ *phMsgMgr = NULL; ++ ++ DEV_GetIntfFxns(hDevObject, &pIntfFxns); ++ ++ /* Let WMD message module finish the create: */ ++ status = (*pIntfFxns->pfnMsgCreate)(&hMsgMgr, hDevObject, msgCallback); ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Fill in WCD message module's fields of the MSG_MGR ++ * structure */ ++ pMsgMgr = (struct MSG_MGR_ *)hMsgMgr; ++ pMsgMgr->pIntfFxns = pIntfFxns; ++ ++ /* Finally, return the new message manager handle: */ ++ *phMsgMgr = hMsgMgr; ++ GT_1trace(MSG_debugMask, GT_1CLASS, ++ "MSG_Create: Success pMsgMgr: 0x%x\n", pMsgMgr); ++ } else { ++ status = DSP_EFAIL; ++ } ++ return status; ++} ++ ++/* ++ * ======== MSG_Delete ======== ++ * Purpose: ++ * Delete a MSG manager allocated in MSG_Create(). ++ */ ++void MSG_Delete(struct MSG_MGR *hMsgMgr) ++{ ++ struct MSG_MGR_ *pMsgMgr = (struct MSG_MGR_ *)hMsgMgr; ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(pMsgMgr, MSGMGR_SIGNATURE)); ++ ++ GT_1trace(MSG_debugMask, GT_ENTER, "MSG_Delete: hMsgMgr: 0x%x\n", ++ hMsgMgr); ++ ++ pIntfFxns = pMsgMgr->pIntfFxns; ++ ++ /* Let WMD message module destroy the MSG_MGR: */ ++ (*pIntfFxns->pfnMsgDelete)(hMsgMgr); ++ ++ DBC_Ensure(!MEM_IsValidHandle(pMsgMgr, MSGMGR_SIGNATURE)); ++} ++ ++/* ++ * ======== MSG_Exit ======== ++ */ ++void MSG_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ cRefs--; ++ GT_1trace(MSG_debugMask, GT_5CLASS, ++ "Entered MSG_Exit, ref count: 0x%x\n", cRefs); ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== MSG_Init ======== ++ */ ++bool MSG_Init(void) ++{ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++ DBC_Assert(!MSG_debugMask.flags); ++ GT_create(&MSG_debugMask, "MG"); /* "MG" for MsG */ ++ } ++ ++ cRefs++; ++ ++ GT_1trace(MSG_debugMask, GT_5CLASS, "MSG_Init(), ref count: 0x%x\n", ++ cRefs); ++ ++ DBC_Ensure(cRefs >= 0); ++ ++ return true; ++} ++ +diff --git a/drivers/dsp/bridge/pmgr/msgobj.h b/drivers/dsp/bridge/pmgr/msgobj.h +new file mode 100644 +index 0000000..63d025b +--- /dev/null ++++ b/drivers/dsp/bridge/pmgr/msgobj.h +@@ -0,0 +1,52 @@ ++/* ++ * msgobj.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== msgobj.h ======== ++ * Description: ++ * Structure subcomponents of channel class library MSG objects which ++ * are exposed to class driver from mini-driver. ++ * ++ * Public Functions: ++ * None. ++ * ++ *! Revision History: ++ *! ================ ++ *! 24-Feb-2003 swa PMGR Code review comments incorporated. ++ *! 17-Nov-2000 jeh Created. ++ */ ++ ++#ifndef MSGOBJ_ ++#define MSGOBJ_ ++ ++#include ++ ++#include ++ ++/* ++ * This struct is the first field in a MSG_MGR struct, as implemented in ++ * a WMD channel class library. Other, implementation specific fields ++ * follow this structure in memory. ++ */ ++struct MSG_MGR_ { ++ /* The first two fields must match those in msgobj.h */ ++ u32 dwSignature; ++ struct WMD_DRV_INTERFACE *pIntfFxns; /* Function interface to WMD. */ ++}; ++ ++#endif /* MSGOBJ_ */ ++ +diff --git a/drivers/dsp/bridge/pmgr/wcd.c b/drivers/dsp/bridge/pmgr/wcd.c +new file mode 100644 +index 0000000..859043d +--- /dev/null ++++ b/drivers/dsp/bridge/pmgr/wcd.c +@@ -0,0 +1,1647 @@ ++/* ++ * wcd.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== wcd.c ======== ++ * Description: ++ * Common WCD functions, also includes the wrapper ++ * functions called directly by the DeviceIOControl interface. ++ * ++ * Public Functions: ++ * WCD_CallDevIOCtl ++ * WCD_Init ++ * WCD_InitComplete2 ++ * WCD_Exit ++ * WRAP_* ++ * ++ *! Revision History: ++ *! ================ ++ *! 29-Apr-2004 hp Call PROC_AutoStart only for DSP device ++ *! 19-Apr-2004 sb Aligned DMM definitions with Symbian ++ *! 08-Mar-2004 sb Added the Dynamic Memory Mapping APIs ++ *! 03-Apr-2003 sb Process environment pointer in PROCWRAP_Load ++ *! 24-Feb-2003 swa PMGR Code review comments incorporated. ++ *! 30-Jan-2002 ag CMMWRAP_AllocBuf name changed to CMMWRAP_CallocBuf ++ *! 15-Jan-2002 ag Added actual bufSize param to STRMWRAP_Reclaim[issue]. ++ *! 14-Dec-2001 rr ARGS_NODE_CONNECT maps the pAttr. ++ *! 03-Oct-2001 rr ARGS_NODE_ALLOCMSGBUF/FREEMSGBUF maps the pAttr. ++ *! 10-Sep-2001 ag Added CMD_CMM_GETHANDLE. ++ *! 23-Apr-2001 jeh Pass pStatus to NODE_Terminate. ++ *! 11-Apr-2001 jeh STRMWRAP_Reclaim embedded pointer is mapped and unmapped. ++ *! 13-Feb-2001 kc: DSP/BIOS Bridge name updates. ++ *! 06-Dec-2000 jeh WRAP_MAP2CALLER pointers in RegisterNotify calls. ++ *! 05-Dec-2000 ag: Removed MAP2CALLER in NODEWRAP_FreeMsgBuf(). ++ *! 22-Nov-2000 kc: Added MGRWRAP_GetPerf_Data(). ++ *! 20-Nov-2000 jeh Added MSG_Init()/MSG_Exit(), IO_Init()/IO_Exit(). ++ *! WRAP pointers to handles for PROC_Attach, NODE_Allocate. ++ *! 27-Oct-2000 jeh Added NODEWRAP_AllocMsgBuf, NODEWRAP_FreeMsgBuf. Removed ++ *! NODEWRAP_GetMessageStream. ++ *! 12-Oct-2000 ag: Added user CMM wrappers. ++ *! 05-Oct-2000 rr: WcdInitComplete2 will fail even if one BRD or PROC ++ *! AutoStart fails. ++ *! 25-Sep-2000 rr: Updated to Version 0.9 ++ *! 13-Sep-2000 jeh Pass ARGS_NODE_CONNECT.pAttrs to NODE_Connect(). ++ *! 11-Aug-2000 rr: Part of node enabled. ++ *! 31-Jul-2000 rr: UTIL_Wrap and MEM_Wrap added to RM. ++ *! 27-Jul-2000 rr: PROCWRAP, NODEWRAP and STRMWRAP implemented. ++ *! STRM and some NODE Wrappers are not implemented. ++ *! 27-Jun-2000 rr: MGRWRAP fxns added.IFDEF to build for PM or DSP/BIOS Bridge ++ *! 08-Feb-2000 rr File name changed to wcd.c ++ *! 03-Feb-2000 rr: Module initialization are done by SERVICES init. GT Class ++ *! changes for module init/exit fxns. ++ *! 24-Jan-2000 rr: Merged with Scott's code. ++ *! 21-Jan-1999 sg: Changed ARGS_CHNL_GETMODE field name from pdwMode to pMode. ++ *! 17-Jan-2000 rr: BRD_GetStatus does WRAP_MAP2CALLER for state. ++ *! 14-Dec-1999 ag: Removed _MAP2CALLER in CHNL_GetMgr(). ++ *! 13-Dec-1999 rr: BRDWRAP_GetSymbol, BRDWRAP_GetTrace uses WRAP_MAP2CALLER ++ *! macros.BRDWRAP_Load maps and unmaps embedded pointers. ++ *! 10-Dec-1999 ag: User CHNL bufs mapped in _AddIOReq & _GetIOCompletion. ++ *! 09-Dec-1999 rr: BRDWRAP_Open and CHNLWRAP_GetMgr does not map ++ *! pointer as there was a change in config.c ++ *! 06-Dec-1999 rr: BRD_Read and Write Maps the buf pointers. ++ *! 03-Dec-1999 rr: CHNLWRAP_GetMgr and BRDWRAP_Open maps hDevNode pointer. ++ *! WCD_InitComplete2 Included for BRD_AutoStart. ++ *! 16-Nov-1999 ag: Map buf to process in CHNLWRAP_AllocBuffer(). ++ *! CHNL_GetMgr() Mapping Fix. ++ *! 10-Nov-1999 ag: Removed unnecessary calls to WRAP_MAP2CALLER. ++ *! 08-Nov-1999 kc: Added MEMRY & enabled BRD_IOCtl for tests. ++ *! 29-Oct-1999 ag: Added CHNL. ++ *! 29-Oct-1999 kc: Added trace statements; added ptr mapping; updated ++ *! use of UTIL module API. ++ *! 29-Oct-1999 rr: Wrapper functions does the Mapping of the Pointers. ++ *! in WinCE all the explicit pointers will be converted ++ *! by the OS during interprocess but not the embedded pointers. ++ *! 16-Oct-1999 kc: Code review cleanup. ++ *! 07-Oct-1999 kc: Added UTILWRAP_TestDll() to run PM test harness. See ++ *! /src/doc/pmtest.doc for more detail. ++ *! 09-Sep-1999 rr: After exactly two years(!). Adopted for WinCE. GT Enabled. ++ *! 09-Sep-1997 gp: Created. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* ----------------------------------- Resource Manager */ ++#include ++#include ++#include ++#include ++ ++ ++/* ----------------------------------- Others */ ++#include ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++#include ++ ++#ifndef RES_CLEANUP_DISABLE ++#include ++#endif ++ ++/* ----------------------------------- Defines, Data Structures, Typedefs */ ++#define MAX_TRACEBUFLEN 255 ++#define MAX_LOADARGS 16 ++#define MAX_NODES 64 ++#define MAX_STREAMS 16 ++#define MAX_BUFS 64 ++ ++/* Following two macros should ideally have do{}while(0) */ ++ ++#define cp_fm_usr(dest, src, status, elements) \ ++ if (DSP_SUCCEEDED(status)) {\ ++ if (unlikely(src == NULL) || \ ++ unlikely(copy_from_user(dest, src, elements * sizeof(*(dest))))) { \ ++ GT_1trace(WCD_debugMask, GT_7CLASS, \ ++ "copy_from_user failed, src=0x%x\n", src); \ ++ status = DSP_EPOINTER ; \ ++ } \ ++ } ++ ++#define cp_to_usr(dest, src, status, elements) \ ++ if (DSP_SUCCEEDED(status)) {\ ++ if (unlikely(dest == NULL) || \ ++ unlikely(copy_to_user(dest, src, elements * sizeof(*(src))))) { \ ++ GT_1trace(WCD_debugMask, GT_7CLASS, \ ++ "copy_to_user failed, dest=0x%x\n", dest); \ ++ status = DSP_EPOINTER ;\ ++ } \ ++ } ++ ++/* Device IOCtl function pointer */ ++struct WCD_Cmd { ++ u32(*fxn)(union Trapped_Args *args); ++ u32 dwIndex; ++} ; ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask WCD_debugMask = { NULL, NULL }; /* Core VxD Mask */ ++#endif ++static u32 WCD_cRefs; ++ ++/* ++ * Function table. ++ * The order of these functions MUST be the same as the order of the command ++ * numbers defined in wcdioctl.h This is how an IOCTL number in user mode ++ * turns into a function call in kernel mode. ++ */ ++static struct WCD_Cmd WCD_cmdTable[] = { ++ /* MGR module */ ++ {MGRWRAP_EnumNode_Info, CMD_MGR_ENUMNODE_INFO_OFFSET}, ++ {MGRWRAP_EnumProc_Info, CMD_MGR_ENUMPROC_INFO_OFFSET}, ++ {MGRWRAP_RegisterObject, CMD_MGR_REGISTEROBJECT_OFFSET}, ++ {MGRWRAP_UnregisterObject, CMD_MGR_UNREGISTEROBJECT_OFFSET}, ++ {MGRWRAP_WaitForBridgeEvents, CMD_MGR_WAIT_OFFSET}, ++#ifndef RES_CLEANUP_DISABLE ++ {MGRWRAP_GetProcessResourcesInfo, CMD_MGR_RESOUCES_OFFSET}, ++#endif ++ /* PROC Module */ ++ {PROCWRAP_Attach, CMD_PROC_ATTACH_OFFSET}, ++ {PROCWRAP_Ctrl, CMD_PROC_CTRL_OFFSET}, ++ {PROCWRAP_Detach, CMD_PROC_DETACH_OFFSET}, ++ {PROCWRAP_EnumNode_Info, CMD_PROC_ENUMNODE_OFFSET}, ++ {PROCWRAP_EnumResources, CMD_PROC_ENUMRESOURCES_OFFSET}, ++ {PROCWRAP_GetState, CMD_PROC_GETSTATE_OFFSET}, ++ {PROCWRAP_GetTrace, CMD_PROC_GETTRACE_OFFSET}, ++ {PROCWRAP_Load, CMD_PROC_LOAD_OFFSET}, ++ {PROCWRAP_RegisterNotify, CMD_PROC_REGISTERNOTIFY_OFFSET}, ++ {PROCWRAP_Start, CMD_PROC_START_OFFSET}, ++ {PROCWRAP_ReserveMemory, CMD_PROC_RSVMEM_OFFSET}, ++ {PROCWRAP_UnReserveMemory, CMD_PROC_UNRSVMEM_OFFSET}, ++ {PROCWRAP_Map, CMD_PROC_MAPMEM_OFFSET}, ++ {PROCWRAP_UnMap, CMD_PROC_UNMAPMEM_OFFSET}, ++ {PROCWRAP_FlushMemory, CMD_PROC_FLUSHMEMORY_OFFSET}, ++ {PROCWRAP_Stop, CMD_PROC_STOP_OFFSET}, ++ {PROCWRAP_InvalidateMemory, CMD_PROC_INVALIDATEMEMORY_OFFSET}, ++ /* NODE Module */ ++ {NODEWRAP_Allocate, CMD_NODE_ALLOCATE_OFFSET}, ++ {NODEWRAP_AllocMsgBuf, CMD_NODE_ALLOCMSGBUF_OFFSET}, ++ {NODEWRAP_ChangePriority, CMD_NODE_CHANGEPRIORITY_OFFSET}, ++ {NODEWRAP_Connect, CMD_NODE_CONNECT_OFFSET}, ++ {NODEWRAP_Create, CMD_NODE_CREATE_OFFSET}, ++ {NODEWRAP_Delete, CMD_NODE_DELETE_OFFSET}, ++ {NODEWRAP_FreeMsgBuf, CMD_NODE_FREEMSGBUF_OFFSET}, ++ {NODEWRAP_GetAttr, CMD_NODE_GETATTR_OFFSET}, ++ {NODEWRAP_GetMessage, CMD_NODE_GETMESSAGE_OFFSET}, ++ {NODEWRAP_Pause, CMD_NODE_PAUSE_OFFSET}, ++ {NODEWRAP_PutMessage, CMD_NODE_PUTMESSAGE_OFFSET}, ++ {NODEWRAP_RegisterNotify, CMD_NODE_REGISTERNOTIFY_OFFSET}, ++ {NODEWRAP_Run, CMD_NODE_RUN_OFFSET}, ++ {NODEWRAP_Terminate, CMD_NODE_TERMINATE_OFFSET}, ++ {NODEWRAP_GetUUIDProps, CMD_NODE_GETUUIDPROPS_OFFSET}, ++ /* STRM wrapper functions */ ++ {STRMWRAP_AllocateBuffer, CMD_STRM_ALLOCATEBUFFER_OFFSET}, ++ {STRMWRAP_Close, CMD_STRM_CLOSE_OFFSET}, ++ {STRMWRAP_FreeBuffer, CMD_STRM_FREEBUFFER_OFFSET}, ++ {STRMWRAP_GetEventHandle, CMD_STRM_GETEVENTHANDLE_OFFSET}, ++ {STRMWRAP_GetInfo, CMD_STRM_GETINFO_OFFSET}, ++ {STRMWRAP_Idle, CMD_STRM_IDLE_OFFSET}, ++ {STRMWRAP_Issue, CMD_STRM_ISSUE_OFFSET}, ++ {STRMWRAP_Open, CMD_STRM_OPEN_OFFSET}, ++ {STRMWRAP_Reclaim, CMD_STRM_RECLAIM_OFFSET}, ++ {STRMWRAP_RegisterNotify, CMD_STRM_REGISTERNOTIFY_OFFSET}, ++ {STRMWRAP_Select, CMD_STRM_SELECT_OFFSET}, ++ /* CMM module */ ++ {CMMWRAP_CallocBuf, CMD_CMM_ALLOCBUF_OFFSET}, ++ {CMMWRAP_FreeBuf, CMD_CMM_FREEBUF_OFFSET}, ++ {CMMWRAP_GetHandle, CMD_CMM_GETHANDLE_OFFSET}, ++ {CMMWRAP_GetInfo, CMD_CMM_GETINFO_OFFSET} ++}; ++ ++/* ++ * ======== WCD_CallDevIOCtl ======== ++ * Purpose: ++ * Call the (wrapper) function for the corresponding WCD IOCTL. ++ */ ++inline DSP_STATUS WCD_CallDevIOCtl(u32 cmd, union Trapped_Args *args, ++ u32 *pResult) ++{ ++ if ((cmd < (sizeof(WCD_cmdTable) / sizeof(struct WCD_Cmd)))) { ++ /* make the fxn call via the cmd table */ ++ *pResult = (*WCD_cmdTable[cmd].fxn) (args); ++ return DSP_SOK; ++ } else { ++ return DSP_EINVALIDARG; ++ } ++} ++ ++/* ++ * ======== WCD_Exit ======== ++ */ ++void WCD_Exit(void) ++{ ++ DBC_Require(WCD_cRefs > 0); ++ WCD_cRefs--; ++ GT_1trace(WCD_debugMask, GT_5CLASS, ++ "Entered WCD_Exit, ref count: 0x%x\n", WCD_cRefs); ++ if (WCD_cRefs == 0) { ++ /* Release all WCD modules initialized in WCD_Init(). */ ++ COD_Exit(); ++ DEV_Exit(); ++ CHNL_Exit(); ++ MSG_Exit(); ++ IO_Exit(); ++ STRM_Exit(); ++ NTFY_Exit(); ++ DISP_Exit(); ++ NODE_Exit(); ++ PROC_Exit(); ++ MGR_Exit(); ++ RMM_exit(); ++ DRV_Exit(); ++ SERVICES_Exit(); ++ } ++ DBC_Ensure(WCD_cRefs >= 0); ++} ++ ++/* ++ * ======== WCD_Init ======== ++ * Purpose: ++ * Module initialization is done by SERVICES Init. ++ */ ++bool WCD_Init(void) ++{ ++ bool fInit = true; ++ bool fDRV, fDEV, fCOD, fSERVICES, fCHNL, fMSG, fIO; ++ bool fMGR, fPROC, fNODE, fDISP, fNTFY, fSTRM, fRMM; ++#ifdef DEBUG ++ /* runtime check of Device IOCtl array. */ ++ u32 i; ++ for (i = 1; i < (sizeof(WCD_cmdTable) / sizeof(struct WCD_Cmd)); i++) ++ DBC_Assert(WCD_cmdTable[i - 1].dwIndex == i); ++ ++#endif ++ if (WCD_cRefs == 0) { ++ /* initialize all SERVICES modules */ ++ fSERVICES = SERVICES_Init(); ++ /* initialize debugging module */ ++ DBC_Assert(!WCD_debugMask.flags); ++ GT_create(&WCD_debugMask, "CD"); /* CD for class driver */ ++ /* initialize class driver and other modules */ ++ fDRV = DRV_Init(); ++ fMGR = MGR_Init(); ++ fPROC = PROC_Init(); ++ fNODE = NODE_Init(); ++ fDISP = DISP_Init(); ++ fNTFY = NTFY_Init(); ++ fSTRM = STRM_Init(); ++ fRMM = RMM_init(); ++ fCHNL = CHNL_Init(); ++ fMSG = MSG_Init(); ++ fIO = IO_Init(); ++ fDEV = DEV_Init(); ++ fCOD = COD_Init(); ++ fInit = fSERVICES && fDRV && fDEV && fCHNL && fCOD && ++ fMSG && fIO; ++ fInit = fInit && fMGR && fPROC && fRMM; ++ if (!fInit) { ++ if (fSERVICES) ++ SERVICES_Exit(); ++ ++ if (fDRV) ++ DRV_Exit(); ++ ++ if (fMGR) ++ MGR_Exit(); ++ ++ if (fSTRM) ++ STRM_Exit(); ++ ++ if (fPROC) ++ PROC_Exit(); ++ ++ if (fNODE) ++ NODE_Exit(); ++ ++ if (fDISP) ++ DISP_Exit(); ++ ++ if (fNTFY) ++ NTFY_Exit(); ++ ++ if (fCHNL) ++ CHNL_Exit(); ++ ++ if (fMSG) ++ MSG_Exit(); ++ ++ if (fIO) ++ IO_Exit(); ++ ++ if (fDEV) ++ DEV_Exit(); ++ ++ if (fCOD) ++ COD_Exit(); ++ ++ if (fRMM) ++ RMM_exit(); ++ ++ } ++ } ++ if (fInit) ++ WCD_cRefs++; ++ ++ GT_1trace(WCD_debugMask, GT_5CLASS, ++ "Entered WCD_Init, ref count: 0x%x\n", WCD_cRefs); ++ return fInit; ++} ++ ++/* ++ * ======== WCD_InitComplete2 ======== ++ * Purpose: ++ * Perform any required WCD, and WMD initialization which ++ * cannot not be performed in WCD_Init() or DEV_StartDevice() due ++ * to the fact that some services are not yet ++ * completely initialized. ++ * Parameters: ++ * Returns: ++ * DSP_SOK: Allow this device to load ++ * DSP_EFAIL: Failure. ++ * Requires: ++ * WCD initialized. ++ * Ensures: ++ */ ++DSP_STATUS WCD_InitComplete2(void) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct CFG_DEVNODE *DevNode; ++ struct DEV_OBJECT *hDevObject; ++ u32 devType; ++ ++ DBC_Require(WCD_cRefs > 0); ++ GT_0trace(WCD_debugMask, GT_ENTER, "Entered WCD_InitComplete\n"); ++ /* Walk the list of DevObjects, get each devnode, and attempting to ++ * autostart the board. Note that this requires COF loading, which ++ * requires KFILE. */ ++ for (hDevObject = DEV_GetFirst(); hDevObject != NULL; ++ hDevObject = DEV_GetNext(hDevObject)) { ++ if (DSP_FAILED(DEV_GetDevNode(hDevObject, &DevNode))) ++ continue; ++ ++ if (DSP_FAILED(DEV_GetDevType(hDevObject, &devType))) ++ continue; ++ ++ if ((devType == DSP_UNIT) || (devType == IVA_UNIT)) { ++ if (DSP_FAILED(PROC_AutoStart(DevNode, hDevObject))) { ++ GT_0trace(WCD_debugMask, GT_1CLASS, ++ "WCD_InitComplete2 Failed\n"); ++ status = DSP_EFAIL; ++ /* break; */ ++ } ++ } else ++ GT_1trace(WCD_debugMask, GT_ENTER, ++ "Ignoring PROC_AutoStart " ++ "for Device Type = 0x%x \n", devType); ++ } /* End For Loop */ ++ GT_1trace(WCD_debugMask, GT_ENTER, ++ "Exiting WCD_InitComplete status 0x%x\n", status); ++ return status; ++} ++ ++/* ++ * ======== MGRWRAP_EnumNode_Info ======== ++ */ ++u32 MGRWRAP_EnumNode_Info(union Trapped_Args *args) ++{ ++ u8 *pNDBProps; ++ u32 uNumNodes; ++ DSP_STATUS status = DSP_SOK; ++ u32 size = args->ARGS_MGR_ENUMNODE_INFO.uNDBPropsSize; ++ ++ GT_4trace(WCD_debugMask, GT_ENTER, ++ "MGR_EnumNodeInfo: entered args:\n0x%x" ++ " uNode: 0x%x\tpNDBProps: 0x%x\tuNDBPropsSize: " ++ "0x%x\tpuNumNodes\n", args->ARGS_MGR_ENUMNODE_INFO.uNode, ++ args->ARGS_MGR_ENUMNODE_INFO.pNDBProps, ++ args->ARGS_MGR_ENUMNODE_INFO.uNDBPropsSize, ++ args->ARGS_MGR_ENUMNODE_INFO.puNumNodes); ++ pNDBProps = MEM_Alloc(size, MEM_NONPAGED); ++ if (pNDBProps == NULL) ++ status = DSP_EMEMORY; ++ ++ if (DSP_SUCCEEDED(status)) { ++ status = MGR_EnumNodeInfo(args->ARGS_MGR_ENUMNODE_INFO.uNode, ++ (struct DSP_NDBPROPS *)pNDBProps, ++ size, &uNumNodes); ++ } ++ cp_to_usr(args->ARGS_MGR_ENUMNODE_INFO.pNDBProps, pNDBProps, status, ++ size); ++ cp_to_usr(args->ARGS_MGR_ENUMNODE_INFO.puNumNodes, &uNumNodes, status, ++ 1); ++ if (pNDBProps) ++ MEM_Free(pNDBProps); ++ ++ return status; ++} ++ ++/* ++ * ======== MGRWRAP_EnumProc_Info ======== ++ */ ++u32 MGRWRAP_EnumProc_Info(union Trapped_Args *args) ++{ ++ u8 *pProcessorInfo; ++ u32 uNumProcs; ++ DSP_STATUS status = DSP_SOK; ++ u32 size = args->ARGS_MGR_ENUMPROC_INFO.uProcessorInfoSize; ++ ++ GT_4trace(WCD_debugMask, GT_ENTER, ++ "MGRWRAP_EnumProc_Info: entered args:\n" ++ "0x%x uProcessor: 0x%x\tpProcessorInfo: 0x%x\t" ++ "uProcessorInfoSize: 0x%x\tpuNumProcs \n", ++ args->ARGS_MGR_ENUMPROC_INFO.uProcessor, ++ args->ARGS_MGR_ENUMPROC_INFO.pProcessorInfo, ++ args->ARGS_MGR_ENUMPROC_INFO.uProcessorInfoSize, ++ args->ARGS_MGR_ENUMPROC_INFO.puNumProcs); ++ pProcessorInfo = MEM_Alloc(size, MEM_NONPAGED); ++ if (pProcessorInfo == NULL) ++ status = DSP_EMEMORY; ++ ++ if (DSP_SUCCEEDED(status)) { ++ status = MGR_EnumProcessorInfo(args-> ++ ARGS_MGR_ENUMPROC_INFO.uProcessor, ++ (struct DSP_PROCESSORINFO *)pProcessorInfo, ++ size, &uNumProcs); ++ } ++ cp_to_usr(args->ARGS_MGR_ENUMPROC_INFO.pProcessorInfo, pProcessorInfo, ++ status, size); ++ cp_to_usr(args->ARGS_MGR_ENUMPROC_INFO.puNumProcs, &uNumProcs, ++ status, 1); ++ if (pProcessorInfo) ++ MEM_Free(pProcessorInfo); ++ ++ return status; ++} ++ ++#define WRAP_MAP2CALLER(x) x ++/* ++ * ======== MGRWRAP_RegisterObject ======== ++ */ ++u32 MGRWRAP_RegisterObject(union Trapped_Args *args) ++{ ++ u32 retVal; ++ ++ GT_1trace(WCD_debugMask, GT_ENTER, ++ "MGRWRAP_RegisterObject: entered pg2hMsg " ++ "0x%x\n", args->ARGS_MGR_REGISTEROBJECT.pUuid); ++ retVal = DCD_RegisterObject(WRAP_MAP2CALLER ++ (args->ARGS_MGR_REGISTEROBJECT.pUuid), ++ args->ARGS_MGR_REGISTEROBJECT.objType, ++ WRAP_MAP2CALLER(args->ARGS_MGR_REGISTEROBJECT.pszPathName)); ++ return retVal; ++} ++ ++/* ++ * ======== MGRWRAP_UnregisterObject ======== ++ */ ++u32 MGRWRAP_UnregisterObject(union Trapped_Args *args) ++{ ++ u32 retVal; ++ ++ GT_1trace(WCD_debugMask, GT_ENTER, ++ "MGRWRAP_UnregisterObject: entered pg2hMsg" ++ " 0x%x\n", args->ARGS_MGR_UNREGISTEROBJECT.pUuid); ++ retVal = DCD_UnregisterObject(WRAP_MAP2CALLER ++ (args->ARGS_MGR_UNREGISTEROBJECT.pUuid), ++ args->ARGS_MGR_UNREGISTEROBJECT.objType); ++ ++ return retVal; ++} ++ ++/* ++ * ======== MGRWRAP_WaitForBridgeEvents ======== ++ */ ++u32 MGRWRAP_WaitForBridgeEvents(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DSP_NOTIFICATION *aNotifications[MAX_EVENTS]; ++ struct DSP_NOTIFICATION notifications[MAX_EVENTS]; ++ u32 uIndex, i; ++ u32 uCount = args->ARGS_MGR_WAIT.uCount; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, ++ "MGRWRAP_WaitForBridgeEvents: entered\n"); ++ ++ if (uCount > MAX_EVENTS) ++ status = DSP_EINVALIDARG; ++ ++ /* get the array of pointers to user structures */ ++ cp_fm_usr(aNotifications, args->ARGS_MGR_WAIT.aNotifications, ++ status, uCount); ++ /* get the events */ ++ for (i = 0; i < uCount; i++) { ++ cp_fm_usr(¬ifications[i], aNotifications[i], status, 1); ++ if (DSP_SUCCEEDED(status)) { ++ /* set the array of pointers to kernel structures*/ ++ aNotifications[i] = ¬ifications[i]; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = MGR_WaitForBridgeEvents(aNotifications, uCount, ++ &uIndex, args->ARGS_MGR_WAIT.uTimeout); ++ } ++ cp_to_usr(args->ARGS_MGR_WAIT.puIndex, &uIndex, status, 1); ++ return status; ++} ++ ++ ++#ifndef RES_CLEANUP_DISABLE ++/* ++ * ======== MGRWRAP_GetProcessResourceInfo ======== ++ */ ++u32 MGRWRAP_GetProcessResourcesInfo(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ u32 uSize = 0; ++ u8 *pBuf = MEM_Alloc(8092, MEM_NONPAGED); ++ status = DRV_ProcDisplayResInfo(pBuf, &uSize); ++ GT_1trace(WCD_debugMask, GT_ENTER, ++ "MGRWRAP_GetProcessResourcesInfo:uSize=%d :\n", uSize); ++ cp_to_usr(args->ARGS_PROC_GETTRACE.pBuf, pBuf, status, uSize); ++ GT_0trace(WCD_debugMask, GT_ENTER, "\n***********" ++ "123MGRWRAP_GetProcessResourcesInfo:**************\n"); ++ GT_0trace(WCD_debugMask, GT_ENTER, "\n***********" ++ "456MGRWRAP_GetProcessResourcesInfo:**************\n"); ++ cp_to_usr(args->ARGS_PROC_GETTRACE.pSize, &uSize, status, 1); ++ MEM_Free(pBuf); ++ return status; ++} ++#endif ++ ++ ++/* ++ * ======== PROCWRAP_Attach ======== ++ */ ++u32 PROCWRAP_Attach(union Trapped_Args *args) ++{ ++ DSP_HPROCESSOR processor; ++ DSP_STATUS status = DSP_SOK; ++ struct DSP_PROCESSORATTRIN attrIn, *pAttrIn = NULL; ++ ++ GT_3trace(WCD_debugMask, GT_ENTER, ++ "PROCWRAP_Attach: entered args:\n" "0x%x" ++ " uProcessor: 0x%x\tpAttrIn: 0x%x\tphProcessor \n", ++ args->ARGS_PROC_ATTACH.uProcessor, ++ args->ARGS_PROC_ATTACH.pAttrIn, ++ args->ARGS_PROC_ATTACH.phProcessor); ++ /* Optional argument */ ++ if (args->ARGS_PROC_ATTACH.pAttrIn) { ++ cp_fm_usr(&attrIn, args->ARGS_PROC_ATTACH.pAttrIn, status, 1); ++ if (DSP_SUCCEEDED(status)) ++ pAttrIn = &attrIn; ++ ++ } ++ status = PROC_Attach(args->ARGS_PROC_ATTACH.uProcessor, pAttrIn, ++ &processor); ++ cp_to_usr(args->ARGS_PROC_ATTACH.phProcessor, &processor, status, 1); ++ return status; ++} ++ ++/* ++ * ======== PROCWRAP_Ctrl ======== ++ */ ++u32 PROCWRAP_Ctrl(union Trapped_Args *args) ++{ ++ u32 cbDataSize, __user *pSize = (u32 __user *) ++ args->ARGS_PROC_CTRL.pArgs; ++ u8 *pArgs = NULL; ++ DSP_STATUS status = DSP_SOK; ++ ++ GT_3trace(WCD_debugMask, GT_ENTER, ++ "PROCWRAP_Ctrl: entered args:\n 0x%x" ++ " uProcessor: 0x%x\tdwCmd: 0x%x\tpArgs \n", ++ args->ARGS_PROC_CTRL.hProcessor, ++ args->ARGS_PROC_CTRL.dwCmd, ++ args->ARGS_PROC_CTRL.pArgs); ++ if (pSize) { ++ if (get_user(cbDataSize, pSize)) ++ status = DSP_EFAIL; ++ ++ cbDataSize += sizeof(u32); ++ if (DSP_SUCCEEDED(status)) { ++ pArgs = MEM_Alloc(cbDataSize, MEM_NONPAGED); ++ if (pArgs == NULL) ++ status = DSP_EMEMORY; ++ ++ } ++ cp_fm_usr(pArgs, args->ARGS_PROC_CTRL.pArgs, status, ++ cbDataSize); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = PROC_Ctrl(args->ARGS_PROC_CTRL.hProcessor, ++ args->ARGS_PROC_CTRL.dwCmd, ++ (struct DSP_CBDATA *)pArgs); ++ } ++ ++ /* cp_to_usr(args->ARGS_PROC_CTRL.pArgs, pArgs, status, 1);*/ ++ if (pArgs) ++ MEM_Free(pArgs); ++ ++ return status; ++} ++ ++/* ++ * ======== PROCWRAP_Detach ======== ++ */ ++u32 PROCWRAP_Detach(union Trapped_Args *args) ++{ ++ u32 retVal; ++ ++ GT_1trace(WCD_debugMask, GT_ENTER, ++ "PROCWRAP_Detach: entered args\n0x%x " ++ "hProceesor \n", args->ARGS_PROC_DETACH.hProcessor); ++ retVal = PROC_Detach(args->ARGS_PROC_DETACH.hProcessor); ++ ++ return retVal; ++} ++ ++/* ++ * ======== PROCWRAP_EnumNode_Info ======== ++ */ ++u32 PROCWRAP_EnumNode_Info(union Trapped_Args *args) ++{ ++ DSP_STATUS status; ++ DSP_HNODE aNodeTab[MAX_NODES]; ++ u32 uNumNodes; ++ u32 uAllocated; ++ ++ GT_5trace(WCD_debugMask, GT_ENTER, ++ "PROCWRAP_EnumNode_Info:entered args:\n0x" ++ "%xhProcessor:0x%x\taNodeTab:0x%x\tuNodeTabSize:" ++ "%0x%x\tpuNumNodes%\n0x%x puAllocated: \n", ++ args->ARGS_PROC_ENUMNODE_INFO.hProcessor, ++ args->ARGS_PROC_ENUMNODE_INFO.aNodeTab, ++ args->ARGS_PROC_ENUMNODE_INFO.uNodeTabSize, ++ args->ARGS_PROC_ENUMNODE_INFO.puNumNodes, ++ args->ARGS_PROC_ENUMNODE_INFO.puAllocated); ++ DBC_Require(args->ARGS_PROC_ENUMNODE_INFO.uNodeTabSize <= MAX_NODES); ++ status = PROC_EnumNodes(args->ARGS_PROC_ENUMNODE_INFO.hProcessor, ++ aNodeTab, ++ args->ARGS_PROC_ENUMNODE_INFO.uNodeTabSize, ++ &uNumNodes, &uAllocated); ++ cp_to_usr(args->ARGS_PROC_ENUMNODE_INFO.aNodeTab, aNodeTab, status, ++ uNumNodes); ++ cp_to_usr(args->ARGS_PROC_ENUMNODE_INFO.puNumNodes, &uNumNodes, ++ status, 1); ++ cp_to_usr(args->ARGS_PROC_ENUMNODE_INFO.puAllocated, &uAllocated, ++ status, 1); ++ return status; ++} ++ ++/* ++ * ======== PROCWRAP_FlushMemory ======== ++ */ ++u32 PROCWRAP_FlushMemory(union Trapped_Args *args) ++{ ++ DSP_STATUS status; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "PROCWRAP_FlushMemory: entered\n"); ++ ++ status = PROC_FlushMemory(args->ARGS_PROC_FLUSHMEMORY.hProcessor, ++ args->ARGS_PROC_FLUSHMEMORY.pMpuAddr, ++ args->ARGS_PROC_FLUSHMEMORY.ulSize, ++ args->ARGS_PROC_FLUSHMEMORY.ulFlags); ++ return status; ++} ++ ++ ++/* ++ * ======== PROCWRAP_InvalidateMemory ======== ++ */ ++u32 PROCWRAP_InvalidateMemory(union Trapped_Args *args) ++{ ++ DSP_STATUS status; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, ++ "PROCWRAP_InvalidateMemory:entered\n"); ++ ++ status = PROC_InvalidateMemory( ++ args->ARGS_PROC_INVALIDATEMEMORY.hProcessor, ++ args->ARGS_PROC_INVALIDATEMEMORY.pMpuAddr, ++ args->ARGS_PROC_INVALIDATEMEMORY.ulSize); ++ return status; ++} ++ ++ ++/* ++ * ======== PROCWRAP_EnumResources ======== ++ */ ++u32 PROCWRAP_EnumResources(union Trapped_Args *args) ++{ ++ u32 retVal; ++ ++ GT_4trace(WCD_debugMask, GT_ENTER, ++ "PROCWRAP_EnumResources: entered args:\n" ++ "0x%x hProcessor: 0x%x\tuResourceMask: 0x%x\tpResourceInfo" ++ " 0x%x\tuResourceInfoSixe \n", ++ args->ARGS_PROC_ENUMRESOURCES.hProcessor, ++ args->ARGS_PROC_ENUMRESOURCES.uResourceType, ++ args->ARGS_PROC_ENUMRESOURCES.pResourceInfo, ++ args->ARGS_PROC_ENUMRESOURCES.uResourceInfoSize); ++ retVal = PROC_GetResourceInfo(args->ARGS_PROC_ENUMRESOURCES.hProcessor, ++ args->ARGS_PROC_ENUMRESOURCES.uResourceType, ++ WRAP_MAP2CALLER(args->ARGS_PROC_ENUMRESOURCES. ++ pResourceInfo), args->ARGS_PROC_ENUMRESOURCES. ++ uResourceInfoSize); ++ ++ return retVal; ++} ++ ++/* ++ * ======== PROCWRAP_GetState ======== ++ */ ++u32 PROCWRAP_GetState(union Trapped_Args *args) ++{ ++ DSP_STATUS status; ++ struct DSP_PROCESSORSTATE procStatus; ++ GT_0trace(WCD_debugMask, GT_ENTER, "PROCWRAP_GetState: entered\n"); ++ status = PROC_GetState(args->ARGS_PROC_GETSTATE.hProcessor, &procStatus, ++ args->ARGS_PROC_GETSTATE.uStateInfoSize); ++ cp_to_usr(args->ARGS_PROC_GETSTATE.pProcStatus, &procStatus, status, 1); ++ return status; ++ ++} ++ ++/* ++ * ======== PROCWRAP_GetTrace ======== ++ */ ++u32 PROCWRAP_GetTrace(union Trapped_Args *args) ++{ ++ DSP_STATUS status; ++ u8 *pBuf; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "PROCWRAP_GetTrace: entered\n"); ++ ++ DBC_Require(args->ARGS_PROC_GETTRACE.uMaxSize <= MAX_TRACEBUFLEN); ++ ++ pBuf = MEM_Calloc(args->ARGS_PROC_GETTRACE.uMaxSize, MEM_NONPAGED); ++ if (pBuf != NULL) { ++ status = PROC_GetTrace(args->ARGS_PROC_GETTRACE.hProcessor, ++ pBuf, args->ARGS_PROC_GETTRACE.uMaxSize); ++ } else { ++ status = DSP_EMEMORY; ++ } ++ cp_to_usr(args->ARGS_PROC_GETTRACE.pBuf, pBuf, status, ++ args->ARGS_PROC_GETTRACE.uMaxSize); ++ if (pBuf) ++ MEM_Free(pBuf); ++ ++ return status; ++} ++ ++/* ++ * ======== PROCWRAP_Load ======== ++ */ ++u32 PROCWRAP_Load(union Trapped_Args *args) ++{ ++ s32 i, len; ++ DSP_STATUS status = DSP_SOK; ++ char *temp; ++ s32 argc = args->ARGS_PROC_LOAD.iArgc; ++ u8 **argv, **envp = NULL; ++ ++ ++ DBC_Require(argc > 0); ++ DBC_Require(argc <= MAX_LOADARGS); ++ ++ argv = MEM_Alloc(argc * sizeof(u8 *), MEM_NONPAGED); ++ if (argv == NULL) ++ status = DSP_EMEMORY; ++ ++ cp_fm_usr(argv, args->ARGS_PROC_LOAD.aArgv, status, argc); ++ for (i = 0; DSP_SUCCEEDED(status) && (i < argc); i++) { ++ if (argv[i] != NULL) { ++ /* User space pointer to argument */ ++ temp = (char *) argv[i]; ++ len = strlen_user((char *)temp); ++ /* Kernel space pointer to argument */ ++ argv[i] = MEM_Alloc(len, MEM_NONPAGED); ++ if (argv[i] == NULL) { ++ status = DSP_EMEMORY; ++ break; ++ } ++ cp_fm_usr(argv[i], temp, status, len); ++ } ++ } ++ /* TODO: validate this */ ++ if (args->ARGS_PROC_LOAD.aEnvp != NULL) { ++ /* number of elements in the envp array including NULL */ ++ len = 0; ++ do { ++ len++; ++ get_user(temp, args->ARGS_PROC_LOAD.aEnvp); ++ } while (temp); ++ envp = MEM_Alloc(len * sizeof(u8 *), MEM_NONPAGED); ++ if (envp == NULL) ++ status = DSP_EMEMORY; ++ ++ cp_fm_usr(envp, args->ARGS_PROC_LOAD.aEnvp, status, len); ++ for (i = 0; DSP_SUCCEEDED(status) && (envp[i] != NULL); i++) { ++ /* User space pointer to argument */ ++ temp = (char *)envp[i]; ++ len = strlen_user((char *)temp); ++ /* Kernel space pointer to argument */ ++ envp[i] = MEM_Alloc(len, MEM_NONPAGED); ++ if (envp[i] == NULL) { ++ status = DSP_EMEMORY; ++ break; ++ } ++ cp_fm_usr(envp[i], temp, status, len); ++ } ++ } ++ GT_5trace(WCD_debugMask, GT_ENTER, ++ "PROCWRAP_Load, hProcessor: 0x%x\n\tiArgc:" ++ "0x%x\n\taArgv: 0x%x\n\taArgv[0]: %s\n\taEnvp: 0x%0x\n", ++ args->ARGS_PROC_LOAD.hProcessor, ++ args->ARGS_PROC_LOAD.iArgc, args->ARGS_PROC_LOAD.aArgv, ++ argv[0], args->ARGS_PROC_LOAD.aEnvp); ++ if (DSP_SUCCEEDED(status)) { ++ status = PROC_Load(args->ARGS_PROC_LOAD.hProcessor, ++ args->ARGS_PROC_LOAD.iArgc, ++ (CONST char **)argv, (CONST char **)envp); ++ } ++ if (envp != NULL) { ++ i = 0; ++ while (envp[i] != NULL) ++ MEM_Free(envp[i++]); ++ ++ MEM_Free(envp); ++ } ++ if (argv != NULL) { ++ for (i = 0; i < argc; i++) { ++ if (argv[i] != NULL) ++ MEM_Free(argv[i]); ++ ++ } ++ MEM_Free(argv); ++ } ++ return status; ++} ++ ++/* ++ * ======== PROCWRAP_Map ======== ++ */ ++u32 PROCWRAP_Map(union Trapped_Args *args) ++{ ++ DSP_STATUS status; ++ void *pMapAddr; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "PROCWRAP_Map: entered\n"); ++ status = PROC_Map(args->ARGS_PROC_MAPMEM.hProcessor, ++ args->ARGS_PROC_MAPMEM.pMpuAddr, ++ args->ARGS_PROC_MAPMEM.ulSize, ++ args->ARGS_PROC_MAPMEM.pReqAddr, &pMapAddr, ++ args->ARGS_PROC_MAPMEM.ulMapAttr); ++ if (DSP_SUCCEEDED(status)) { ++ if (put_user(pMapAddr, args->ARGS_PROC_MAPMEM.ppMapAddr)) ++ status = DSP_EINVALIDARG; ++ ++ } ++ return status; ++} ++ ++/* ++ * ======== PROCWRAP_RegisterNotify ======== ++ */ ++u32 PROCWRAP_RegisterNotify(union Trapped_Args *args) ++{ ++ DSP_STATUS status; ++ struct DSP_NOTIFICATION notification; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, ++ "PROCWRAP_RegisterNotify: entered\n"); ++ ++ /* Initialize the notification data structure */ ++ notification.psName = NULL; ++ notification.handle = NULL; ++ ++ status = PROC_RegisterNotify(args->ARGS_PROC_REGISTER_NOTIFY.hProcessor, ++ args->ARGS_PROC_REGISTER_NOTIFY.uEventMask, ++ args->ARGS_PROC_REGISTER_NOTIFY.uNotifyType, ++ ¬ification); ++ cp_to_usr(args->ARGS_PROC_REGISTER_NOTIFY.hNotification, ¬ification, ++ status, 1); ++ return status; ++} ++ ++/* ++ * ======== PROCWRAP_ReserveMemory ======== ++ */ ++u32 PROCWRAP_ReserveMemory(union Trapped_Args *args) ++{ ++ DSP_STATUS status; ++ void *pRsvAddr; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "PROCWRAP_ReserveMemory: entered\n"); ++ status = PROC_ReserveMemory(args->ARGS_PROC_RSVMEM.hProcessor, ++ args->ARGS_PROC_RSVMEM.ulSize, &pRsvAddr); ++ if (put_user(pRsvAddr, args->ARGS_PROC_RSVMEM.ppRsvAddr)) ++ status = DSP_EINVALIDARG; ++ ++ return status; ++} ++ ++/* ++ * ======== PROCWRAP_Start ======== ++ */ ++u32 PROCWRAP_Start(union Trapped_Args *args) ++{ ++ u32 retVal; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "PROCWRAP_Start: entered\n"); ++ retVal = PROC_Start(args->ARGS_PROC_START.hProcessor); ++ return retVal; ++} ++ ++/* ++ * ======== PROCWRAP_UnMap ======== ++ */ ++u32 PROCWRAP_UnMap(union Trapped_Args *args) ++{ ++ DSP_STATUS status; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "PROCWRAP_UnMap: entered\n"); ++ status = PROC_UnMap(args->ARGS_PROC_UNMAPMEM.hProcessor, ++ args->ARGS_PROC_UNMAPMEM.pMapAddr); ++ return status; ++} ++ ++/* ++ * ======== PROCWRAP_UnReserveMemory ======== ++ */ ++u32 PROCWRAP_UnReserveMemory(union Trapped_Args *args) ++{ ++ DSP_STATUS status; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, ++ "PROCWRAP_UnReserveMemory: entered\n"); ++ status = PROC_UnReserveMemory(args->ARGS_PROC_UNRSVMEM.hProcessor, ++ args->ARGS_PROC_UNRSVMEM.pRsvAddr); ++ return status; ++} ++ ++/* ++ * ======== PROCWRAP_Stop ======== ++ */ ++u32 PROCWRAP_Stop(union Trapped_Args *args) ++{ ++ u32 retVal; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "PROCWRAP_Stop: entered\n"); ++ retVal = PROC_Stop(args->ARGS_PROC_STOP.hProcessor); ++ ++ return retVal; ++} ++ ++/* ++ * ======== NODEWRAP_Allocate ======== ++ */ ++u32 NODEWRAP_Allocate(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DSP_UUID nodeId; ++ u32 cbDataSize; ++ u32 __user *pSize = (u32 __user *)args->ARGS_NODE_ALLOCATE.pArgs; ++ u8 *pArgs = NULL; ++ struct DSP_NODEATTRIN attrIn, *pAttrIn = NULL; ++ struct NODE_OBJECT *hNode; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "NODEWRAP_Allocate: entered\n"); ++ ++ /* Optional argument */ ++ if (pSize) { ++ if (get_user(cbDataSize, pSize)) ++ status = DSP_EFAIL; ++ ++ cbDataSize += sizeof(u32); ++ if (DSP_SUCCEEDED(status)) { ++ pArgs = MEM_Alloc(cbDataSize, MEM_NONPAGED); ++ if (pArgs == NULL) ++ status = DSP_EMEMORY; ++ ++ } ++ cp_fm_usr(pArgs, args->ARGS_NODE_ALLOCATE.pArgs, status, ++ cbDataSize); ++ } ++ cp_fm_usr(&nodeId, args->ARGS_NODE_ALLOCATE.pNodeID, status, 1); ++ /* Optional argument */ ++ if (args->ARGS_NODE_ALLOCATE.pAttrIn) { ++ cp_fm_usr(&attrIn, args->ARGS_NODE_ALLOCATE.pAttrIn, status, 1); ++ if (DSP_SUCCEEDED(status)) ++ pAttrIn = &attrIn; ++ ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = NODE_Allocate(args->ARGS_NODE_ALLOCATE.hProcessor, ++ &nodeId, (struct DSP_CBDATA *)pArgs, ++ pAttrIn, &hNode); ++ } ++ cp_to_usr(args->ARGS_NODE_ALLOCATE.phNode, &hNode, status, 1); ++ if (pArgs) ++ MEM_Free(pArgs); ++ ++ return status; ++} ++ ++/* ++ * ======== NODEWRAP_AllocMsgBuf ======== ++ */ ++u32 NODEWRAP_AllocMsgBuf(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DSP_BUFFERATTR *pAttr = NULL; ++ struct DSP_BUFFERATTR attr; ++ u8 *pBuffer = NULL; ++ ++ if (args->ARGS_NODE_ALLOCMSGBUF.pAttr) { /* Optional argument */ ++ cp_fm_usr(&attr, args->ARGS_NODE_ALLOCMSGBUF.pAttr, status, 1); ++ if (DSP_SUCCEEDED(status)) ++ pAttr = &attr; ++ ++ } ++ /* IN OUT argument */ ++ cp_fm_usr(&pBuffer, args->ARGS_NODE_ALLOCMSGBUF.pBuffer, status, 1); ++ if (DSP_SUCCEEDED(status)) { ++ status = NODE_AllocMsgBuf(args->ARGS_NODE_ALLOCMSGBUF.hNode, ++ args->ARGS_NODE_ALLOCMSGBUF.uSize, ++ pAttr, &pBuffer); ++ } ++ cp_to_usr(args->ARGS_NODE_ALLOCMSGBUF.pBuffer, &pBuffer, status, 1) ++ return status; ++} ++ ++/* ++ * ======== NODEWRAP_ChangePriority ======== ++ */ ++u32 NODEWRAP_ChangePriority(union Trapped_Args *args) ++{ ++ u32 retVal; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, ++ "NODEWRAP_ChangePriority: entered\n"); ++ retVal = NODE_ChangePriority(args->ARGS_NODE_CHANGEPRIORITY.hNode, ++ args->ARGS_NODE_CHANGEPRIORITY.iPriority); ++ ++ return retVal; ++} ++ ++/* ++ * ======== NODEWRAP_Connect ======== ++ */ ++u32 NODEWRAP_Connect(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DSP_STRMATTR attrs; ++ struct DSP_STRMATTR *pAttrs = NULL; ++ u32 cbDataSize; ++ u32 __user *pSize = (u32 __user *)args->ARGS_NODE_CONNECT.pConnParam; ++ u8 *pArgs = NULL; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "NODEWRAP_Connect: entered\n"); ++ ++ /* Optional argument */ ++ if (pSize) { ++ if (get_user(cbDataSize, pSize)) ++ status = DSP_EFAIL; ++ ++ cbDataSize += sizeof(u32); ++ if (DSP_SUCCEEDED(status)) { ++ pArgs = MEM_Alloc(cbDataSize, MEM_NONPAGED); ++ if (pArgs == NULL) ++ status = DSP_EMEMORY; ++ ++ } ++ cp_fm_usr(pArgs, args->ARGS_NODE_CONNECT.pConnParam, status, ++ cbDataSize); ++ } ++ if (args->ARGS_NODE_CONNECT.pAttrs) { /* Optional argument */ ++ cp_fm_usr(&attrs, args->ARGS_NODE_CONNECT.pAttrs, status, 1); ++ if (DSP_SUCCEEDED(status)) ++ pAttrs = &attrs; ++ ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = NODE_Connect(args->ARGS_NODE_CONNECT.hNode, ++ args->ARGS_NODE_CONNECT.uStream, ++ args->ARGS_NODE_CONNECT.hOtherNode, ++ args->ARGS_NODE_CONNECT.uOtherStream, ++ pAttrs, (struct DSP_CBDATA *)pArgs); ++ } ++ if (pArgs) ++ MEM_Free(pArgs); ++ ++ return status; ++} ++ ++/* ++ * ======== NODEWRAP_Create ======== ++ */ ++u32 NODEWRAP_Create(union Trapped_Args *args) ++{ ++ u32 retVal; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "NODEWRAP_Create: entered\n"); ++ retVal = NODE_Create(args->ARGS_NODE_CREATE.hNode); ++ ++ return retVal; ++} ++ ++/* ++ * ======== NODEWRAP_Delete ======== ++ */ ++u32 NODEWRAP_Delete(union Trapped_Args *args) ++{ ++ u32 retVal; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "NODEWRAP_Delete: entered\n"); ++ retVal = NODE_Delete(args->ARGS_NODE_DELETE.hNode); ++ ++ return retVal; ++} ++ ++/* ++ * ======== NODEWRAP_FreeMsgBuf ======== ++ */ ++u32 NODEWRAP_FreeMsgBuf(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DSP_BUFFERATTR *pAttr = NULL; ++ struct DSP_BUFFERATTR attr; ++ if (args->ARGS_NODE_FREEMSGBUF.pAttr) { /* Optional argument */ ++ cp_fm_usr(&attr, args->ARGS_NODE_FREEMSGBUF.pAttr, status, 1); ++ if (DSP_SUCCEEDED(status)) ++ pAttr = &attr; ++ ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = NODE_FreeMsgBuf(args->ARGS_NODE_FREEMSGBUF.hNode, ++ args->ARGS_NODE_FREEMSGBUF.pBuffer, ++ pAttr); ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== NODEWRAP_GetAttr ======== ++ */ ++u32 NODEWRAP_GetAttr(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DSP_NODEATTR attr; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "NODEWRAP_GetAttr: entered\n"); ++ ++ status = NODE_GetAttr(args->ARGS_NODE_GETATTR.hNode, &attr, ++ args->ARGS_NODE_GETATTR.uAttrSize); ++ cp_to_usr(args->ARGS_NODE_GETATTR.pAttr, &attr, status, 1); ++ ++ return status; ++} ++ ++/* ++ * ======== NODEWRAP_GetMessage ======== ++ */ ++u32 NODEWRAP_GetMessage(union Trapped_Args *args) ++{ ++ DSP_STATUS status; ++ struct DSP_MSG msg; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "NODEWRAP_GetMessage: entered\n"); ++ ++ status = NODE_GetMessage(args->ARGS_NODE_GETMESSAGE.hNode, &msg, ++ args->ARGS_NODE_GETMESSAGE.uTimeout); ++ ++ cp_to_usr(args->ARGS_NODE_GETMESSAGE.pMessage, &msg, status, 1); ++ ++ return status; ++} ++ ++/* ++ * ======== NODEWRAP_Pause ======== ++ */ ++u32 NODEWRAP_Pause(union Trapped_Args *args) ++{ ++ u32 retVal; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "NODEWRAP_Pause: entered\n"); ++ retVal = NODE_Pause(args->ARGS_NODE_PAUSE.hNode); ++ ++ return retVal; ++} ++ ++/* ++ * ======== NODEWRAP_PutMessage ======== ++ */ ++u32 NODEWRAP_PutMessage(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DSP_MSG msg; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "NODEWRAP_PutMessage: entered\n"); ++ ++ cp_fm_usr(&msg, args->ARGS_NODE_PUTMESSAGE.pMessage, status, 1); ++ ++ if (DSP_SUCCEEDED(status)) { ++ status = NODE_PutMessage(args->ARGS_NODE_PUTMESSAGE.hNode, &msg, ++ args->ARGS_NODE_PUTMESSAGE.uTimeout); ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== NODEWRAP_RegisterNotify ======== ++ */ ++u32 NODEWRAP_RegisterNotify(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DSP_NOTIFICATION notification; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, ++ "NODEWRAP_RegisterNotify: entered\n"); ++ ++ /* Initialize the notification data structure */ ++ notification.psName = NULL; ++ notification.handle = NULL; ++ ++ status = NODE_RegisterNotify(args->ARGS_NODE_REGISTERNOTIFY.hNode, ++ args->ARGS_NODE_REGISTERNOTIFY.uEventMask, ++ args->ARGS_NODE_REGISTERNOTIFY.uNotifyType, ++ ¬ification); ++ cp_to_usr(args->ARGS_NODE_REGISTERNOTIFY.hNotification, ¬ification, ++ status, 1); ++ return status; ++} ++ ++/* ++ * ======== NODEWRAP_Run ======== ++ */ ++u32 NODEWRAP_Run(union Trapped_Args *args) ++{ ++ u32 retVal; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "NODEWRAP_Run: entered\n"); ++ retVal = NODE_Run(args->ARGS_NODE_RUN.hNode); ++ ++ return retVal; ++} ++ ++/* ++ * ======== NODEWRAP_Terminate ======== ++ */ ++u32 NODEWRAP_Terminate(union Trapped_Args *args) ++{ ++ DSP_STATUS status; ++ DSP_STATUS tempstatus; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, "NODEWRAP_Terminate: entered\n"); ++ ++ status = NODE_Terminate(args->ARGS_NODE_TERMINATE.hNode, &tempstatus); ++ ++ cp_to_usr(args->ARGS_NODE_TERMINATE.pStatus, &tempstatus, status, 1); ++ ++ return status; ++} ++ ++ ++/* ++ * ======== NODEWRAP_GetUUIDProps ======== ++ */ ++u32 NODEWRAP_GetUUIDProps(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DSP_UUID nodeId; ++ struct DSP_NDBPROPS *pnodeProps = NULL; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, ++ "NODEWRAP_GetUUIDPropste: entered\n"); ++ ++ ++ cp_fm_usr(&nodeId, args->ARGS_NODE_GETUUIDPROPS.pNodeID, status, 1); ++ pnodeProps = MEM_Alloc(sizeof(struct DSP_NDBPROPS), MEM_NONPAGED); ++ if (pnodeProps != NULL) { ++ status = NODE_GetUUIDProps(args-> ++ ARGS_NODE_GETUUIDPROPS.hProcessor, ++ &nodeId, pnodeProps); ++ cp_to_usr(args->ARGS_NODE_GETUUIDPROPS.pNodeProps, pnodeProps, ++ status, 1); ++ } else ++ status = DSP_EMEMORY; ++ if (pnodeProps) ++ MEM_Free(pnodeProps); ++ return status; ++} ++ ++/* ++ * ======== STRMWRAP_AllocateBuffer ======== ++ */ ++u32 STRMWRAP_AllocateBuffer(union Trapped_Args *args) ++{ ++ DSP_STATUS status; ++ u8 **apBuffer = NULL; ++ u32 uNumBufs = args->ARGS_STRM_ALLOCATEBUFFER.uNumBufs; ++ ++ DBC_Require(uNumBufs <= MAX_BUFS); ++ ++ apBuffer = MEM_Alloc((uNumBufs * sizeof(u8 *)), MEM_NONPAGED); ++ ++ status = STRM_AllocateBuffer(args->ARGS_STRM_ALLOCATEBUFFER.hStream, ++ args->ARGS_STRM_ALLOCATEBUFFER.uSize, ++ apBuffer, uNumBufs); ++ cp_to_usr(args->ARGS_STRM_ALLOCATEBUFFER.apBuffer, apBuffer, status, ++ uNumBufs); ++ if (apBuffer) ++ MEM_Free(apBuffer); ++ ++ return status; ++} ++ ++/* ++ * ======== STRMWRAP_Close ======== ++ */ ++u32 STRMWRAP_Close(union Trapped_Args *args) ++{ ++ u32 retVal; ++ ++ retVal = STRM_Close(args->ARGS_STRM_CLOSE.hStream); ++ ++ return retVal; ++} ++ ++/* ++ * ======== STRMWRAP_FreeBuffer ======== ++ */ ++u32 STRMWRAP_FreeBuffer(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ u8 **apBuffer = NULL; ++ u32 uNumBufs = args->ARGS_STRM_FREEBUFFER.uNumBufs; ++ ++ DBC_Require(uNumBufs <= MAX_BUFS); ++ ++ apBuffer = MEM_Alloc((uNumBufs * sizeof(u8 *)), MEM_NONPAGED); ++ ++ cp_fm_usr(apBuffer, args->ARGS_STRM_FREEBUFFER.apBuffer, status, ++ uNumBufs); ++ ++ if (DSP_SUCCEEDED(status)) { ++ status = STRM_FreeBuffer(args->ARGS_STRM_FREEBUFFER.hStream, ++ apBuffer, uNumBufs); ++ } ++ cp_to_usr(args->ARGS_STRM_FREEBUFFER.apBuffer, apBuffer, status, ++ uNumBufs); ++ if (apBuffer) ++ MEM_Free(apBuffer); ++ ++ return status; ++} ++ ++/* ++ * ======== STRMWRAP_GetEventHandle ======== ++ */ ++u32 STRMWRAP_GetEventHandle(union Trapped_Args *args) ++{ ++ return DSP_ENOTIMPL; ++} ++ ++/* ++ * ======== STRMWRAP_GetInfo ======== ++ */ ++u32 STRMWRAP_GetInfo(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct STRM_INFO strmInfo; ++ struct DSP_STREAMINFO user; ++ struct DSP_STREAMINFO *temp; ++ ++ cp_fm_usr(&strmInfo, args->ARGS_STRM_GETINFO.pStreamInfo, status, 1); ++ temp = strmInfo.pUser; ++ ++ strmInfo.pUser = &user; ++ ++ if (DSP_SUCCEEDED(status)) { ++ status = STRM_GetInfo(args->ARGS_STRM_GETINFO.hStream, ++ &strmInfo, args->ARGS_STRM_GETINFO.uStreamInfoSize); ++ } ++ cp_to_usr(temp, strmInfo.pUser, status, 1); ++ strmInfo.pUser = temp; ++ cp_to_usr(args->ARGS_STRM_GETINFO.pStreamInfo, &strmInfo, status, 1); ++ return status; ++} ++ ++/* ++ * ======== STRMWRAP_Idle ======== ++ */ ++u32 STRMWRAP_Idle(union Trapped_Args *args) ++{ ++ u32 retVal; ++ ++ retVal = STRM_Idle(args->ARGS_STRM_IDLE.hStream, ++ args->ARGS_STRM_IDLE.bFlush); ++ ++ return retVal; ++} ++ ++/* ++ * ======== STRMWRAP_Issue ======== ++ */ ++u32 STRMWRAP_Issue(union Trapped_Args *args) ++{ ++ u32 retVal; ++ ++ retVal = STRM_Issue(args->ARGS_STRM_ISSUE.hStream, ++ args->ARGS_STRM_ISSUE.pBuffer, ++ args->ARGS_STRM_ISSUE.dwBytes, ++ args->ARGS_STRM_ISSUE.dwBufSize, ++ args->ARGS_STRM_ISSUE.dwArg); ++ ++ /* This is a user space pointer */ ++ return retVal; ++} ++ ++/* ++ * ======== STRMWRAP_Open ======== ++ */ ++u32 STRMWRAP_Open(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct STRM_ATTR attr; ++ struct STRM_OBJECT *pStrm; ++ struct DSP_STREAMATTRIN strmAttrIn; ++ ++ cp_fm_usr(&attr, args->ARGS_STRM_OPEN.pAttrIn, status, 1); ++ ++ if (attr.pStreamAttrIn != NULL) { /* Optional argument */ ++ cp_fm_usr(&strmAttrIn, attr.pStreamAttrIn, status, 1); ++ if (DSP_SUCCEEDED(status)) ++ attr.pStreamAttrIn = &strmAttrIn; ++ ++ } ++ status = STRM_Open(args->ARGS_STRM_OPEN.hNode, ++ args->ARGS_STRM_OPEN.uDirection, ++ args->ARGS_STRM_OPEN.uIndex, &attr, &pStrm); ++ cp_to_usr(args->ARGS_STRM_OPEN.phStream, &pStrm, status, 1); ++ return status; ++} ++ ++/* ++ * ======== STRMWRAP_Reclaim ======== ++ */ ++u32 STRMWRAP_Reclaim(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ u8 *pBufPtr; ++ u32 ulBytes; ++ u32 dwArg; ++ u32 ulBufSize; ++ ++ status = STRM_Reclaim(args->ARGS_STRM_RECLAIM.hStream, &pBufPtr, ++ &ulBytes, &ulBufSize, &dwArg); ++ cp_to_usr(args->ARGS_STRM_RECLAIM.pBufPtr, &pBufPtr, status, 1); ++ cp_to_usr(args->ARGS_STRM_RECLAIM.pBytes, &ulBytes, status, 1); ++ cp_to_usr(args->ARGS_STRM_RECLAIM.pdwArg, &dwArg, status, 1); ++ ++ if (args->ARGS_STRM_RECLAIM.pBufSize != NULL) { ++ cp_to_usr(args->ARGS_STRM_RECLAIM.pBufSize, &ulBufSize, ++ status, 1); ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== STRMWRAP_RegisterNotify ======== ++ */ ++u32 STRMWRAP_RegisterNotify(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DSP_NOTIFICATION notification; ++ ++ GT_0trace(WCD_debugMask, GT_ENTER, ++ "NODEWRAP_RegisterNotify: entered\n"); ++ ++ /* Initialize the notification data structure */ ++ notification.psName = NULL; ++ notification.handle = NULL; ++ ++ status = STRM_RegisterNotify(args->ARGS_STRM_REGISTERNOTIFY.hStream, ++ args->ARGS_STRM_REGISTERNOTIFY.uEventMask, ++ args->ARGS_STRM_REGISTERNOTIFY.uNotifyType, ++ ¬ification); ++ cp_to_usr(args->ARGS_STRM_REGISTERNOTIFY.hNotification, ¬ification, ++ status, 1); ++ ++ return status; ++} ++ ++/* ++ * ======== STRMWRAP_Select ======== ++ */ ++u32 STRMWRAP_Select(union Trapped_Args *args) ++{ ++ u32 mask; ++ struct STRM_OBJECT *aStrmTab[MAX_STREAMS]; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(args->ARGS_STRM_SELECT.nStreams <= MAX_STREAMS); ++ ++ cp_fm_usr(aStrmTab, args->ARGS_STRM_SELECT.aStreamTab, status, ++ args->ARGS_STRM_SELECT.nStreams); ++ if (DSP_SUCCEEDED(status)) { ++ status = STRM_Select(aStrmTab, args->ARGS_STRM_SELECT.nStreams, ++ &mask, args->ARGS_STRM_SELECT.uTimeout); ++ } ++ cp_to_usr(args->ARGS_STRM_SELECT.pMask, &mask, status, 1); ++ return status; ++} ++ ++/* CMM */ ++ ++/* ++ * ======== CMMWRAP_CallocBuf ======== ++ */ ++u32 CMMWRAP_CallocBuf(union Trapped_Args *args) ++{ ++ /* This operation is done in kernel */ ++ return DSP_ENOTIMPL; ++} ++ ++/* ++ * ======== CMMWRAP_FreeBuf ======== ++ */ ++u32 CMMWRAP_FreeBuf(union Trapped_Args *args) ++{ ++ /* This operation is done in kernel */ ++ return DSP_ENOTIMPL; ++} ++ ++/* ++ * ======== CMMWRAP_GetHandle ======== ++ */ ++u32 CMMWRAP_GetHandle(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct CMM_OBJECT *hCmmMgr; ++ ++ status = CMM_GetHandle(args->ARGS_CMM_GETHANDLE.hProcessor, &hCmmMgr); ++ ++ cp_to_usr(args->ARGS_CMM_GETHANDLE.phCmmMgr, &hCmmMgr, status, 1); ++ ++ return status; ++} ++ ++/* ++ * ======== CMMWRAP_GetInfo ======== ++ */ ++u32 CMMWRAP_GetInfo(union Trapped_Args *args) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct CMM_INFO cmmInfo; ++ ++ status = CMM_GetInfo(args->ARGS_CMM_GETINFO.hCmmMgr, &cmmInfo); ++ ++ cp_to_usr(args->ARGS_CMM_GETINFO.pCmmInfo, &cmmInfo, status, 1); ++ ++ return status; ++} +diff --git a/drivers/dsp/bridge/rmgr/dbdcd.c b/drivers/dsp/bridge/rmgr/dbdcd.c +new file mode 100644 +index 0000000..c5ec8f9 +--- /dev/null ++++ b/drivers/dsp/bridge/rmgr/dbdcd.c +@@ -0,0 +1,1573 @@ ++/* ++ * dbdcd.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dbdcd.c ======== ++ * Description: ++ * This file contains the implementation of the DSP/BIOS Bridge ++ * Configuration Database (DCD). ++ * ++ * Notes: ++ * The fxn DCD_GetObjects can apply a callback fxn to each DCD object ++ * that is located in a specified COFF file. At the moment, ++ * DCD_AutoRegister, DCD_AutoUnregister, and NLDR module all use ++ * DCD_GetObjects. ++ * ++ *! Revision History ++ *! ================ ++ *! 03-Dec-2003 map Changed DCD_OBJTYPE to DSP_DCDOBJTYPE ++ *! 17-Dec-2002 map Modified DCD_GetDepLibs, DCD_GetNumDepLibs, GetDepLibInfo ++ *! to include phase information ++ *! 02-Dec-2002 map Modified DCD_GetLibraryName for phases in different ++ *! libraries ++ *! 26-Feb-2003 kc Updated DCD_AutoUnregister and DCD_GetObjects to simplify ++ *! DCD implementation. ++ *! 17-Jul-2002 jeh Call COD_Open() instead of COD_OpenBase(), call COD_Close() ++ *! 11-Jul-2002 jeh Added DCD_GetDepLibs(), DCD_GetNumDepLibs(). ++ *! 18-Feb-2003 vp Code review updates ++ *! 18-Oct-2002 vp Ported to Linux platform ++ *! 15-Mar-2002 jeh Read dynamic loading memory requirements into node object ++ *! data. Added DCD_GetLibraryName(). ++ *! 13-Feb-2002 jeh Get system stack size in GetAttrsFromBuf(). ++ *! 01-Aug-2001 ag: Added check for PROC "extended" attributes used for ++ *! DSP-MMU setup. These are private attributes. ++ *! 18-Apr-2001 jeh Use COD_OpenBase instead of COD_LoadBase. ++ *! 03-Apr-2001 sg: Changed error names to DSP_EDCD* format. ++ *! 11-Jan-2001 jeh Changes to DCD_GetObjectDef to match node.cdb, proc.cdb. ++ *! 12-Dec-2000 kc: Added DCD_AutoUnregister. MSGNODE, DAISNODE added in ++ *! GetAttrsFromBuf ++ *! 22-Nov-2000 kc: Replaced sprintf() calls with strncat. ++ *! 09-Nov-2000 kc: Optimized DCD module. ++ *! 30-Oct-2000 kc: Added DCD_AutoRegister function; changed local var. names. ++ *! 29-Sep-2000 kc: Added code review changes (src/reviews/dcd_reviews.txt). ++ *! 06-Sep-2000 jeh Get message segid, message notification type. Added Atoi() ++ *! to replace atoi(), until cdb generation can output in ++ *! decimal format. ++ *! 26-Jul-2000 kc: Created. ++ *! ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++ ++/* ----------------------------------- Others */ ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ----------------------------------- Global defines. */ ++#define SIGNATURE 0x5f444344 /* "DCD_" (in reverse). */ ++ ++#define IsValidHandle(h) (((h) != NULL) && (h->dwSignature == SIGNATURE)) ++ ++#define MAX_INT2CHAR_LENGTH 16 /* Maximum int2char len of 32 bit int. */ ++ ++/* Name of section containing dependent libraries */ ++#define DEPLIBSECT ".dspbridge_deplibs" ++ ++/* DCD specific structures. */ ++struct DCD_MANAGER { ++ u32 dwSignature; /* Used for object validation. */ ++ struct COD_MANAGER *hCodMgr; /* Handle to COD manager object. */ ++}; ++ ++/* Global reference variables. */ ++static u32 cRefs; ++static u32 cEnumRefs; ++ ++extern struct GT_Mask curTrace; ++ ++/* helper function prototypes. */ ++static s32 Atoi(char *pszBuf); ++ ++static DSP_STATUS GetAttrsFromBuf(char *pszBuf, u32 ulBufSize, ++ enum DSP_DCDOBJTYPE objType, ++ struct DCD_GENERICOBJ *pGenObj); ++ ++static void CompressBuf(char *pszBuf, u32 ulBufSize, s32 cCharSize); ++ ++static char DspChar2GppChar(char *pWord, s32 cDspCharSize); ++ ++static DSP_STATUS GetDepLibInfo(IN struct DCD_MANAGER *hDcdMgr, ++ IN struct DSP_UUID *pUuid, ++ IN OUT u16 *pNumLibs, ++ OPTIONAL OUT u16 *pNumPersLibs, ++ OPTIONAL OUT struct DSP_UUID *pDepLibUuids, ++ OPTIONAL OUT bool *pPersistentDepLibs, ++ IN enum NLDR_PHASE phase); ++ ++/* ++ * ======== DCD_AutoRegister ======== ++ * Purpose: ++ * Parses the supplied image and resigsters with DCD. ++ */ ++ ++DSP_STATUS DCD_AutoRegister(IN struct DCD_MANAGER *hDcdMgr, ++ IN char *pszCoffPath) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ ++ GT_1trace(curTrace, GT_ENTER, "DCD_AutoRegister: hDcdMgr 0x%x\n", ++ hDcdMgr); ++ ++ if (IsValidHandle(hDcdMgr)) { ++ status = DCD_GetObjects(hDcdMgr, pszCoffPath, ++ (DCD_REGISTERFXN)DCD_RegisterObject, ++ (void *)pszCoffPath); ++ } else { ++ status = DSP_EHANDLE; ++ GT_0trace(curTrace, GT_6CLASS, ++ "DCD_AutoRegister: invalid DCD manager handle.\n"); ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== DCD_AutoUnregister ======== ++ * Purpose: ++ * Parses the supplied DSP image and unresiters from DCD. ++ */ ++DSP_STATUS DCD_AutoUnregister(IN struct DCD_MANAGER *hDcdMgr, ++ IN char *pszCoffPath) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ ++ GT_1trace(curTrace, GT_ENTER, "DCD_AutoUnregister: hDcdMgr 0x%x\n", ++ hDcdMgr); ++ ++ if (IsValidHandle(hDcdMgr)) { ++ status = DCD_GetObjects(hDcdMgr, pszCoffPath, ++ (DCD_REGISTERFXN)DCD_RegisterObject, ++ NULL); ++ } else { ++ status = DSP_EHANDLE; ++ GT_0trace(curTrace, GT_6CLASS, ++ "DCD_AutoUnregister: invalid DCD manager" ++ " handle.\n"); ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== DCD_CreateManager ======== ++ * Purpose: ++ * Creates DCD manager. ++ */ ++DSP_STATUS DCD_CreateManager(IN char *pszZlDllName, ++ OUT struct DCD_MANAGER **phDcdMgr) ++{ ++ struct COD_MANAGER *hCodMgr; /* COD manager handle */ ++ struct DCD_MANAGER *pDcdMgr = NULL; /* DCD Manager pointer */ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs >= 0); ++ DBC_Require(phDcdMgr); ++ ++ GT_1trace(curTrace, GT_ENTER, "DCD_CreateManager: phDcdMgr 0x%x\n", ++ phDcdMgr); ++ ++ status = COD_Create(&hCodMgr, pszZlDllName, NULL); ++ if (DSP_SUCCEEDED(status)) { ++ ++ /* Create a DCD object. */ ++ MEM_AllocObject(pDcdMgr, struct DCD_MANAGER, SIGNATURE); ++ if (pDcdMgr != NULL) { ++ ++ /* Fill out the object. */ ++ pDcdMgr->hCodMgr = hCodMgr; ++ ++ /* Return handle to this DCD interface. */ ++ *phDcdMgr = pDcdMgr; ++ ++ GT_2trace(curTrace, GT_5CLASS, ++ "DCD_CreateManager: pDcdMgr 0x%x, " ++ " hCodMgr 0x%x", pDcdMgr, hCodMgr); ++ } else { ++ status = DSP_EMEMORY; ++ ++ /* ++ * If allocation of DcdManager object failed, delete the ++ * COD manager. ++ */ ++ COD_Delete(hCodMgr); ++ ++ GT_0trace(curTrace, GT_6CLASS, ++ "DCD_CreateManager: MEM_AllocObject failed\n"); ++ } ++ } else { ++ status = DSP_EFAIL; ++ GT_0trace(curTrace, GT_6CLASS, ++ "DCD_CreateManager: COD_Create failed\n"); ++ } ++ ++ DBC_Ensure((DSP_SUCCEEDED(status)) || ((hCodMgr == NULL) && ++ (status == DSP_EFAIL)) || ((pDcdMgr == NULL) && ++ (status == DSP_EMEMORY))); ++ ++ return status; ++} ++ ++/* ++ * ======== DCD_DestroyManager ======== ++ * Purpose: ++ * Frees DCD Manager object. ++ */ ++DSP_STATUS DCD_DestroyManager(IN struct DCD_MANAGER *hDcdMgr) ++{ ++ struct DCD_MANAGER *pDcdMgr = hDcdMgr; ++ DSP_STATUS status = DSP_EHANDLE; ++ ++ DBC_Require(cRefs >= 0); ++ ++ GT_1trace(curTrace, GT_ENTER, "DCD_DestroyManager: hDcdMgr 0x%x\n", ++ hDcdMgr); ++ ++ if (IsValidHandle(hDcdMgr)) { ++ ++ /* Delete the COD manager. */ ++ COD_Delete(pDcdMgr->hCodMgr); ++ ++ /* Deallocate a DCD manager object. */ ++ MEM_FreeObject(pDcdMgr); ++ ++ status = DSP_SOK; ++ } else { ++ GT_0trace(curTrace, GT_6CLASS, ++ "DCD_DestroyManager: invalid DCD manager handle.\n"); ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== DCD_EnumerateObject ======== ++ * Purpose: ++ * Enumerates objects in the DCD. ++ */ ++DSP_STATUS DCD_EnumerateObject(IN s32 cIndex, IN enum DSP_DCDOBJTYPE objType, ++ OUT struct DSP_UUID *pUuid) ++{ ++ DSP_STATUS status = DSP_SOK; ++ char szRegKey[REG_MAXREGPATHLENGTH]; ++ char szValue[REG_MAXREGPATHLENGTH]; ++ char szData[REG_MAXREGPATHLENGTH]; ++ u32 dwValueSize; ++ u32 dwDataSize; ++ struct DSP_UUID dspUuid; ++ char szObjType[MAX_INT2CHAR_LENGTH]; /* str. rep. of objType. */ ++ u32 dwKeyLen = 0; ++ ++ DBC_Require(cRefs >= 0); ++ DBC_Require(cIndex >= 0); ++ DBC_Require(pUuid != NULL); ++ ++ GT_3trace(curTrace, GT_ENTER, ++ "DCD_EnumerateObject: cIndex %d, objType %d, " ++ " pUuid 0x%x\n", cIndex, objType, pUuid); ++ ++ if ((cIndex != 0) && (cEnumRefs == 0)) { ++ /* ++ * If an enumeration is being performed on an index greater ++ * than zero, then the current cEnumRefs must have been ++ * incremented to greater than zero. ++ */ ++ status = DSP_ECHANGEDURINGENUM; ++ } else { ++ /* Enumerate a specific key in the registry by index. */ ++ dwValueSize = REG_MAXREGPATHLENGTH; ++ dwDataSize = REG_MAXREGPATHLENGTH; ++ ++ /* ++ * Pre-determine final key length. It's length of DCD_REGKEY + ++ * "_\0" + length of szObjType string + terminating NULL. ++ */ ++ dwKeyLen = strlen(DCD_REGKEY) + 1 + sizeof(szObjType) + 1; ++ DBC_Assert(dwKeyLen < REG_MAXREGPATHLENGTH); ++ ++ /* Create proper REG key; concatenate DCD_REGKEY with ++ * objType. */ ++ strncpy(szRegKey, DCD_REGKEY, strlen(DCD_REGKEY) + 1); ++ if ((strlen(szRegKey) + strlen("_\0")) < ++ REG_MAXREGPATHLENGTH) { ++ strncat(szRegKey, "_\0", 2); ++ } else { ++ status = DSP_EFAIL; ++ } ++ ++ /* This snprintf is guaranteed not to exceed max size of an ++ * integer. */ ++ status = snprintf(szObjType, MAX_INT2CHAR_LENGTH, "%d", ++ objType); ++ ++ if (status == -1) { ++ status = DSP_EFAIL; ++ } else { ++ status = DSP_SOK; ++ if ((strlen(szRegKey) + strlen(szObjType)) < ++ REG_MAXREGPATHLENGTH) { ++ strncat(szRegKey, szObjType, ++ strlen(szObjType) + 1); ++ } else { ++ status = DSP_EFAIL; ++ } ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ status = REG_EnumValue(NULL, cIndex, szRegKey, szValue, ++ &dwValueSize, szData, ++ &dwDataSize); ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Create UUID value using string retrieved from ++ * registry. */ ++ UUID_UuidFromString(szValue, &dspUuid); ++ ++ *pUuid = dspUuid; ++ ++ /* Increment cEnumRefs to update reference count. */ ++ cEnumRefs++; ++ ++ status = DSP_SOK; ++ } else if (status == REG_E_NOMOREITEMS) { ++ /* At the end of enumeration. Reset cEnumRefs. */ ++ cEnumRefs = 0; ++ ++ status = DSP_SENUMCOMPLETE; ++ } else { ++ status = DSP_EFAIL; ++ GT_1trace(curTrace, GT_6CLASS, ++ "DCD_EnumerateObject: REG_EnumValue" ++ " failed, status = 0x%x\n", status); ++ } ++ } ++ ++ DBC_Ensure(pUuid || (status == DSP_EFAIL)); ++ ++ return status; ++} ++ ++/* ++ * ======== DCD_Exit ======== ++ * Purpose: ++ * Discontinue usage of the DCD module. ++ */ ++void DCD_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ GT_1trace(curTrace, GT_5CLASS, "DCD_Exit: cRefs 0x%x\n", cRefs); ++ ++ cRefs--; ++ if (cRefs == 0) { ++ REG_Exit(); ++ COD_Exit(); ++ MEM_Exit(); ++ } ++ ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== DCD_GetDepLibs ======== ++ */ ++DSP_STATUS DCD_GetDepLibs(IN struct DCD_MANAGER *hDcdMgr, ++ IN struct DSP_UUID *pUuid, ++ u16 numLibs, OUT struct DSP_UUID *pDepLibUuids, ++ OUT bool *pPersistentDepLibs, IN enum NLDR_PHASE phase) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(IsValidHandle(hDcdMgr)); ++ DBC_Require(pUuid != NULL); ++ DBC_Require(pDepLibUuids != NULL); ++ DBC_Require(pPersistentDepLibs != NULL); ++ ++ GT_1trace(curTrace, GT_ENTER, "DCD_GetDepLibs: hDcdMgr 0x%x\n", ++ hDcdMgr); ++ ++ status = GetDepLibInfo(hDcdMgr, pUuid, &numLibs, NULL, pDepLibUuids, ++ pPersistentDepLibs, phase); ++ ++ return status; ++} ++ ++/* ++ * ======== DCD_GetNumDepLibs ======== ++ */ ++DSP_STATUS DCD_GetNumDepLibs(IN struct DCD_MANAGER *hDcdMgr, ++ IN struct DSP_UUID *pUuid, OUT u16 *pNumLibs, ++ OUT u16 *pNumPersLibs, IN enum NLDR_PHASE phase) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(IsValidHandle(hDcdMgr)); ++ DBC_Require(pNumLibs != NULL); ++ DBC_Require(pNumPersLibs != NULL); ++ DBC_Require(pUuid != NULL); ++ ++ GT_1trace(curTrace, GT_ENTER, "DCD_GetNumDepLibs: hDcdMgr 0x%x\n", ++ hDcdMgr); ++ ++ status = GetDepLibInfo(hDcdMgr, pUuid, pNumLibs, pNumPersLibs, ++ NULL, NULL, phase); ++ ++ return status; ++} ++ ++/* ++ * ======== DCD_GetObjectDef ======== ++ * Purpose: ++ * Retrieves the properties of a node or processor based on the UUID and ++ * object type. ++ */ ++DSP_STATUS DCD_GetObjectDef(IN struct DCD_MANAGER *hDcdMgr, ++ IN struct DSP_UUID *pObjUuid, ++ IN enum DSP_DCDOBJTYPE objType, ++ OUT struct DCD_GENERICOBJ *pObjDef) ++{ ++ struct DCD_MANAGER *pDcdMgr = hDcdMgr; /* pointer to DCD manager */ ++ struct COD_LIBRARYOBJ *lib = NULL; ++ DSP_STATUS status = DSP_SOK; ++ u32 ulAddr = 0; /* Used by COD_GetSection */ ++ u32 ulLen = 0; /* Used by COD_GetSection */ ++ u32 dwBufSize; /* Used by REG functions */ ++ char szRegKey[REG_MAXREGPATHLENGTH]; ++ char *szUuid; /*[MAXUUIDLEN];*/ ++ char szRegData[REG_MAXREGPATHLENGTH]; ++ char szSectName[MAXUUIDLEN + 2]; /* ".[UUID]\0" */ ++ char *pszCoffBuf; ++ u32 dwKeyLen; /* Len of REG key. */ ++ char szObjType[MAX_INT2CHAR_LENGTH]; /* str. rep. of objType. */ ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pObjDef != NULL); ++ DBC_Require(pObjUuid != NULL); ++ ++ GT_4trace(curTrace, GT_ENTER, ++ "DCD_GetObjectDef: hDcdMgr 0x%x, " "objUuid" ++ " 0x%x, objType 0x%x, pObjDef 0x%x\n", hDcdMgr, pObjUuid, ++ objType, pObjDef); ++ szUuid = (char *)MEM_Calloc(MAXUUIDLEN, MEM_PAGED); ++ if (!szUuid) ++ return status = DSP_EMEMORY; ++ ++ if (!IsValidHandle(hDcdMgr)) { ++ status = DSP_EHANDLE; ++ GT_0trace(curTrace, GT_6CLASS, "DCD_GetObjectDef: invalid " ++ "DCD manager handle.\n"); ++ goto func_end; ++ } ++ /* Pre-determine final key length. It's length of DCD_REGKEY + ++ * "_\0" + length of szObjType string + terminating NULL */ ++ dwKeyLen = strlen(DCD_REGKEY) + 1 + sizeof(szObjType) + 1; ++ DBC_Assert(dwKeyLen < REG_MAXREGPATHLENGTH); ++ /* Create proper REG key; concatenate DCD_REGKEY with objType. */ ++ strncpy(szRegKey, DCD_REGKEY, strlen(DCD_REGKEY) + 1); ++ ++ if ((strlen(szRegKey) + strlen("_\0")) < REG_MAXREGPATHLENGTH) ++ strncat(szRegKey, "_\0", 2); ++ else ++ status = DSP_EFAIL; ++ ++ status = snprintf(szObjType, MAX_INT2CHAR_LENGTH, "%d", objType); ++ if (status == -1) { ++ status = DSP_EFAIL; ++ } else { ++ status = DSP_SOK; ++ ++ if ((strlen(szRegKey) + strlen(szObjType)) < ++ REG_MAXREGPATHLENGTH) { ++ strncat(szRegKey, szObjType, strlen(szObjType) + 1); ++ } else { ++ status = DSP_EFAIL; ++ } ++ /* Create UUID value to set in registry. */ ++ UUID_UuidToString(pObjUuid, szUuid, MAXUUIDLEN); ++ ++ if ((strlen(szRegKey) + MAXUUIDLEN) < REG_MAXREGPATHLENGTH) ++ strncat(szRegKey, szUuid, MAXUUIDLEN); ++ else ++ status = DSP_EFAIL; ++ ++ /* Retrieve paths from the registry based on struct DSP_UUID */ ++ dwBufSize = REG_MAXREGPATHLENGTH; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = REG_GetValue(NULL, szRegKey, szRegKey, (u8 *)szRegData, ++ &dwBufSize); ++ } ++ if (DSP_FAILED(status)) { ++ status = DSP_EUUID; ++ GT_0trace(curTrace, GT_6CLASS, "DCD_GetObjectDef: " ++ "REG_GetValue() failed\n"); ++ goto func_end; ++ } ++ /* Open COFF file. */ ++ status = COD_Open(pDcdMgr->hCodMgr, szRegData, COD_NOLOAD, &lib); ++ if (DSP_FAILED(status)) { ++ status = DSP_EDCDLOADBASE; ++ GT_0trace(curTrace, GT_6CLASS, "DCD_GetObjectDef: " ++ "COD_OpenBase() failed\n"); ++ goto func_end; ++ } ++ /* Ensure szUuid + 1 is not greater than sizeof szSectName. */ ++ DBC_Assert((strlen(szUuid) + 1) < sizeof(szSectName)); ++ /* Create section name based on node UUID. A period is ++ * pre-pended to the UUID string to form the section name. ++ * I.e. ".24BC8D90_BB45_11d4_B756_006008BDB66F" */ ++ strncpy(szSectName, ".", 2); ++ strncat(szSectName, szUuid, strlen(szUuid)); ++ /* Get section information. */ ++ status = COD_GetSection(lib, szSectName, &ulAddr, &ulLen); ++ if (DSP_FAILED(status)) { ++ status = DSP_EDCDGETSECT; ++ GT_0trace(curTrace, GT_6CLASS, "DCD_GetObjectDef:" ++ " COD_GetSection() failed\n"); ++ goto func_end; ++ } ++ /* Allocate zeroed buffer. */ ++ pszCoffBuf = MEM_Calloc(ulLen + 4, MEM_PAGED); ++#ifdef _DB_TIOMAP ++ if (strstr(szRegData, "iva") == NULL) { ++ /* Locate section by objectID and read its content. */ ++ status = COD_ReadSection(lib, szSectName, pszCoffBuf, ulLen); ++ } else { ++ status = COD_ReadSection(lib, szSectName, pszCoffBuf, ulLen); ++ GT_0trace(curTrace, GT_4CLASS, ++ "Skipped Byte swap for IVA !!\n"); ++ } ++#else ++ status = COD_ReadSection(lib, szSectName, pszCoffBuf, ulLen); ++#endif ++ if (DSP_SUCCEEDED(status)) { ++ /* Compres DSP buffer to conform to PC format. */ ++ if (strstr(szRegData, "iva") == NULL) { ++ CompressBuf(pszCoffBuf, ulLen, DSPWORDSIZE); ++ } else { ++ CompressBuf(pszCoffBuf, ulLen, 1); ++ GT_0trace(curTrace, GT_4CLASS, "Compressing IVA " ++ "COFF buffer by 1 for IVA !!\n"); ++ } ++ /* Parse the content of the COFF buffer. */ ++ status = GetAttrsFromBuf(pszCoffBuf, ulLen, objType, pObjDef); ++ if (DSP_FAILED(status)) { ++ status = DSP_EDCDPARSESECT; ++ GT_0trace(curTrace, GT_6CLASS, "DCD_GetObjectDef: " ++ "GetAttrsFromBuf() failed\n"); ++ } ++ } else { ++ status = DSP_EDCDREADSECT; ++ GT_0trace(curTrace, GT_6CLASS, "DCD_GetObjectDef: " ++ "COD_ReadSection() failed\n"); ++ } ++ /* Free the previously allocated dynamic buffer. */ ++ MEM_Free(pszCoffBuf); ++func_end: ++ if (lib) ++ COD_Close(lib); ++ ++ if (szUuid) ++ MEM_Free(szUuid); ++ return status; ++} ++ ++/* ++ * ======== DCD_GetObjects ======== ++ */ ++DSP_STATUS DCD_GetObjects(IN struct DCD_MANAGER *hDcdMgr, IN char *pszCoffPath, ++ DCD_REGISTERFXN registerFxn, void *handle) ++{ ++ struct DCD_MANAGER *pDcdMgr = hDcdMgr; /* pointer to DCD manager */ ++ DSP_STATUS status = DSP_SOK; ++ char *pszCoffBuf; ++ char *pszCur; ++ struct COD_LIBRARYOBJ *lib = NULL; ++ u32 ulAddr = 0; /* Used by COD_GetSection */ ++ u32 ulLen = 0; /* Used by COD_GetSection */ ++ char seps[] = ":, "; ++ char *pToken = NULL; ++ struct DSP_UUID dspUuid; ++ s32 cObjectType; ++ ++ DBC_Require(cRefs > 0); ++ GT_1trace(curTrace, GT_ENTER, ++ "DCD_GetObjects: hDcdMgr 0x%x\n", hDcdMgr); ++ if (!IsValidHandle(hDcdMgr)) { ++ status = DSP_EHANDLE; ++ GT_0trace(curTrace, GT_6CLASS, ++ "DCD_GetObjects: invalid DCD manager handle.\n"); ++ goto func_end; ++ } ++ /* Open DSP coff file, don't load symbols. */ ++ status = COD_Open(pDcdMgr->hCodMgr, pszCoffPath, COD_NOLOAD, &lib); ++ if (DSP_FAILED(status)) { ++ status = DSP_EDCDLOADBASE; ++ GT_0trace(curTrace, GT_6CLASS, ++ "DCD_AutoRegister: COD_Open() failed\n"); ++ goto func_cont; ++ } ++ /* Get DCD_RESIGER_SECTION section information. */ ++ status = COD_GetSection(lib, DCD_REGISTER_SECTION, &ulAddr, &ulLen); ++ if (DSP_FAILED(status) || !(ulLen > 0)) { ++ status = DSP_EDCDNOAUTOREGISTER; ++ GT_0trace(curTrace, GT_6CLASS, ++ "DCD_GetObjects: COD_GetSection() " ++ "- no auto register section\n"); ++ goto func_cont; ++ } ++ /* Allocate zeroed buffer. */ ++ pszCoffBuf = MEM_Calloc(ulLen + 4, MEM_PAGED); ++#ifdef _DB_TIOMAP ++ if (strstr(pszCoffPath, "iva") == NULL) { ++ /* Locate section by objectID and read its content. */ ++ status = COD_ReadSection(lib, DCD_REGISTER_SECTION, ++ pszCoffBuf, ulLen); ++ } else { ++ GT_0trace(curTrace, GT_4CLASS, "Skipped Byte swap for IVA!!\n"); ++ status = COD_ReadSection(lib, DCD_REGISTER_SECTION, ++ pszCoffBuf, ulLen); ++ } ++#else ++ status = COD_ReadSection(lib, DCD_REGISTER_SECTION, pszCoffBuf, ulLen); ++#endif ++ if (DSP_SUCCEEDED(status)) { ++ /* Compress DSP buffer to conform to PC format. */ ++ GT_0trace(curTrace, GT_4CLASS, ++ "Successfully read section !!\n"); ++ if (strstr(pszCoffPath, "iva") == NULL) { ++ CompressBuf(pszCoffBuf, ulLen, DSPWORDSIZE); ++ } else { ++ CompressBuf(pszCoffBuf, ulLen, 1); ++ GT_0trace(curTrace, GT_4CLASS, "Compress COFF buffer " ++ "with 1 word for IVA !!\n"); ++ } ++ /* Read from buffer and register object in buffer. */ ++ pszCur = pszCoffBuf; ++ while ((pToken = strsep(&pszCur, seps)) && *pToken != '\0') { ++ /* Retrieve UUID string. */ ++ UUID_UuidFromString(pToken, &dspUuid); ++ /* Retrieve object type */ ++ pToken = strsep(&pszCur, seps); ++ /* Retrieve object type */ ++ cObjectType = Atoi(pToken); ++ /* ++ * Apply registerFxn to the found DCD object. ++ * Possible actions include: ++ * ++ * 1) Register found DCD object. ++ * 2) Unregister found DCD object (when handle == NULL) ++ * 3) Add overlay node. ++ */ ++ GT_1trace(curTrace, GT_4CLASS, "Registering objtype " ++ "%d \n", cObjectType); ++ status = registerFxn(&dspUuid, cObjectType, handle); ++ if (DSP_SUCCEEDED(status)) { ++ GT_1trace(curTrace, GT_5CLASS, ++ "DCD_GetObjects: status 0x%x\n", ++ status); ++ } else { ++ GT_0trace(curTrace, GT_6CLASS, ++ "DCD_GetObjects: " ++ "registration() failed\n"); ++ /* if error occurs, break from while loop. */ ++ break; ++ } ++ } ++ } else { ++ status = DSP_EDCDREADSECT; ++ GT_0trace(curTrace, GT_6CLASS, "DCD_GetObjects: " ++ "COD_ReadSection() failed\n"); ++ } ++ /* Free the previously allocated dynamic buffer. */ ++ MEM_Free(pszCoffBuf); ++func_cont: ++ if (lib) ++ COD_Close(lib); ++ ++func_end: ++ return status; ++} ++ ++/* ++ * ======== DCD_GetLibraryName ======== ++ * Purpose: ++ * Retrieves the library name for the given UUID. ++ * ++ */ ++DSP_STATUS DCD_GetLibraryName(IN struct DCD_MANAGER *hDcdMgr, ++ IN struct DSP_UUID *pUuid, ++ IN OUT char *pstrLibName, IN OUT u32 *pdwSize, ++ enum NLDR_PHASE phase, OUT bool *fPhaseSplit) ++{ ++ char szRegKey[REG_MAXREGPATHLENGTH]; ++ char szUuid[MAXUUIDLEN]; ++ u32 dwKeyLen; /* Len of REG key. */ ++ char szObjType[MAX_INT2CHAR_LENGTH]; /* str. rep. of objType. */ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(pUuid != NULL); ++ DBC_Require(pstrLibName != NULL); ++ DBC_Require(pdwSize != NULL); ++ DBC_Require(IsValidHandle(hDcdMgr)); ++ ++ GT_4trace(curTrace, GT_ENTER, ++ "DCD_GetLibraryName: hDcdMgr 0x%x, pUuid 0x%x, " ++ " pstrLibName 0x%x, pdwSize 0x%x\n", hDcdMgr, pUuid, ++ pstrLibName, pdwSize); ++ /* ++ * Pre-determine final key length. It's length of DCD_REGKEY + ++ * "_\0" + length of szObjType string + terminating NULL. ++ */ ++ dwKeyLen = strlen(DCD_REGKEY) + 1 + sizeof(szObjType) + 1; ++ DBC_Assert(dwKeyLen < REG_MAXREGPATHLENGTH); ++ /* Create proper REG key; concatenate DCD_REGKEY with objType. */ ++ strncpy(szRegKey, DCD_REGKEY, strlen(DCD_REGKEY) + 1); ++ if ((strlen(szRegKey) + strlen("_\0")) < REG_MAXREGPATHLENGTH) ++ strncat(szRegKey, "_\0", 2); ++ else ++ status = DSP_EFAIL; ++ ++ switch (phase) { ++ case NLDR_CREATE: ++ /* create phase type */ ++ sprintf(szObjType, "%d", DSP_DCDCREATELIBTYPE); ++ break; ++ case NLDR_EXECUTE: ++ /* execute phase type */ ++ sprintf(szObjType, "%d", DSP_DCDEXECUTELIBTYPE); ++ break; ++ case NLDR_DELETE: ++ /* delete phase type */ ++ sprintf(szObjType, "%d", DSP_DCDDELETELIBTYPE); ++ break; ++ case NLDR_NOPHASE: ++ /* known to be a dependent library */ ++ sprintf(szObjType, "%d", DSP_DCDLIBRARYTYPE); ++ break; ++ default: ++ status = -1; ++ DBC_Assert(false); ++ } ++ if (status == -1) { ++ status = DSP_EFAIL; ++ } else { ++ status = DSP_SOK; ++ if ((strlen(szRegKey) + strlen(szObjType)) ++ < REG_MAXREGPATHLENGTH) { ++ strncat(szRegKey, szObjType, strlen(szObjType) + 1); ++ } else { ++ status = DSP_EFAIL; ++ } ++ /* Create UUID value to find match in registry. */ ++ UUID_UuidToString(pUuid, szUuid, MAXUUIDLEN); ++ if ((strlen(szRegKey) + MAXUUIDLEN) < ++ REG_MAXREGPATHLENGTH) { ++ strncat(szRegKey, szUuid, MAXUUIDLEN); ++ } else { ++ status = DSP_EFAIL; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Retrieve path from the registry based on DSP_UUID */ ++ status = REG_GetValue(NULL, szRegKey, szRegKey, ++ (u8 *)pstrLibName, pdwSize); ++ } ++ /* If can't find, phases might be registered as generic LIBRARYTYPE */ ++ if (DSP_FAILED(status) && phase != NLDR_NOPHASE) { ++ if (fPhaseSplit) ++ *fPhaseSplit = false; ++ ++ strncpy(szRegKey, DCD_REGKEY, strlen(DCD_REGKEY) + 1); ++ if ((strlen(szRegKey) + strlen("_\0")) < ++ REG_MAXREGPATHLENGTH) { ++ strncat(szRegKey, "_\0", 2); ++ } else { ++ status = DSP_EFAIL; ++ } ++ sprintf(szObjType, "%d", DSP_DCDLIBRARYTYPE); ++ if ((strlen(szRegKey) + strlen(szObjType)) ++ < REG_MAXREGPATHLENGTH) { ++ strncat(szRegKey, szObjType, strlen(szObjType) + 1); ++ } else { ++ status = DSP_EFAIL; ++ } ++ UUID_UuidToString(pUuid, szUuid, MAXUUIDLEN); ++ if ((strlen(szRegKey) + MAXUUIDLEN) < REG_MAXREGPATHLENGTH) ++ strncat(szRegKey, szUuid, MAXUUIDLEN); ++ else ++ status = DSP_EFAIL; ++ ++ status = REG_GetValue(NULL, szRegKey, szRegKey, ++ (u8 *)pstrLibName, pdwSize); ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== DCD_Init ======== ++ * Purpose: ++ * Initialize the DCD module. ++ */ ++bool DCD_Init(void) ++{ ++ bool fInitMEM; ++ bool fInitREG; ++ bool fInitCOD; ++ bool fInit = true; ++ ++ DBC_Require(cRefs >= 0); ++ ++ GT_1trace(curTrace, GT_ENTER, "DCD_Init: (on enter) cRefs = 0x%x\n", ++ cRefs); ++ ++ if (cRefs == 0) { ++ ++ /* Initialize required modules. */ ++ fInitMEM = MEM_Init(); ++ fInitCOD = COD_Init(); ++ fInitREG = REG_Init(); ++ if (!fInitMEM || !fInitCOD || !fInitREG) { ++ fInit = false; ++ GT_0trace(curTrace, GT_6CLASS, "DCD_Init failed\n"); ++ /* Exit initialized modules. */ ++ if (fInitMEM) ++ MEM_Exit(); ++ ++ if (fInitCOD) ++ COD_Exit(); ++ ++ if (fInitREG) ++ REG_Exit(); ++ ++ } ++ } ++ ++ if (fInit) ++ cRefs++; ++ ++ ++ GT_1trace(curTrace, GT_5CLASS, "DCD_Init: (on exit) cRefs = 0x%x\n", ++ cRefs); ++ ++ DBC_Ensure((fInit && (cRefs > 0)) || (!fInit && (cRefs == 0))); ++ ++ return fInit; ++} ++ ++/* ++ * ======== DCD_RegisterObject ======== ++ * Purpose: ++ * Registers a node or a processor with the DCD. ++ * If pszPathName == NULL, unregister the specified DCD object. ++ */ ++DSP_STATUS DCD_RegisterObject(IN struct DSP_UUID *pUuid, ++ IN enum DSP_DCDOBJTYPE objType, ++ IN char *pszPathName) ++{ ++ DSP_STATUS status = DSP_SOK; ++ char szRegKey[REG_MAXREGPATHLENGTH]; ++ char szUuid[MAXUUIDLEN + 1]; ++ u32 dwPathSize = 0; ++ u32 dwKeyLen; /* Len of REG key. */ ++ char szObjType[MAX_INT2CHAR_LENGTH]; /* str. rep. of objType. */ ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pUuid != NULL); ++ DBC_Require((objType == DSP_DCDNODETYPE) || ++ (objType == DSP_DCDPROCESSORTYPE) || ++ (objType == DSP_DCDLIBRARYTYPE) || ++ (objType == DSP_DCDCREATELIBTYPE) || ++ (objType == DSP_DCDEXECUTELIBTYPE) || ++ (objType == DSP_DCDDELETELIBTYPE)); ++ ++ GT_3trace(curTrace, GT_ENTER, "DCD_RegisterObject: object UUID 0x%x, " ++ "objType %d, szPathName %s\n", pUuid, objType, pszPathName); ++ /* ++ * Pre-determine final key length. It's length of DCD_REGKEY + ++ * "_\0" + length of szObjType string + terminating NULL. ++ */ ++ dwKeyLen = strlen(DCD_REGKEY) + 1 + sizeof(szObjType) + 1; ++ DBC_Assert(dwKeyLen < REG_MAXREGPATHLENGTH); ++ /* Create proper REG key; concatenate DCD_REGKEY with objType. */ ++ strncpy(szRegKey, DCD_REGKEY, strlen(DCD_REGKEY) + 1); ++ if ((strlen(szRegKey) + strlen("_\0")) < REG_MAXREGPATHLENGTH) ++ strncat(szRegKey, "_\0", 2); ++ else ++ status = DSP_EFAIL; ++ ++ status = snprintf(szObjType, MAX_INT2CHAR_LENGTH, "%d", objType); ++ if (status == -1) { ++ status = DSP_EFAIL; ++ } else { ++ status = DSP_SOK; ++ if ((strlen(szRegKey) + strlen(szObjType)) < ++ REG_MAXREGPATHLENGTH) { ++ strncat(szRegKey, szObjType, strlen(szObjType) + 1); ++ } else { ++ status = DSP_EFAIL; ++ } ++ /* Create UUID value to set in registry. */ ++ UUID_UuidToString(pUuid, szUuid, MAXUUIDLEN); ++ if ((strlen(szRegKey) + MAXUUIDLEN) < REG_MAXREGPATHLENGTH) ++ strncat(szRegKey, szUuid, MAXUUIDLEN); ++ else ++ status = DSP_EFAIL; ++ ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* ++ * If pszPathName != NULL, perform registration, otherwise, ++ * perform unregistration. ++ */ ++ if (pszPathName) { ++ /* Add new reg value (UUID+objType) with COFF path ++ * info. */ ++ dwPathSize = strlen(pszPathName) + 1; ++ status = REG_SetValue(NULL, szRegKey, szRegKey, REG_SZ, ++ (u8 *)pszPathName, dwPathSize); ++ GT_3trace(curTrace, GT_6CLASS, ++ "REG_SetValue REG_SZ=%d, " ++ "(u8 *)pszPathName=%s, dwPathSize=%d\n", ++ REG_SZ, pszPathName, dwPathSize); ++ if (DSP_FAILED(status)) { ++ status = DSP_EFAIL; ++ GT_0trace(curTrace, GT_6CLASS, ++ "DCD_RegisterObject: REG_SetValue failed!\n"); ++ } ++ } else { ++ /* Deregister an existing object. */ ++ status = REG_DeleteValue(NULL, szRegKey, szRegKey); ++ if (DSP_FAILED(status)) { ++ status = DSP_EFAIL; ++ GT_0trace(curTrace, GT_6CLASS, ++ "DCD_UnregisterObject: " ++ "REG_DeleteValue failed!\n"); ++ } ++ } ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* ++ * Because the node database has been updated through a ++ * successful object registration/de-registration operation, ++ * we need to reset the object enumeration counter to allow ++ * current enumerations to reflect this update in the node ++ * database. ++ */ ++ ++ cEnumRefs = 0; ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== DCD_UnregisterObject ======== ++ * Call DCD_Register object with pszPathName set to NULL to ++ * perform actual object de-registration. ++ */ ++DSP_STATUS DCD_UnregisterObject(IN struct DSP_UUID *pUuid, ++ IN enum DSP_DCDOBJTYPE objType) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pUuid != NULL); ++ DBC_Require((objType == DSP_DCDNODETYPE) || ++ (objType == DSP_DCDPROCESSORTYPE) || ++ (objType == DSP_DCDLIBRARYTYPE) || ++ (objType == DSP_DCDCREATELIBTYPE) || ++ (objType == DSP_DCDEXECUTELIBTYPE) || ++ (objType == DSP_DCDDELETELIBTYPE)); ++ ++ GT_2trace(curTrace, GT_ENTER, ++ "DCD_UnregisterObject: object UUID 0x%x, " ++ "objType %d\n", pUuid, objType); ++ ++ /* ++ * When DCD_RegisterObject is called with NULL as pathname, ++ * it indicates an unregister object operation. ++ */ ++ status = DCD_RegisterObject(pUuid, objType, NULL); ++ ++ return status; ++} ++ ++/* ++ ********************************************************************** ++ * DCD Helper Functions ++ ********************************************************************** ++ */ ++ ++/* ++ * ======== Atoi ======== ++ * Purpose: ++ * This function converts strings in decimal or hex format to integers. ++ */ ++static s32 Atoi(char *pszBuf) ++{ ++ s32 result = 0; ++ char *pch = pszBuf; ++ char c; ++ char first; ++ s32 base = 10; ++ s32 len; ++ ++ while (isspace(*pch)) ++ pch++; ++ ++ first = *pch; ++ if (first == '-' || first == '+') { ++ pch++; ++ } else { ++ /* Determine if base 10 or base 16 */ ++ len = strlen(pch); ++ if (len > 1) { ++ c = pch[1]; ++ if ((*pch == '0' && (c == 'x' || c == 'X'))) { ++ base = 16; ++ pch += 2; ++ } ++ c = pch[len - 1]; ++ if (c == 'h' || c == 'H') ++ base = 16; ++ ++ } ++ } ++ ++ while (isdigit(c = *pch) || ((base == 16) && isxdigit(c))) { ++ result *= base; ++ if ('A' <= c && c <= 'F') { ++ c = c - 'A' + 10; ++ } else { ++ if ('a' <= c && c <= 'f') ++ c = c - 'a' + 10; ++ else ++ c -= '0'; ++ ++ } ++ result += c; ++ ++pch; ++ } ++ ++ return result; ++} ++ ++/* ++ * ======== GetAttrsFromBuf ======== ++ * Purpose: ++ * Parse the content of a buffer filled with DSP-side data and ++ * retrieve an object's attributes from it. IMPORTANT: Assume the ++ * buffer has been converted from DSP format to GPP format. ++ */ ++static DSP_STATUS GetAttrsFromBuf(char *pszBuf, u32 ulBufSize, ++ enum DSP_DCDOBJTYPE objType, ++ struct DCD_GENERICOBJ *pGenObj) ++{ ++ DSP_STATUS status = DSP_SOK; ++ char seps[] = ", "; ++ char *pszCur; ++ char *token; ++ s32 cLen = 0; ++ u32 i = 0; ++#ifdef _DB_TIOMAP ++ s32 iEntry; ++#endif ++ ++ DBC_Require(pszBuf != NULL); ++ DBC_Require(ulBufSize != 0); ++ DBC_Require((objType == DSP_DCDNODETYPE) ++ || (objType == DSP_DCDPROCESSORTYPE)); ++ DBC_Require(pGenObj != NULL); ++ ++ ++ switch (objType) { ++ case DSP_DCDNODETYPE: ++ /* ++ * Parse COFF sect buffer to retrieve individual tokens used ++ * to fill in object attrs. ++ */ ++ pszCur = pszBuf; ++ token = strsep(&pszCur, seps); ++ ++ /* u32 cbStruct */ ++ pGenObj->objData.nodeObj.ndbProps.cbStruct = ++ (u32) Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ /* DSP_UUID uiNodeID */ ++ UUID_UuidFromString(token, ++ &pGenObj->objData.nodeObj.ndbProps.uiNodeID); ++ token = strsep(&pszCur, seps); ++ ++ /* acName */ ++ DBC_Require(token); ++ cLen = strlen(token); ++ if (cLen > DSP_MAXNAMELEN - 1) ++ cLen = DSP_MAXNAMELEN - 1; ++ ++ strncpy(pGenObj->objData.nodeObj.ndbProps.acName, ++ token, cLen); ++ pGenObj->objData.nodeObj.ndbProps.acName[cLen] = '\0'; ++ token = strsep(&pszCur, seps); ++ /* u32 uNodeType */ ++ pGenObj->objData.nodeObj.ndbProps.uNodeType = Atoi(token); ++ token = strsep(&pszCur, seps); ++ /* u32 bCacheOnGPP */ ++ pGenObj->objData.nodeObj.ndbProps.bCacheOnGPP = Atoi(token); ++ token = strsep(&pszCur, seps); ++ /* DSP_RESOURCEREQMTS dspResourceReqmts */ ++ pGenObj->objData.nodeObj.ndbProps.dspResourceReqmts.cbStruct = ++ (u32) Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ pGenObj->objData.nodeObj.ndbProps.dspResourceReqmts. ++ uStaticDataSize = Atoi(token); ++ token = strsep(&pszCur, seps); ++ pGenObj->objData.nodeObj.ndbProps.dspResourceReqmts. ++ uGlobalDataSize = Atoi(token); ++ token = strsep(&pszCur, seps); ++ pGenObj->objData.nodeObj.ndbProps.dspResourceReqmts. ++ uProgramMemSize = Atoi(token); ++ token = strsep(&pszCur, seps); ++ pGenObj->objData.nodeObj.ndbProps.dspResourceReqmts. ++ uWCExecutionTime = Atoi(token); ++ token = strsep(&pszCur, seps); ++ pGenObj->objData.nodeObj.ndbProps.dspResourceReqmts. ++ uWCPeriod = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ pGenObj->objData.nodeObj.ndbProps.dspResourceReqmts. ++ uWCDeadline = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ pGenObj->objData.nodeObj.ndbProps.dspResourceReqmts. ++ uAvgExectionTime = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ pGenObj->objData.nodeObj.ndbProps.dspResourceReqmts. ++ uMinimumPeriod = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ /* s32 iPriority */ ++ pGenObj->objData.nodeObj.ndbProps.iPriority = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ /* u32 uStackSize */ ++ pGenObj->objData.nodeObj.ndbProps.uStackSize = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ /* u32 uSysStackSize */ ++ pGenObj->objData.nodeObj.ndbProps.uSysStackSize = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ /* u32 uStackSeg */ ++ pGenObj->objData.nodeObj.ndbProps.uStackSeg = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ /* u32 uMessageDepth */ ++ pGenObj->objData.nodeObj.ndbProps.uMessageDepth = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ /* u32 uNumInputStreams */ ++ pGenObj->objData.nodeObj.ndbProps.uNumInputStreams = ++ Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ /* u32 uNumOutputStreams */ ++ pGenObj->objData.nodeObj.ndbProps.uNumOutputStreams = ++ Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ /* u32 uTimeout */ ++ pGenObj->objData.nodeObj.ndbProps.uTimeout = ++ Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ /* char * pstrCreatePhaseFxn */ ++ DBC_Require(token); ++ cLen = strlen(token); ++ pGenObj->objData.nodeObj.pstrCreatePhaseFxn = ++ MEM_Calloc(cLen + 1, MEM_PAGED); ++ strncpy(pGenObj->objData.nodeObj.pstrCreatePhaseFxn, ++ token, cLen); ++ pGenObj->objData.nodeObj.pstrCreatePhaseFxn[cLen] = '\0'; ++ token = strsep(&pszCur, seps); ++ ++ /* char * pstrExecutePhaseFxn */ ++ DBC_Require(token); ++ cLen = strlen(token); ++ pGenObj->objData.nodeObj.pstrExecutePhaseFxn = ++ MEM_Calloc(cLen + 1, MEM_PAGED); ++ strncpy(pGenObj->objData.nodeObj.pstrExecutePhaseFxn, ++ token, cLen); ++ pGenObj->objData.nodeObj.pstrExecutePhaseFxn[cLen] = '\0'; ++ token = strsep(&pszCur, seps); ++ ++ /* char * pstrDeletePhaseFxn */ ++ DBC_Require(token); ++ cLen = strlen(token); ++ pGenObj->objData.nodeObj.pstrDeletePhaseFxn = ++ MEM_Calloc(cLen + 1, MEM_PAGED); ++ strncpy(pGenObj->objData.nodeObj.pstrDeletePhaseFxn, ++ token, cLen); ++ pGenObj->objData.nodeObj.pstrDeletePhaseFxn[cLen] = '\0'; ++ token = strsep(&pszCur, seps); ++ ++ /* Segment id for message buffers */ ++ pGenObj->objData.nodeObj.uMsgSegid = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ /* Message notification type */ ++ pGenObj->objData.nodeObj.uMsgNotifyType = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ /* char * pstrIAlgName */ ++ if (token) { ++ cLen = strlen(token); ++ pGenObj->objData.nodeObj.pstrIAlgName = ++ MEM_Calloc(cLen + 1, MEM_PAGED); ++ strncpy(pGenObj->objData.nodeObj.pstrIAlgName, ++ token, cLen); ++ pGenObj->objData.nodeObj.pstrIAlgName[cLen] = '\0'; ++ token = strsep(&pszCur, seps); ++ } ++ ++ /* Load type (static, dynamic, or overlay) */ ++ if (token) { ++ pGenObj->objData.nodeObj.usLoadType = Atoi(token); ++ token = strsep(&pszCur, seps); ++ } ++ ++ /* Dynamic load data requirements */ ++ if (token) { ++ pGenObj->objData.nodeObj.ulDataMemSegMask = Atoi(token); ++ token = strsep(&pszCur, seps); ++ } ++ ++ /* Dynamic load code requirements */ ++ if (token) { ++ pGenObj->objData.nodeObj.ulCodeMemSegMask = Atoi(token); ++ token = strsep(&pszCur, seps); ++ } ++ ++ /* Extract node profiles into node properties */ ++ if (token) { ++ ++ pGenObj->objData.nodeObj.ndbProps.uCountProfiles = ++ Atoi(token); ++ for (i = 0; i < pGenObj->objData.nodeObj.ndbProps. ++ uCountProfiles; i++) { ++ token = strsep(&pszCur, seps); ++ if (token) { ++ /* Heap Size for the node */ ++ pGenObj->objData.nodeObj.ndbProps. ++ aProfiles[i].ulHeapSize = ++ Atoi(token); ++ } ++ } ++ } ++ token = strsep(&pszCur, seps); ++ if (token) { ++ pGenObj->objData.nodeObj.ndbProps.uStackSegName = ++ (u32)(token); ++ } ++ ++ break; ++ ++ case DSP_DCDPROCESSORTYPE: ++ /* ++ * Parse COFF sect buffer to retrieve individual tokens used ++ * to fill in object attrs. ++ */ ++ pszCur = pszBuf; ++ token = strsep(&pszCur, seps); ++ ++ pGenObj->objData.procObj.cbStruct = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ pGenObj->objData.procObj.uProcessorFamily = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ pGenObj->objData.procObj.uProcessorType = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ pGenObj->objData.procObj.uClockRate = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ pGenObj->objData.procObj.ulInternalMemSize = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ pGenObj->objData.procObj.ulExternalMemSize = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ pGenObj->objData.procObj.uProcessorID = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ pGenObj->objData.procObj.tyRunningRTOS = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ pGenObj->objData.procObj.nNodeMinPriority = Atoi(token); ++ token = strsep(&pszCur, seps); ++ ++ pGenObj->objData.procObj.nNodeMaxPriority = Atoi(token); ++ ++#ifdef _DB_TIOMAP ++ /* Proc object may contain additional(extended) attributes. */ ++ /* attr must match proc.hxx */ ++ for (iEntry = 0; iEntry < 7; iEntry++) { ++ token = strsep(&pszCur, seps); ++ pGenObj->objData.extProcObj.tyTlb[iEntry].ulGppPhys = ++ Atoi(token); ++ ++ token = strsep(&pszCur, seps); ++ pGenObj->objData.extProcObj.tyTlb[iEntry].ulDspVirt = ++ Atoi(token); ++ } ++#endif ++ ++ break; ++ ++ default: ++ status = DSP_EFAIL; ++ break; ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== CompressBuffer ======== ++ * Purpose: ++ * Compress the DSP buffer, if necessary, to conform to PC format. ++ */ ++static void CompressBuf(char *pszBuf, u32 ulBufSize, s32 cCharSize) ++{ ++ char *p; ++ char ch; ++ char *q; ++ ++ p = pszBuf; ++ if (p == NULL) ++ return; ++ ++ for (q = pszBuf; q < (pszBuf + ulBufSize);) { ++ ++ ch = DspChar2GppChar(q, cCharSize); ++ if (ch == '\\') { ++ q += cCharSize; ++ ch = DspChar2GppChar(q, cCharSize); ++ switch (ch) { ++ case 't': ++ *p = '\t'; ++ break; ++ ++ case 'n': ++ *p = '\n'; ++ break; ++ ++ case 'r': ++ *p = '\r'; ++ break; ++ ++ case '0': ++ *p = '\0'; ++ break; ++ ++ default: ++ *p = ch; ++ break; ++ } ++ } else { ++ *p = ch; ++ } ++ p++; ++ q += cCharSize; ++ } ++ ++ /* NULL out remainder of buffer. */ ++ while (p < q) ++ *p++ = '\0'; ++ ++} ++ ++/* ++ * ======== DspChar2GppChar ======== ++ * Purpose: ++ * Convert DSP char to host GPP char in a portable manner ++ */ ++static char DspChar2GppChar(char *pWord, s32 cDspCharSize) ++{ ++ char ch = '\0'; ++ char *chSrc; ++ s32 i; ++ ++ for (chSrc = pWord, i = cDspCharSize; i > 0; i--) ++ ch |= *chSrc++; ++ ++ return ch; ++} ++ ++/* ++ * ======== GetDepLibInfo ======== ++ */ ++static DSP_STATUS GetDepLibInfo(IN struct DCD_MANAGER *hDcdMgr, ++ IN struct DSP_UUID *pUuid, ++ IN OUT u16 *pNumLibs, ++ OPTIONAL OUT u16 *pNumPersLibs, ++ OPTIONAL OUT struct DSP_UUID *pDepLibUuids, ++ OPTIONAL OUT bool *pPersistentDepLibs, ++ enum NLDR_PHASE phase) ++{ ++ struct DCD_MANAGER *pDcdMgr = hDcdMgr; /* pointer to DCD manager */ ++ char *pszCoffBuf = NULL; ++ char *pszCur; ++ char *pszFileName = NULL; ++ struct COD_LIBRARYOBJ *lib = NULL; ++ u32 ulAddr = 0; /* Used by COD_GetSection */ ++ u32 ulLen = 0; /* Used by COD_GetSection */ ++ u32 dwDataSize = COD_MAXPATHLENGTH; ++ char seps[] = ", "; ++ char *pToken = NULL; ++ bool fGetUuids = (pDepLibUuids != NULL); ++ u16 nDepLibs = 0; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ ++ DBC_Require(IsValidHandle(hDcdMgr)); ++ DBC_Require(pNumLibs != NULL); ++ DBC_Require(pUuid != NULL); ++ ++ GT_1trace(curTrace, GT_ENTER, "DCD_GetNumDepLibs: hDcdMgr 0x%x\n", ++ hDcdMgr); ++ ++ /* Initialize to 0 dependent libraries, if only counting number of ++ * dependent libraries */ ++ if (!fGetUuids) { ++ *pNumLibs = 0; ++ *pNumPersLibs = 0; ++ } ++ ++ /* Allocate a buffer for file name */ ++ pszFileName = MEM_Calloc(dwDataSize, MEM_PAGED); ++ if (pszFileName == NULL) { ++ status = DSP_EMEMORY; ++ } else { ++ /* Get the name of the library */ ++ status = DCD_GetLibraryName(hDcdMgr, pUuid, pszFileName, ++ &dwDataSize, phase, NULL); ++ } ++ /* Open the library */ ++ if (DSP_SUCCEEDED(status)) { ++ status = COD_Open(pDcdMgr->hCodMgr, pszFileName, ++ COD_NOLOAD, &lib); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Get dependent library section information. */ ++ status = COD_GetSection(lib, DEPLIBSECT, &ulAddr, &ulLen); ++ ++ if (DSP_FAILED(status)) { ++ /* Ok, no dependent libraries */ ++ ulLen = 0; ++ status = DSP_SNODEPENDENTLIBS; ++ } ++ } ++ ++ if (DSP_FAILED(status) || !(ulLen > 0)) ++ goto func_cont; ++ ++ /* Allocate zeroed buffer. */ ++ pszCoffBuf = MEM_Calloc(ulLen, MEM_PAGED); ++ if (pszCoffBuf == NULL) ++ status = DSP_EMEMORY; ++ ++ /* Read section contents. */ ++ status = COD_ReadSection(lib, DEPLIBSECT, pszCoffBuf, ulLen); ++ if (DSP_FAILED(status)) ++ goto func_cont; ++ ++ /* Compress and format DSP buffer to conform to PC format. */ ++ CompressBuf(pszCoffBuf, ulLen, DSPWORDSIZE); ++ /* Read from buffer */ ++ pszCur = pszCoffBuf; ++ while ((pToken = strsep(&pszCur, seps)) && *pToken != '\0') { ++ if (fGetUuids) { ++ if (nDepLibs >= *pNumLibs) { ++ /* Gone beyond the limit */ ++ break; ++ } else { ++ /* Retrieve UUID string. */ ++ UUID_UuidFromString(pToken, ++ &(pDepLibUuids[nDepLibs])); ++ /* Is this library persistent? */ ++ pToken = strsep(&pszCur, seps); ++ pPersistentDepLibs[nDepLibs] = Atoi(pToken); ++ nDepLibs++; ++ } ++ } else { ++ /* Advanc to next token */ ++ pToken = strsep(&pszCur, seps); ++ if (Atoi(pToken)) ++ (*pNumPersLibs)++; ++ ++ /* Just counting number of dependent libraries */ ++ (*pNumLibs)++; ++ } ++ } ++func_cont: ++ if (lib) ++ COD_Close(lib); ++ ++ /* Free previously allocated dynamic buffers. */ ++ if (pszFileName) ++ MEM_Free(pszFileName); ++ ++ if (pszCoffBuf) ++ MEM_Free(pszCoffBuf); ++ ++ return status; ++} ++ +diff --git a/drivers/dsp/bridge/rmgr/disp.c b/drivers/dsp/bridge/rmgr/disp.c +new file mode 100644 +index 0000000..3fbbf01 +--- /dev/null ++++ b/drivers/dsp/bridge/rmgr/disp.c +@@ -0,0 +1,916 @@ ++/* ++ * disp.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== disp.c ======== ++ * ++ * Description: ++ * Node Dispatcher interface. Communicates with Resource Manager Server ++ * (RMS) on DSP. Access to RMS is synchronized in NODE. ++ * ++ * Public Functions: ++ * DISP_Create ++ * DISP_Delete ++ * DISP_Exit ++ * DISP_Init ++ * DISP_NodeChangePriority ++ * DISP_NodeCreate ++ * DISP_NodeDelete ++ * DISP_NodePause ++ * DISP_NodeRun ++ * ++ *! Revision History: ++ *! ================= ++ *! 18-Feb-2003 vp Code review updates ++ *! 18-Oct-2002 vp Ported to Linux platform ++ *! 16-May-2002 jeh Added DISP_DoCinit(). ++ *! 24-Apr-2002 jeh Added DISP_MemWrite(). ++ *! 13-Feb-2002 jeh Pass system stack size to RMS. ++ *! 16-Jan-2002 ag Added bufsize param to _ChnlAddIOReq() fxn ++ *! 10-May-2001 jeh Code Review cleanup. ++ *! 26-Sep-2000 jeh Fixed status values in SendMessage(). ++ *! 19-Jun-2000 jeh Created. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Link Driver */ ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++#include ++ ++/* ----------------------------------- Resource Manager */ ++#include ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++#define DISP_SIGNATURE 0x50534944 /* "PSID" */ ++ ++/* Size of a reply from RMS */ ++#define REPLYSIZE (3 * sizeof(RMS_WORD)) ++ ++/* Reserved channel offsets for communication with RMS */ ++#define CHNLTORMSOFFSET 0 ++#define CHNLFROMRMSOFFSET 1 ++ ++#define CHNLIOREQS 1 ++ ++#define SwapWord(x) (((u32)(x) >> 16) | ((u32)(x) << 16)) ++ ++/* ++ * ======== DISP_OBJECT ======== ++ */ ++struct DISP_OBJECT { ++ u32 dwSignature; /* Used for object validation */ ++ struct DEV_OBJECT *hDevObject; /* Device for this processor */ ++ struct WMD_DRV_INTERFACE *pIntfFxns; /* Function interface to WMD */ ++ struct CHNL_MGR *hChnlMgr; /* Channel manager */ ++ struct CHNL_OBJECT *hChnlToDsp; /* Channel for commands to RMS */ ++ struct CHNL_OBJECT *hChnlFromDsp; /* Channel for replies from RMS */ ++ u8 *pBuf; /* Buffer for commands, replies */ ++ u32 ulBufsize; /* pBuf size in bytes */ ++ u32 ulBufsizeRMS; /* pBuf size in RMS words */ ++ u32 uCharSize; /* Size of DSP character */ ++ u32 uWordSize; /* Size of DSP word */ ++ u32 uDataMauSize; /* Size of DSP Data MAU */ ++}; ++ ++static u32 cRefs; ++ ++/* Debug msgs: */ ++#if GT_TRACE ++static struct GT_Mask DISP_DebugMask = { NULL, NULL }; ++#endif ++ ++static void DeleteDisp(struct DISP_OBJECT *hDisp); ++static DSP_STATUS FillStreamDef(RMS_WORD *pdwBuf, u32 *ptotal, u32 offset, ++ struct NODE_STRMDEF strmDef, u32 max, ++ u32 uCharsInRMSWord); ++static DSP_STATUS SendMessage(struct DISP_OBJECT *hDisp, u32 dwTimeout, ++ u32 ulBytes, OUT u32 *pdwArg); ++ ++/* ++ * ======== DISP_Create ======== ++ * Create a NODE Dispatcher object. ++ */ ++DSP_STATUS DISP_Create(OUT struct DISP_OBJECT **phDispObject, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct DISP_ATTRS *pDispAttrs) ++{ ++ struct DISP_OBJECT *pDisp; ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ u32 ulChnlId; ++ struct CHNL_ATTRS chnlAttrs; ++ DSP_STATUS status = DSP_SOK; ++ u32 devType; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phDispObject != NULL); ++ DBC_Require(pDispAttrs != NULL); ++ DBC_Require(hDevObject != NULL); ++ ++ GT_3trace(DISP_DebugMask, GT_ENTER, "DISP_Create: phDispObject: 0x%x\t" ++ "hDevObject: 0x%x\tpDispAttrs: 0x%x\n", phDispObject, ++ hDevObject, pDispAttrs); ++ ++ *phDispObject = NULL; ++ ++ /* Allocate Node Dispatcher object */ ++ MEM_AllocObject(pDisp, struct DISP_OBJECT, DISP_SIGNATURE); ++ if (pDisp == NULL) { ++ status = DSP_EMEMORY; ++ GT_0trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_Create: MEM_AllocObject() failed!\n"); ++ } else { ++ pDisp->hDevObject = hDevObject; ++ } ++ ++ /* Get Channel manager and WMD function interface */ ++ if (DSP_SUCCEEDED(status)) { ++ status = DEV_GetChnlMgr(hDevObject, &(pDisp->hChnlMgr)); ++ if (DSP_SUCCEEDED(status)) { ++ (void) DEV_GetIntfFxns(hDevObject, &pIntfFxns); ++ pDisp->pIntfFxns = pIntfFxns; ++ } else { ++ GT_1trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_Create: Failed to get " ++ "channel manager! status = 0x%x\n", status); ++ } ++ } ++ ++ /* check device type and decide if streams or messag'ing is used for ++ * RMS/EDS */ ++ if (DSP_FAILED(status)) ++ goto func_cont; ++ ++ status = DEV_GetDevType(hDevObject, &devType); ++ GT_1trace(DISP_DebugMask, GT_6CLASS, "DISP_Create: Creating DISP for " ++ "device = 0x%x\n", devType); ++ if (DSP_FAILED(status)) ++ goto func_cont; ++ ++ if (devType != DSP_UNIT) { ++ GT_0trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_Create: Unkown device " ++ "type in Device object !! \n"); ++ status = DSP_EFAIL; ++ goto func_cont; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ pDisp->uCharSize = DSPWORDSIZE; ++ pDisp->uWordSize = DSPWORDSIZE; ++ pDisp->uDataMauSize = DSPWORDSIZE; ++ /* Open channels for communicating with the RMS */ ++ chnlAttrs.uIOReqs = CHNLIOREQS; ++ chnlAttrs.hEvent = NULL; ++ ulChnlId = pDispAttrs->ulChnlOffset + CHNLTORMSOFFSET; ++ status = (*pIntfFxns->pfnChnlOpen)(&(pDisp->hChnlToDsp), ++ pDisp->hChnlMgr, CHNL_MODETODSP, ulChnlId, &chnlAttrs); ++ if (DSP_FAILED(status)) { ++ GT_2trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_Create: Channel to RMS " ++ "open failed, chnl id = %d, status = 0x%x\n", ++ ulChnlId, status); ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ ulChnlId = pDispAttrs->ulChnlOffset + CHNLFROMRMSOFFSET; ++ status = (*pIntfFxns->pfnChnlOpen)(&(pDisp->hChnlFromDsp), ++ pDisp->hChnlMgr, CHNL_MODEFROMDSP, ulChnlId, ++ &chnlAttrs); ++ if (DSP_FAILED(status)) { ++ GT_2trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_Create: Channel from RMS " ++ "open failed, chnl id = %d, status = 0x%x\n", ++ ulChnlId, status); ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Allocate buffer for commands, replies */ ++ pDisp->ulBufsize = pDispAttrs->ulChnlBufSize; ++ pDisp->ulBufsizeRMS = RMS_COMMANDBUFSIZE; ++ pDisp->pBuf = MEM_Calloc(pDisp->ulBufsize, MEM_PAGED); ++ if (pDisp->pBuf == NULL) { ++ status = DSP_EMEMORY; ++ GT_0trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_Create: Failed " ++ "to allocate channel buffer!\n"); ++ } ++ } ++func_cont: ++ if (DSP_SUCCEEDED(status)) ++ *phDispObject = pDisp; ++ else ++ DeleteDisp(pDisp); ++ ++ DBC_Ensure(((DSP_FAILED(status)) && ((*phDispObject == NULL))) || ++ ((DSP_SUCCEEDED(status)) && ++ (MEM_IsValidHandle((*phDispObject), DISP_SIGNATURE)))); ++ return status; ++} ++ ++/* ++ * ======== DISP_Delete ======== ++ * Delete the NODE Dispatcher. ++ */ ++void DISP_Delete(struct DISP_OBJECT *hDisp) ++{ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(hDisp, DISP_SIGNATURE)); ++ ++ GT_1trace(DISP_DebugMask, GT_ENTER, ++ "DISP_Delete: hDisp: 0x%x\n", hDisp); ++ ++ DeleteDisp(hDisp); ++ ++ DBC_Ensure(!MEM_IsValidHandle(hDisp, DISP_SIGNATURE)); ++} ++ ++/* ++ * ======== DISP_Exit ======== ++ * Discontinue usage of DISP module. ++ */ ++void DISP_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ cRefs--; ++ ++ GT_1trace(DISP_DebugMask, GT_5CLASS, ++ "Entered DISP_Exit, ref count: 0x%x\n", cRefs); ++ ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== DISP_Init ======== ++ * Initialize the DISP module. ++ */ ++bool DISP_Init(void) ++{ ++ bool fRetVal = true; ++ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++ DBC_Assert(!DISP_DebugMask.flags); ++ GT_create(&DISP_DebugMask, "DI"); /* "DI" for DIspatcher */ ++ } ++ ++ if (fRetVal) ++ cRefs++; ++ ++ GT_1trace(DISP_DebugMask, GT_5CLASS, ++ "DISP_Init(), ref count: 0x%x\n", cRefs); ++ ++ DBC_Ensure((fRetVal && (cRefs > 0)) || (!fRetVal && (cRefs >= 0))); ++ return fRetVal; ++} ++ ++/* ++ * ======== DISP_NodeChangePriority ======== ++ * Change the priority of a node currently running on the target. ++ */ ++DSP_STATUS DISP_NodeChangePriority(struct DISP_OBJECT *hDisp, ++ struct NODE_OBJECT *hNode, ++ u32 ulRMSFxn, NODE_ENV nodeEnv, ++ s32 nPriority) ++{ ++ u32 dwArg; ++ struct RMS_Command *pCommand; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(hDisp, DISP_SIGNATURE)); ++ DBC_Require(hNode != NULL); ++ ++ GT_5trace(DISP_DebugMask, GT_ENTER, "DISP_NodeChangePriority: hDisp: " ++ "0x%x\thNode: 0x%x\tulRMSFxn: 0x%x\tnodeEnv: 0x%x\tnPriority\n", ++ hDisp, hNode, ulRMSFxn, nodeEnv, nPriority); ++ ++ /* Send message to RMS to change priority */ ++ pCommand = (struct RMS_Command *)(hDisp->pBuf); ++ pCommand->fxn = (RMS_WORD)(ulRMSFxn); ++ pCommand->arg1 = (RMS_WORD)nodeEnv; ++ pCommand->arg2 = nPriority; ++ status = SendMessage(hDisp, NODE_GetTimeout(hNode), ++ sizeof(struct RMS_Command), &dwArg); ++ if (DSP_FAILED(status)) { ++ GT_1trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_NodeChangePriority failed! " ++ "status = 0x%x\n", status); ++ } ++ return status; ++} ++ ++/* ++ * ======== DISP_NodeCreate ======== ++ * Create a node on the DSP by remotely calling the node's create function. ++ */ ++DSP_STATUS DISP_NodeCreate(struct DISP_OBJECT *hDisp, struct NODE_OBJECT *hNode, ++ u32 ulRMSFxn, u32 ulCreateFxn, ++ IN CONST struct NODE_CREATEARGS *pArgs, ++ OUT NODE_ENV *pNodeEnv) ++{ ++ struct NODE_MSGARGS msgArgs; ++ struct NODE_TASKARGS taskArgs; ++ struct RMS_Command *pCommand; ++ struct RMS_MsgArgs *pMsgArgs; ++ struct RMS_MoreTaskArgs *pMoreTaskArgs; ++ enum NODE_TYPE nodeType; ++ u32 dwLength; ++ RMS_WORD *pdwBuf = NULL; ++ u32 ulBytes; ++ u32 i; ++ u32 total; ++ u32 uCharsInRMSWord; ++ s32 taskArgsOffset; ++ s32 sioInDefOffset; ++ s32 sioOutDefOffset; ++ s32 sioDefsOffset; ++ s32 argsOffset = -1; ++ s32 offset; ++ struct NODE_STRMDEF strmDef; ++ u32 max; ++ DSP_STATUS status = DSP_SOK; ++ struct DSP_NODEINFO nodeInfo; ++ u32 devType; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(hDisp, DISP_SIGNATURE)); ++ DBC_Require(hNode != NULL); ++ DBC_Require(NODE_GetType(hNode) != NODE_DEVICE); ++ DBC_Require(pNodeEnv != NULL); ++ ++ GT_6trace(DISP_DebugMask, GT_ENTER, ++ "DISP_NodeCreate: hDisp: 0x%x\thNode:" ++ " 0x%x\tulRMSFxn: 0x%x\tulCreateFxn: 0x%x\tpArgs: 0x%x\tpNodeEnv:" ++ " 0x%x\n", hDisp, hNode, ulRMSFxn, ulCreateFxn, pArgs, pNodeEnv); ++ ++ status = DEV_GetDevType(hDisp->hDevObject, &devType); ++ ++ GT_1trace(DISP_DebugMask, GT_6CLASS, "DISP_Create: Creating DISP " ++ "for device = 0x%x\n", devType); ++ ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ if (devType != DSP_UNIT) { ++ GT_1trace(DISP_DebugMask, GT_7CLASS, ++ "DISP_NodeCreate unknown device " ++ "type = 0x%x\n", devType); ++ goto func_end; ++ } ++ DBC_Require(pArgs != NULL); ++ nodeType = NODE_GetType(hNode); ++ msgArgs = pArgs->asa.msgArgs; ++ max = hDisp->ulBufsizeRMS; /*Max # of RMS words that can be sent */ ++ DBC_Assert(max == RMS_COMMANDBUFSIZE); ++ uCharsInRMSWord = sizeof(RMS_WORD) / hDisp->uCharSize; ++ /* Number of RMS words needed to hold arg data */ ++ dwLength = (msgArgs.uArgLength + uCharsInRMSWord - 1) / uCharsInRMSWord; ++ /* Make sure msg args and command fit in buffer */ ++ total = sizeof(struct RMS_Command) / sizeof(RMS_WORD) + ++ sizeof(struct RMS_MsgArgs) ++ / sizeof(RMS_WORD) - 1 + dwLength; ++ if (total >= max) { ++ status = DSP_EFAIL; ++ GT_2trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_NodeCreate: Message args too" ++ " large for buffer! Message args size = %d, max = %d\n", ++ total, max); ++ } ++ /* ++ * Fill in buffer to send to RMS. ++ * The buffer will have the following format: ++ * ++ * RMS command: ++ * Address of RMS_CreateNode() ++ * Address of node's create function ++ * dummy argument ++ * node type ++ * ++ * Message Args: ++ * max number of messages ++ * segid for message buffer allocation ++ * notification type to use when message is received ++ * length of message arg data ++ * message args data ++ * ++ * Task Args (if task or socket node): ++ * priority ++ * stack size ++ * system stack size ++ * stack segment ++ * misc ++ * number of input streams ++ * pSTRMInDef[] - offsets of STRM definitions for input streams ++ * number of output streams ++ * pSTRMOutDef[] - offsets of STRM definitions for output ++ * streams ++ * STRMInDef[] - array of STRM definitions for input streams ++ * STRMOutDef[] - array of STRM definitions for output streams ++ * ++ * Socket Args (if DAIS socket node): ++ * ++ */ ++ if (DSP_SUCCEEDED(status)) { ++ total = 0; /* Total number of words in buffer so far */ ++ pdwBuf = (RMS_WORD *)hDisp->pBuf; ++ pCommand = (struct RMS_Command *)pdwBuf; ++ pCommand->fxn = (RMS_WORD)(ulRMSFxn); ++ pCommand->arg1 = (RMS_WORD)(ulCreateFxn); ++ if (NODE_GetLoadType(hNode) == NLDR_DYNAMICLOAD) { ++ /* Flush ICACHE on Load */ ++ pCommand->arg2 = 1; /* dummy argument */ ++ } else { ++ /* Do not flush ICACHE */ ++ pCommand->arg2 = 0; /* dummy argument */ ++ } ++ pCommand->data = NODE_GetType(hNode); ++ /* ++ * argsOffset is the offset of the data field in struct ++ * RMS_Command structure. We need this to calculate stream ++ * definition offsets. ++ */ ++ argsOffset = 3; ++ total += sizeof(struct RMS_Command) / sizeof(RMS_WORD); ++ /* Message args */ ++ pMsgArgs = (struct RMS_MsgArgs *) (pdwBuf + total); ++ pMsgArgs->maxMessages = msgArgs.uMaxMessages; ++ pMsgArgs->segid = msgArgs.uSegid; ++ pMsgArgs->notifyType = msgArgs.uNotifyType; ++ pMsgArgs->argLength = msgArgs.uArgLength; ++ total += sizeof(struct RMS_MsgArgs) / sizeof(RMS_WORD) - 1; ++ memcpy(pdwBuf + total, msgArgs.pData, msgArgs.uArgLength); ++ total += dwLength; ++ } ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ /* If node is a task node, copy task create arguments into buffer */ ++ if (nodeType == NODE_TASK || nodeType == NODE_DAISSOCKET) { ++ taskArgs = pArgs->asa.taskArgs; ++ taskArgsOffset = total; ++ total += sizeof(struct RMS_MoreTaskArgs) / sizeof(RMS_WORD) + ++ 1 + taskArgs.uNumInputs + taskArgs.uNumOutputs; ++ /* Copy task arguments */ ++ if (total < max) { ++ total = taskArgsOffset; ++ pMoreTaskArgs = (struct RMS_MoreTaskArgs *)(pdwBuf + ++ total); ++ /* ++ * Get some important info about the node. Note that we ++ * don't just reach into the hNode struct because ++ * that would break the node object's abstraction. ++ */ ++ GetNodeInfo(hNode, &nodeInfo); ++ GT_2trace(DISP_DebugMask, GT_ENTER, ++ "uExecutionPriority %x, nPriority %x\n", ++ nodeInfo.uExecutionPriority, ++ taskArgs.nPriority); ++ pMoreTaskArgs->priority = nodeInfo.uExecutionPriority; ++ pMoreTaskArgs->stackSize = taskArgs.uStackSize; ++ pMoreTaskArgs->sysstackSize = taskArgs.uSysStackSize; ++ pMoreTaskArgs->stackSeg = taskArgs.uStackSeg; ++ pMoreTaskArgs->heapAddr = taskArgs.uDSPHeapAddr; ++ pMoreTaskArgs->heapSize = taskArgs.uHeapSize; ++ pMoreTaskArgs->misc = taskArgs.ulDaisArg; ++ pMoreTaskArgs->numInputStreams = taskArgs.uNumInputs; ++ total += ++ sizeof(struct RMS_MoreTaskArgs) / sizeof(RMS_WORD); ++ GT_2trace(DISP_DebugMask, GT_7CLASS, ++ "DISP::::uDSPHeapAddr %x, " ++ "uHeapSize %x\n", taskArgs.uDSPHeapAddr, ++ taskArgs.uHeapSize); ++ /* Keep track of pSIOInDef[] and pSIOOutDef[] ++ * positions in the buffer, since this needs to be ++ * filled in later. */ ++ sioInDefOffset = total; ++ total += taskArgs.uNumInputs; ++ pdwBuf[total++] = taskArgs.uNumOutputs; ++ sioOutDefOffset = total; ++ total += taskArgs.uNumOutputs; ++ sioDefsOffset = total; ++ /* Fill SIO defs and offsets */ ++ offset = sioDefsOffset; ++ for (i = 0; i < taskArgs.uNumInputs; i++) { ++ if (DSP_FAILED(status)) ++ break; ++ ++ pdwBuf[sioInDefOffset + i] = ++ (offset - argsOffset) ++ * (sizeof(RMS_WORD) / DSPWORDSIZE); ++ strmDef = taskArgs.strmInDef[i]; ++ status = FillStreamDef(pdwBuf, &total, offset, ++ strmDef, max, uCharsInRMSWord); ++ offset = total; ++ } ++ for (i = 0; (i < taskArgs.uNumOutputs) && ++ (DSP_SUCCEEDED(status)); i++) { ++ pdwBuf[sioOutDefOffset + i] = ++ (offset - argsOffset) ++ * (sizeof(RMS_WORD) / DSPWORDSIZE); ++ strmDef = taskArgs.strmOutDef[i]; ++ status = FillStreamDef(pdwBuf, &total, offset, ++ strmDef, max, uCharsInRMSWord); ++ offset = total; ++ } ++ if (DSP_FAILED(status)) { ++ GT_2trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_NodeCreate: Message" ++ " args to large for buffer! Message args" ++ " size = %d, max = %d\n", total, max); ++ } ++ } else { ++ /* Args won't fit */ ++ status = DSP_EFAIL; ++ GT_2trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_NodeCreate: Message args " ++ " too large for buffer! Message args size = %d" ++ ", max = %d\n", total, max); ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ ulBytes = total * sizeof(RMS_WORD); ++ DBC_Assert(ulBytes < (RMS_COMMANDBUFSIZE * sizeof(RMS_WORD))); ++ status = SendMessage(hDisp, NODE_GetTimeout(hNode), ++ ulBytes, pNodeEnv); ++ if (DSP_FAILED(status)) { ++ GT_1trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_NodeCreate failed! " ++ "status = 0x%x\n", status); ++ } else { ++ /* ++ * Message successfully received from RMS. ++ * Return the status of the Node's create function ++ * on the DSP-side ++ */ ++ status = (((RMS_WORD *)(hDisp->pBuf))[0]); ++ if (DSP_FAILED(status)) { ++ GT_1trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_NodeCreate, " ++ "DSP-side Node Create failed: 0x%x\n", ++ status); ++ } ++ ++ } ++ } ++func_end: ++ return status; ++} ++ ++/* ++ * ======== DISP_NodeDelete ======== ++ * purpose: ++ * Delete a node on the DSP by remotely calling the node's delete function. ++ * ++ */ ++DSP_STATUS DISP_NodeDelete(struct DISP_OBJECT *hDisp, struct NODE_OBJECT *hNode, ++ u32 ulRMSFxn, u32 ulDeleteFxn, NODE_ENV nodeEnv) ++{ ++ u32 dwArg; ++ struct RMS_Command *pCommand; ++ DSP_STATUS status = DSP_SOK; ++ u32 devType; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(hDisp, DISP_SIGNATURE)); ++ DBC_Require(hNode != NULL); ++ ++ GT_5trace(DISP_DebugMask, GT_ENTER, ++ "DISP_NodeDelete: hDisp: 0x%xthNode: " ++ "0x%x\tulRMSFxn: 0x%x\tulDeleteFxn: 0x%x\tnodeEnv: 0x%x\n", ++ hDisp, hNode, ulRMSFxn, ulDeleteFxn, nodeEnv); ++ ++ status = DEV_GetDevType(hDisp->hDevObject, &devType); ++ ++ if (DSP_SUCCEEDED(status)) { ++ ++ if (devType == DSP_UNIT) { ++ ++ /* ++ * Fill in buffer to send to RMS ++ */ ++ pCommand = (struct RMS_Command *)hDisp->pBuf; ++ pCommand->fxn = (RMS_WORD)(ulRMSFxn); ++ pCommand->arg1 = (RMS_WORD)nodeEnv; ++ pCommand->arg2 = (RMS_WORD)(ulDeleteFxn); ++ pCommand->data = NODE_GetType(hNode); ++ ++ status = SendMessage(hDisp, NODE_GetTimeout(hNode), ++ sizeof(struct RMS_Command), &dwArg); ++ if (DSP_FAILED(status)) { ++ GT_1trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_NodeDelete failed!" ++ "status = 0x%x\n", status); ++ } else { ++ /* ++ * Message successfully received from RMS. ++ * Return the status of the Node's delete ++ * function on the DSP-side ++ */ ++ status = (((RMS_WORD *)(hDisp->pBuf))[0]); ++ if (DSP_FAILED(status)) { ++ GT_1trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_NodeDelete, " ++ "DSP-side Node Delete failed: 0x%x\n", ++ status); ++ } ++ } ++ ++ ++ } ++ } ++ return status; ++} ++ ++/* ++ * ======== DISP_NodeRun ======== ++ * purpose: ++ * Start execution of a node's execute phase, or resume execution of a node ++ * that has been suspended (via DISP_NodePause()) on the DSP. ++ */ ++DSP_STATUS DISP_NodeRun(struct DISP_OBJECT *hDisp, struct NODE_OBJECT *hNode, ++ u32 ulRMSFxn, u32 ulExecuteFxn, NODE_ENV nodeEnv) ++{ ++ u32 dwArg; ++ struct RMS_Command *pCommand; ++ DSP_STATUS status = DSP_SOK; ++ u32 devType; ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(hDisp, DISP_SIGNATURE)); ++ DBC_Require(hNode != NULL); ++ ++ GT_5trace(DISP_DebugMask, GT_ENTER, "DISP_NodeRun: hDisp: 0x%xthNode: \ ++ 0x%x\tulRMSFxn: 0x%x\tulExecuteFxn: 0x%x\tnodeEnv: 0x%x\n", \ ++ hDisp, hNode, ulRMSFxn, ulExecuteFxn, nodeEnv); ++ ++ status = DEV_GetDevType(hDisp->hDevObject, &devType); ++ ++ if (DSP_SUCCEEDED(status)) { ++ ++ if (devType == DSP_UNIT) { ++ ++ /* ++ * Fill in buffer to send to RMS. ++ */ ++ pCommand = (struct RMS_Command *) hDisp->pBuf; ++ pCommand->fxn = (RMS_WORD) (ulRMSFxn); ++ pCommand->arg1 = (RMS_WORD) nodeEnv; ++ pCommand->arg2 = (RMS_WORD) (ulExecuteFxn); ++ pCommand->data = NODE_GetType(hNode); ++ ++ status = SendMessage(hDisp, NODE_GetTimeout(hNode), ++ sizeof(struct RMS_Command), &dwArg); ++ if (DSP_FAILED(status)) { ++ GT_1trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_NodeRun failed!" ++ "status = 0x%x\n", status); ++ } else { ++ /* ++ * Message successfully received from RMS. ++ * Return the status of the Node's execute ++ * function on the DSP-side ++ */ ++ status = (((RMS_WORD *)(hDisp->pBuf))[0]); ++ if (DSP_FAILED(status)) { ++ GT_1trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_NodeRun, DSP-side Node " ++ "Execute failed: 0x%x\n", ++ status); ++ } ++ } ++ ++ } ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== DeleteDisp ======== ++ * purpose: ++ * Frees the resources allocated for the dispatcher. ++ */ ++static void DeleteDisp(struct DISP_OBJECT *hDisp) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ ++ if (MEM_IsValidHandle(hDisp, DISP_SIGNATURE)) { ++ pIntfFxns = hDisp->pIntfFxns; ++ ++ /* Free Node Dispatcher resources */ ++ if (hDisp->hChnlFromDsp) { ++ /* Channel close can fail only if the channel handle ++ * is invalid. */ ++ status = (*pIntfFxns->pfnChnlClose) ++ (hDisp->hChnlFromDsp); ++ if (DSP_FAILED(status)) { ++ GT_1trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_Delete: Failed to " ++ "close channel from RMS: 0x%x\n", ++ status); ++ } ++ } ++ if (hDisp->hChnlToDsp) { ++ status = (*pIntfFxns->pfnChnlClose)(hDisp->hChnlToDsp); ++ if (DSP_FAILED(status)) { ++ GT_1trace(DISP_DebugMask, GT_6CLASS, ++ "DISP_Delete: Failed to " ++ "close channel to RMS: 0x%x\n", ++ status); ++ } ++ } ++ if (hDisp->pBuf) ++ MEM_Free(hDisp->pBuf); ++ ++ MEM_FreeObject(hDisp); ++ } ++} ++ ++/* ++ * ======== FillStreamDef ======== ++ * purpose: ++ * Fills stream definitions. ++ */ ++static DSP_STATUS FillStreamDef(RMS_WORD *pdwBuf, u32 *ptotal, u32 offset, ++ struct NODE_STRMDEF strmDef, u32 max, ++ u32 uCharsInRMSWord) ++{ ++ struct RMS_StrmDef *pStrmDef; ++ u32 total = *ptotal; ++ u32 uNameLen; ++ u32 dwLength; ++ DSP_STATUS status = DSP_SOK; ++ ++ if (total + sizeof(struct RMS_StrmDef) / sizeof(RMS_WORD) >= max) { ++ status = DSP_EFAIL; ++ } else { ++ pStrmDef = (struct RMS_StrmDef *)(pdwBuf + total); ++ pStrmDef->bufsize = strmDef.uBufsize; ++ pStrmDef->nbufs = strmDef.uNumBufs; ++ pStrmDef->segid = strmDef.uSegid; ++ pStrmDef->align = strmDef.uAlignment; ++ pStrmDef->timeout = strmDef.uTimeout; ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* ++ * Since we haven't added the device name yet, subtract ++ * 1 from total. ++ */ ++ total += sizeof(struct RMS_StrmDef) / sizeof(RMS_WORD) - 1; ++ DBC_Require(strmDef.szDevice); ++ dwLength = strlen(strmDef.szDevice) + 1; ++ ++ /* Number of RMS_WORDS needed to hold device name */ ++ uNameLen = (dwLength + uCharsInRMSWord - 1) / uCharsInRMSWord; ++ ++ if (total + uNameLen >= max) { ++ status = DSP_EFAIL; ++ } else { ++ /* ++ * Zero out last word, since the device name may not ++ * extend to completely fill this word. ++ */ ++ pdwBuf[total + uNameLen - 1] = 0; ++ /** TODO USE SERVICES **/ ++ memcpy(pdwBuf + total, strmDef.szDevice, dwLength); ++ total += uNameLen; ++ *ptotal = total; ++ } ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== SendMessage ====== ++ * Send command message to RMS, get reply from RMS. ++ */ ++static DSP_STATUS SendMessage(struct DISP_OBJECT *hDisp, u32 dwTimeout, ++ u32 ulBytes, u32 *pdwArg) ++{ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ struct CHNL_OBJECT *hChnl; ++ u32 dwArg = 0; ++ u8 *pBuf; ++ struct CHNL_IOC chnlIOC; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(pdwArg != NULL); ++ ++ *pdwArg = (u32) NULL; ++ pIntfFxns = hDisp->pIntfFxns; ++ hChnl = hDisp->hChnlToDsp; ++ pBuf = hDisp->pBuf; ++ ++ /* Send the command */ ++ status = (*pIntfFxns->pfnChnlAddIOReq) (hChnl, pBuf, ulBytes, 0, ++ 0L, dwArg); ++ ++ if (DSP_FAILED(status)) { ++ GT_1trace(DISP_DebugMask, GT_6CLASS, ++ "SendMessage: Channel AddIOReq to" ++ " RMS failed! Status = 0x%x\n", status); ++ goto func_cont; ++ } ++ status = (*pIntfFxns->pfnChnlGetIOC) (hChnl, dwTimeout, &chnlIOC); ++ if (DSP_SUCCEEDED(status)) { ++ if (!CHNL_IsIOComplete(chnlIOC)) { ++ if (CHNL_IsTimedOut(chnlIOC)) { ++ status = DSP_ETIMEOUT; ++ } else { ++ GT_1trace(DISP_DebugMask, GT_6CLASS, ++ "SendMessage failed! " ++ "Channel IOC status = 0x%x\n", ++ chnlIOC.status); ++ status = DSP_EFAIL; ++ } ++ } ++ } else { ++ GT_1trace(DISP_DebugMask, GT_6CLASS, ++ "SendMessage: Channel GetIOC to" ++ " RMS failed! Status = 0x%x\n", status); ++ } ++func_cont: ++ /* Get the reply */ ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ hChnl = hDisp->hChnlFromDsp; ++ ulBytes = REPLYSIZE; ++ status = (*pIntfFxns->pfnChnlAddIOReq)(hChnl, pBuf, ulBytes, ++ 0, 0L, dwArg); ++ if (DSP_FAILED(status)) { ++ GT_1trace(DISP_DebugMask, GT_6CLASS, ++ "SendMessage: Channel AddIOReq " ++ "from RMS failed! Status = 0x%x\n", status); ++ goto func_end; ++ } ++ status = (*pIntfFxns->pfnChnlGetIOC) (hChnl, dwTimeout, &chnlIOC); ++ if (DSP_SUCCEEDED(status)) { ++ if (CHNL_IsTimedOut(chnlIOC)) { ++ status = DSP_ETIMEOUT; ++ } else if (chnlIOC.cBytes < ulBytes) { ++ /* Did not get all of the reply from the RMS */ ++ GT_1trace(DISP_DebugMask, GT_6CLASS, ++ "SendMessage: Did not get all" ++ "of reply from RMS! Bytes received: %d\n", ++ chnlIOC.cBytes); ++ status = DSP_EFAIL; ++ } else { ++ if (CHNL_IsIOComplete(chnlIOC)) { ++ DBC_Assert(chnlIOC.pBuf == pBuf); ++ status = (*((RMS_WORD *)chnlIOC.pBuf)); ++ *pdwArg = (((RMS_WORD *)(chnlIOC.pBuf))[1]); ++ } else { ++ status = DSP_EFAIL; ++ } ++ } ++ } else { ++ /* GetIOC failed */ ++ GT_1trace(DISP_DebugMask, GT_6CLASS, ++ "SendMessage: Failed to get " ++ "reply from RMS! Status = 0x%x\n", status); ++ } ++func_end: ++ return status; ++} +diff --git a/drivers/dsp/bridge/rmgr/drv.c b/drivers/dsp/bridge/rmgr/drv.c +new file mode 100644 +index 0000000..256ce12 +--- /dev/null ++++ b/drivers/dsp/bridge/rmgr/drv.c +@@ -0,0 +1,1893 @@ ++/* ++ * drv.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== drv.c ======== ++ * Description: ++ * DSP/BIOS Bridge resource allocation module. ++ * ++ * Public Functions: ++ * DRV_Create ++ * DRV_Destroy ++ * DRV_Exit ++ * DRV_GetDevObject ++ * DRV_GetDevExtension ++ * DRV_GetFirstDevObject ++ * DRV_GetNextDevObject ++ * DRV_GetNextDevExtension ++ * DRV_Init ++ * DRV_InsertDevObject ++ * DRV_RemoveDevObject ++ * DRV_RequestResources ++ * DRV_ReleaseResources ++ * ++ *! Revision History ++ *! ======== ======== ++ *! 19-Apr-2004 sb: Replaced OS specific APIs with MEM_AllocPhysMem and ++ MEM_FreePhysMem. Fixed warnings. Cosmetic updates. ++ *! 12-Apr-2004 hp: IVA clean up during bridge-uninstall ++ *! 05-Jan-2004 vp: Updated for 24xx platform ++ *! 21-Mar-2003 sb: Get SHM size from registry ++ *! 10-Feb-2003 vp: Code review updates ++ *! 18-Oct-2002 vp: Ported to Linux platform ++ *! 30-Oct-2000 kc: Modified usage of REG_SetValue. ++ *! 06-Sep-2000 jeh Read channel info into struct CFG_HOSTRES in ++ *! RequestISAResources() ++ *! 21-Sep-2000 rr: numwindows is calculated instead of default value in ++ *! RequestISAResources. ++ *! 07-Aug-2000 rr: static list of dev objects removed. ++ *! 27-Jul-2000 rr: RequestResources split into two(Request and Release) ++ *! Device extension created to hold the DevNodeString. ++ *! 17-Jul-2000 rr: Driver Object holds the list of Device Objects. ++ *! Added DRV_Create, DRV_Destroy, DRV_GetDevObject, ++ *! DRV_GetFirst/NextDevObject, DRV_Insert/RemoveDevObject. ++ *! 09-May-2000 rr: PCI Support is not L301 specific.Use of MEM_Calloc ++ *! instead of MEM_Alloc. ++ *! 28-Mar-2000 rr: PCI Support added. L301 Specific. TBD. ++ *! 03-Feb-2000 rr: GT and Module Init/exit Changes. Merged with kc. ++ *! 19-Jan-2000 rr: DBC_Ensure in RequestPCMCIA moved within PCCARD ifdef ++ *! 29-Dec-1999 rr: PCCard support for any slot.Bus type stored in the ++ *! struct CFG_HOSTRES Structure. ++ *! 17-Dec-1999 rr: if PCCARD_Init fails we return DSP_EFAIL. ++ *! DBC_Ensure checks for sucess and pDevice != NULL ++ *! 11-Dec-1999 ag: #define "Isa" renamed to "IsaBus". ++ *! 09-Dec-1999 rr: windows.h included to remove warnings. ++ *! 02-Dec-1999 rr: struct GT_Mask is with in if DEBUG. Request resources checks ++ *! status while making call to Reg functions. ++ *! 23-Nov-1999 rr: windows.h included ++ *! 19-Nov-1999 rr: DRV_RELEASE bug while setting the registry to zero. ++ *! fixed. ++ *! 12-Nov-1999 rr: RequestResources() reads values from the registry. ++ *! Hardcoded bIRQRegister define removed. ++ *! 05-Nov-1999 rr: Added hardcoded device interrupt. ++ *! 25-Oct-1999 rr: Resource structure removed. Now it uses the Host ++ *! Resource structure directly. ++ *! 15-Oct-1999 rr: Resource Structure modified. See drv.h ++ *! dwBusType taken from the registry.Hard coded ++ *! registry entries removed. ++ *! 05-Oct-1999 rr: Calling DEV_StartDevice moved to wcdce.c. DRV_Register ++ *! MiniDriver has been renamed to DRV_RequestResources. ++ *! DRV_UnRegisterMiniDriver fxn removed. ++ *! 24-Sep-1999 rr: Significant changes to the RegisterMiniDriver fxns. ++ *! Now it is simpler. IT stores the dev node in the ++ *! registry, assign resources and calls the DEV_Start. ++ *! 10-Sep-1999 rr: Register Minidriver modified. ++ *! - Resource structure follows the NT model ++ *! 08-Aug-1999 rr: Adopted for WinCE. Exports Fxns removed. Hull Created. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Others */ ++#include ++ ++/* ----------------------------------- This */ ++#include ++#include ++ ++#ifndef RES_CLEANUP_DISABLE ++#include ++#include ++#include ++#include ++#include ++#include ++#endif ++ ++/* ----------------------------------- Defines, Data Structures, Typedefs */ ++#define SIGNATURE 0x5f52474d /* "DRV_" (in reverse) */ ++ ++struct DRV_OBJECT { ++ u32 dwSignature; ++ struct LST_LIST *devList; ++ struct LST_LIST *devNodeString; ++#ifndef RES_CLEANUP_DISABLE ++ struct PROCESS_CONTEXT *procCtxtList; ++#endif ++}; ++ ++/* ++ * This is the Device Extension. Named with the Prefix ++ * DRV_ since it is living in this module ++ */ ++struct DRV_EXT { ++ struct LST_ELEM link; ++ char szString[MAXREGPATHLENGTH]; ++}; ++ ++/* ----------------------------------- Globals */ ++static s32 cRefs; ++ ++#if GT_TRACE ++extern struct GT_Mask curTrace; ++#endif ++ ++/* ----------------------------------- Function Prototypes */ ++static DSP_STATUS RequestBridgeResources(u32 dwContext, s32 fRequest); ++static DSP_STATUS RequestBridgeResourcesDSP(u32 dwContext, s32 fRequest); ++ ++#ifndef RES_CLEANUP_DISABLE ++/* GPP PROCESS CLEANUP CODE */ ++ ++static DSP_STATUS PrintProcessInformation(void); ++static DSP_STATUS DRV_ProcFreeNodeRes(HANDLE hPCtxt); ++static DSP_STATUS DRV_ProcFreeSTRMRes(HANDLE hPCtxt); ++extern enum NODE_STATE NODE_GetState(HANDLE hNode); ++ ++/* Get the process context list from driver object */ ++ ++/* Set the Process ID */ ++DSP_STATUS DRV_ProcSetPID(HANDLE hPCtxt, s32 hProcess) ++{ ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Assert(hPCtxt != NULL); ++ ++ pCtxt->pid = hProcess; ++ return status; ++} ++ ++ ++/* Getting the head of the process context list */ ++DSP_STATUS DRV_GetProcCtxtList(struct PROCESS_CONTEXT **pPctxt, ++ struct DRV_OBJECT *hDrvObject) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DRV_OBJECT *pDrvObject = (struct DRV_OBJECT *)hDrvObject; ++ ++ DBC_Assert(hDrvObject != NULL); ++ GT_2trace(curTrace, GT_ENTER, ++ "DRV_GetProcCtxtList: 2 *pPctxt:%x, pDrvObject" ++ ":%x", *pPctxt, pDrvObject); ++ *pPctxt = pDrvObject->procCtxtList; ++ GT_2trace(curTrace, GT_ENTER, ++ "DRV_GetProcCtxtList: 3 *pPctxt:%x, pDrvObject" ++ ":%x", *pPctxt, pDrvObject); ++ return status; ++} ++ ++ ++ ++/* Get a particular process context based on process handle (phProcess) */ ++DSP_STATUS DRV_GetProcContext(u32 phProcess, ++ struct DRV_OBJECT *hDrvObject, ++ HANDLE hPCtxt, DSP_HNODE hNode, ++ u32 pMapAddr) ++{ ++ struct PROCESS_CONTEXT **pCtxt = (struct PROCESS_CONTEXT **)hPCtxt; ++ DSP_STATUS status = DSP_SOK; ++ struct PROCESS_CONTEXT *pCtxtList = NULL; ++ struct DRV_OBJECT *pDrvObject = (struct DRV_OBJECT *)hDrvObject; ++ struct NODE_RES_OBJECT *pTempNode2 = NULL; ++ struct NODE_RES_OBJECT *pTempNode = NULL; ++ struct DMM_RES_OBJECT *pTempDMM2 = NULL; ++ struct DMM_RES_OBJECT *pTempDMM = NULL; ++ s32 pCtxtFound = 0; ++ ++ DBC_Assert(pDrvObject != NULL); ++ pCtxtList = pDrvObject->procCtxtList; ++ GT_0trace(curTrace, GT_ENTER, "2DRV_GetProcContext: 2"); ++ while ((pCtxtList != NULL) && (pCtxtList->pid != phProcess)) { ++ pCtxtList = pCtxtList->next; ++ GT_0trace(curTrace, GT_ENTER, "2DRV_GetProcContext: 3"); ++ } ++ if (pCtxtList == NULL) { ++ if (hNode != NULL) { ++ pCtxtList = pDrvObject->procCtxtList; ++ while ((pCtxtList != NULL) && (pCtxtFound == 0)) { ++ pTempNode = pCtxtList->pNodeList; ++ while ((pTempNode != NULL) && ++ (pTempNode->hNode != hNode)) { ++ pTempNode2 = pTempNode; ++ pTempNode = pTempNode->next; ++ } ++ if (pTempNode != NULL) { ++ pCtxtFound = 1; ++ status = DSP_SOK; ++ } else { ++ pCtxtList = pCtxtList->next; ++ } ++ } ++ } else if ((pMapAddr != 0) && (pCtxtFound == 0)) { ++ pCtxtList = pDrvObject->procCtxtList; ++ while ((pCtxtList != NULL) && (pCtxtFound == 0)) { ++ pTempDMM = pCtxtList->pDMMList; ++ while ((pTempDMM != NULL) && ++ (pTempDMM->ulDSPAddr != pMapAddr)) { ++ pTempDMM2 = pTempDMM; ++ pTempDMM = pTempDMM->next; ++ } ++ if (pTempDMM != NULL) { ++ pCtxtFound = 1; ++ status = DSP_SOK; ++ } else { ++ pCtxtList = pCtxtList->next; ++ } ++ } ++ if (pCtxtList == NULL) ++ status = DSP_ENOTFOUND; ++ ++ } ++ } else{ ++ status = DSP_SOK; ++ } ++ GT_0trace(curTrace, GT_ENTER, "2DRV_GetProcContext: 4"); ++ *pCtxt = pCtxtList; ++ return status; ++} ++ ++ ++/* Add a new process context to process context list */ ++DSP_STATUS DRV_InsertProcContext(struct DRV_OBJECT *hDrVObject, HANDLE hPCtxt) ++{ ++ struct PROCESS_CONTEXT **pCtxt = (struct PROCESS_CONTEXT **)hPCtxt; ++ DSP_STATUS status = DSP_SOK; ++ struct PROCESS_CONTEXT *pCtxtList = NULL; ++ struct DRV_OBJECT *hDRVObject; ++ ++ GT_0trace(curTrace, GT_ENTER, "\n In DRV_InsertProcContext\n"); ++ status = CFG_GetObject((u32 *)&hDRVObject, REG_DRV_OBJECT); ++ DBC_Assert(hDRVObject != NULL); ++ *pCtxt = MEM_Calloc(1 * sizeof(struct PROCESS_CONTEXT), MEM_PAGED); ++ GT_0trace(curTrace, GT_ENTER, ++ "\n In DRV_InsertProcContext Calling " ++ "DRV_GetProcCtxtList\n"); ++ DRV_GetProcCtxtList(&pCtxtList, hDRVObject); ++ GT_0trace(curTrace, GT_ENTER, ++ "\n In DRV_InsertProcContext After Calling " ++ "DRV_GetProcCtxtList\n"); ++ if (pCtxtList != NULL) { ++ GT_0trace(curTrace, GT_ENTER, ++ "\n In DRV_InsertProcContext and pCtxt is " ++ "not Null\n"); ++ while (pCtxtList->next != NULL) ++ pCtxtList = pCtxtList->next; ++ ++ pCtxtList->next = *pCtxt; ++ } else { ++ GT_0trace(curTrace, GT_ENTER, ++ "\n In DRV_InsertProcContext and " ++ "pCtxt is Null\n"); ++ hDRVObject->procCtxtList = *pCtxt; ++ } ++ return status; ++} ++ ++/* Delete a process context from process resource context list */ ++DSP_STATUS DRV_RemoveProcContext(struct DRV_OBJECT *hDRVObject, ++ HANDLE hPCtxt, HANDLE hProcess) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct PROCESS_CONTEXT *pCtxt2 = NULL; ++ struct PROCESS_CONTEXT *pTmp = NULL; ++ struct PROCESS_CONTEXT *pCtxtList = NULL; ++ ++ DBC_Assert(hDRVObject != NULL); ++ DRV_GetProcContext((u32)hProcess, hDRVObject, &pCtxt2, NULL, 0); ++ ++ GT_0trace(curTrace, GT_ENTER, "DRV_RemoveProcContext: 12"); ++ DRV_GetProcCtxtList(&pCtxtList, hDRVObject); ++ GT_0trace(curTrace, GT_ENTER, "DRV_RemoveProcContext: 13"); ++ pTmp = pCtxtList; ++ while ((pCtxtList != NULL) && (pCtxtList != pCtxt2)) { ++ pTmp = pCtxtList; ++ pCtxtList = pCtxtList->next; ++ GT_0trace(curTrace, GT_ENTER, ++ "DRV_RemoveProcContext: 2"); ++ } ++ GT_0trace(curTrace, GT_ENTER, "DRV_RemoveProcContext: 3"); ++ if (hDRVObject->procCtxtList == pCtxt2) ++ hDRVObject->procCtxtList = pCtxt2->next; ++ ++ if (pCtxtList == NULL) ++ return DSP_ENOTFOUND; ++ else if (pTmp->next != NULL) ++ pTmp->next = pTmp->next->next; ++ ++ MEM_Free(pCtxt2); ++ GT_0trace(curTrace, GT_ENTER, "DRV_RemoveProcContext: 7"); ++ ++ return status; ++} ++ ++/* Update the state of process context */ ++DSP_STATUS DRV_ProcUpdatestate(HANDLE hPCtxt, enum GPP_PROC_RES_STATE status) ++{ ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ DSP_STATUS status1 = DSP_SOK; ++ if (pCtxt != NULL) { ++ pCtxt->resState = status; ++ } else { ++ GT_0trace(curTrace, GT_ENTER, ++ "DRV_ProcUpdatestate: Failed to update " ++ "process state"); ++ } ++ return status1; ++} ++ ++/* Allocate and add a node resource element ++* This function is called from .Node_Allocate. */ ++DSP_STATUS DRV_InsertNodeResElement(HANDLE hNode, HANDLE hNodeRes, ++ HANDLE hPCtxt) ++{ ++ struct NODE_RES_OBJECT **pNodeRes = (struct NODE_RES_OBJECT **)hNodeRes; ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ DSP_STATUS status = DSP_SOK; ++ struct NODE_RES_OBJECT *pTempNodeRes = NULL; ++ GT_0trace(curTrace, GT_ENTER, "DRV_InsertNodeResElement: 1"); ++ *pNodeRes = (struct NODE_RES_OBJECT *)MEM_Calloc ++ (1 * sizeof(struct NODE_RES_OBJECT), MEM_PAGED); ++ DBC_Assert(hPCtxt != NULL); ++ if ((*pNodeRes == NULL) || (hPCtxt == NULL)) { ++ GT_0trace(curTrace, GT_ENTER, "DRV_InsertNodeResElement: 12"); ++ status = DSP_EHANDLE; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ (*pNodeRes)->hNode = hNode; ++ if (pCtxt->pNodeList != NULL) { ++ pTempNodeRes = pCtxt->pNodeList; ++ while (pTempNodeRes->next != NULL) ++ pTempNodeRes = pTempNodeRes->next; ++ ++ pTempNodeRes->next = *pNodeRes; ++ GT_0trace(curTrace, GT_ENTER, ++ "DRV_InsertNodeResElement: 2"); ++ } else { ++ pCtxt->pNodeList = *pNodeRes; ++ GT_0trace(curTrace, GT_ENTER, ++ "DRV_InsertNodeResElement: 3"); ++ } ++ } ++ GT_0trace(curTrace, GT_ENTER, "DRV_InsertNodeResElement: 4"); ++ return status; ++} ++ ++/* Release all Node resources and its context ++* This is called from .Node_Delete. */ ++DSP_STATUS DRV_RemoveNodeResElement(HANDLE hNodeRes, HANDLE hPCtxt) ++{ ++ struct NODE_RES_OBJECT *pNodeRes = (struct NODE_RES_OBJECT *)hNodeRes; ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ DSP_STATUS status = DSP_SOK; ++ struct NODE_RES_OBJECT *pTempNode2 = pCtxt->pNodeList; ++ struct NODE_RES_OBJECT *pTempNode = pCtxt->pNodeList; ++ ++ DBC_Assert(hPCtxt != NULL); ++ GT_0trace(curTrace, GT_ENTER, "\nDRV_RemoveNodeResElement: 1\n"); ++ while ((pTempNode != NULL) && (pTempNode != pNodeRes)) { ++ pTempNode2 = pTempNode; ++ pTempNode = pTempNode->next; ++ } ++ if (pCtxt->pNodeList == pNodeRes) ++ pCtxt->pNodeList = pNodeRes->next; ++ ++ if (pTempNode == NULL) ++ return DSP_ENOTFOUND; ++ else if (pTempNode2->next != NULL) ++ pTempNode2->next = pTempNode2->next->next; ++ ++ MEM_Free(pTempNode); ++ return status; ++} ++ ++/* Actual Node De-Allocation */ ++static DSP_STATUS DRV_ProcFreeNodeRes(HANDLE hPCtxt) ++{ ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ DSP_STATUS status = DSP_SOK; ++ struct NODE_RES_OBJECT *pNodeList = NULL; ++ struct NODE_RES_OBJECT *pNodeRes = NULL; ++ u32 nState; ++ ++ DBC_Assert(hPCtxt != NULL); ++ pNodeList = pCtxt->pNodeList; ++ while (pNodeList != NULL) { ++ GT_0trace(curTrace, GT_ENTER, "DRV_ProcFreeNodeRes: 1"); ++ pNodeRes = pNodeList; ++ pNodeList = pNodeList->next; ++ if (pNodeRes->nodeAllocated) { ++ nState = NODE_GetState(pNodeRes->hNode) ; ++ GT_1trace(curTrace, GT_5CLASS, ++ "DRV_ProcFreeNodeRes: Node state %x\n", nState); ++ if (nState <= NODE_DELETING) { ++ if ((nState == NODE_RUNNING) || ++ (nState == NODE_PAUSED) || ++ (nState == NODE_TERMINATING)) { ++ GT_1trace(curTrace, GT_5CLASS, ++ "Calling Node_Terminate for Node:" ++ " 0x%x\n", pNodeRes->hNode); ++ status = NODE_Terminate ++ (pNodeRes->hNode, &status); ++ GT_1trace(curTrace, GT_5CLASS, ++ "Calling Node_Delete for Node:" ++ " 0x%x\n", pNodeRes->hNode); ++ status = NODE_Delete(pNodeRes->hNode); ++ GT_1trace(curTrace, GT_5CLASS, ++ "the status after the NodeDelete %x\n", ++ status); ++ } else if ((nState == NODE_ALLOCATED) ++ || (nState == NODE_CREATED)) ++ status = NODE_Delete(pNodeRes->hNode); ++ } ++ } ++ pNodeRes->nodeAllocated = 0; ++ } ++ return status; ++} ++ ++/* Allocate the DMM resource element ++* This is called from Proc_Map. after the actual resource is allocated */ ++DSP_STATUS DRV_InsertDMMResElement(HANDLE hDMMRes, HANDLE hPCtxt) ++{ ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ struct DMM_RES_OBJECT **pDMMRes = (struct DMM_RES_OBJECT **)hDMMRes; ++ DSP_STATUS status = DSP_SOK; ++ struct DMM_RES_OBJECT *pTempDMMRes = NULL; ++ ++ *pDMMRes = (struct DMM_RES_OBJECT *) ++ MEM_Calloc(1 * sizeof(struct DMM_RES_OBJECT), MEM_PAGED); ++ DBC_Assert(hPCtxt != NULL); ++ GT_0trace(curTrace, GT_ENTER, "DRV_InsertDMMResElement: 1"); ++ if ((*pDMMRes == NULL) || (hPCtxt == NULL)) { ++ GT_0trace(curTrace, GT_5CLASS, "DRV_InsertDMMResElement: 2"); ++ status = DSP_EHANDLE; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ if (pCtxt->pDMMList != NULL) { ++ GT_0trace(curTrace, GT_5CLASS, ++ "DRV_InsertDMMResElement: 3"); ++ pTempDMMRes = pCtxt->pDMMList; ++ while (pTempDMMRes->next != NULL) ++ pTempDMMRes = pTempDMMRes->next; ++ ++ pTempDMMRes->next = *pDMMRes; ++ } else { ++ pCtxt->pDMMList = *pDMMRes; ++ GT_0trace(curTrace, GT_5CLASS, ++ "DRV_InsertDMMResElement: 4"); ++ } ++ } ++ GT_0trace(curTrace, GT_ENTER, "DRV_InsertDMMResElement: 5"); ++ return status; ++} ++ ++ ++ ++/* Release DMM resource element context ++* This is called from Proc_UnMap. after the actual resource is freed */ ++DSP_STATUS DRV_RemoveDMMResElement(HANDLE hDMMRes, HANDLE hPCtxt) ++{ ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ struct DMM_RES_OBJECT *pDMMRes = (struct DMM_RES_OBJECT *)hDMMRes; ++ DSP_STATUS status = DSP_SOK; ++ struct DMM_RES_OBJECT *pTempDMMRes2 = NULL; ++ struct DMM_RES_OBJECT *pTempDMMRes = NULL; ++ ++ DBC_Assert(hPCtxt != NULL); ++ pTempDMMRes2 = pCtxt->pDMMList; ++ pTempDMMRes = pCtxt->pDMMList; ++ GT_0trace(curTrace, GT_ENTER, "DRV_RemoveDMMResElement: 1"); ++ while ((pTempDMMRes != NULL) && (pTempDMMRes != pDMMRes)) { ++ GT_0trace(curTrace, GT_ENTER, "DRV_RemoveDMMResElement: 2"); ++ pTempDMMRes2 = pTempDMMRes; ++ pTempDMMRes = pTempDMMRes->next; ++ } ++ GT_0trace(curTrace, GT_ENTER, "DRV_RemoveDMMResElement: 3"); ++ if (pCtxt->pDMMList == pTempDMMRes) ++ pCtxt->pDMMList = pTempDMMRes->next; ++ ++ if (pTempDMMRes == NULL) ++ return DSP_ENOTFOUND; ++ else if (pTempDMMRes2->next != NULL) ++ pTempDMMRes2->next = pTempDMMRes2->next->next; ++ ++ MEM_Free(pDMMRes); ++ GT_0trace(curTrace, GT_ENTER, "DRV_RemoveDMMResElement: 4"); ++ return status; ++} ++ ++/* Update DMM resource status */ ++DSP_STATUS DRV_UpdateDMMResElement(HANDLE hDMMRes, u32 pMpuAddr, u32 ulSize, ++ u32 pReqAddr, u32 pMapAddr, ++ HANDLE hProcessor) ++{ ++ struct DMM_RES_OBJECT *pDMMRes = (struct DMM_RES_OBJECT *)hDMMRes; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Assert(hDMMRes != NULL); ++ pDMMRes->ulMpuAddr = pMpuAddr; ++ pDMMRes->ulDSPAddr = pMapAddr; ++ pDMMRes->ulDSPResAddr = pReqAddr; ++ pDMMRes->dmmSize = ulSize; ++ pDMMRes->hProcessor = hProcessor; ++ pDMMRes->dmmAllocated = 1; ++ ++ return status; ++} ++ ++/* Actual DMM De-Allocation */ ++DSP_STATUS DRV_ProcFreeDMMRes(HANDLE hPCtxt) ++{ ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ DSP_STATUS status = DSP_SOK; ++ struct DMM_RES_OBJECT *pDMMList = pCtxt->pDMMList; ++ struct DMM_RES_OBJECT *pDMMRes = NULL; ++ ++ DBC_Assert(hPCtxt != NULL); ++ GT_0trace(curTrace, GT_ENTER, "\nDRV_ProcFreeDMMRes: 1\n"); ++ while (pDMMList != NULL) { ++ pDMMRes = pDMMList; ++ pDMMList = pDMMList->next; ++ if (pDMMRes->dmmAllocated) { ++ status = PROC_UnMap(pDMMRes->hProcessor, ++ (void *)pDMMRes->ulDSPResAddr); ++ status = PROC_UnReserveMemory(pDMMRes->hProcessor, ++ (void *)pDMMRes->ulDSPResAddr); ++ pDMMRes->dmmAllocated = 0; ++ } ++ } ++ return status; ++} ++ ++ ++/* Release all DMM resources and its context ++* This is called from .bridge_release. */ ++DSP_STATUS DRV_RemoveAllDMMResElements(HANDLE hPCtxt) ++{ ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ DSP_STATUS status = DSP_SOK; ++ struct DMM_RES_OBJECT *pTempDMMRes2 = NULL; ++ struct DMM_RES_OBJECT *pTempDMMRes = NULL; ++ ++ DBC_Assert(pCtxt != NULL); ++ DRV_ProcFreeDMMRes(pCtxt); ++ pTempDMMRes = pCtxt->pDMMList; ++ while (pTempDMMRes != NULL) { ++ pTempDMMRes2 = pTempDMMRes; ++ pTempDMMRes = pTempDMMRes->next; ++ MEM_Free(pTempDMMRes2); ++ } ++ pCtxt->pDMMList = NULL; ++ return status; ++} ++ ++DSP_STATUS DRV_GetDMMResElement(u32 pMapAddr, HANDLE hDMMRes, HANDLE hPCtxt) ++{ ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ struct DMM_RES_OBJECT **pDMMRes = (struct DMM_RES_OBJECT **)hDMMRes; ++ DSP_STATUS status = DSP_SOK; ++ struct DMM_RES_OBJECT *pTempDMM2 = NULL; ++ struct DMM_RES_OBJECT *pTempDMM = NULL; ++ ++ DBC_Assert(hPCtxt != NULL); ++ pTempDMM = pCtxt->pDMMList; ++ while ((pTempDMM != NULL) && (pTempDMM->ulDSPAddr != pMapAddr)) { ++ GT_3trace(curTrace, GT_ENTER, ++ "DRV_GetDMMResElement: 2 pTempDMM:%x " ++ "pTempDMM->ulDSPAddr:%x pMapAddr:%x\n", pTempDMM, ++ pTempDMM->ulDSPAddr, pMapAddr); ++ pTempDMM2 = pTempDMM; ++ pTempDMM = pTempDMM->next; ++ } ++ if (pTempDMM != NULL) { ++ GT_0trace(curTrace, GT_ENTER, "DRV_GetDMMResElement: 3"); ++ *pDMMRes = pTempDMM; ++ } else { ++ status = DSP_ENOTFOUND; ++ } GT_0trace(curTrace, GT_ENTER, "DRV_GetDMMResElement: 4"); ++ return status; ++} ++ ++/* Update Node allocation status */ ++void DRV_ProcNodeUpdateStatus(HANDLE hNodeRes, s32 status) ++{ ++ struct NODE_RES_OBJECT *pNodeRes = (struct NODE_RES_OBJECT *)hNodeRes; ++ DBC_Assert(hNodeRes != NULL); ++ pNodeRes->nodeAllocated = status; ++} ++ ++/* Update Node Heap status */ ++void DRV_ProcNodeUpdateHeapStatus(HANDLE hNodeRes, s32 status) ++{ ++ struct NODE_RES_OBJECT *pNodeRes = (struct NODE_RES_OBJECT *)hNodeRes; ++ DBC_Assert(hNodeRes != NULL); ++ pNodeRes->heapAllocated = status; ++} ++ ++/* Release all Node resources and its context ++* This is called from .bridge_release. ++*/ ++DSP_STATUS DRV_RemoveAllNodeResElements(HANDLE hPCtxt) ++{ ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ DSP_STATUS status = DSP_SOK; ++ struct NODE_RES_OBJECT *pTempNode2 = NULL; ++ struct NODE_RES_OBJECT *pTempNode = NULL; ++ ++ DBC_Assert(hPCtxt != NULL); ++ DRV_ProcFreeNodeRes(pCtxt); ++ pTempNode = pCtxt->pNodeList; ++ while (pTempNode != NULL) { ++ pTempNode2 = pTempNode; ++ pTempNode = pTempNode->next; ++ MEM_Free(pTempNode2); ++ } ++ pCtxt->pNodeList = NULL; ++ return status; ++} ++ ++/* Getting the node resource element */ ++ ++DSP_STATUS DRV_GetNodeResElement(HANDLE hNode, HANDLE hNodeRes, HANDLE hPCtxt) ++{ ++ struct NODE_RES_OBJECT **nodeRes = (struct NODE_RES_OBJECT **)hNodeRes; ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ DSP_STATUS status = DSP_SOK; ++ struct NODE_RES_OBJECT *pTempNode2 = NULL; ++ struct NODE_RES_OBJECT *pTempNode = NULL; ++ ++ DBC_Assert(hPCtxt != NULL); ++ pTempNode = pCtxt->pNodeList; ++ GT_0trace(curTrace, GT_ENTER, "DRV_GetNodeResElement: 1"); ++ while ((pTempNode != NULL) && (pTempNode->hNode != hNode)) { ++ pTempNode2 = pTempNode; ++ pTempNode = pTempNode->next; ++ } ++ if (pTempNode != NULL) ++ *nodeRes = pTempNode; ++ else ++ status = DSP_ENOTFOUND; ++ ++ return status; ++} ++ ++ ++ ++/* Allocate the STRM resource element ++* This is called after the actual resource is allocated ++*/ ++DSP_STATUS DRV_ProcInsertSTRMResElement(HANDLE hStreamHandle, HANDLE hSTRMRes, ++ HANDLE hPCtxt) ++{ ++ struct STRM_RES_OBJECT **pSTRMRes = (struct STRM_RES_OBJECT **)hSTRMRes; ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ DSP_STATUS status = DSP_SOK; ++ struct STRM_RES_OBJECT *pTempSTRMRes = NULL; ++ DBC_Assert(hPCtxt != NULL); ++ ++ *pSTRMRes = (struct STRM_RES_OBJECT *) ++ MEM_Calloc(1 * sizeof(struct STRM_RES_OBJECT), MEM_PAGED); ++ if ((*pSTRMRes == NULL) || (hPCtxt == NULL)) { ++ GT_0trace(curTrace, GT_ENTER, "DRV_InsertSTRMResElement: 2"); ++ status = DSP_EHANDLE; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ (*pSTRMRes)->hStream = hStreamHandle; ++ if (pCtxt->pSTRMList != NULL) { ++ GT_0trace(curTrace, GT_ENTER, ++ "DRV_InsertiSTRMResElement: 3"); ++ pTempSTRMRes = pCtxt->pSTRMList; ++ while (pTempSTRMRes->next != NULL) ++ pTempSTRMRes = pTempSTRMRes->next; ++ ++ pTempSTRMRes->next = *pSTRMRes; ++ } else { ++ pCtxt->pSTRMList = *pSTRMRes; ++ GT_0trace(curTrace, GT_ENTER, ++ "DRV_InsertSTRMResElement: 4"); ++ } ++ } ++ return status; ++} ++ ++ ++ ++/* Release Stream resource element context ++* This function called after the actual resource is freed ++*/ ++DSP_STATUS DRV_ProcRemoveSTRMResElement(HANDLE hSTRMRes, HANDLE hPCtxt) ++{ ++ struct STRM_RES_OBJECT *pSTRMRes = (struct STRM_RES_OBJECT *)hSTRMRes; ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ DSP_STATUS status = DSP_SOK; ++ struct STRM_RES_OBJECT *pTempSTRMRes2 = pCtxt->pSTRMList; ++ struct STRM_RES_OBJECT *pTempSTRMRes = pCtxt->pSTRMList; ++ ++ DBC_Assert(hPCtxt != NULL); ++ while ((pTempSTRMRes != NULL) && (pTempSTRMRes != pSTRMRes)) { ++ pTempSTRMRes2 = pTempSTRMRes; ++ pTempSTRMRes = pTempSTRMRes->next; ++ } ++ if (pCtxt->pSTRMList == pTempSTRMRes) ++ pCtxt->pSTRMList = pTempSTRMRes->next; ++ ++ if (pTempSTRMRes == NULL) ++ status = DSP_ENOTFOUND; ++ else if (pTempSTRMRes2->next != NULL) ++ pTempSTRMRes2->next = pTempSTRMRes2->next->next; ++ ++ MEM_Free(pSTRMRes); ++ return status; ++} ++ ++ ++/* Actual Stream De-Allocation */ ++static DSP_STATUS DRV_ProcFreeSTRMRes(HANDLE hPCtxt) ++{ ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ DSP_STATUS status = DSP_SOK; ++ DSP_STATUS status1 = DSP_SOK; ++ u8 **apBuffer = NULL; ++ struct STRM_RES_OBJECT *pSTRMList = NULL; ++ struct STRM_RES_OBJECT *pSTRMRes = NULL; ++ u8 *pBufPtr; ++ u32 ulBytes; ++ u32 dwArg; ++ s32 ulBufSize; ++ ++ ++ DBC_Assert(hPCtxt != NULL); ++ pSTRMList = pCtxt->pSTRMList; ++ while (pSTRMList != NULL) { ++ pSTRMRes = pSTRMList; ++ pSTRMList = pSTRMList->next; ++ if (pSTRMRes->uNumBufs != 0) { ++ apBuffer = MEM_Alloc((pSTRMRes->uNumBufs * ++ sizeof(u8 *)), MEM_NONPAGED); ++ status = STRM_FreeBuffer(pSTRMRes->hStream, apBuffer, ++ pSTRMRes->uNumBufs); ++ MEM_Free(apBuffer); ++ } ++ status = STRM_Close(pSTRMRes->hStream); ++ if (DSP_FAILED(status)) { ++ if (status == DSP_EPENDING) { ++ status = STRM_Reclaim(pSTRMRes->hStream, ++ &pBufPtr, &ulBytes, ++ (u32 *)&ulBufSize, &dwArg); ++ if (DSP_SUCCEEDED(status)) ++ status = STRM_Close(pSTRMRes->hStream); ++ ++ } ++ } ++ } ++ return status1; ++} ++ ++/* Release all Stream resources and its context ++* This is called from .bridge_release. ++*/ ++DSP_STATUS DRV_RemoveAllSTRMResElements(HANDLE hPCtxt) ++{ ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ DSP_STATUS status = DSP_SOK; ++ struct STRM_RES_OBJECT *pTempSTRMRes2 = NULL; ++ struct STRM_RES_OBJECT *pTempSTRMRes = NULL; ++ ++ DBC_Assert(hPCtxt != NULL); ++ DRV_ProcFreeSTRMRes(pCtxt); ++ pTempSTRMRes = pCtxt->pSTRMList; ++ while (pTempSTRMRes != NULL) { ++ pTempSTRMRes2 = pTempSTRMRes; ++ pTempSTRMRes = pTempSTRMRes->next; ++ MEM_Free(pTempSTRMRes2); ++ } ++ pCtxt->pSTRMList = NULL; ++ return status; ++} ++ ++ ++/* Getting the stream resource element */ ++DSP_STATUS DRV_GetSTRMResElement(HANDLE hStrm, HANDLE hSTRMRes, HANDLE hPCtxt) ++{ ++ struct STRM_RES_OBJECT **STRMRes = (struct STRM_RES_OBJECT **)hSTRMRes; ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ DSP_STATUS status = DSP_SOK; ++ struct STRM_RES_OBJECT *pTempSTRM2 = NULL; ++ struct STRM_RES_OBJECT *pTempSTRM = pCtxt->pSTRMList; ++ ++ DBC_Assert(hPCtxt != NULL); ++ while ((pTempSTRM != NULL) && (pTempSTRM->hStream != hStrm)) { ++ GT_0trace(curTrace, GT_ENTER, "DRV_GetSTRMResElement: 2"); ++ pTempSTRM2 = pTempSTRM; ++ pTempSTRM = pTempSTRM->next; ++ } ++ if (pTempSTRM != NULL) { ++ GT_0trace(curTrace, GT_ENTER, "DRV_GetSTRMResElement: 3"); ++ *STRMRes = pTempSTRM; ++ } else { ++ GT_0trace(curTrace, GT_ENTER, "DRV_GetSTRMResElement: 4"); ++ status = DSP_ENOTFOUND; ++ } ++ GT_0trace(curTrace, GT_ENTER, "DRV_GetSTRMResElement: 5"); ++ return status; ++} ++ ++/* Updating the stream resource element */ ++DSP_STATUS DRV_ProcUpdateSTRMRes(u32 uNumBufs, HANDLE hSTRMRes, HANDLE hPCtxt) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct STRM_RES_OBJECT **STRMRes = (struct STRM_RES_OBJECT **)hSTRMRes; ++ ++ DBC_Assert(hPCtxt != NULL); ++ (*STRMRes)->uNumBufs = uNumBufs; ++ return status; ++} ++ ++/* Displaying the resources allocated by a process */ ++DSP_STATUS DRV_ProcDisplayResInfo(u8 *pBuf1, u32 *pSize) ++{ ++ struct PROCESS_CONTEXT *pCtxt = NULL; ++ struct NODE_RES_OBJECT *pNodeRes = NULL; ++ struct DMM_RES_OBJECT *pDMMRes = NULL; ++ struct STRM_RES_OBJECT *pSTRMRes = NULL; ++ struct DSPHEAP_RES_OBJECT *pDSPHEAPRes = NULL; ++ u32 tempCount = 1; ++ HANDLE hDrvObject = NULL; ++ void *pBuf = pBuf1; ++ u8 pTempBuf[250]; ++ u32 tempStrLen = 0, tempStrLen2 = 0; ++ DSP_STATUS status = DSP_SOK; ++ ++ CFG_GetObject((u32 *)&hDrvObject, REG_DRV_OBJECT); ++ DRV_GetProcCtxtList(&pCtxt, (struct DRV_OBJECT *)hDrvObject); ++ GT_0trace(curTrace, GT_ENTER, "*********************" ++ "DRV_ProcDisplayResourceInfo:*\n"); ++ while (pCtxt != NULL) { ++ tempStrLen2 = sprintf((char *)pTempBuf, ++ "-------------------------------------" ++ "-----------------------------------\n"); ++ tempStrLen2 += 2; ++ memmove(pBuf+tempStrLen, pTempBuf, tempStrLen2); ++ tempStrLen += tempStrLen2; ++ if (pCtxt->resState == PROC_RES_ALLOCATED) { ++ tempStrLen2 = sprintf((char *)pTempBuf, ++ "GPP Process Resource State: " ++ "pCtxt->resState = PROC_RES_ALLOCATED, " ++ " Process ID: %d\n", pCtxt->pid); ++ tempStrLen2 += 2; ++ memmove(pBuf+tempStrLen, pTempBuf, tempStrLen2); ++ tempStrLen += tempStrLen2; ++ } else { ++ tempStrLen2 = sprintf((char *)pTempBuf, ++ "GPP Resource State: pCtxt->resState" ++ " = PROC_RES_DEALLOCATED, Process ID:%d\n", ++ pCtxt->pid); ++ tempStrLen2 += 2; ++ memmove(pBuf+tempStrLen, pTempBuf, tempStrLen2); ++ tempStrLen += tempStrLen2; ++ } ++ pNodeRes = pCtxt->pNodeList; ++ tempCount = 1; ++ while (pNodeRes != NULL) { ++ GT_2trace(curTrace, GT_ENTER, ++ "DRV_ProcDisplayResourceInfo: #:%d " ++ "pCtxt->pNodeList->hNode:%x\n", ++ tempCount, pNodeRes->hNode); ++ tempStrLen2 = sprintf((char *)pTempBuf, ++ "Node Resource Information: Node #" ++ " %d Node Handle hNode:0X%x\n", ++ tempCount, (u32)pNodeRes->hNode); ++ pNodeRes = pNodeRes->next; ++ tempStrLen2 += 2; ++ memmove(pBuf+tempStrLen, pTempBuf, tempStrLen2); ++ tempStrLen += tempStrLen2; ++ tempCount++; ++ } ++ tempCount = 1; ++ pDSPHEAPRes = pCtxt->pDSPHEAPList; ++ while (pDSPHEAPRes != NULL) { ++ GT_2trace(curTrace, GT_ENTER, ++ "DRV_ProcDisplayResourceInfo: #:%d " ++ "pCtxt->pDSPHEAPRList->ulMpuAddr:%x\n", ++ tempCount, pDSPHEAPRes->ulMpuAddr); ++ tempStrLen2 = sprintf((char *)pTempBuf, ++ "DSP Heap Resource Info: HEAP # %d" ++ " Mapped GPP Address: 0x%x, size: 0x%x\n", ++ tempCount, (u32)pDSPHEAPRes->ulMpuAddr, ++ (u32)pDSPHEAPRes->heapSize); ++ pDSPHEAPRes = pDSPHEAPRes->next; ++ tempStrLen2 += 2; ++ memmove(pBuf+tempStrLen, pTempBuf, tempStrLen2); ++ tempStrLen += tempStrLen2; ++ tempCount++; ++ } ++ tempCount = 1; ++ pDMMRes = pCtxt->pDMMList; ++ while (pDMMRes != NULL) { ++ GT_2trace(curTrace, GT_ENTER, ++ "DRV_ProcDisplayResourceInfo: #:%d " ++ " pCtxt->pDMMList->ulMpuAddr:%x\n", ++ tempCount, ++ pDMMRes->ulMpuAddr); ++ tempStrLen2 = sprintf((char *)pTempBuf, ++ "DMM Resource Info: DMM # %d Mapped" ++ " GPP Address: 0x%x, size: 0x%x\n", ++ tempCount, (u32)pDMMRes->ulMpuAddr, ++ (u32)pDMMRes->dmmSize); ++ pDMMRes = pDMMRes->next; ++ tempStrLen2 += 2; ++ memmove(pBuf+tempStrLen, pTempBuf, tempStrLen2); ++ tempStrLen += tempStrLen2; ++ tempCount++; ++ } ++ tempCount = 1; ++ pSTRMRes = pCtxt->pSTRMList; ++ while (pSTRMRes != NULL) { ++ GT_2trace(curTrace, GT_ENTER, ++ "DRV_ProcDisplayResourceInfo: #:%d " ++ "pCtxt->pSTRMList->hStream:%x\n", tempCount, ++ pSTRMRes->hStream); ++ tempStrLen2 = sprintf((char *)pTempBuf, ++ "Stream Resource info: STRM # %d " ++ "Stream Handle: 0x%x \n", ++ tempCount, (u32)pSTRMRes->hStream); ++ pSTRMRes = pSTRMRes->next; ++ tempStrLen2 += 2; ++ memmove(pBuf+tempStrLen, pTempBuf, tempStrLen2); ++ tempStrLen += tempStrLen2; ++ tempCount++; ++ } ++ pCtxt = pCtxt->next; ++ } ++ *pSize = tempStrLen; ++ status = PrintProcessInformation(); ++ GT_0trace(curTrace, GT_ENTER, "*********************" ++ "DRV_ProcDisplayResourceInfo:**\n"); ++ return status; ++} ++ ++/* ++ * ======== PrintProcessInformation ======== ++ * Purpose: ++ * This function prints the Process's information stored in ++ * the process context list. Some of the information that ++ * it displays is Process's state, Node, Stream, DMM, and ++ * Heap information. ++ */ ++static DSP_STATUS PrintProcessInformation(void) ++{ ++ struct DRV_OBJECT *hDrvObject = NULL; ++ struct PROCESS_CONTEXT *pCtxtList = NULL; ++ struct NODE_RES_OBJECT *pNodeRes = NULL; ++ struct DMM_RES_OBJECT *pDMMRes = NULL; ++ struct STRM_RES_OBJECT *pSTRMRes = NULL; ++ struct DSPHEAP_RES_OBJECT *pDSPHEAPRes = NULL; ++ DSP_STATUS status = DSP_SOK; ++ u32 tempCount; ++ u32 procID; ++ ++ /* Get the Process context list */ ++ CFG_GetObject((u32 *)&hDrvObject, REG_DRV_OBJECT); ++ DRV_GetProcCtxtList(&pCtxtList, hDrvObject); ++ GT_0trace(curTrace, GT_4CLASS, "\n### Debug information" ++ " for DSP bridge ##\n"); ++ GT_0trace(curTrace, GT_4CLASS, " \n ###The processes" ++ " information is as follows ### \n") ; ++ GT_0trace(curTrace, GT_4CLASS, " =====================" ++ "============ \n"); ++ /* Go through the entries in the Process context list */ ++ while (pCtxtList != NULL) { ++ GT_1trace(curTrace, GT_4CLASS, "\nThe process" ++ " id is %d\n", pCtxtList->pid); ++ GT_0trace(curTrace, GT_4CLASS, " -------------------" ++ "---------\n"); ++ if (pCtxtList->resState == PROC_RES_ALLOCATED) { ++ GT_0trace(curTrace, GT_4CLASS, " \nThe Process" ++ " is in Allocated state\n"); ++ } else { ++ GT_0trace(curTrace, GT_4CLASS, "\nThe Process" ++ " is in DeAllocated state\n"); ++ } ++ GT_1trace(curTrace, GT_4CLASS, "\nThe hProcessor" ++ " handle is: 0X%x\n", ++ (u32)pCtxtList->hProcessor); ++ if (pCtxtList->hProcessor != NULL) { ++ PROC_GetProcessorId(pCtxtList->hProcessor, &procID); ++ if (procID == DSP_UNIT) { ++ GT_0trace(curTrace, GT_4CLASS, ++ "\nProcess connected to" ++ " DSP Processor\n"); ++ } else if (procID == IVA_UNIT) { ++ GT_0trace(curTrace, GT_4CLASS, ++ "\nProcess connected to" ++ " IVA Processor\n"); ++ } else { ++ GT_0trace(curTrace, GT_7CLASS, ++ "\n***ERROR:Invalid Processor Id***\n"); ++ } ++ } ++ pNodeRes = pCtxtList->pNodeList; ++ tempCount = 1; ++ while (pNodeRes != NULL) { ++ if (tempCount == 1) ++ GT_0trace(curTrace, GT_4CLASS, ++ "\n***The Nodes allocated by" ++ " this Process are***\n"); ++ GT_2trace(curTrace, GT_4CLASS, ++ "Node # %d Node Handle hNode:0x%x\n", ++ tempCount, (u32)pNodeRes->hNode); ++ pNodeRes = pNodeRes->next; ++ tempCount++; ++ } ++ if (tempCount == 1) ++ GT_0trace(curTrace, GT_4CLASS, ++ "\n ***There are no Nodes" ++ " allocated by this Process***\n"); ++ tempCount = 1; ++ pDSPHEAPRes = pCtxtList->pDSPHEAPList; ++ while (pDSPHEAPRes != NULL) { ++ if (tempCount == 1) ++ GT_0trace(curTrace, GT_4CLASS, ++ "\n***The Heaps allocated by" ++ " this Process are***\n"); ++ GT_3trace(curTrace, GT_4CLASS, ++ "DSP Heap Resource Info: HEAP # %d " ++ "Mapped GPP Address:0x%x, Size: 0x%lx\n", ++ tempCount, (u32)pDSPHEAPRes->ulMpuAddr, ++ pDSPHEAPRes->heapSize); ++ pDSPHEAPRes = pDSPHEAPRes->next; ++ tempCount++; ++ } ++ if (tempCount == 1) ++ GT_0trace(curTrace, GT_4CLASS, ++ "\n ***There are no Heaps allocated" ++ " by this Process***\n"); ++ tempCount = 1; ++ pDMMRes = pCtxtList->pDMMList; ++ while (pDMMRes != NULL) { ++ if (tempCount == 1) ++ GT_0trace(curTrace, GT_4CLASS, ++ "\n ***The DMM resources allocated by" ++ " this Process are***\n"); ++ GT_3trace(curTrace, GT_4CLASS, ++ "DMM Resource Info: DMM # %d " ++ "Mapped GPP Address:0X%lx, Size: 0X%lx\n", ++ tempCount, pDMMRes->ulMpuAddr, ++ pDMMRes->dmmSize); ++ pDMMRes = pDMMRes->next; ++ tempCount++; ++ } ++ if (tempCount == 1) ++ GT_0trace(curTrace, GT_4CLASS, ++ "\n ***There are no DMM resources" ++ " allocated by this Process***\n"); ++ tempCount = 1; ++ pSTRMRes = pCtxtList->pSTRMList; ++ while (pSTRMRes != NULL) { ++ if (tempCount == 1) ++ GT_0trace(curTrace, GT_4CLASS, ++ "\n***The Stream resources allocated by" ++ " this Process are***\n"); ++ GT_2trace(curTrace, GT_4CLASS, ++ "Stream Resource info: STRM # %d" ++ "Stream Handle:0X%x\n", tempCount, ++ (u32)pSTRMRes->hStream); ++ pSTRMRes = pSTRMRes->next; ++ tempCount++; ++ } ++ if (tempCount == 1) ++ GT_0trace(curTrace, GT_4CLASS, ++ "\n ***There are no Stream resources" ++ "allocated by this Process***\n"); ++ pCtxtList = pCtxtList->next; ++ } ++ return status; ++} ++ ++/* GPP PROCESS CLEANUP CODE END */ ++#endif ++ ++/* ++ * ======== = DRV_Create ======== = ++ * Purpose: ++ * DRV Object gets created only once during Driver Loading. ++ */ ++DSP_STATUS DRV_Create(OUT struct DRV_OBJECT **phDRVObject) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DRV_OBJECT *pDRVObject = NULL; ++ ++ DBC_Require(phDRVObject != NULL); ++ DBC_Require(cRefs > 0); ++ GT_1trace(curTrace, GT_ENTER, "Entering DRV_Create" ++ " phDRVObject 0x%x\n", phDRVObject); ++ MEM_AllocObject(pDRVObject, struct DRV_OBJECT, SIGNATURE); ++ if (pDRVObject) { ++ /* Create and Initialize List of device objects */ ++ pDRVObject->devList = LST_Create(); ++ if (pDRVObject->devList) { ++ /* Create and Initialize List of device Extension */ ++ pDRVObject->devNodeString = LST_Create(); ++ if (!(pDRVObject->devNodeString)) { ++ status = DSP_EFAIL; ++ GT_0trace(curTrace, GT_7CLASS, ++ "Failed to Create DRV_EXT list "); ++ MEM_FreeObject(pDRVObject); ++ } ++ } else { ++ status = DSP_EFAIL; ++ GT_0trace(curTrace, GT_7CLASS, ++ "Failed to Create Dev List "); ++ MEM_FreeObject(pDRVObject); ++ } ++ } else { ++ status = DSP_EFAIL; ++ GT_0trace(curTrace, GT_7CLASS, ++ "Failed to Allocate Memory for DRV Obj"); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Store the DRV Object in the Registry */ ++ if (DSP_SUCCEEDED ++ (CFG_SetObject((u32) pDRVObject, REG_DRV_OBJECT))) { ++ GT_1trace(curTrace, GT_1CLASS, ++ "DRV Obj Created pDrvObject 0x%x\n ", ++ pDRVObject); ++ *phDRVObject = pDRVObject; ++ } else { ++ /* Free the DRV Object */ ++ status = DSP_EFAIL; ++ MEM_Free(pDRVObject); ++ GT_0trace(curTrace, GT_7CLASS, ++ "Failed to update the Registry with " ++ "DRV Object "); ++ } ++ } ++ GT_2trace(curTrace, GT_ENTER, ++ "Exiting DRV_Create: phDRVObject: 0x%x\tstatus:" ++ "0x%x\n", phDRVObject, status); ++ DBC_Ensure(DSP_FAILED(status) || ++ MEM_IsValidHandle(pDRVObject, SIGNATURE)); ++ return status; ++} ++ ++/* ++ * ======== DRV_Exit ======== ++ * Purpose: ++ * Discontinue usage of the DRV module. ++ */ ++void DRV_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ GT_0trace(curTrace, GT_5CLASS, "Entering DRV_Exit \n"); ++ ++ cRefs--; ++ ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== = DRV_Destroy ======== = ++ * purpose: ++ * Invoked during bridge de-initialization ++ */ ++DSP_STATUS DRV_Destroy(struct DRV_OBJECT *hDRVObject) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DRV_OBJECT *pDRVObject = (struct DRV_OBJECT *)hDRVObject; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(pDRVObject, SIGNATURE)); ++ ++ GT_1trace(curTrace, GT_ENTER, "Entering DRV_Destroy" ++ " hDRVObject 0x%x\n", hDRVObject); ++ /* ++ * Delete the List if it exists.Should not come here ++ * as the DRV_RemoveDevObject and the Last DRV_RequestResources ++ * removes the list if the lists are empty. ++ */ ++ if (pDRVObject->devList) { ++ /* Could assert if the list is not empty */ ++ LST_Delete(pDRVObject->devList); ++ } ++ if (pDRVObject->devNodeString) { ++ /* Could assert if the list is not empty */ ++ LST_Delete(pDRVObject->devNodeString); ++ } ++ MEM_FreeObject(pDRVObject); ++ /* Update the DRV Object in Registry to be 0 */ ++ (void)CFG_SetObject(0, REG_DRV_OBJECT); ++ GT_2trace(curTrace, GT_ENTER, ++ "Exiting DRV_Destroy: hDRVObject: 0x%x\tstatus:" ++ "0x%x\n", hDRVObject, status); ++ DBC_Ensure(!MEM_IsValidHandle(pDRVObject, SIGNATURE)); ++ return status; ++} ++ ++/* ++ * ======== DRV_GetDevObject ======== ++ * Purpose: ++ * Given a index, returns a handle to DevObject from the list. ++ */ ++DSP_STATUS DRV_GetDevObject(u32 uIndex, struct DRV_OBJECT *hDrvObject, ++ struct DEV_OBJECT **phDevObject) ++{ ++ DSP_STATUS status = DSP_SOK; ++#if GT_TRACE /* pDrvObject is used only for Assertions and debug messages.*/ ++ struct DRV_OBJECT *pDrvObject = (struct DRV_OBJECT *)hDrvObject; ++#endif ++ struct DEV_OBJECT *pDevObject; ++ u32 i; ++ DBC_Require(MEM_IsValidHandle(pDrvObject, SIGNATURE)); ++ DBC_Require(phDevObject != NULL); ++ DBC_Require(uIndex >= 0); ++ DBC_Require(cRefs > 0); ++ DBC_Assert(!(LST_IsEmpty(pDrvObject->devList))); ++ GT_3trace(curTrace, GT_ENTER, ++ "Entered DRV_GetDevObject, args:\n\tuIndex: " ++ "0x%x\n\thDrvObject: 0x%x\n\tphDevObject: 0x%x\n", ++ uIndex, hDrvObject, phDevObject); ++ pDevObject = (struct DEV_OBJECT *)DRV_GetFirstDevObject(); ++ for (i = 0; i < uIndex; i++) { ++ pDevObject = ++ (struct DEV_OBJECT *)DRV_GetNextDevObject((u32)pDevObject); ++ } ++ if (pDevObject) { ++ *phDevObject = (struct DEV_OBJECT *) pDevObject; ++ status = DSP_SOK; ++ } else { ++ *phDevObject = NULL; ++ status = DSP_EFAIL; ++ GT_0trace(curTrace, GT_7CLASS, ++ "DRV: Could not get the DevObject\n"); ++ } ++ GT_2trace(curTrace, GT_ENTER, ++ "Exiting Drv_GetDevObject\n\tstatus: 0x%x\n\t" ++ "hDevObject: 0x%x\n", status, *phDevObject); ++ return status; ++} ++ ++/* ++ * ======== DRV_GetFirstDevObject ======== ++ * Purpose: ++ * Retrieve the first Device Object handle from an internal linked list of ++ * of DEV_OBJECTs maintained by DRV. ++ */ ++u32 DRV_GetFirstDevObject(void) ++{ ++ u32 dwDevObject = 0; ++ struct DRV_OBJECT *pDrvObject; ++ ++ if (DSP_SUCCEEDED ++ (CFG_GetObject((u32 *)&pDrvObject, REG_DRV_OBJECT))) { ++ if ((pDrvObject->devList != NULL) && ++ !LST_IsEmpty(pDrvObject->devList)) ++ dwDevObject = (u32) LST_First(pDrvObject->devList); ++ } ++ ++ return dwDevObject; ++} ++ ++/* ++ * ======== DRV_GetFirstDevNodeString ======== ++ * Purpose: ++ * Retrieve the first Device Extension from an internal linked list of ++ * of Pointer to DevNode Strings maintained by DRV. ++ */ ++u32 DRV_GetFirstDevExtension(void) ++{ ++ u32 dwDevExtension = 0; ++ struct DRV_OBJECT *pDrvObject; ++ ++ if (DSP_SUCCEEDED ++ (CFG_GetObject((u32 *)&pDrvObject, REG_DRV_OBJECT))) { ++ ++ if ((pDrvObject->devNodeString != NULL) && ++ !LST_IsEmpty(pDrvObject->devNodeString)) { ++ dwDevExtension = (u32)LST_First(pDrvObject-> ++ devNodeString); ++ } ++ } ++ ++ return dwDevExtension; ++} ++ ++/* ++ * ======== DRV_GetNextDevObject ======== ++ * Purpose: ++ * Retrieve the next Device Object handle from an internal linked list of ++ * of DEV_OBJECTs maintained by DRV, after having previously called ++ * DRV_GetFirstDevObject() and zero or more DRV_GetNext. ++ */ ++u32 DRV_GetNextDevObject(u32 hDevObject) ++{ ++ u32 dwNextDevObject = 0; ++ struct DRV_OBJECT *pDrvObject; ++ ++ DBC_Require(hDevObject != 0); ++ ++ if (DSP_SUCCEEDED ++ (CFG_GetObject((u32 *)&pDrvObject, REG_DRV_OBJECT))) { ++ ++ if ((pDrvObject->devList != NULL) && ++ !LST_IsEmpty(pDrvObject->devList)) { ++ dwNextDevObject = (u32)LST_Next(pDrvObject->devList, ++ (struct LST_ELEM *)hDevObject); ++ } ++ } ++ return dwNextDevObject; ++} ++ ++/* ++ * ======== DRV_GetNextDevExtension ======== ++ * Purpose: ++ * Retrieve the next Device Extension from an internal linked list of ++ * of pointer to DevNodeString maintained by DRV, after having previously ++ * called DRV_GetFirstDevExtension() and zero or more ++ * DRV_GetNextDevExtension(). ++ */ ++u32 DRV_GetNextDevExtension(u32 hDevExtension) ++{ ++ u32 dwDevExtension = 0; ++ struct DRV_OBJECT *pDrvObject; ++ ++ DBC_Require(hDevExtension != 0); ++ ++ if (DSP_SUCCEEDED(CFG_GetObject((u32 *)&pDrvObject, ++ REG_DRV_OBJECT))) { ++ if ((pDrvObject->devNodeString != NULL) && ++ !LST_IsEmpty(pDrvObject->devNodeString)) { ++ dwDevExtension = (u32)LST_Next(pDrvObject-> ++ devNodeString, ++ (struct LST_ELEM *)hDevExtension); ++ } ++ } ++ ++ return dwDevExtension; ++} ++ ++/* ++ * ======== DRV_Init ======== ++ * Purpose: ++ * Initialize DRV module private state. ++ */ ++DSP_STATUS DRV_Init(void) ++{ ++ s32 fRetval = 1; /* function return value */ ++ ++ DBC_Require(cRefs >= 0); ++ ++ if (fRetval) ++ cRefs++; ++ ++ GT_1trace(curTrace, GT_5CLASS, "Entering DRV_Entry crefs 0x%x \n", ++ cRefs); ++ ++ DBC_Ensure((fRetval && (cRefs > 0)) || (!fRetval && (cRefs >= 0))); ++ ++ return fRetval; ++} ++ ++/* ++ * ======== DRV_InsertDevObject ======== ++ * Purpose: ++ * Insert a DevObject into the list of Manager object. ++ */ ++DSP_STATUS DRV_InsertDevObject(struct DRV_OBJECT *hDRVObject, ++ struct DEV_OBJECT *hDevObject) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DRV_OBJECT *pDRVObject = (struct DRV_OBJECT *)hDRVObject; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(hDevObject != NULL); ++ DBC_Require(MEM_IsValidHandle(pDRVObject, SIGNATURE)); ++ DBC_Assert(pDRVObject->devList); ++ ++ GT_2trace(curTrace, GT_ENTER, ++ "Entering DRV_InsertProcObject hDRVObject " ++ "0x%x\n, hDevObject 0x%x\n", hDRVObject, hDevObject); ++ ++ LST_PutTail(pDRVObject->devList, (struct LST_ELEM *)hDevObject); ++ ++ GT_1trace(curTrace, GT_ENTER, ++ "Exiting InsertDevObject status 0x%x\n", status); ++ ++ DBC_Ensure(DSP_SUCCEEDED(status) && !LST_IsEmpty(pDRVObject->devList)); ++ ++ return status; ++} ++ ++/* ++ * ======== DRV_RemoveDevObject ======== ++ * Purpose: ++ * Search for and remove a DeviceObject from the given list of DRV ++ * objects. ++ */ ++DSP_STATUS DRV_RemoveDevObject(struct DRV_OBJECT *hDRVObject, ++ struct DEV_OBJECT *hDevObject) ++{ ++ DSP_STATUS status = DSP_EFAIL; ++ struct DRV_OBJECT *pDRVObject = (struct DRV_OBJECT *)hDRVObject; ++ struct LST_ELEM *pCurElem; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(pDRVObject, SIGNATURE)); ++ DBC_Require(hDevObject != NULL); ++ ++ DBC_Require(pDRVObject->devList != NULL); ++ DBC_Require(!LST_IsEmpty(pDRVObject->devList)); ++ ++ GT_2trace(curTrace, GT_ENTER, ++ "Entering DRV_RemoveDevObject hDevObject " ++ "0x%x\n, hDRVObject 0x%x\n", hDevObject, hDRVObject); ++ /* Search list for pProcObject: */ ++ for (pCurElem = LST_First(pDRVObject->devList); pCurElem != NULL; ++ pCurElem = LST_Next(pDRVObject->devList, pCurElem)) { ++ /* If found, remove it. */ ++ if ((struct DEV_OBJECT *) pCurElem == hDevObject) { ++ LST_RemoveElem(pDRVObject->devList, pCurElem); ++ status = DSP_SOK; ++ break; ++ } ++ } ++ /* Remove list if empty. */ ++ if (LST_IsEmpty(pDRVObject->devList)) { ++ LST_Delete(pDRVObject->devList); ++ pDRVObject->devList = NULL; ++ } ++ DBC_Ensure((pDRVObject->devList == NULL) || ++ !LST_IsEmpty(pDRVObject->devList)); ++ GT_1trace(curTrace, GT_ENTER, ++ "DRV_RemoveDevObject returning 0x%x\n", status); ++ return status; ++} ++ ++/* ++ * ======== DRV_RequestResources ======== ++ * Purpose: ++ * Requests resources from the OS. ++ */ ++DSP_STATUS DRV_RequestResources(u32 dwContext, u32 *pDevNodeString) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DRV_OBJECT *pDRVObject; ++ struct DRV_EXT *pszdevNode; ++ ++ DBC_Require(dwContext != 0); ++ DBC_Require(pDevNodeString != NULL); ++ GT_0trace(curTrace, GT_ENTER, "Entering DRV_RequestResources\n"); ++ /* ++ * Allocate memory to hold the string. This will live untill ++ * it is freed in the Release resources. Update the driver object ++ * list. ++ */ ++ if (DSP_SUCCEEDED(CFG_GetObject((u32 *)&pDRVObject, ++ REG_DRV_OBJECT))) { ++ pszdevNode = MEM_Calloc(sizeof(struct DRV_EXT), MEM_NONPAGED); ++ if (pszdevNode) { ++ LST_InitElem(&pszdevNode->link); ++ strncpy((char *) pszdevNode->szString, ++ (char *)dwContext, MAXREGPATHLENGTH); ++ /* Update the Driver Object List */ ++ *pDevNodeString = (u32)pszdevNode->szString; ++ LST_PutTail(pDRVObject->devNodeString, ++ (struct LST_ELEM *)pszdevNode); ++ } else { ++ GT_0trace(curTrace, GT_7CLASS, ++ "Failed to Allocate Memory devNodeString "); ++ status = DSP_EFAIL; ++ *pDevNodeString = 0; ++ } ++ } else { ++ status = DSP_EFAIL; ++ GT_0trace(curTrace, GT_7CLASS, ++ "Failed to get Driver Object from Registry"); ++ *pDevNodeString = 0; ++ } ++ ++ if (!(strcmp((char *) dwContext, "TIOMAP1510"))) { ++ GT_0trace(curTrace, GT_1CLASS, ++ " Allocating resources for UMA \n"); ++ status = RequestBridgeResourcesDSP(dwContext, DRV_ASSIGN); ++ } else { ++ status = DSP_EFAIL; ++ GT_0trace(curTrace, GT_7CLASS, "Unknown Device "); ++ } ++ ++ if (DSP_FAILED(status)) { ++ GT_0trace(curTrace, GT_7CLASS, ++ "Failed to reserve bridge resources "); ++ } ++ DBC_Ensure((DSP_SUCCEEDED(status) && pDevNodeString != NULL && ++ !LST_IsEmpty(pDRVObject->devNodeString)) || ++ (DSP_FAILED(status) && *pDevNodeString == 0)); ++ ++ return status; ++} ++ ++/* ++ * ======== DRV_ReleaseResources ======== ++ * Purpose: ++ * Releases resources from the OS. ++ */ ++DSP_STATUS DRV_ReleaseResources(u32 dwContext, struct DRV_OBJECT *hDrvObject) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DRV_OBJECT *pDRVObject = (struct DRV_OBJECT *)hDrvObject; ++ struct DRV_EXT *pszdevNode; ++ ++ GT_0trace(curTrace, GT_ENTER, "Entering DRV_Release Resources\n"); ++ ++ if (!(strcmp((char *)((struct DRV_EXT *)dwContext)->szString, ++ "TIOMAP1510"))) { ++ GT_0trace(curTrace, GT_1CLASS, ++ " Releasing DSP-Bridge resources \n"); ++ status = RequestBridgeResources(dwContext, DRV_RELEASE); ++ } else { ++ GT_0trace(curTrace, GT_1CLASS, " Unknown device\n"); ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ GT_0trace(curTrace, GT_1CLASS, ++ "Failed to relese bridge resources\n"); ++ } ++ ++ /* ++ * Irrespective of the status go ahead and clean it ++ * The following will over write the status. ++ */ ++ for (pszdevNode = (struct DRV_EXT *)DRV_GetFirstDevExtension(); ++ pszdevNode != NULL; pszdevNode = (struct DRV_EXT *) ++ DRV_GetNextDevExtension((u32)pszdevNode)) { ++ if ((u32)pszdevNode == dwContext) { ++ /* Found it */ ++ /* Delete from the Driver object list */ ++ LST_RemoveElem(pDRVObject->devNodeString, ++ (struct LST_ELEM *)pszdevNode); ++ MEM_Free((void *) pszdevNode); ++ break; ++ } ++ /* Delete the List if it is empty */ ++ if (LST_IsEmpty(pDRVObject->devNodeString)) { ++ LST_Delete(pDRVObject->devNodeString); ++ pDRVObject->devNodeString = NULL; ++ } ++ } ++ return status; ++} ++ ++/* ++ * ======== RequestBridgeResources ======== ++ * Purpose: ++ * Reserves shared memory for bridge. ++ */ ++static DSP_STATUS RequestBridgeResources(u32 dwContext, s32 bRequest) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct CFG_HOSTRES *pResources; ++ u32 dwBuffSize; ++ ++ struct DRV_EXT *driverExt; ++ u32 shm_size; ++ ++ DBC_Require(dwContext != 0); ++ ++ GT_0trace(curTrace, GT_ENTER, "->RequestBridgeResources \n"); ++ ++ if (!bRequest) { ++ driverExt = (struct DRV_EXT *)dwContext; ++ /* Releasing resources by deleting the registry key */ ++ dwBuffSize = sizeof(struct CFG_HOSTRES); ++ pResources = MEM_Calloc(dwBuffSize, MEM_NONPAGED); ++ if (DSP_FAILED(REG_GetValue(NULL, (char *)driverExt->szString, ++ CURRENTCONFIG, (u8 *)pResources, &dwBuffSize))) { ++ status = CFG_E_RESOURCENOTAVAIL; ++ GT_0trace(curTrace, GT_1CLASS, ++ "REG_GetValue Failed \n"); ++ } else { ++ GT_0trace(curTrace, GT_1CLASS, ++ "REG_GetValue Succeeded \n"); ++ } ++ ++ if (pResources != NULL) { ++ dwBuffSize = sizeof(shm_size); ++ status = REG_GetValue(NULL, CURRENTCONFIG, SHMSIZE, ++ (u8 *)&shm_size, &dwBuffSize); ++ if (DSP_SUCCEEDED(status)) { ++ if ((pResources->dwMemBase[1]) && ++ (pResources->dwMemPhys[1])) { ++ MEM_FreePhysMem((void *)pResources-> ++ dwMemBase[1], pResources->dwMemPhys[1], ++ shm_size); ++ } ++ } else { ++ GT_1trace(curTrace, GT_7CLASS, ++ "Error getting SHM size from registry: " ++ "%x. Not calling MEM_FreePhysMem\n", ++ status); ++ } ++ pResources->dwMemBase[1] = 0; ++ pResources->dwMemPhys[1] = 0; ++ ++ if (pResources->dwPrmBase) ++ iounmap((void *)pResources->dwPrmBase); ++ if (pResources->dwCmBase) ++ iounmap((void *)pResources->dwCmBase); ++ if (pResources->dwMboxBase) ++ iounmap((void *)pResources->dwMboxBase); ++ if (pResources->dwMemBase[0]) ++ iounmap((void *)pResources->dwMemBase[0]); ++ if (pResources->dwMemBase[2]) ++ iounmap((void *)pResources->dwMemBase[2]); ++ if (pResources->dwMemBase[3]) ++ iounmap((void *)pResources->dwMemBase[3]); ++ if (pResources->dwMemBase[4]) ++ iounmap((void *)pResources->dwMemBase[4]); ++ if (pResources->dwWdTimerDspBase) ++ iounmap((void *)pResources->dwWdTimerDspBase); ++ if (pResources->dwDmmuBase) ++ iounmap((void *)pResources->dwDmmuBase); ++ if (pResources->dwPerBase) ++ iounmap((void *)pResources->dwPerBase); ++ if (pResources->dwPerPmBase) ++ iounmap((void *)pResources->dwPerPmBase); ++ if (pResources->dwCorePmBase) ++ iounmap((void *)pResources->dwCorePmBase); ++ if (pResources->dwSysCtrlBase) { ++ iounmap((void *)pResources->dwSysCtrlBase); ++ /* don't set pResources->dwSysCtrlBase to null ++ * as it is used in BOARD_Stop */ ++ } ++ pResources->dwPrmBase = (u32) NULL; ++ pResources->dwCmBase = (u32) NULL; ++ pResources->dwMboxBase = (u32) NULL; ++ pResources->dwMemBase[0] = (u32) NULL; ++ pResources->dwMemBase[2] = (u32) NULL; ++ pResources->dwMemBase[3] = (u32) NULL; ++ pResources->dwMemBase[4] = (u32) NULL; ++ pResources->dwWdTimerDspBase = (u32) NULL; ++ pResources->dwDmmuBase = (u32) NULL; ++ ++ dwBuffSize = sizeof(struct CFG_HOSTRES); ++ status = REG_SetValue(NULL, (char *)driverExt->szString, ++ CURRENTCONFIG, REG_BINARY, (u8 *)pResources, ++ (u32)dwBuffSize); ++ /* Set all the other entries to NULL */ ++ MEM_Free(pResources); ++ } ++ GT_0trace(curTrace, GT_ENTER, " <- RequestBridgeResources \n"); ++ return status; ++ } ++ dwBuffSize = sizeof(struct CFG_HOSTRES); ++ pResources = MEM_Calloc(dwBuffSize, MEM_NONPAGED); ++ if (pResources != NULL) { ++ /* wNumMemWindows must not be more than CFG_MAXMEMREGISTERS */ ++ pResources->wNumMemWindows = 2; ++ /* First window is for DSP internal memory */ ++ ++ pResources->dwPrmBase = (u32)ioremap(OMAP_IVA2_PRM_BASE, ++ OMAP_IVA2_PRM_SIZE); ++ pResources->dwCmBase = (u32)ioremap(OMAP_IVA2_CM_BASE, ++ OMAP_IVA2_CM_SIZE); ++ pResources->dwMboxBase = (u32)ioremap(OMAP_MBOX_BASE, ++ OMAP_MBOX_SIZE); ++ pResources->dwSysCtrlBase = (u32)ioremap(OMAP_SYSC_BASE, ++ OMAP_SYSC_SIZE); ++ GT_1trace(curTrace, GT_2CLASS, "dwMemBase[0] 0x%x\n", ++ pResources->dwMemBase[0]); ++ GT_1trace(curTrace, GT_2CLASS, "dwMemBase[3] 0x%x\n", ++ pResources->dwMemBase[3]); ++ GT_1trace(curTrace, GT_2CLASS, "dwPrmBase 0x%x\n", ++ pResources->dwPrmBase); ++ GT_1trace(curTrace, GT_2CLASS, "dwCmBase 0x%x\n", ++ pResources->dwCmBase); ++ GT_1trace(curTrace, GT_2CLASS, "dwWdTimerDspBase 0x%x\n", ++ pResources->dwWdTimerDspBase); ++ GT_1trace(curTrace, GT_2CLASS, "dwMboxBase 0x%x\n", ++ pResources->dwMboxBase); ++ GT_1trace(curTrace, GT_2CLASS, "dwDmmuBase 0x%x\n", ++ pResources->dwDmmuBase); ++ ++ /* for 24xx base port is not mapping the mamory for DSP ++ * internal memory TODO Do a ioremap here */ ++ /* Second window is for DSP external memory shared with MPU */ ++ if (DSP_SUCCEEDED(status)) { ++ /* for Linux, these are hard-coded values */ ++ pResources->dwBusType = 0; ++ pResources->bIRQRegisters = 0; ++ pResources->bIRQAttrib = 0; ++ pResources->dwOffsetForMonitor = 0; ++ pResources->dwChnlOffset = 0; ++ /* CHNL_MAXCHANNELS */ ++ pResources->dwNumChnls = CHNL_MAXCHANNELS; ++ pResources->dwChnlBufSize = 0x400; ++ dwBuffSize = sizeof(struct CFG_HOSTRES); ++ status = REG_SetValue(NULL, (char *) dwContext, ++ CURRENTCONFIG, REG_BINARY, ++ (u8 *)pResources, ++ sizeof(struct CFG_HOSTRES)); ++ if (DSP_SUCCEEDED(status)) { ++ GT_0trace(curTrace, GT_1CLASS, ++ " Successfully set the registry " ++ "value for CURRENTCONFIG\n"); ++ } else { ++ GT_0trace(curTrace, GT_7CLASS, ++ " Failed to set the registry " ++ "value for CURRENTCONFIG\n"); ++ } ++ } ++ MEM_Free(pResources); ++ } ++ /* End Mem alloc */ ++ return status; ++} ++ ++/* ++ * ======== RequestBridgeResourcesDSP ======== ++ * Purpose: ++ * Reserves shared memory for bridge. ++ */ ++static DSP_STATUS RequestBridgeResourcesDSP(u32 dwContext, s32 bRequest) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct CFG_HOSTRES *pResources; ++ u32 dwBuffSize; ++ u32 dmaAddr; ++ u32 shm_size; ++ ++ DBC_Require(dwContext != 0); ++ ++ GT_0trace(curTrace, GT_ENTER, "->RequestBridgeResourcesDSP \n"); ++ ++ dwBuffSize = sizeof(struct CFG_HOSTRES); ++ ++ pResources = MEM_Calloc(dwBuffSize, MEM_NONPAGED); ++ ++ if (pResources != NULL) { ++ if (DSP_FAILED(CFG_GetHostResources((struct CFG_DEVNODE *) ++ dwContext, pResources))) { ++ /* Call CFG_GetHostResources to get reserve resouces */ ++ status = RequestBridgeResources(dwContext, bRequest); ++ if (DSP_SUCCEEDED(status)) { ++ status = CFG_GetHostResources ++ ((struct CFG_DEVNODE *) dwContext, ++ pResources); ++ } ++ } ++ /* wNumMemWindows must not be more than CFG_MAXMEMREGISTERS */ ++ pResources->wNumMemWindows = 4; ++ ++ pResources->dwMemBase[0] = 0; ++ pResources->dwMemBase[2] = (u32)ioremap(OMAP_DSP_MEM1_BASE, ++ OMAP_DSP_MEM1_SIZE); ++ pResources->dwMemBase[3] = (u32)ioremap(OMAP_DSP_MEM2_BASE, ++ OMAP_DSP_MEM2_SIZE); ++ pResources->dwMemBase[4] = (u32)ioremap(OMAP_DSP_MEM3_BASE, ++ OMAP_DSP_MEM3_SIZE); ++ pResources->dwPerBase = (u32)ioremap(OMAP_PER_CM_BASE, ++ OMAP_PER_CM_SIZE); ++ pResources->dwPerPmBase = (u32)ioremap(OMAP_PER_PRM_BASE, ++ OMAP_PER_PRM_SIZE); ++ pResources->dwCorePmBase = (u32)ioremap(OMAP_CORE_PRM_BASE, ++ OMAP_CORE_PRM_SIZE); ++ pResources->dwDmmuBase = (u32)ioremap(OMAP_DMMU_BASE, ++ OMAP_DMMU_SIZE); ++ pResources->dwWdTimerDspBase = 0; ++ ++ GT_1trace(curTrace, GT_2CLASS, "dwMemBase[0] 0x%x\n", ++ pResources->dwMemBase[0]); ++ GT_1trace(curTrace, GT_2CLASS, "dwMemBase[1] 0x%x\n", ++ pResources->dwMemBase[1]); ++ GT_1trace(curTrace, GT_2CLASS, "dwMemBase[2] 0x%x\n", ++ pResources->dwMemBase[2]); ++ GT_1trace(curTrace, GT_2CLASS, "dwMemBase[3] 0x%x\n", ++ pResources->dwMemBase[3]); ++ GT_1trace(curTrace, GT_2CLASS, "dwMemBase[4] 0x%x\n", ++ pResources->dwMemBase[4]); ++ GT_1trace(curTrace, GT_2CLASS, "dwPrmBase 0x%x\n", ++ pResources->dwPrmBase); ++ GT_1trace(curTrace, GT_2CLASS, "dwCmBase 0x%x\n", ++ pResources->dwCmBase); ++ GT_1trace(curTrace, GT_2CLASS, "dwWdTimerDspBase 0x%x\n", ++ pResources->dwWdTimerDspBase); ++ GT_1trace(curTrace, GT_2CLASS, "dwMboxBase 0x%x\n", ++ pResources->dwMboxBase); ++ GT_1trace(curTrace, GT_2CLASS, "dwDmmuBase 0x%x\n", ++ pResources->dwDmmuBase); ++ dwBuffSize = sizeof(shm_size); ++ status = REG_GetValue(NULL, CURRENTCONFIG, SHMSIZE, ++ (u8 *)&shm_size, &dwBuffSize); ++ if (DSP_SUCCEEDED(status)) { ++ /* Allocate Physically contiguous, ++ * non-cacheable memory */ ++ pResources->dwMemBase[1] = ++ (u32)MEM_AllocPhysMem(shm_size, 0x100000, ++ &dmaAddr); ++ if (pResources->dwMemBase[1] == 0) { ++ status = DSP_EMEMORY; ++ GT_0trace(curTrace, GT_7CLASS, ++ "SHM reservation Failed\n"); ++ } else { ++ pResources->dwMemLength[1] = shm_size; ++ pResources->dwMemPhys[1] = dmaAddr; ++ ++ GT_3trace(curTrace, GT_1CLASS, ++ "Bridge SHM address 0x%x dmaAddr" ++ " %x size %x\n", ++ pResources->dwMemBase[1], ++ dmaAddr, shm_size); ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* for Linux, these are hard-coded values */ ++ pResources->dwBusType = 0; ++ pResources->bIRQRegisters = 0; ++ pResources->bIRQAttrib = 0; ++ pResources->dwOffsetForMonitor = 0; ++ pResources->dwChnlOffset = 0; ++ /* CHNL_MAXCHANNELS */ ++ pResources->dwNumChnls = CHNL_MAXCHANNELS; ++ pResources->dwChnlBufSize = 0x400; ++ dwBuffSize = sizeof(struct CFG_HOSTRES); ++ status = REG_SetValue(NULL, (char *)dwContext, ++ CURRENTCONFIG, REG_BINARY, ++ (u8 *)pResources, ++ sizeof(struct CFG_HOSTRES)); ++ if (DSP_SUCCEEDED(status)) { ++ GT_0trace(curTrace, GT_1CLASS, ++ " Successfully set the registry" ++ " value for CURRENTCONFIG\n"); ++ } else { ++ GT_0trace(curTrace, GT_7CLASS, ++ " Failed to set the registry value" ++ " for CURRENTCONFIG\n"); ++ } ++ } ++ MEM_Free(pResources); ++ } ++ /* End Mem alloc */ ++ return status; ++} +diff --git a/drivers/dsp/bridge/rmgr/drv_interface.c b/drivers/dsp/bridge/rmgr/drv_interface.c +new file mode 100644 +index 0000000..4aa7de6 +--- /dev/null ++++ b/drivers/dsp/bridge/rmgr/drv_interface.c +@@ -0,0 +1,777 @@ ++/* ++ * drv_interface.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== linux_driver.c ======== ++ * Description: ++ * DSP/BIOS Bridge driver interface. ++ * ++ * Public Functions: ++ * driver_init ++ * driver_exit ++ * driver_open ++ * driver_release ++ * driver_ioctl ++ * driver_mmap ++ * ++ *! Revision History ++ *! ================ ++ *! 21-Apr-2004 map Deprecated use of MODULE_PARM for kernel versions ++ *! greater than 2.5, use module_param. ++ *! 08-Mar-2004 sb Added the dsp_debug argument, which keeps the DSP in self ++ *! loop after image load and waits in a loop for DSP to start ++ *! 16-Feb-2004 vp Deprecated the usage of MOD_INC_USE_COUNT and ++ *! MOD_DEC_USE_COUNT ++ *! for kernel versions greater than 2.5 ++ *! 20-May-2003 vp Added unregister functions for the DPM. ++ *! 24-Mar-2003 sb Pass pid instead of driverContext to DSP_Close ++ *! 24-Mar-2003 vp Added Power Management support. ++ *! 21-Mar-2003 sb Configure SHM size using insmod argument shm_size ++ *! 10-Feb-2003 vp Updated based on code review comments ++ *! 18-Oct-2002 sb Created initial version ++ */ ++ ++/* ----------------------------------- Host OS */ ++ ++#include ++#include ++#include ++ ++#ifdef MODULE ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Resource Manager */ ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++#ifndef RES_CLEANUP_DISABLE ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#endif ++ ++#ifdef CONFIG_BRIDGE_DVFS ++#include ++#include ++#endif ++ ++#define BRIDGE_NAME "C6410" ++/* ----------------------------------- Globals */ ++#define DRIVER_NAME "DspBridge" ++#define DRIVER_MAJOR 0 /* Linux assigns our Major device number */ ++#define DRIVER_MINOR 0 /* Linux assigns our Major device number */ ++s32 dsp_debug; ++ ++struct platform_device *omap_dspbridge_dev; ++ ++struct bridge_dev { ++ struct cdev cdev; ++}; ++ ++static struct bridge_dev *bridge_device; ++ ++static struct class *bridge_class; ++ ++static u32 driverContext; ++#ifdef CONFIG_BRIDGE_DEBUG ++static char *GT_str; ++#endif /* CONFIG_BRIDGE_DEBUG */ ++static s32 driver_major = DRIVER_MAJOR; ++static s32 driver_minor = DRIVER_MINOR; ++static char *base_img; ++char *iva_img; ++static char *num_procs = "C55=1"; ++static s32 shm_size = 0x400000; /* 4 MB */ ++static u32 phys_mempool_base; ++static u32 phys_mempool_size; ++static int tc_wordswapon; /* Default value is always false */ ++ ++#ifdef CONFIG_PM ++struct omap34xx_bridge_suspend_data { ++ int suspended; ++ wait_queue_head_t suspend_wq; ++}; ++ ++static struct omap34xx_bridge_suspend_data bridge_suspend_data; ++ ++static int omap34xxbridge_suspend_lockout( ++ struct omap34xx_bridge_suspend_data *s, struct file *f) ++{ ++ if ((s)->suspended) { ++ if ((f)->f_flags & O_NONBLOCK) ++ return DSP_EDPMSUSPEND; ++ wait_event_interruptible((s)->suspend_wq, (s)->suspended == 0); ++ } ++ return 0; ++} ++ ++#endif ++ ++#ifdef DEBUG ++module_param(GT_str, charp, 0); ++MODULE_PARM_DESC(GT_str, "GT string, default = NULL"); ++ ++module_param(dsp_debug, int, 0); ++MODULE_PARM_DESC(dsp_debug, "Wait after loading DSP image. default = false"); ++#endif ++ ++module_param(driver_major, int, 0); /* Driver's major number */ ++MODULE_PARM_DESC(driver_major, "Major device number, default = 0 (auto)"); ++ ++module_param(driver_minor, int, 0); /* Driver's major number */ ++MODULE_PARM_DESC(driver_minor, "Minor device number, default = 0 (auto)"); ++ ++module_param(base_img, charp, 0); ++MODULE_PARM_DESC(base_img, "DSP base image, default = NULL"); ++ ++module_param(shm_size, int, 0); ++MODULE_PARM_DESC(shm_size, "SHM size, default = 4 MB, minimum = 64 KB"); ++ ++module_param(phys_mempool_base, uint, 0); ++MODULE_PARM_DESC(phys_mempool_base, ++ "Physical memory pool base passed to driver"); ++ ++module_param(phys_mempool_size, uint, 0); ++MODULE_PARM_DESC(phys_mempool_size, ++ "Physical memory pool size passed to driver"); ++module_param(tc_wordswapon, int, 0); ++MODULE_PARM_DESC(tc_wordswapon, "TC Word Swap Option. default = 0"); ++ ++MODULE_AUTHOR("Texas Instruments"); ++MODULE_LICENSE("GPL"); ++ ++static char *driver_name = DRIVER_NAME; ++ ++#ifdef CONFIG_BRIDGE_DEBUG ++static struct GT_Mask driverTrace; ++#endif /* CONFIG_BRIDGE_DEBUG */ ++ ++static struct file_operations bridge_fops = { ++ .open = bridge_open, ++ .release = bridge_release, ++ .ioctl = bridge_ioctl, ++ .mmap = bridge_mmap, ++}; ++ ++#ifdef CONFIG_PM ++static u32 timeOut = 1000; ++#ifdef CONFIG_BRIDGE_DVFS ++static struct clk *clk_handle; ++s32 dsp_max_opps = VDD1_OPP3; ++#endif ++ ++/* Maximum Opps that can be requested by IVA*/ ++/*vdd1 rate table*/ ++#ifdef CONFIG_BRIDGE_DVFS ++const struct omap_opp vdd1_rate_table_bridge[] = { ++ {0, 0, 0}, ++ /*OPP1*/ ++ {S125M, VDD1_OPP1, 0}, ++ /*OPP2*/ ++ {S250M, VDD1_OPP2, 0}, ++ /*OPP3*/ ++ {S500M, VDD1_OPP3, 0}, ++ /*OPP4*/ ++ {S550M, VDD1_OPP4, 0}, ++ /*OPP5*/ ++ {S600M, VDD1_OPP5, 0}, ++}; ++#endif ++#endif ++ ++struct dspbridge_platform_data *omap_dspbridge_pdata; ++ ++u32 vdd1_dsp_freq[6][4] = { ++ {0, 0, 0, 0}, ++ /*OPP1*/ ++ {0, 90000, 0, 86000}, ++ /*OPP2*/ ++ {0, 180000, 80000, 170000}, ++ /*OPP3*/ ++ {0, 360000, 160000, 340000}, ++ /*OPP4*/ ++ {0, 396000, 325000, 376000}, ++ /*OPP5*/ ++ {0, 430000, 355000, 430000}, ++}; ++ ++#ifdef CONFIG_BRIDGE_DVFS ++static int dspbridge_post_scale(struct notifier_block *op, unsigned long level, ++ void *ptr) ++{ ++ PWR_PM_PostScale(PRCM_VDD1, level); ++ return 0; ++} ++ ++static struct notifier_block iva_clk_notifier = { ++ .notifier_call = dspbridge_post_scale, ++ NULL, ++}; ++#endif ++ ++static int __devinit omap34xx_bridge_probe(struct platform_device *pdev) ++{ ++ int status; ++ u32 initStatus; ++ u32 temp; ++ dev_t dev = 0 ; ++ int result; ++#ifdef CONFIG_BRIDGE_DVFS ++ int i = 0; ++#endif ++ struct dspbridge_platform_data *pdata = pdev->dev.platform_data; ++ ++ omap_dspbridge_dev = pdev; ++ ++ /* use 2.6 device model */ ++ if (driver_major) { ++ dev = MKDEV(driver_major, driver_minor); ++ result = register_chrdev_region(dev, 1, driver_name); ++ } else { ++ result = alloc_chrdev_region(&dev, driver_minor, 1, ++ driver_name); ++ driver_major = MAJOR(dev); ++ } ++ ++ if (result < 0) { ++ GT_1trace(driverTrace, GT_7CLASS, "bridge_init: " ++ "Can't get Major %d \n", driver_major); ++ return result; ++ } ++ ++ bridge_device = kmalloc(sizeof(struct bridge_dev), GFP_KERNEL); ++ if (!bridge_device) { ++ result = -ENOMEM; ++ unregister_chrdev_region(dev, 1); ++ return result; ++ } ++ memset(bridge_device, 0, sizeof(struct bridge_dev)); ++ cdev_init(&bridge_device->cdev, &bridge_fops); ++ bridge_device->cdev.owner = THIS_MODULE; ++ bridge_device->cdev.ops = &bridge_fops; ++ ++ status = cdev_add(&bridge_device->cdev, dev, 1); ++ ++ if (status) { ++ GT_0trace(driverTrace, GT_7CLASS, ++ "Failed to add the bridge device \n"); ++ return status; ++ } ++ ++ /* udev support */ ++ bridge_class = class_create(THIS_MODULE, "ti_bridge"); ++ ++ if (IS_ERR(bridge_class)) ++ GT_0trace(driverTrace, GT_7CLASS, ++ "Error creating bridge class \n"); ++ ++ device_create(bridge_class, NULL, MKDEV(driver_major, driver_minor), ++ NULL, "DspBridge"); ++ ++ GT_init(); ++ GT_create(&driverTrace, "LD"); ++ ++#ifdef DEBUG ++ if (GT_str) ++ GT_set(GT_str); ++#elif defined(DDSP_DEBUG_PRODUCT) && GT_TRACE ++ GT_set("**=67"); ++#endif ++ ++ GT_0trace(driverTrace, GT_ENTER, "-> driver_init\n"); ++ ++#ifdef CONFIG_PM ++ /* Initialize the wait queue */ ++ if (!status) { ++ bridge_suspend_data.suspended = 0; ++ init_waitqueue_head(&bridge_suspend_data.suspend_wq); ++ } ++#endif ++ ++ SERVICES_Init(); ++ ++ /* Autostart flag. This should be set to true if the DSP image should ++ * be loaded and run during bridge module initialization */ ++ ++ if (base_img) { ++ temp = true; ++ REG_SetValue(NULL, NULL, AUTOSTART, REG_DWORD, (u8 *)&temp, ++ sizeof(temp)); ++ REG_SetValue(NULL, NULL, DEFEXEC, REG_SZ, (u8 *)base_img, ++ strlen(base_img) + 1); ++ } else { ++ temp = false; ++ REG_SetValue(NULL, NULL, AUTOSTART, REG_DWORD, (u8 *)&temp, ++ sizeof(temp)); ++ REG_SetValue(NULL, NULL, DEFEXEC, REG_SZ, (u8 *) "\0", (u32)2); ++ } ++ REG_SetValue(NULL, NULL, NUMPROCS, REG_SZ, (u8 *) num_procs, ++ strlen(num_procs) + 1); ++ ++ if (shm_size >= 0x10000) { /* 64 KB */ ++ initStatus = REG_SetValue(NULL, NULL, SHMSIZE, REG_DWORD, ++ (u8 *)&shm_size, sizeof(shm_size)); ++ } else { ++ initStatus = DSP_EINVALIDARG; ++ status = -1; ++ GT_0trace(driverTrace, GT_7CLASS, ++ "SHM size must be at least 64 KB\n"); ++ } ++ GT_1trace(driverTrace, GT_7CLASS, ++ "requested shm_size = 0x%x\n", shm_size); ++ ++ if (pdata->phys_mempool_base && pdata->phys_mempool_size) { ++ phys_mempool_base = pdata->phys_mempool_base; ++ phys_mempool_size = pdata->phys_mempool_size; ++ } ++ ++ if (phys_mempool_base > 0x0) { ++ initStatus = REG_SetValue(NULL, NULL, PHYSMEMPOOLBASE, ++ REG_DWORD, (u8 *)&phys_mempool_base, ++ sizeof(phys_mempool_base)); ++ } ++ GT_1trace(driverTrace, GT_7CLASS, "phys_mempool_base = 0x%x \n", ++ phys_mempool_base); ++ ++ if (phys_mempool_size > 0x0) { ++ initStatus = REG_SetValue(NULL, NULL, PHYSMEMPOOLSIZE, ++ REG_DWORD, (u8 *)&phys_mempool_size, ++ sizeof(phys_mempool_size)); ++ } ++ GT_1trace(driverTrace, GT_7CLASS, "phys_mempool_size = 0x%x\n", ++ phys_mempool_base); ++ if ((phys_mempool_base > 0x0) && (phys_mempool_size > 0x0)) ++ MEM_ExtPhysPoolInit(phys_mempool_base, phys_mempool_size); ++ if (tc_wordswapon) { ++ GT_0trace(driverTrace, GT_7CLASS, "TC Word Swap is enabled\n"); ++ REG_SetValue(NULL, NULL, TCWORDSWAP, REG_DWORD, ++ (u8 *)&tc_wordswapon, sizeof(tc_wordswapon)); ++ } else { ++ GT_0trace(driverTrace, GT_7CLASS, "TC Word Swap is disabled\n"); ++ REG_SetValue(NULL, NULL, TCWORDSWAP, ++ REG_DWORD, (u8 *)&tc_wordswapon, ++ sizeof(tc_wordswapon)); ++ } ++ if (DSP_SUCCEEDED(initStatus)) { ++#ifdef CONFIG_BRIDGE_DVFS ++ for (i = 0; i < 6; i++) ++ pdata->mpu_speed[i] = vdd1_rate_table_bridge[i].rate; ++ ++ clk_handle = clk_get(NULL, "iva2_ck"); ++ if (!clk_handle) { ++ GT_0trace(driverTrace, GT_7CLASS, ++ "clk_get failed to get iva2_ck \n"); ++ } else { ++ GT_0trace(driverTrace, GT_7CLASS, ++ "clk_get PASS to get iva2_ck \n"); ++ } ++ if (!clk_notifier_register(clk_handle, &iva_clk_notifier)) { ++ GT_0trace(driverTrace, GT_7CLASS, ++ "clk_notifier_register PASS for iva2_ck \n"); ++ } else { ++ GT_0trace(driverTrace, GT_7CLASS, ++ "clk_notifier_register FAIL for iva2_ck \n"); ++ } ++#endif ++ driverContext = DSP_Init(&initStatus); ++ if (DSP_FAILED(initStatus)) { ++ status = -1; ++ GT_0trace(driverTrace, GT_7CLASS, ++ "DSP/BIOS Bridge initialization Failed\n"); ++ } else { ++ GT_0trace(driverTrace, GT_5CLASS, ++ "DSP/BIOS Bridge driver loaded\n"); ++ } ++ } ++ ++ DBC_Assert(status == 0); ++ DBC_Assert(DSP_SUCCEEDED(initStatus)); ++ GT_0trace(driverTrace, GT_ENTER, " <- driver_init\n"); ++ return status; ++} ++ ++static int __devexit omap34xx_bridge_remove(struct platform_device *pdev) ++{ ++ dev_t devno; ++ bool ret; ++ DSP_STATUS dsp_status = DSP_SOK; ++ HANDLE hDrvObject = NULL; ++ struct PROCESS_CONTEXT *pTmp = NULL; ++ struct PROCESS_CONTEXT *pCtxtclosed = NULL; ++ struct PROCESS_CONTEXT *pCtxttraverse = NULL; ++ ++ GT_0trace(driverTrace, GT_ENTER, "-> driver_exit\n"); ++ ++ dsp_status = CFG_GetObject((u32 *)&hDrvObject, REG_DRV_OBJECT); ++ DRV_GetProcCtxtList(&pCtxtclosed, (struct DRV_OBJECT *)hDrvObject); ++ while (pCtxtclosed != NULL) { ++ DRV_RemoveAllResources(pCtxtclosed); ++ if (pCtxtclosed->hProcessor != NULL) { ++ DRV_GetProcCtxtList(&pCtxttraverse, ++ (struct DRV_OBJECT *)hDrvObject); ++ if (pCtxttraverse->next == NULL) { ++ PROC_Detach(pCtxtclosed->hProcessor); ++ goto func_cont; ++ } ++ if ((pCtxtclosed->pid == pCtxttraverse->pid) && ++ (pCtxttraverse->next != NULL)) { ++ pCtxttraverse = pCtxttraverse->next; ++ } ++ while ((pCtxttraverse != NULL) && ++ (pCtxtclosed->hProcessor ++ != pCtxttraverse->hProcessor)) { ++ pCtxttraverse = pCtxttraverse->next; ++ if ((pCtxttraverse != NULL) && ++ (pCtxtclosed->pid == pCtxttraverse->pid)) { ++ pCtxttraverse = pCtxttraverse->next; ++ } ++ } ++ if (pCtxttraverse == NULL) ++ PROC_Detach(pCtxtclosed->hProcessor); ++ } ++func_cont: ++ pTmp = pCtxtclosed->next; ++ DRV_RemoveProcContext((struct DRV_OBJECT *)hDrvObject, ++ pCtxtclosed, (void *)pCtxtclosed->pid); ++ pCtxtclosed = pTmp; ++ } ++ ++ /* unregister the clock notifier */ ++#ifdef CONFIG_BRIDGE_DVFS ++ if (!clk_notifier_unregister(clk_handle, &iva_clk_notifier)) { ++ GT_0trace(driverTrace, GT_7CLASS, ++ "clk_notifier_unregister PASS for iva2_ck \n"); ++ } else { ++ GT_0trace(driverTrace, GT_7CLASS, ++ "clk_notifier_unregister PASS for iva2_ck \n"); ++ } ++ ++ clk_put(clk_handle); ++ clk_handle = NULL; ++#endif /* #ifdef CONFIG_BRIDGE_DVFS */ ++ ++ if (driverContext) { ++ ret = DSP_Deinit(driverContext); ++ driverContext = 0; ++ ++ DBC_Assert(ret == true); ++ } ++ SERVICES_Exit(); ++ GT_exit(); ++ ++ devno = MKDEV(driver_major, driver_minor); ++ if (bridge_device) { ++ cdev_del(&bridge_device->cdev); ++ kfree(bridge_device); ++ } ++ unregister_chrdev_region(devno, 1); ++ if (bridge_class) { ++ /* remove the device from sysfs */ ++ device_destroy(bridge_class, MKDEV(driver_major, driver_minor)); ++ class_destroy(bridge_class); ++ ++ } ++ return 0; ++} ++ ++ ++#ifdef CONFIG_PM ++static int bridge_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ u32 status; ++ u32 command = PWR_EMERGENCYDEEPSLEEP; ++ ++ status = PWR_SleepDSP(command, timeOut); ++ if (DSP_FAILED(status)) ++ return -1; ++ ++ bridge_suspend_data.suspended = 1; ++ return 0; ++} ++ ++static int bridge_resume(struct platform_device *pdev) ++{ ++ u32 status; ++ ++ status = PWR_WakeDSP(timeOut); ++ if (DSP_FAILED(status)) ++ return -1; ++ ++ bridge_suspend_data.suspended = 0; ++ wake_up(&bridge_suspend_data.suspend_wq); ++ return 0; ++} ++#else ++#define bridge_suspend NULL ++#define bridge_resume NULL ++#endif ++ ++static struct platform_driver bridge_driver = { ++ .driver = { ++ .name = BRIDGE_NAME, ++ }, ++ .probe = omap34xx_bridge_probe, ++ .remove = __devexit_p(omap34xx_bridge_remove), ++ .suspend = bridge_suspend, ++ .resume = bridge_resume, ++}; ++ ++static int __init bridge_init(void) ++{ ++ return platform_driver_register(&bridge_driver); ++} ++ ++static void __exit bridge_exit(void) ++{ ++ platform_driver_unregister(&bridge_driver); ++} ++ ++/* This function is called when an application opens handle to the ++ * bridge driver. */ ++ ++static int bridge_open(struct inode *ip, struct file *filp) ++{ ++ int status = 0; ++#ifndef RES_CLEANUP_DISABLE ++ u32 hProcess; ++ DSP_STATUS dsp_status = DSP_SOK; ++ HANDLE hDrvObject = NULL; ++ struct PROCESS_CONTEXT *pPctxt = NULL; ++ struct PROCESS_CONTEXT *next_node = NULL; ++ struct PROCESS_CONTEXT *pCtxtclosed = NULL; ++ struct PROCESS_CONTEXT *pCtxttraverse = NULL; ++ struct task_struct *tsk = NULL; ++ GT_0trace(driverTrace, GT_ENTER, "-> driver_open\n"); ++ dsp_status = CFG_GetObject((u32 *)&hDrvObject, REG_DRV_OBJECT); ++ ++ /* Checking weather task structure for all process existing ++ * in the process context list If not removing those processes*/ ++ if (DSP_FAILED(dsp_status)) ++ goto func_cont; ++ ++ DRV_GetProcCtxtList(&pCtxtclosed, (struct DRV_OBJECT *)hDrvObject); ++ while (pCtxtclosed != NULL) { ++ tsk = find_task_by_vpid(pCtxtclosed->pid); ++ next_node = pCtxtclosed->next; ++ ++ if ((tsk == NULL) || (tsk->exit_state == EXIT_ZOMBIE)) { ++ ++ GT_1trace(driverTrace, GT_5CLASS, ++ "***Task structure not existing for " ++ "process***%d\n", pCtxtclosed->pid); ++ DRV_RemoveAllResources(pCtxtclosed); ++ if (pCtxtclosed->hProcessor != NULL) { ++ DRV_GetProcCtxtList(&pCtxttraverse, ++ (struct DRV_OBJECT *)hDrvObject); ++ if (pCtxttraverse->next == NULL) { ++ PROC_Detach(pCtxtclosed->hProcessor); ++ } else { ++ if ((pCtxtclosed->pid == ++ pCtxttraverse->pid) && ++ (pCtxttraverse->next != NULL)) { ++ pCtxttraverse = ++ pCtxttraverse->next; ++ } ++ while ((pCtxttraverse != NULL) && ++ (pCtxtclosed->hProcessor ++ != pCtxttraverse->hProcessor)) { ++ pCtxttraverse = ++ pCtxttraverse->next; ++ if ((pCtxttraverse != NULL) && ++ (pCtxtclosed->pid == ++ pCtxttraverse->pid)) { ++ pCtxttraverse = ++ pCtxttraverse->next; ++ } ++ } ++ if (pCtxttraverse == NULL) { ++ PROC_Detach ++ (pCtxtclosed->hProcessor); ++ } ++ } ++ } ++ DRV_RemoveProcContext((struct DRV_OBJECT *)hDrvObject, ++ pCtxtclosed, ++ (void *)pCtxtclosed->pid); ++ } ++ pCtxtclosed = next_node; ++ } ++func_cont: ++ dsp_status = CFG_GetObject((u32 *)&hDrvObject, REG_DRV_OBJECT); ++ if (DSP_SUCCEEDED(dsp_status)) ++ dsp_status = DRV_InsertProcContext( ++ (struct DRV_OBJECT *)hDrvObject, &pPctxt); ++ ++ if (pPctxt != NULL) { ++ /* Return PID instead of process handle */ ++ hProcess = current->pid; ++ ++ DRV_ProcUpdatestate(pPctxt, PROC_RES_ALLOCATED); ++ DRV_ProcSetPID(pPctxt, hProcess); ++ } ++#endif ++ ++ GT_0trace(driverTrace, GT_ENTER, " <- driver_open\n"); ++ return status; ++} ++ ++/* This function is called when an application closes handle to the bridge ++ * driver. */ ++static int bridge_release(struct inode *ip, struct file *filp) ++{ ++ int status; ++ u32 pid; ++ ++ GT_0trace(driverTrace, GT_ENTER, "-> driver_release\n"); ++ ++ /* Return PID instead of process handle */ ++ pid = current->pid; ++ ++ status = DSP_Close(pid); ++ ++ ++ (status == true) ? (status = 0) : (status = -1); ++ ++ GT_0trace(driverTrace, GT_ENTER, " <- driver_release\n"); ++ ++ return status; ++} ++ ++/* This function provides IO interface to the bridge driver. */ ++static int bridge_ioctl(struct inode *ip, struct file *filp, unsigned int code, ++ unsigned long args) ++{ ++ int status; ++ u32 retval = DSP_SOK; ++ union Trapped_Args pBufIn; ++ ++ DBC_Require(filp != NULL); ++#ifdef CONFIG_PM ++ status = omap34xxbridge_suspend_lockout(&bridge_suspend_data, filp); ++ if (status != 0) ++ return status; ++#endif ++ ++ GT_0trace(driverTrace, GT_ENTER, " -> driver_ioctl\n"); ++ ++ /* Deduct one for the CMD_BASE. */ ++ code = (code - 1); ++ ++ status = copy_from_user(&pBufIn, (union Trapped_Args *)args, ++ sizeof(union Trapped_Args)); ++ ++ if (status >= 0) { ++ status = WCD_CallDevIOCtl(code, &pBufIn, &retval); ++ ++ if (DSP_SUCCEEDED(status)) { ++ status = retval; ++ } else { ++ GT_1trace(driverTrace, GT_7CLASS, ++ "IOCTL Failed, code : 0x%x\n", code); ++ status = -1; ++ } ++ ++ } ++ ++ GT_0trace(driverTrace, GT_ENTER, " <- driver_ioctl\n"); ++ ++ return status; ++} ++ ++/* This function maps kernel space memory to user space memory. */ ++static int bridge_mmap(struct file *filp, struct vm_area_struct *vma) ++{ ++#if GT_TRACE ++ u32 offset = vma->vm_pgoff << PAGE_SHIFT; ++#endif ++ u32 status; ++ ++ DBC_Assert(vma->vm_start < vma->vm_end); ++ ++ vma->vm_flags |= VM_RESERVED | VM_IO; ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ ++ GT_6trace(driverTrace, GT_3CLASS, ++ "vm filp %p offset %lx start %lx end %lx" ++ " page_prot %lx flags %lx\n", filp, offset, vma->vm_start, ++ vma->vm_end, vma->vm_page_prot, vma->vm_flags); ++ ++ status = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, ++ vma->vm_end - vma->vm_start, vma->vm_page_prot); ++ if (status != 0) ++ status = -EAGAIN; ++ ++ return status; ++} ++ ++#ifndef RES_CLEANUP_DISABLE ++/* To remove all process resources before removing the process from the ++ * process context list*/ ++DSP_STATUS DRV_RemoveAllResources(HANDLE hPCtxt) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct PROCESS_CONTEXT *pCtxt = (struct PROCESS_CONTEXT *)hPCtxt; ++ if (pCtxt != NULL) { ++ DRV_RemoveAllSTRMResElements(pCtxt); ++ DRV_RemoveAllNodeResElements(pCtxt); ++ DRV_RemoveAllDMMResElements(pCtxt); ++ DRV_ProcUpdatestate(pCtxt, PROC_RES_FREED); ++ } ++ return status; ++} ++#endif ++ ++/* Bridge driver initialization and de-initialization functions */ ++module_init(bridge_init); ++module_exit(bridge_exit); ++ +diff --git a/drivers/dsp/bridge/rmgr/drv_interface.h b/drivers/dsp/bridge/rmgr/drv_interface.h +new file mode 100644 +index 0000000..f5b068e +--- /dev/null ++++ b/drivers/dsp/bridge/rmgr/drv_interface.h +@@ -0,0 +1,40 @@ ++/* ++ * drv_interface.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== drv_interface.h ======== ++ * ++ *! Revision History ++ *! ================ ++ *! 24-Mar-2003 vp Added hooks for Power Management Test ++ *! 18-Feb-2003 vp Code review updates ++ *! 18-Oct-2002 sb Created initial version ++ ++ */ ++ ++#ifndef _DRV_INTERFACE_H_ ++#define _DRV_INTERFACE_H_ ++ ++/* Prototypes for all functions in this bridge */ ++static int __init bridge_init(void); /* Initialize bridge */ ++static void __exit bridge_exit(void); /* Opposite of initialize */ ++static int bridge_open(struct inode *, struct file *); /* Open */ ++static int bridge_release(struct inode *, struct file *); /* Release */ ++static int bridge_ioctl(struct inode *, struct file *, unsigned int, ++ unsigned long); ++static int bridge_mmap(struct file *filp, struct vm_area_struct *vma); ++#endif /* ifndef _DRV_INTERFACE_H_ */ +diff --git a/drivers/dsp/bridge/rmgr/dspdrv.c b/drivers/dsp/bridge/rmgr/dspdrv.c +new file mode 100644 +index 0000000..a7a74fc +--- /dev/null ++++ b/drivers/dsp/bridge/rmgr/dspdrv.c +@@ -0,0 +1,276 @@ ++/* ++ * dspdrv.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dspdrv.c ======== ++ * Description: ++ * Interface to allocate and free bridge resources. ++ * ++ *! Revision History ++ *! ================ ++ *! 12-Apr-2004 hp: Compile IVA only for 24xx. ++ *! 09-Feb-2004 vp: Updated to support IVA. ++ *! 10-Feb-2003 vp: Code review updates. ++ *! 18-oct-2002 vp: Ported to the Linux platform. ++ *! 03-Mar-2002 rr: DSP_Deinit bug fixed (gets the Mgrhandle from registry ++ *! before calling MGR_Destroy. ++ *! 11-Jul-2001 jeh Moved MGR_Create() from DSP_Init() to DEV_StartDevice(). ++ *! 02-Apr-2001 rr: WCD_InitComplete2 return value is not checked thus ++ *! sllowing the class driver to load irrespective of ++ *! the image load. ++ *! 30-Oct-2000 kc: Made changes w.r.t. usage of REG_SetValue. ++ *! 05-Oct-2000 rr: WCD_InitComplete2 return value checked for RM. ++ *! Failure in WCD_InitComplete2 will cause the ++ *! DSP_Init to fail. ++ *! 12-Aug-2000 kc: Changed REG_EnumValue to REG_EnumKey. ++ *! 07-Aug-2000 rr: MGR_Create does the job of loading the DCD Dll. ++ *! 26-Jul-2000 rr: Driver Object holds the DevNodeStrings for each ++ *! DevObjects. Static variables removed. Returns ++ *! the Driver Object in DSP_Init. ++ *! 17-Jul-2000 rr: Driver Object is created in DSP_Init and that holds ++ *! the list of Device objects. ++ *! 07-Jul-2000 rr: RM implementaion started. ++ *! 24-May-2000 ag: Cleaned up debug msgs. ++ *! 02-May-2000 rr: DSP_Open returns GetCallerProcess as dwOpenContext. ++ *! 03-Feb-2000 rr: GT Changes. ++ *! 28-Jan-2000 rr: Code Cleaned up.Type void changed to void. ++ *! DSP_Deinit checks return values.dwCode in ++ *! DSP_IO_CONTROL is decoded(not hard coded) ++ *! 27-Jan-2000 rr: REG_EnumValue Used .EnumerateKey fxn removed. ++ *! 13-Jan-2000 rr: CFG_GetPrivateDword renamed to CFG_GetDevObject. ++ *! 29-Dec-1999 rr: Code Cleaned up ++ *! 09-Dec-1999 rr: EnumerateKey changed for retail build. ++ *! 06-Dec-1999 rr: ArrayofInstalledNode, index and ArrayofInstalledDev ++ *! is Global.DevObject stores this pointer as hDevNode. ++ *! 02-Dec-1999 rr: DBG_SetGT and RetailMSG conditionally included. ++ *! Comments changed.Deinit handled.Code cleaned up. ++ *! DSP_IOControl, Close, Deinit returns bool values. ++ *! Calls WCD_InitComplete2 for Board AutoStart. ++ *! 29-Nov-1999 rr: DSP_IOControl returns the result through pBufOut. ++ *! Global Arrays keeps track of installed devices. ++ *! 19-Nov-1999 rr: DSP_Init handles multiple drivers. ++ *! 12-Nov-1999 rr: GetDriverKey and EnumerateKey functions added. ++ *! for multiple mini driver support.PCCARD flag ++ *! checking to include PCMCIA related stuff. ++ *! 25-Oct-1999 rr: GT_Init is called within the Process Attach. ++ *! return value initalized to S_OK upfront in the ++ *! Process Attach. ++ *! 15-Oct-1999 rr: DSP_DeInit handles the return values ++ *! 05-Oct-1999 rr: All the PCMCIA related functions are now in PCCARD.c ++ *! DRV_Request Resources is used instead of the ++ *! RegisterMiniDriver as it sounds close to what we are doing. ++ *! 24-Sep-1999 rr: DRV_RegisterMiniDriver is being called from here. Only ++ *! neccessaryPCMCIA fxns are here. Soon they will move out ++ *! either to a seperate file for bus specific inits. ++ *! 10-Sep-1999 rr: GT Enabled. Considerably changed the driver structure as ++ *! - This is the Class driver. After successfully initialized ++ *! the Class driver will attempt to load the Mini driver. ++ *! - Need to seperate the PCMCIA stuff based on bus type. ++ *! - Changed the name of the file to wcdce.c ++ *! - Made the Media Handle as Global again ++ *! ++ *! 19-Aug-1999 rr: Removed the Global hbhMediaHandle. Included the MemTest. ++ *! Modified the DSP_Init, now three windows are opened. ++ *! Split the driver into PDD so that hardware dependent ++ *! functions will reside in PDD. ++ *! 16-Jul-1999 ag Adapted from rkw's CAC Bullet card driver. ++ *! ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Resource Manager */ ++#include ++ ++/* ----------------------------------- Others */ ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ----------------------------------- Globals */ ++struct GT_Mask curTrace; ++ ++/* ++ * ======== DSP_Init ======== ++ * Allocates bridge resources. Loads a base image onto DSP, if specified. ++ */ ++u32 DSP_Init(OUT u32 *initStatus) ++{ ++ char devNode[MAXREGPATHLENGTH] = "TIOMAP1510"; ++ DSP_STATUS status = DSP_EFAIL; ++ struct DRV_OBJECT *drvObject = NULL; ++ u32 index = 0; ++ u32 deviceNode; ++ u32 deviceNodeString; ++ ++ GT_create(&curTrace, "DD"); ++ ++ GT_0trace(curTrace, GT_ENTER, "Entering DSP_Init \r\n"); ++ ++ if (DSP_FAILED(WCD_Init())) { ++ GT_0trace(curTrace, GT_7CLASS, "DSP_Init Failed \n"); ++ goto func_cont; ++ } /* End WCD_Exit */ ++ if (DSP_FAILED(DRV_Create(&drvObject))) { ++ GT_0trace(curTrace, GT_7CLASS, "DSP_Init:DRV_Create Failed \n"); ++ WCD_Exit(); ++ goto func_cont; ++ } /* End DRV_Create */ ++ GT_0trace(curTrace, GT_5CLASS, "DSP_Init:DRV Created \r\n"); ++ ++ /* Request Resources */ ++ if (DSP_SUCCEEDED(DRV_RequestResources((u32)&devNode, ++ &deviceNodeString))) { ++ /* Attempt to Start the Device */ ++ if (DSP_SUCCEEDED(DEV_StartDevice( ++ (struct CFG_DEVNODE *)deviceNodeString))) { ++ /* Retreive the DevObject from the Registry */ ++ GT_2trace(curTrace, GT_1CLASS, ++ "DSP_Init Succeeded for Device1:" ++ "%d: value: %x\n", index, deviceNodeString); ++ status = DSP_SOK; ++ } else { ++ GT_0trace(curTrace, GT_7CLASS, ++ "DSP_Init:DEV_StartDevice Failed\n"); ++ (void)DRV_ReleaseResources ++ ((u32) deviceNodeString, drvObject); ++ status = DSP_EFAIL; ++ } ++ } else { ++ GT_0trace(curTrace, GT_7CLASS, ++ "DSP_Init:DRV_RequestResources Failed \r\n"); ++ status = DSP_EFAIL; ++ } /* DRV_RequestResources */ ++ index++; ++ ++ /* Unwind whatever was loaded */ ++ if (DSP_FAILED(status)) { ++ /* irrespective of the status of DEV_RemoveDevice we conitinue ++ * unloading. Get the Driver Object iterate through and remove. ++ * Reset the status to E_FAIL to avoid going through ++ * WCD_InitComplete2. */ ++ status = DSP_EFAIL; ++ for (deviceNode = DRV_GetFirstDevExtension(); deviceNode != 0; ++ deviceNode = DRV_GetNextDevExtension(deviceNode)) { ++ (void)DEV_RemoveDevice ++ ((struct CFG_DEVNODE *)deviceNode); ++ (void)DRV_ReleaseResources((u32)deviceNode, ++ drvObject); ++ } ++ /* Remove the Driver Object */ ++ (void)DRV_Destroy(drvObject); ++ drvObject = NULL; ++ WCD_Exit(); ++ GT_0trace(curTrace, GT_7CLASS, ++ "DSP_Init:Logical device Failed to Load\n"); ++ } /* Unwinding the loaded drivers */ ++func_cont: ++ /* Attempt to Start the Board */ ++ if (DSP_SUCCEEDED(status)) { ++ /* BRD_AutoStart could fail if the dsp execuetable is not the ++ * correct one. We should not propagate that error ++ * into the device loader. */ ++ (void)WCD_InitComplete2(); ++ GT_0trace(curTrace, GT_1CLASS, "DSP_Init Succeeded\n"); ++ } else { ++ GT_0trace(curTrace, GT_7CLASS, "DSP_Init Failed\n"); ++ } /* End WCD_InitComplete2 */ ++ DBC_Ensure((DSP_SUCCEEDED(status) && drvObject != NULL) || ++ (DSP_FAILED(status) && drvObject == NULL)); ++ *initStatus = status; ++ /* Return the Driver Object */ ++ return (u32)drvObject; ++} ++ ++/* ++ * ======== DSP_Deinit ======== ++ * Frees the resources allocated for bridge. ++ */ ++bool DSP_Deinit(u32 deviceContext) ++{ ++ bool retVal = true; ++ u32 deviceNode; ++ struct MGR_OBJECT *mgrObject = NULL; ++ ++ GT_0trace(curTrace, GT_ENTER, "Entering DSP_Deinit \r\n"); ++ ++ while ((deviceNode = DRV_GetFirstDevExtension()) != 0) { ++ (void)DEV_RemoveDevice((struct CFG_DEVNODE *)deviceNode); ++ ++ (void)DRV_ReleaseResources((u32)deviceNode, ++ (struct DRV_OBJECT *)deviceContext); ++ } ++ ++ (void) DRV_Destroy((struct DRV_OBJECT *) deviceContext); ++ ++ /* Get the Manager Object from Registry ++ * MGR Destroy will unload the DCD dll */ ++ if (DSP_SUCCEEDED(CFG_GetObject((u32 *)&mgrObject, REG_MGR_OBJECT))) ++ (void)MGR_Destroy(mgrObject); ++ ++ WCD_Exit(); ++ ++ return retVal; ++} ++ ++/* ++ * ======== DSP_Close ======== ++ * The Calling Process handle is passed to DEV_CleanupProcesState ++ * for cleaning up of any resources used by the application ++ */ ++bool DSP_Close(u32 dwOpenContext) ++{ ++ bool retVal = false; ++ ++ DBC_Require(dwOpenContext != 0); ++ ++ GT_0trace(curTrace, GT_ENTER, "Entering DSP_Close\n"); ++ ++#ifdef RES_CLEANUP_DISABLE ++ ++ if (DSP_SUCCEEDED(DEV_CleanupProcessState((HANDLE) dwOpenContext))) { ++ GT_0trace(curTrace, GT_1CLASS, "DSP_Close Succeeded \r\n"); ++ retVal = true; ++ } else { ++ GT_0trace(curTrace, GT_7CLASS, "DSP_Close failed \r\n"); ++ } ++#endif ++ ++ return retVal; ++} +diff --git a/drivers/dsp/bridge/rmgr/mgr.c b/drivers/dsp/bridge/rmgr/mgr.c +new file mode 100644 +index 0000000..943cf93 +--- /dev/null ++++ b/drivers/dsp/bridge/rmgr/mgr.c +@@ -0,0 +1,491 @@ ++/* ++ * mgr.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== mgr.c ======== ++ * Description: ++ * Implementation of Manager interface to the device object at the ++ * driver level. This queries the NDB data base and retrieves the ++ * data about Node and Processor. ++ * ++ * ++ *! Revision History: ++ *! ================ ++ *! 12-Feb-2003 vp: Code review updates. ++ *! 18-Oct-2002 vp: Ported to Linux platform ++ *! 01-Aug-2001 ag: Added extended info for DSP-MMU setup support. ++ *! 13-Feb-2001 kc: DSP/BIOS Bridge name updates. ++ *! 22-Nov-2000 kc: Added MGR_GetPerfData. ++ *! 03-Nov-2000 rr: Updated after code review. ++ *! 25-Sep-2000 rr: Updated to Version 0.9 ++ *! 10-Aug-2000 rr: dwSignature is not specifically inserted in MGR Obj ++ *! as it is taken care by MEM_AllocObject. stdwin.h added ++ *! for retail build to succeed. ++ *! 07-Aug-2000 rr: MGR_Create does the job of Loading DCD Dll. ++ *! 26-Jul-2000 rr: MGR_Destroy releases the hNDBDll. ++ *! 20-Jun-2000 rr: Created. ++ */ ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Others */ ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ----------------------------------- Defines, Data Structures, Typedefs */ ++#define ZLDLLNAME "" ++#define SIGNATURE 0x5f52474d /* "MGR_" (in reverse) */ ++ ++struct MGR_OBJECT { ++ u32 dwSignature; ++ struct DCD_MANAGER *hDcdMgr; /* Proc/Node data manager */ ++}; ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask MGR_DebugMask = { NULL, NULL }; ++#endif ++ ++static u32 cRefs; ++ ++/* ++ * ========= MGR_Create ========= ++ * Purpose: ++ * MGR Object gets created only once during driver Loading. ++ */ ++DSP_STATUS MGR_Create(OUT struct MGR_OBJECT **phMgrObject, ++ struct CFG_DEVNODE *hDevNode) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct MGR_OBJECT *pMgrObject = NULL; ++ ++ DBC_Require(phMgrObject != NULL); ++ DBC_Require(cRefs > 0); ++ GT_1trace(MGR_DebugMask, GT_ENTER, ++ "Entering MGR_Create phMgrObject 0x%x\n ", ++ phMgrObject); ++ MEM_AllocObject(pMgrObject, struct MGR_OBJECT, SIGNATURE); ++ if (pMgrObject) { ++ if (DSP_SUCCEEDED(DCD_CreateManager(ZLDLLNAME, ++ &pMgrObject->hDcdMgr))) { ++ /* If succeeded store the handle in the MGR Object */ ++ if (DSP_SUCCEEDED(CFG_SetObject((u32)pMgrObject, ++ REG_MGR_OBJECT))) { ++ *phMgrObject = pMgrObject; ++ GT_0trace(MGR_DebugMask, GT_1CLASS, ++ "MGR_Create:MGR Created\r\n"); ++ } else { ++ status = DSP_EFAIL; ++ GT_0trace(MGR_DebugMask, GT_7CLASS, ++ "MGR_Create:CFG_SetObject " ++ "Failed\r\n"); ++ DCD_DestroyManager(pMgrObject->hDcdMgr); ++ MEM_FreeObject(pMgrObject); ++ } ++ } else { ++ /* failed to Create DCD Manager */ ++ status = DSP_EFAIL; ++ GT_0trace(MGR_DebugMask, GT_7CLASS, ++ "MGR_Create:DCD_ManagerCreate Failed\r\n"); ++ MEM_FreeObject(pMgrObject); ++ } ++ } else { ++ status = DSP_EMEMORY; ++ GT_0trace(MGR_DebugMask, GT_7CLASS, ++ "MGR_Create DSP_FAILED to allocate memory \n"); ++ } ++ GT_2trace(MGR_DebugMask, GT_ENTER, ++ "Exiting MGR_Create: phMgrObject: 0x%x\t" ++ "status: 0x%x\n", phMgrObject, status); ++ DBC_Ensure(DSP_FAILED(status) || ++ MEM_IsValidHandle(pMgrObject, SIGNATURE)); ++ return status; ++} ++ ++/* ++ * ========= MGR_Destroy ========= ++ * This function is invoked during bridge driver unloading.Frees MGR object. ++ */ ++DSP_STATUS MGR_Destroy(struct MGR_OBJECT *hMgrObject) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct MGR_OBJECT *pMgrObject = (struct MGR_OBJECT *)hMgrObject; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(hMgrObject, SIGNATURE)); ++ ++ GT_1trace(MGR_DebugMask, GT_ENTER, ++ "Entering MGR_Destroy hMgrObject 0x%x\n", hMgrObject); ++ /* Free resources */ ++ if (hMgrObject->hDcdMgr) ++ DCD_DestroyManager(hMgrObject->hDcdMgr); ++ ++ MEM_FreeObject(pMgrObject); ++ /* Update the Registry with NULL for MGR Object */ ++ (void)CFG_SetObject(0, REG_MGR_OBJECT); ++ ++ GT_2trace(MGR_DebugMask, GT_ENTER, ++ "Exiting MGR_Destroy: hMgrObject: 0x%x\t" ++ "status: 0x%x\n", hMgrObject, status); ++ ++ DBC_Ensure(DSP_FAILED(status) || ++ !MEM_IsValidHandle(hMgrObject, SIGNATURE)); ++ ++ return status; ++} ++ ++/* ++ * ======== MGR_EnumNodeInfo ======== ++ * Enumerate and get configuration information about nodes configured ++ * in the node database. ++ */ ++DSP_STATUS MGR_EnumNodeInfo(u32 uNode, OUT struct DSP_NDBPROPS *pNDBProps, ++ u32 uNDBPropsSize, OUT u32 *puNumNodes) ++{ ++ DSP_STATUS status = DSP_SOK; ++ DSP_STATUS status1 = DSP_SOK; ++ struct DSP_UUID Uuid, uTempUuid; ++ u32 uTempIndex = 0; ++ u32 uNodeIndex = 0; ++ struct DCD_GENERICOBJ GenObj; ++ struct MGR_OBJECT *pMgrObject = NULL; ++ ++ DBC_Require(pNDBProps != NULL); ++ DBC_Require(puNumNodes != NULL); ++ DBC_Require(uNDBPropsSize >= sizeof(struct DSP_NDBPROPS)); ++ DBC_Require(cRefs > 0); ++ ++ GT_4trace(MGR_DebugMask, GT_ENTER, "Entered Manager_EnumNodeInfo, " ++ "args:\n\t uNode: 0x%x\n\tpNDBProps: 0x%x\n\tuNDBPropsSize:" ++ "0x%x\tpuNumNodes: 0x%x\n", uNode, pNDBProps, ++ uNDBPropsSize, puNumNodes); ++ *puNumNodes = 0; ++ /* Get The Manager Object from the Registry */ ++ if (DSP_FAILED(CFG_GetObject((u32 *)&pMgrObject, ++ REG_MGR_OBJECT))) { ++ GT_0trace(MGR_DebugMask, GT_7CLASS, ++ "Manager_EnumNodeInfo:Failed To Get" ++ " MGR Object from Registry\r\n"); ++ goto func_cont; ++ } ++ DBC_Assert(MEM_IsValidHandle(pMgrObject, SIGNATURE)); ++ /* Forever loop till we hit failed or no more items in the ++ * Enumeration. We will exit the loop other than DSP_SOK; */ ++ while (status == DSP_SOK) { ++ status = DCD_EnumerateObject(uTempIndex++, DSP_DCDNODETYPE, ++ &uTempUuid); ++ if (status == DSP_SOK) { ++ uNodeIndex++; ++ if (uNode == (uNodeIndex - 1)) ++ Uuid = uTempUuid; ++ ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ if (uNode > (uNodeIndex - 1)) { ++ status = DSP_EINVALIDARG; ++ GT_0trace(MGR_DebugMask, GT_7CLASS, ++ "Manager_EnumNodeInfo: uNode" ++ " is Invalid \r\n"); ++ } else { ++ status1 = DCD_GetObjectDef(pMgrObject->hDcdMgr, ++ (struct DSP_UUID *)&Uuid, ++ DSP_DCDNODETYPE, &GenObj); ++ if (DSP_SUCCEEDED(status1)) { ++ /* Get the Obj def */ ++ *pNDBProps = GenObj.objData.nodeObj.ndbProps; ++ *puNumNodes = uNodeIndex; ++ status = DSP_SOK; ++ } else { ++ GT_0trace(MGR_DebugMask, GT_7CLASS, ++ "Manager_EnumNodeInfo: " ++ "Failed to Get Node Info \r\n"); ++ status = DSP_EFAIL; ++ } ++ } ++ } else { ++ /* This could be changed during enum, EFAIL ... */ ++ GT_0trace(MGR_DebugMask, GT_7CLASS, "Manager_EnumNodeInfo: " ++ "Enumeration failure\r\n"); ++ status = DSP_EFAIL; ++ } ++func_cont: ++ GT_4trace(MGR_DebugMask, GT_ENTER, ++ "Exiting Manager_EnumNodeInfo, args:\n\t" ++ "uNode: 0x%x\n\tpNDBProps: 0x%x\n\tuNDBPropsSize:" ++ " 0x%x\tuNumNodes: 0x%x\n", uNode, pNDBProps, ++ uNDBPropsSize, *puNumNodes); ++ DBC_Ensure((DSP_SUCCEEDED(status) && *puNumNodes > 0) || ++ (DSP_FAILED(status) && *puNumNodes == 0)); ++ ++ return status; ++} ++ ++/* ++ * ======== MGR_EnumProcessorInfo ======== ++ * Enumerate and get configuration information about available ++ * DSP processors. ++ */ ++DSP_STATUS MGR_EnumProcessorInfo(u32 uProcessor, ++ OUT struct DSP_PROCESSORINFO *pProcessorInfo, ++ u32 uProcessorInfoSize, OUT u32 *puNumProcs) ++{ ++ DSP_STATUS status = DSP_SOK; ++ DSP_STATUS status1 = DSP_SOK; ++ DSP_STATUS status2 = DSP_SOK; ++ struct DSP_UUID uTempUuid; ++ u32 uTempIndex = 0; ++ u32 uProcIndex = 0; ++ struct DCD_GENERICOBJ GenObj; ++ struct MGR_OBJECT *pMgrObject = NULL; ++ struct MGR_PROCESSOREXTINFO *pExtInfo; ++ struct DEV_OBJECT *hDevObject; ++ struct DRV_OBJECT *hDrvObject; ++ s32 devType; ++ struct CFG_DEVNODE *devNode; ++ struct CFG_DSPRES chipResources; ++ bool procDetect = false; ++ ++ DBC_Require(pProcessorInfo != NULL); ++ DBC_Require(puNumProcs != NULL); ++ DBC_Require(uProcessorInfoSize >= sizeof(struct DSP_PROCESSORINFO)); ++ DBC_Require(cRefs > 0); ++ ++ GT_4trace(MGR_DebugMask, GT_ENTER, ++ "Entered Manager_EnumProcessorInfo, " ++ "args:\n\tuProcessor: 0x%x\n\tpProcessorInfo: 0x%x\n\t" ++ "uProcessorInfoSize: 0x%x\tpuNumProcs: 0x%x\n", uProcessor, ++ pProcessorInfo, uProcessorInfoSize, puNumProcs); ++ *puNumProcs = 0; ++ status = CFG_GetObject((u32 *)&hDrvObject, REG_DRV_OBJECT); ++ if (DSP_SUCCEEDED(status)) { ++ status = DRV_GetDevObject(uProcessor, hDrvObject, &hDevObject); ++ if (DSP_SUCCEEDED(status)) { ++ status = DEV_GetDevType(hDevObject, (u32 *) &devType); ++ status = DEV_GetDevNode(hDevObject, &devNode); ++ if (devType == DSP_UNIT) { ++ status = CFG_GetDSPResources(devNode, ++ &chipResources); ++ } else { ++ status = DSP_EFAIL; ++ GT_1trace(MGR_DebugMask, GT_7CLASS, ++ "Unsupported dev type gotten" ++ "from device object %d\n", devType); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ pProcessorInfo->uProcessorType = ++ chipResources.uChipType; ++ } ++ } ++ } ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ /* Get The Manager Object from the Registry */ ++ if (DSP_FAILED(CFG_GetObject((u32 *)&pMgrObject, ++ REG_MGR_OBJECT))) { ++ GT_0trace(MGR_DebugMask, GT_7CLASS, ++ "Manager_EnumProcessorInfo: " ++ "Failed To Get MGR Object from Registry\r\n"); ++ goto func_end; ++ } ++ DBC_Assert(MEM_IsValidHandle(pMgrObject, SIGNATURE)); ++ /* Forever loop till we hit no more items in the ++ * Enumeration. We will exit the loop other than DSP_SOK; */ ++ while (status1 == DSP_SOK) { ++ status1 = DCD_EnumerateObject(uTempIndex++, ++ DSP_DCDPROCESSORTYPE, ++ &uTempUuid); ++ if (status1 != DSP_SOK) ++ break; ++ ++ uProcIndex++; ++ /* Get the Object properties to find the Device/Processor ++ * Type */ ++ if (procDetect != false) ++ continue; ++ ++ status2 = DCD_GetObjectDef(pMgrObject->hDcdMgr, ++ (struct DSP_UUID *)&uTempUuid, ++ DSP_DCDPROCESSORTYPE, ++ &GenObj); ++ if (DSP_SUCCEEDED(status2)) { ++ /* Get the Obj def */ ++ if (uProcessorInfoSize < ++ sizeof(struct MGR_PROCESSOREXTINFO)) { ++ *pProcessorInfo = GenObj.objData.procObj; ++ } else { ++ /* extended info */ ++ pExtInfo = (struct MGR_PROCESSOREXTINFO *) ++ pProcessorInfo; ++ *pExtInfo = GenObj.objData.extProcObj; ++ } ++ GT_1trace(MGR_DebugMask, GT_7CLASS, ++ "Manager_EnumProcessorInfo: Got" ++ " Proctype from DCD %x \r\n", ++ pProcessorInfo->uProcessorType); ++ /* See if we got the needed processor */ ++ if (devType == DSP_UNIT) { ++ if (pProcessorInfo->uProcessorType == ++ DSPPROCTYPE_C64) ++ procDetect = true; ++ } else if (devType == IVA_UNIT) { ++ if (pProcessorInfo->uProcessorType == ++ IVAPROCTYPE_ARM7) ++ procDetect = true; ++ } ++ /* User applciatiuons aonly check for chip type, so ++ * this clumsy overwrite */ ++ pProcessorInfo->uProcessorType = ++ chipResources.uChipType; ++ } else { ++ GT_1trace(MGR_DebugMask, GT_7CLASS, ++ "Manager_EnumProcessorInfo: " ++ "Failed to Get DCD Processor Info %x \r\n", ++ status2); ++ status = DSP_EFAIL; ++ } ++ } ++ *puNumProcs = uProcIndex; ++ if (procDetect == false) { ++ GT_0trace(MGR_DebugMask, GT_7CLASS, ++ "Manager_EnumProcessorInfo: Failed" ++ " to get Proc info from DCD , so use CFG registry\n"); ++ pProcessorInfo->uProcessorType = chipResources.uChipType; ++ } ++func_end: ++ return status; ++} ++ ++/* ++ * ======== MGR_Exit ======== ++ * Decrement reference count, and free resources when reference count is ++ * 0. ++ */ ++void MGR_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ cRefs--; ++ if (cRefs == 0) ++ DCD_Exit(); ++ ++ GT_1trace(MGR_DebugMask, GT_5CLASS, ++ "Entered MGR_Exit, ref count: 0x%x\n", cRefs); ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== MGR_GetDCDHandle ======== ++ * Retrieves the MGR handle. Accessor Function. ++ */ ++DSP_STATUS MGR_GetDCDHandle(struct MGR_OBJECT *hMGRHandle, ++ OUT u32 *phDCDHandle) ++{ ++ DSP_STATUS status = DSP_EFAIL; ++ struct MGR_OBJECT *pMgrObject = (struct MGR_OBJECT *)hMGRHandle; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phDCDHandle != NULL); ++ ++ *phDCDHandle = (u32)NULL; ++ if (MEM_IsValidHandle(pMgrObject, SIGNATURE)) { ++ *phDCDHandle = (u32) pMgrObject->hDcdMgr; ++ status = DSP_SOK; ++ } ++ DBC_Ensure((DSP_SUCCEEDED(status) && *phDCDHandle != (u32)NULL) || ++ (DSP_FAILED(status) && *phDCDHandle == (u32)NULL)); ++ ++ return status; ++} ++ ++/* ++ * ======== MGR_Init ======== ++ * Initialize MGR's private state, keeping a reference count on each call. ++ */ ++bool MGR_Init(void) ++{ ++ bool fRetval = true; ++ bool fInitDCD = false; ++ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++ ++ /* Set the Trace mask */ ++ DBC_Assert(!MGR_DebugMask.flags); ++ ++ GT_create(&MGR_DebugMask, "MG"); /* "MG" for Manager */ ++ fInitDCD = DCD_Init(); /* DCD Module */ ++ ++ if (!fInitDCD) { ++ fRetval = false; ++ GT_0trace(MGR_DebugMask, GT_6CLASS, ++ "MGR_Init failed\n"); ++ } ++ } ++ ++ if (fRetval) ++ cRefs++; ++ ++ ++ GT_1trace(MGR_DebugMask, GT_5CLASS, ++ "Entered MGR_Init, ref count: 0x%x\n", cRefs); ++ DBC_Ensure((fRetval && (cRefs > 0)) || (!fRetval && (cRefs >= 0))); ++ ++ return fRetval; ++} ++ ++/* ++ * ======== MGR_WaitForBridgeEvents ======== ++ * Block on any Bridge event(s) ++ */ ++DSP_STATUS MGR_WaitForBridgeEvents(struct DSP_NOTIFICATION **aNotifications, ++ u32 uCount, OUT u32 *puIndex, u32 uTimeout) ++{ ++ DSP_STATUS status; ++ struct SYNC_OBJECT *hSyncEvents[MAX_EVENTS]; ++ u32 i; ++ ++ DBC_Require(uCount < MAX_EVENTS); ++ ++ for (i = 0; i < uCount; i++) ++ hSyncEvents[i] = aNotifications[i]->handle; ++ ++ status = SYNC_WaitOnMultipleEvents(hSyncEvents, uCount, uTimeout, ++ puIndex); ++ ++ return status; ++ ++} ++ +diff --git a/drivers/dsp/bridge/rmgr/nldr.c b/drivers/dsp/bridge/rmgr/nldr.c +new file mode 100644 +index 0000000..f3c429c +--- /dev/null ++++ b/drivers/dsp/bridge/rmgr/nldr.c +@@ -0,0 +1,1967 @@ ++/* ++ * nldr.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== nldr.c ======== ++ * Description: ++ * DSP/BIOS Bridge dynamic + overlay Node loader. ++ * ++ * Public Functions: ++ * NLDR_Allocate ++ * NLDR_Create ++ * NLDR_Delete ++ * NLDR_Exit ++ * NLDR_Free ++ * NLDR_GetFxnAddr ++ * NLDR_Init ++ * NLDR_Load ++ * NLDR_Unload ++ * ++ * Notes: ++ * ++ *! Revision History ++ *! ================ ++ *! 07-Apr-2003 map Removed references to dead DLDR module ++ *! 23-Jan-2003 map Updated RemoteAlloc to support memory granularity ++ *! 20-Jan-2003 map Updated to maintain persistent dependent libraries ++ *! 15-Jan-2003 map Adapted for use with multiple dynamic phase libraries ++ *! 19-Dec-2002 map Fixed overlay bug in AddOvlySect for overlay ++ *! sections > 1024 bytes. ++ *! 13-Dec-2002 map Fixed NLDR_GetFxnAddr bug by searching dependent ++ *! libs for symbols ++ *! 27-Sep-2002 map Added RemoteFree to convert size to words for ++ *! correct deallocation ++ *! 16-Sep-2002 map Code Review Cleanup(from dldr.c) ++ *! 29-Aug-2002 map Adjusted for ARM-side overlay copy ++ *! 05-Aug-2002 jeh Created. ++ */ ++ ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#ifdef DEBUG ++#include ++#endif ++ ++/* OS adaptation layer */ ++#include ++#include ++ ++/* Platform manager */ ++#include ++#include ++ ++/* Resource manager */ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define NLDR_SIGNATURE 0x52444c4e /* "RDLN" */ ++#define NLDR_NODESIGNATURE 0x4e444c4e /* "NDLN" */ ++ ++/* Name of section containing dynamic load mem */ ++#define DYNMEMSECT ".dspbridge_mem" ++ ++/* Name of section containing dependent library information */ ++#define DEPLIBSECT ".dspbridge_deplibs" ++ ++/* Max depth of recursion for loading node's dependent libraries */ ++#define MAXDEPTH 5 ++ ++/* Max number of persistent libraries kept by a node */ ++#define MAXLIBS 5 ++ ++/* ++ * Defines for extracting packed dynamic load memory requirements from two ++ * masks. ++ * These defines must match node.cdb and dynm.cdb ++ * Format of data/code mask is: ++ * uuuuuuuu|fueeeeee|fudddddd|fucccccc| ++ * where ++ * u = unused ++ * cccccc = prefered/required dynamic mem segid for create phase data/code ++ * dddddd = prefered/required dynamic mem segid for delete phase data/code ++ * eeeeee = prefered/req. dynamic mem segid for execute phase data/code ++ * f = flag indicating if memory is preferred or required: ++ * f = 1 if required, f = 0 if preferred. ++ * ++ * The 6 bits of the segid are interpreted as follows: ++ * ++ * If the 6th bit (bit 5) is not set, then this specifies a memory segment ++ * between 0 and 31 (a maximum of 32 dynamic loading memory segments). ++ * If the 6th bit (bit 5) is set, segid has the following interpretation: ++ * segid = 32 - Any internal memory segment can be used. ++ * segid = 33 - Any external memory segment can be used. ++ * segid = 63 - Any memory segment can be used (in this case the ++ * required/preferred flag is irrelevant). ++ * ++ */ ++/* Maximum allowed dynamic loading memory segments */ ++#define MAXMEMSEGS 32 ++ ++#define MAXSEGID 3 /* Largest possible (real) segid */ ++#define MEMINTERNALID 32 /* Segid meaning use internal mem */ ++#define MEMEXTERNALID 33 /* Segid meaning use external mem */ ++#define NULLID 63 /* Segid meaning no memory req/pref */ ++#define FLAGBIT 7 /* 7th bit is pref./req. flag */ ++#define SEGMASK 0x3f /* Bits 0 - 5 */ ++ ++#define CREATEBIT 0 /* Create segid starts at bit 0 */ ++#define DELETEBIT 8 /* Delete segid starts at bit 8 */ ++#define EXECUTEBIT 16 /* Execute segid starts at bit 16 */ ++ ++/* ++ * Masks that define memory type. Must match defines in dynm.cdb. ++ */ ++#define DYNM_CODE 0x2 ++#define DYNM_DATA 0x4 ++#define DYNM_CODEDATA (DYNM_CODE | DYNM_DATA) ++#define DYNM_INTERNAL 0x8 ++#define DYNM_EXTERNAL 0x10 ++ ++/* ++ * Defines for packing memory requirement/preference flags for code and ++ * data of each of the node's phases into one mask. ++ * The bit is set if the segid is required for loading code/data of the ++ * given phase. The bit is not set, if the segid is preferred only. ++ * ++ * These defines are also used as indeces into a segid array for the node. ++ * eg node's segid[CREATEDATAFLAGBIT] is the memory segment id that the ++ * create phase data is required or preferred to be loaded into. ++ */ ++#define CREATEDATAFLAGBIT 0 ++#define CREATECODEFLAGBIT 1 ++#define EXECUTEDATAFLAGBIT 2 ++#define EXECUTECODEFLAGBIT 3 ++#define DELETEDATAFLAGBIT 4 ++#define DELETECODEFLAGBIT 5 ++#define MAXFLAGS 6 ++ ++#define IsInternal(hNldr, segid) (((segid) <= MAXSEGID && \ ++ hNldr->segTable[(segid)] & DYNM_INTERNAL) || \ ++ (segid) == MEMINTERNALID) ++ ++#define IsExternal(hNldr, segid) (((segid) <= MAXSEGID && \ ++ hNldr->segTable[(segid)] & DYNM_EXTERNAL) || \ ++ (segid) == MEMEXTERNALID) ++ ++#define SWAPLONG(x) ((((x) << 24) & 0xFF000000) | (((x) << 8) & 0xFF0000L) | \ ++ (((x) >> 8) & 0xFF00L) | (((x) >> 24) & 0xFF)) ++ ++#define SWAPWORD(x) ((((x) << 8) & 0xFF00) | (((x) >> 8) & 0xFF)) ++ ++ /* ++ * These names may be embedded in overlay sections to identify which ++ * node phase the section should be overlayed. ++ */ ++#define PCREATE "create" ++#define PDELETE "delete" ++#define PEXECUTE "execute" ++ ++#define IsEqualUUID(uuid1, uuid2) (\ ++ ((uuid1).ulData1 == (uuid2).ulData1) && \ ++ ((uuid1).usData2 == (uuid2).usData2) && \ ++ ((uuid1).usData3 == (uuid2).usData3) && \ ++ ((uuid1).ucData4 == (uuid2).ucData4) && \ ++ ((uuid1).ucData5 == (uuid2).ucData5) && \ ++ (strncmp((void *)(uuid1).ucData6, (void *)(uuid2).ucData6, 6)) == 0) ++ ++ /* ++ * ======== MemInfo ======== ++ * Format of dynamic loading memory segment info in coff file. ++ * Must match dynm.h55. ++ */ ++struct MemInfo { ++ u32 segid; /* Dynamic loading memory segment number */ ++ u32 base; ++ u32 len; ++ u32 type; /* Mask of DYNM_CODE, DYNM_INTERNAL, etc. */ ++}; ++ ++/* ++ * ======== LibNode ======== ++ * For maintaining a tree of library dependencies. ++ */ ++struct LibNode { ++ struct DBLL_LibraryObj *lib; /* The library */ ++ u16 nDepLibs; /* Number of dependent libraries */ ++ struct LibNode *pDepLibs; /* Dependent libraries of lib */ ++}; ++ ++/* ++ * ======== OvlySect ======== ++ * Information needed to overlay a section. ++ */ ++struct OvlySect { ++ struct OvlySect *pNextSect; ++ u32 loadAddr; /* Load address of section */ ++ u32 runAddr; /* Run address of section */ ++ u32 size; /* Size of section */ ++ u16 page; /* DBL_CODE, DBL_DATA */ ++}; ++ ++/* ++ * ======== OvlyNode ======== ++ * For maintaining a list of overlay nodes, with sections that need to be ++ * overlayed for each of the nodes phases. ++ */ ++struct OvlyNode { ++ struct DSP_UUID uuid; ++ char *pNodeName; ++ struct OvlySect *pCreateSects; ++ struct OvlySect *pDeleteSects; ++ struct OvlySect *pExecuteSects; ++ struct OvlySect *pOtherSects; ++ u16 nCreateSects; ++ u16 nDeleteSects; ++ u16 nExecuteSects; ++ u16 nOtherSects; ++ u16 createRef; ++ u16 deleteRef; ++ u16 executeRef; ++ u16 otherRef; ++}; ++ ++/* ++ * ======== NLDR_OBJECT ======== ++ * Overlay loader object. ++ */ ++struct NLDR_OBJECT { ++ u32 dwSignature; /* For object validation */ ++ struct DEV_OBJECT *hDevObject; /* Device object */ ++ struct DCD_MANAGER *hDcdMgr; /* Proc/Node data manager */ ++ struct DBLL_TarObj *dbll; /* The DBL loader */ ++ struct DBLL_LibraryObj *baseLib; /* Base image library */ ++ struct RMM_TargetObj *rmm; /* Remote memory manager for DSP */ ++ struct DBLL_Fxns dbllFxns; /* Loader function table */ ++ struct DBLL_Attrs dbllAttrs; /* attrs to pass to loader functions */ ++ NLDR_OVLYFXN ovlyFxn; /* "write" for overlay nodes */ ++ NLDR_WRITEFXN writeFxn; /* "write" for dynamic nodes */ ++ struct OvlyNode *ovlyTable; /* Table of overlay nodes */ ++ u16 nOvlyNodes; /* Number of overlay nodes in base */ ++ u16 nNode; /* Index for tracking overlay nodes */ ++ u16 nSegs; /* Number of dynamic load mem segs */ ++ u32 *segTable; /* memtypes of dynamic memory segs ++ * indexed by segid ++ */ ++ u16 usDSPMauSize; /* Size of DSP MAU */ ++ u16 usDSPWordSize; /* Size of DSP word */ ++}; ++ ++/* ++ * ======== NLDR_NODEOBJECT ======== ++ * Dynamic node object. This object is created when a node is allocated. ++ */ ++struct NLDR_NODEOBJECT { ++ u32 dwSignature; /* For object validation */ ++ struct NLDR_OBJECT *pNldr; /* Dynamic loader handle */ ++ void *pPrivRef; /* Handle to pass to DBL_WriteFxn */ ++ struct DSP_UUID uuid; /* Node's UUID */ ++ bool fDynamic; /* Dynamically loaded node? */ ++ bool fOverlay; /* Overlay node? */ ++ bool *pfPhaseSplit; /* Multiple phase libraries? */ ++ struct LibNode root; /* Library containing node phase */ ++ struct LibNode createLib; /* Library containing create phase lib */ ++ struct LibNode executeLib; /* Library containing execute phase lib */ ++ struct LibNode deleteLib; /* Library containing delete phase lib */ ++ struct LibNode persLib[MAXLIBS]; /* libs remain loaded until Delete */ ++ s32 nPersLib; /* Number of persistent libraries */ ++ /* Path in lib dependency tree */ ++ struct DBLL_LibraryObj *libPath[MAXDEPTH + 1]; ++ enum NLDR_PHASE phase; /* Node phase currently being loaded */ ++ ++ /* ++ * Dynamic loading memory segments for data and code of each phase. ++ */ ++ u16 segId[MAXFLAGS]; ++ ++ /* ++ * Mask indicating whether each mem segment specified in segId[] ++ * is preferred or required. ++ * For example if (codeDataFlagMask & (1 << EXECUTEDATAFLAGBIT)) != 0, ++ * then it is required to load execute phase data into the memory ++ * specified by segId[EXECUTEDATAFLAGBIT]. ++ */ ++ u32 codeDataFlagMask; ++}; ++ ++/* Dynamic loader function table */ ++static struct DBLL_Fxns dbllFxns = { ++ (DBLL_CloseFxn) DBLL_close, ++ (DBLL_CreateFxn) DBLL_create, ++ (DBLL_DeleteFxn) DBLL_delete, ++ (DBLL_ExitFxn) DBLL_exit, ++ (DBLL_GetAttrsFxn) DBLL_getAttrs, ++ (DBLL_GetAddrFxn) DBLL_getAddr, ++ (DBLL_GetCAddrFxn) DBLL_getCAddr, ++ (DBLL_GetSectFxn) DBLL_getSect, ++ (DBLL_InitFxn) DBLL_init, ++ (DBLL_LoadFxn) DBLL_load, ++ (DBLL_LoadSectFxn) DBLL_loadSect, ++ (DBLL_OpenFxn) DBLL_open, ++ (DBLL_ReadSectFxn) DBLL_readSect, ++ (DBLL_SetAttrsFxn) DBLL_setAttrs, ++ (DBLL_UnloadFxn) DBLL_unload, ++ (DBLL_UnloadSectFxn) DBLL_unloadSect, ++}; ++ ++static struct GT_Mask NLDR_debugMask = { NULL, NULL }; /* GT trace variable */ ++static u32 cRefs; /* module reference count */ ++ ++static DSP_STATUS AddOvlyInfo(void *handle, struct DBLL_SectInfo *sectInfo, ++ u32 addr, u32 nBytes); ++static DSP_STATUS AddOvlyNode(struct DSP_UUID *pUuid, ++ enum DSP_DCDOBJTYPE objType, ++ IN void *handle); ++static DSP_STATUS AddOvlySect(struct NLDR_OBJECT *hNldr, ++ struct OvlySect **pList, ++ struct DBLL_SectInfo *pSectInfo, bool *pExists, ++ u32 addr, u32 nBytes); ++static s32 fakeOvlyWrite(void *handle, u32 dspAddr, void *buf, u32 nBytes, ++ s32 mtype); ++static void FreeSects(struct NLDR_OBJECT *hNldr, struct OvlySect *pPhaseSects, ++ u16 nAlloc); ++static bool GetSymbolValue(void *handle, void *pArg, void *rmmHandle, ++ char *symName, struct DBLL_Symbol **sym); ++static DSP_STATUS LoadLib(struct NLDR_NODEOBJECT *hNldrNode, ++ struct LibNode *root, struct DSP_UUID uuid, ++ bool rootPersistent, struct DBLL_LibraryObj **libPath, ++ enum NLDR_PHASE phase, u16 depth); ++static DSP_STATUS LoadOvly(struct NLDR_NODEOBJECT *hNldrNode, ++ enum NLDR_PHASE phase); ++static DSP_STATUS RemoteAlloc(void **pRef, u16 memType, u32 size, ++ u32 align, u32 *dspAddr, ++ OPTIONAL s32 segmentId, OPTIONAL s32 req, ++ bool reserve); ++static DSP_STATUS RemoteFree(void **pRef, u16 space, u32 dspAddr, ++ u32 size, bool reserve); ++ ++static void UnloadLib(struct NLDR_NODEOBJECT *hNldrNode, struct LibNode *root); ++static void UnloadOvly(struct NLDR_NODEOBJECT *hNldrNode, ++ enum NLDR_PHASE phase); ++static bool findInPersistentLibArray(struct NLDR_NODEOBJECT *hNldrNode, ++ struct DBLL_LibraryObj *lib); ++static u32 findLcm(u32 a, u32 b); ++static u32 findGcf(u32 a, u32 b); ++ ++/* ++ * ======== NLDR_Allocate ======== ++ */ ++DSP_STATUS NLDR_Allocate(struct NLDR_OBJECT *hNldr, void *pPrivRef, ++ IN CONST struct DCD_NODEPROPS *pNodeProps, ++ OUT struct NLDR_NODEOBJECT **phNldrNode, ++ IN bool *pfPhaseSplit) ++{ ++ struct NLDR_NODEOBJECT *pNldrNode = NULL; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pNodeProps != NULL); ++ DBC_Require(phNldrNode != NULL); ++ DBC_Require(MEM_IsValidHandle(hNldr, NLDR_SIGNATURE)); ++ ++ GT_5trace(NLDR_debugMask, GT_ENTER, "NLDR_Allocate(0x%x, 0x%x, 0x%x, " ++ "0x%x, 0x%x)\n", hNldr, pPrivRef, pNodeProps, phNldrNode, ++ pfPhaseSplit); ++ ++ /* Initialize handle in case of failure */ ++ *phNldrNode = NULL; ++ /* Allocate node object */ ++ MEM_AllocObject(pNldrNode, struct NLDR_NODEOBJECT, NLDR_NODESIGNATURE); ++ ++ if (pNldrNode == NULL) { ++ GT_0trace(NLDR_debugMask, GT_6CLASS, "NLDR_Allocate: " ++ "Memory allocation failed\n"); ++ status = DSP_EMEMORY; ++ } else { ++ pNldrNode->pfPhaseSplit = pfPhaseSplit; ++ pNldrNode->nPersLib = 0; ++ pNldrNode->pNldr = hNldr; ++ pNldrNode->pPrivRef = pPrivRef; ++ /* Save node's UUID. */ ++ pNldrNode->uuid = pNodeProps->ndbProps.uiNodeID; ++ /* ++ * Determine if node is a dynamically loaded node from ++ * ndbProps. ++ */ ++ if (pNodeProps->usLoadType == NLDR_DYNAMICLOAD) { ++ /* Dynamic node */ ++ pNldrNode->fDynamic = true; ++ /* ++ * Extract memory requirements from ndbProps masks ++ */ ++ /* Create phase */ ++ pNldrNode->segId[CREATEDATAFLAGBIT] = (u16) ++ (pNodeProps->ulDataMemSegMask >> CREATEBIT) & ++ SEGMASK; ++ pNldrNode->codeDataFlagMask |= ++ ((pNodeProps->ulDataMemSegMask >> ++ (CREATEBIT + FLAGBIT)) & 1) << ++ CREATEDATAFLAGBIT; ++ pNldrNode->segId[CREATECODEFLAGBIT] = (u16) ++ (pNodeProps->ulCodeMemSegMask >> ++ CREATEBIT) & SEGMASK; ++ pNldrNode->codeDataFlagMask |= ++ ((pNodeProps->ulCodeMemSegMask >> ++ (CREATEBIT + FLAGBIT)) & 1) << ++ CREATECODEFLAGBIT; ++ /* Execute phase */ ++ pNldrNode->segId[EXECUTEDATAFLAGBIT] = (u16) ++ (pNodeProps->ulDataMemSegMask >> ++ EXECUTEBIT) & SEGMASK; ++ pNldrNode->codeDataFlagMask |= ++ ((pNodeProps->ulDataMemSegMask >> ++ (EXECUTEBIT + FLAGBIT)) & 1) << ++ EXECUTEDATAFLAGBIT; ++ pNldrNode->segId[EXECUTECODEFLAGBIT] = (u16) ++ (pNodeProps->ulCodeMemSegMask >> ++ EXECUTEBIT) & SEGMASK; ++ pNldrNode->codeDataFlagMask |= ++ ((pNodeProps->ulCodeMemSegMask >> ++ (EXECUTEBIT + FLAGBIT)) & 1) << ++ EXECUTECODEFLAGBIT; ++ /* Delete phase */ ++ pNldrNode->segId[DELETEDATAFLAGBIT] = (u16) ++ (pNodeProps->ulDataMemSegMask >> DELETEBIT) & ++ SEGMASK; ++ pNldrNode->codeDataFlagMask |= ++ ((pNodeProps->ulDataMemSegMask >> ++ (DELETEBIT + FLAGBIT)) & 1) << ++ DELETEDATAFLAGBIT; ++ pNldrNode->segId[DELETECODEFLAGBIT] = (u16) ++ (pNodeProps->ulCodeMemSegMask >> ++ DELETEBIT) & SEGMASK; ++ pNldrNode->codeDataFlagMask |= ++ ((pNodeProps->ulCodeMemSegMask >> ++ (DELETEBIT + FLAGBIT)) & 1) << ++ DELETECODEFLAGBIT; ++ } else { ++ /* Non-dynamically loaded nodes are part of the ++ * base image */ ++ pNldrNode->root.lib = hNldr->baseLib; ++ /* Check for overlay node */ ++ if (pNodeProps->usLoadType == NLDR_OVLYLOAD) ++ pNldrNode->fOverlay = true; ++ ++ } ++ *phNldrNode = (struct NLDR_NODEOBJECT *) pNldrNode; ++ } ++ /* Cleanup on failure */ ++ if (DSP_FAILED(status) && pNldrNode) ++ NLDR_Free((struct NLDR_NODEOBJECT *) pNldrNode); ++ ++ DBC_Ensure((DSP_SUCCEEDED(status) && ++ MEM_IsValidHandle(((struct NLDR_NODEOBJECT *)(*phNldrNode)), ++ NLDR_NODESIGNATURE)) || (DSP_FAILED(status) && ++ *phNldrNode == NULL)); ++ return status; ++} ++ ++/* ++ * ======== NLDR_Create ======== ++ */ ++DSP_STATUS NLDR_Create(OUT struct NLDR_OBJECT **phNldr, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct NLDR_ATTRS *pAttrs) ++{ ++ struct COD_MANAGER *hCodMgr; /* COD manager */ ++ char *pszCoffBuf = NULL; ++ char szZLFile[COD_MAXPATHLENGTH]; ++ struct NLDR_OBJECT *pNldr = NULL; ++ struct DBLL_Attrs saveAttrs; ++ struct DBLL_Attrs newAttrs; ++ DBLL_Flags flags; ++ u32 ulEntry; ++ u16 nSegs = 0; ++ struct MemInfo *pMemInfo; ++ u32 ulLen = 0; ++ u32 ulAddr; ++ struct RMM_Segment *rmmSegs = NULL; ++ u16 i; ++ DSP_STATUS status = DSP_SOK; ++ DBC_Require(cRefs > 0); ++ DBC_Require(phNldr != NULL); ++ DBC_Require(hDevObject != NULL); ++ DBC_Require(pAttrs != NULL); ++ DBC_Require(pAttrs->pfnOvly != NULL); ++ DBC_Require(pAttrs->pfnWrite != NULL); ++ GT_3trace(NLDR_debugMask, GT_ENTER, "NLDR_Create(0x%x, 0x%x, 0x%x)\n", ++ phNldr, hDevObject, pAttrs); ++ /* Allocate dynamic loader object */ ++ MEM_AllocObject(pNldr, struct NLDR_OBJECT, NLDR_SIGNATURE); ++ if (pNldr) { ++ pNldr->hDevObject = hDevObject; ++ /* warning, lazy status checking alert! */ ++ status = DEV_GetCodMgr(hDevObject, &hCodMgr); ++ DBC_Assert(DSP_SUCCEEDED(status)); ++ status = COD_GetLoader(hCodMgr, &pNldr->dbll); ++ DBC_Assert(DSP_SUCCEEDED(status)); ++ status = COD_GetBaseLib(hCodMgr, &pNldr->baseLib); ++ DBC_Assert(DSP_SUCCEEDED(status)); ++ status = COD_GetBaseName(hCodMgr, szZLFile, COD_MAXPATHLENGTH); ++ DBC_Assert(DSP_SUCCEEDED(status)); ++ status = DSP_SOK; ++ /* end lazy status checking */ ++ pNldr->usDSPMauSize = pAttrs->usDSPMauSize; ++ pNldr->usDSPWordSize = pAttrs->usDSPWordSize; ++ pNldr->dbllFxns = dbllFxns; ++ if (!(pNldr->dbllFxns.initFxn())) ++ status = DSP_EMEMORY; ++ ++ } else { ++ GT_0trace(NLDR_debugMask, GT_6CLASS, "NLDR_Create: " ++ "Memory allocation failed\n"); ++ status = DSP_EMEMORY; ++ } ++ /* Create the DCD Manager */ ++ if (DSP_SUCCEEDED(status)) ++ status = DCD_CreateManager(NULL, &pNldr->hDcdMgr); ++ ++ /* Get dynamic loading memory sections from base lib */ ++ if (DSP_SUCCEEDED(status)) { ++ status = pNldr->dbllFxns.getSectFxn(pNldr->baseLib, DYNMEMSECT, ++ &ulAddr, &ulLen); ++ if (DSP_SUCCEEDED(status)) { ++ pszCoffBuf = MEM_Calloc(ulLen * pNldr->usDSPMauSize, ++ MEM_PAGED); ++ if (!pszCoffBuf) { ++ GT_0trace(NLDR_debugMask, GT_6CLASS, ++ "NLDR_Create: Memory " ++ "allocation failed\n"); ++ status = DSP_EMEMORY; ++ } ++ } else { ++ /* Ok to not have dynamic loading memory */ ++ status = DSP_SOK; ++ ulLen = 0; ++ GT_1trace(NLDR_debugMask, GT_6CLASS, ++ "NLDR_Create: DBLL_getSect " ++ "failed (no dynamic loading mem segments): " ++ "0x%lx\n", status); ++ } ++ } ++ if (DSP_SUCCEEDED(status) && ulLen > 0) { ++ /* Read section containing dynamic load mem segments */ ++ status = pNldr->dbllFxns.readSectFxn(pNldr->baseLib, DYNMEMSECT, ++ pszCoffBuf, ulLen); ++ if (DSP_FAILED(status)) { ++ GT_1trace(NLDR_debugMask, GT_6CLASS, ++ "NLDR_Create: DBLL_read Section" ++ "failed: 0x%lx\n", status); ++ } ++ } ++ if (DSP_SUCCEEDED(status) && ulLen > 0) { ++ /* Parse memory segment data */ ++ nSegs = (u16)(*((u32 *)pszCoffBuf)); ++ if (nSegs > MAXMEMSEGS) { ++ GT_1trace(NLDR_debugMask, GT_6CLASS, ++ "NLDR_Create: Invalid number of " ++ "dynamic load mem segments: 0x%lx\n", nSegs); ++ status = DSP_ECORRUPTFILE; ++ } ++ } ++ /* Parse dynamic load memory segments */ ++ if (DSP_SUCCEEDED(status) && nSegs > 0) { ++ rmmSegs = MEM_Calloc(sizeof(struct RMM_Segment) * nSegs, ++ MEM_PAGED); ++ pNldr->segTable = MEM_Calloc(sizeof(u32) * nSegs, MEM_PAGED); ++ if (rmmSegs == NULL || pNldr->segTable == NULL) { ++ status = DSP_EMEMORY; ++ } else { ++ pNldr->nSegs = nSegs; ++ pMemInfo = (struct MemInfo *)(pszCoffBuf + ++ sizeof(u32)); ++ for (i = 0; i < nSegs; i++) { ++ rmmSegs[i].base = (pMemInfo + i)->base; ++ rmmSegs[i].length = (pMemInfo + i)->len; ++ rmmSegs[i].space = 0; ++ pNldr->segTable[i] = (pMemInfo + i)->type; ++#ifdef DEBUG ++ DBG_Trace(DBG_LEVEL7, ++ "** (proc) DLL MEMSEGMENT: %d, Base: 0x%x, " ++ "Length: 0x%x\n", i, rmmSegs[i].base, ++ rmmSegs[i].length); ++#endif ++ } ++ } ++ } ++ /* Create Remote memory manager */ ++ if (DSP_SUCCEEDED(status)) ++ status = RMM_create(&pNldr->rmm, rmmSegs, nSegs); ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* set the alloc, free, write functions for loader */ ++ pNldr->dbllFxns.getAttrsFxn(pNldr->dbll, &saveAttrs); ++ newAttrs = saveAttrs; ++ newAttrs.alloc = (DBLL_AllocFxn) RemoteAlloc; ++ newAttrs.free = (DBLL_FreeFxn) RemoteFree; ++ newAttrs.symLookup = (DBLL_SymLookup) GetSymbolValue; ++ newAttrs.symHandle = pNldr; ++ newAttrs.write = (DBLL_WriteFxn) pAttrs->pfnWrite; ++ pNldr->ovlyFxn = pAttrs->pfnOvly; ++ pNldr->writeFxn = pAttrs->pfnWrite; ++ pNldr->dbllAttrs = newAttrs; ++ } ++ if (rmmSegs) ++ MEM_Free(rmmSegs); ++ ++ if (pszCoffBuf) ++ MEM_Free(pszCoffBuf); ++ ++ /* Get overlay nodes */ ++ if (DSP_SUCCEEDED(status)) { ++ status = COD_GetBaseName(hCodMgr, szZLFile, COD_MAXPATHLENGTH); ++ /* lazy check */ ++ DBC_Assert(DSP_SUCCEEDED(status)); ++ /* First count number of overlay nodes */ ++ status = DCD_GetObjects(pNldr->hDcdMgr, szZLFile, AddOvlyNode, ++ (void *) pNldr); ++ /* Now build table of overlay nodes */ ++ if (DSP_SUCCEEDED(status) && pNldr->nOvlyNodes > 0) { ++ /* Allocate table for overlay nodes */ ++ pNldr->ovlyTable = ++ MEM_Calloc(sizeof(struct OvlyNode) * pNldr->nOvlyNodes, ++ MEM_PAGED); ++ /* Put overlay nodes in the table */ ++ pNldr->nNode = 0; ++ status = DCD_GetObjects(pNldr->hDcdMgr, szZLFile, ++ AddOvlyNode, ++ (void *) pNldr); ++ } ++ } ++ /* Do a fake reload of the base image to get overlay section info */ ++ if (DSP_SUCCEEDED(status) && pNldr->nOvlyNodes > 0) { ++ saveAttrs.write = fakeOvlyWrite; ++ saveAttrs.logWrite = AddOvlyInfo; ++ saveAttrs.logWriteHandle = pNldr; ++ flags = DBLL_CODE | DBLL_DATA | DBLL_SYMB; ++ status = pNldr->dbllFxns.loadFxn(pNldr->baseLib, flags, ++ &saveAttrs, &ulEntry); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ *phNldr = (struct NLDR_OBJECT *) pNldr; ++ } else { ++ if (pNldr) ++ NLDR_Delete((struct NLDR_OBJECT *) pNldr); ++ ++ *phNldr = NULL; ++ } ++ /* FIXME:Temp. Fix. Must be removed */ ++ DBC_Ensure((DSP_SUCCEEDED(status) && ++ MEM_IsValidHandle(((struct NLDR_OBJECT *)*phNldr), ++ NLDR_SIGNATURE)) ++ || (DSP_FAILED(status) && (*phNldr == NULL))); ++ return status; ++} ++ ++/* ++ * ======== NLDR_Delete ======== ++ */ ++void NLDR_Delete(struct NLDR_OBJECT *hNldr) ++{ ++ struct OvlySect *pSect; ++ struct OvlySect *pNext; ++ u16 i; ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(hNldr, NLDR_SIGNATURE)); ++ GT_1trace(NLDR_debugMask, GT_ENTER, "NLDR_Delete(0x%x)\n", hNldr); ++ hNldr->dbllFxns.exitFxn(); ++ if (hNldr->rmm) ++ RMM_delete(hNldr->rmm); ++ ++ if (hNldr->segTable) ++ MEM_Free(hNldr->segTable); ++ ++ if (hNldr->hDcdMgr) ++ DCD_DestroyManager(hNldr->hDcdMgr); ++ ++ /* Free overlay node information */ ++ if (hNldr->ovlyTable) { ++ for (i = 0; i < hNldr->nOvlyNodes; i++) { ++ pSect = hNldr->ovlyTable[i].pCreateSects; ++ while (pSect) { ++ pNext = pSect->pNextSect; ++ MEM_Free(pSect); ++ pSect = pNext; ++ } ++ pSect = hNldr->ovlyTable[i].pDeleteSects; ++ while (pSect) { ++ pNext = pSect->pNextSect; ++ MEM_Free(pSect); ++ pSect = pNext; ++ } ++ pSect = hNldr->ovlyTable[i].pExecuteSects; ++ while (pSect) { ++ pNext = pSect->pNextSect; ++ MEM_Free(pSect); ++ pSect = pNext; ++ } ++ pSect = hNldr->ovlyTable[i].pOtherSects; ++ while (pSect) { ++ pNext = pSect->pNextSect; ++ MEM_Free(pSect); ++ pSect = pNext; ++ } ++ } ++ MEM_Free(hNldr->ovlyTable); ++ } ++ MEM_FreeObject(hNldr); ++ DBC_Ensure(!MEM_IsValidHandle(hNldr, NLDR_SIGNATURE)); ++} ++ ++/* ++ * ======== NLDR_Exit ======== ++ * Discontinue usage of NLDR module. ++ */ ++void NLDR_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ cRefs--; ++ ++ GT_1trace(NLDR_debugMask, GT_5CLASS, ++ "Entered NLDR_Exit, ref count: 0x%x\n", cRefs); ++ ++ if (cRefs == 0) { ++ RMM_exit(); ++ NLDR_debugMask.flags = NULL; ++ } ++ ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== NLDR_Free ======== ++ */ ++void NLDR_Free(struct NLDR_NODEOBJECT *hNldrNode) ++{ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(hNldrNode, NLDR_NODESIGNATURE)); ++ ++ GT_1trace(NLDR_debugMask, GT_ENTER, "NLDR_Free(0x%x)\n", hNldrNode); ++ ++ MEM_FreeObject(hNldrNode); ++} ++ ++/* ++ * ======== NLDR_GetFxnAddr ======== ++ */ ++DSP_STATUS NLDR_GetFxnAddr(struct NLDR_NODEOBJECT *hNldrNode, char *pstrFxn, ++ u32 *pulAddr) ++{ ++ struct DBLL_Symbol *pSym; ++ struct NLDR_OBJECT *hNldr; ++ DSP_STATUS status = DSP_SOK; ++ bool status1 = false; ++ s32 i = 0; ++ struct LibNode root = { NULL, 0, NULL }; ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(hNldrNode, NLDR_NODESIGNATURE)); ++ DBC_Require(pulAddr != NULL); ++ DBC_Require(pstrFxn != NULL); ++ GT_3trace(NLDR_debugMask, GT_ENTER, "NLDR_GetFxnAddr(0x%x, %s, 0x%x)\n", ++ hNldrNode, pstrFxn, pulAddr); ++ ++ hNldr = hNldrNode->pNldr; ++ /* Called from NODE_Create(), NODE_Delete(), or NODE_Run(). */ ++ if (hNldrNode->fDynamic && *hNldrNode->pfPhaseSplit) { ++ switch (hNldrNode->phase) { ++ case NLDR_CREATE: ++ root = hNldrNode->createLib; ++ break; ++ case NLDR_EXECUTE: ++ root = hNldrNode->executeLib; ++ break; ++ case NLDR_DELETE: ++ root = hNldrNode->deleteLib; ++ break; ++ default: ++ DBC_Assert(false); ++ break; ++ } ++ } else { ++ /* for Overlay nodes or non-split Dynamic nodes */ ++ root = hNldrNode->root; ++ } ++ status1 = hNldr->dbllFxns.getCAddrFxn(root.lib, pstrFxn, &pSym); ++ if (!status1) ++ status1 = hNldr->dbllFxns.getAddrFxn(root.lib, pstrFxn, &pSym); ++ ++ /* If symbol not found, check dependent libraries */ ++ if (!status1) { ++ for (i = 0; i < root.nDepLibs; i++) { ++ status1 = hNldr->dbllFxns.getAddrFxn(root.pDepLibs[i]. ++ lib, pstrFxn, &pSym); ++ if (!status1) { ++ status1 = hNldr->dbllFxns.getCAddrFxn(root. ++ pDepLibs[i].lib, pstrFxn, &pSym); ++ } ++ if (status1) { ++ /* Symbol found */ ++ break; ++ } ++ } ++ } ++ /* Check persistent libraries */ ++ if (!status1) { ++ for (i = 0; i < hNldrNode->nPersLib; i++) { ++ status1 = hNldr->dbllFxns.getAddrFxn(hNldrNode-> ++ persLib[i].lib, pstrFxn, &pSym); ++ if (!status1) { ++ status1 = ++ hNldr->dbllFxns.getCAddrFxn(hNldrNode-> ++ persLib[i].lib, pstrFxn, &pSym); ++ } ++ if (status1) { ++ /* Symbol found */ ++ break; ++ } ++ } ++ } ++ ++ if (status1) { ++ *pulAddr = pSym->value; ++ } else { ++ GT_1trace(NLDR_debugMask, GT_6CLASS, ++ "NLDR_GetFxnAddr: Symbol not found: " ++ "%s\n", pstrFxn); ++ status = DSP_ESYMBOL; ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== NLDR_GetRmmManager ======== ++ * Given a NLDR object, retrieve RMM Manager Handle ++ */ ++DSP_STATUS NLDR_GetRmmManager(struct NLDR_OBJECT *hNldrObject, ++ OUT struct RMM_TargetObj **phRmmMgr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct NLDR_OBJECT *pNldrObject = hNldrObject; ++ DBC_Require(phRmmMgr != NULL); ++ GT_2trace(NLDR_debugMask, GT_ENTER, "NLDR_GetRmmManager(0x%x, 0x%x)\n", ++ hNldrObject, phRmmMgr); ++ if (MEM_IsValidHandle(hNldrObject, NLDR_SIGNATURE)) { ++ *phRmmMgr = pNldrObject->rmm; ++ } else { ++ *phRmmMgr = NULL; ++ status = DSP_EHANDLE; ++ GT_0trace(NLDR_debugMask, GT_7CLASS, ++ "NLDR_GetRmmManager:Invalid handle"); ++ } ++ ++ GT_2trace(NLDR_debugMask, GT_ENTER, "Exit NLDR_GetRmmManager: status " ++ "0x%x\n\tphRmmMgr: 0x%x\n", status, *phRmmMgr); ++ ++ DBC_Ensure(DSP_SUCCEEDED(status) || ((phRmmMgr != NULL) && ++ (*phRmmMgr == NULL))); ++ ++ return status; ++} ++ ++/* ++ * ======== NLDR_Init ======== ++ * Initialize the NLDR module. ++ */ ++bool NLDR_Init(void) ++{ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++ DBC_Assert(!NLDR_debugMask.flags); ++ GT_create(&NLDR_debugMask, "DL"); /* "DL" for DLdr */ ++ ++ RMM_init(); ++ } ++ ++ cRefs++; ++ ++ GT_1trace(NLDR_debugMask, GT_5CLASS, "NLDR_Init(), ref count: 0x%x\n", ++ cRefs); ++ ++ DBC_Ensure(cRefs > 0); ++ return true; ++} ++ ++/* ++ * ======== NLDR_Load ======== ++ */ ++DSP_STATUS NLDR_Load(struct NLDR_NODEOBJECT *hNldrNode, enum NLDR_PHASE phase) ++{ ++ struct NLDR_OBJECT *hNldr; ++ struct DSP_UUID libUUID; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(hNldrNode, NLDR_NODESIGNATURE)); ++ ++ hNldr = hNldrNode->pNldr; ++ ++ GT_2trace(NLDR_debugMask, GT_ENTER, "NLDR_Load(0x%x, 0x%x)\n", ++ hNldrNode, phase); ++ ++ if (hNldrNode->fDynamic) { ++ hNldrNode->phase = phase; ++ ++ libUUID = hNldrNode->uuid; ++ ++ /* At this point, we may not know if node is split into ++ * different libraries. So we'll go ahead and load the ++ * library, and then save the pointer to the appropriate ++ * location after we know. */ ++ ++ status = LoadLib(hNldrNode, &hNldrNode->root, libUUID, false, ++ hNldrNode->libPath, phase, 0); ++ ++ if (DSP_SUCCEEDED(status)) { ++ if (*hNldrNode->pfPhaseSplit) { ++ switch (phase) { ++ case NLDR_CREATE: ++ hNldrNode->createLib = hNldrNode->root; ++ break; ++ ++ case NLDR_EXECUTE: ++ hNldrNode->executeLib = hNldrNode->root; ++ break; ++ ++ case NLDR_DELETE: ++ hNldrNode->deleteLib = hNldrNode->root; ++ break; ++ ++ default: ++ DBC_Assert(false); ++ break; ++ } ++ } ++ } ++ } else { ++ if (hNldrNode->fOverlay) ++ status = LoadOvly(hNldrNode, phase); ++ ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== NLDR_Unload ======== ++ */ ++DSP_STATUS NLDR_Unload(struct NLDR_NODEOBJECT *hNldrNode, enum NLDR_PHASE phase) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct LibNode *pRootLib = NULL; ++ s32 i = 0; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(hNldrNode, NLDR_NODESIGNATURE)); ++ GT_2trace(NLDR_debugMask, GT_ENTER, "NLDR_Unload(0x%x, 0x%x)\n", ++ hNldrNode, phase); ++ if (hNldrNode != NULL) { ++ if (hNldrNode->fDynamic) { ++ if (*hNldrNode->pfPhaseSplit) { ++ switch (phase) { ++ case NLDR_CREATE: ++ pRootLib = &hNldrNode->createLib; ++ break; ++ case NLDR_EXECUTE: ++ pRootLib = &hNldrNode->executeLib; ++ break; ++ case NLDR_DELETE: ++ pRootLib = &hNldrNode->deleteLib; ++ /* Unload persistent libraries */ ++ for (i = 0; i < hNldrNode->nPersLib; ++ i++) { ++ UnloadLib(hNldrNode, ++ &hNldrNode->persLib[i]); ++ } ++ hNldrNode->nPersLib = 0; ++ break; ++ default: ++ DBC_Assert(false); ++ break; ++ } ++ } else { ++ /* Unload main library */ ++ pRootLib = &hNldrNode->root; ++ } ++ UnloadLib(hNldrNode, pRootLib); ++ } else { ++ if (hNldrNode->fOverlay) ++ UnloadOvly(hNldrNode, phase); ++ ++ } ++ } ++ return status; ++} ++ ++/* ++ * ======== AddOvlyInfo ======== ++ */ ++static DSP_STATUS AddOvlyInfo(void *handle, struct DBLL_SectInfo *sectInfo, ++ u32 addr, u32 nBytes) ++{ ++ char *pNodeName; ++ char *pSectName = (char *)sectInfo->name; ++ bool fExists = false; ++ char seps = ':'; ++ char *pch; ++ u16 i; ++ struct NLDR_OBJECT *hNldr = (struct NLDR_OBJECT *)handle; ++ DSP_STATUS status = DSP_SOK; ++ ++ /* Is this an overlay section (load address != run address)? */ ++ if (sectInfo->loadAddr == sectInfo->runAddr) ++ goto func_end; ++ ++ /* Find the node it belongs to */ ++ for (i = 0; i < hNldr->nOvlyNodes; i++) { ++ pNodeName = hNldr->ovlyTable[i].pNodeName; ++ DBC_Require(pNodeName); ++ if (strncmp(pNodeName, pSectName + 1, ++ strlen(pNodeName)) == 0) { ++ /* Found the node */ ++ break; ++ } ++ } ++ if (!(i < hNldr->nOvlyNodes)) ++ goto func_end; ++ ++ /* Determine which phase this section belongs to */ ++ for (pch = pSectName + 1; *pch && *pch != seps; pch++) ++ ;; ++ ++ if (*pch) { ++ pch++; /* Skip over the ':' */ ++ if (strncmp(pch, PCREATE, strlen(PCREATE)) == 0) { ++ status = AddOvlySect(hNldr, &hNldr->ovlyTable[i]. ++ pCreateSects, sectInfo, &fExists, addr, nBytes); ++ if (DSP_SUCCEEDED(status) && !fExists) ++ hNldr->ovlyTable[i].nCreateSects++; ++ ++ } else ++ if (strncmp(pch, PDELETE, strlen(PDELETE)) == 0) { ++ status = AddOvlySect(hNldr, &hNldr->ovlyTable[i]. ++ pDeleteSects, sectInfo, &fExists, ++ addr, nBytes); ++ if (DSP_SUCCEEDED(status) && !fExists) ++ hNldr->ovlyTable[i].nDeleteSects++; ++ ++ } else ++ if (strncmp(pch, PEXECUTE, strlen(PEXECUTE)) == 0) { ++ status = AddOvlySect(hNldr, &hNldr->ovlyTable[i]. ++ pExecuteSects, sectInfo, &fExists, ++ addr, nBytes); ++ if (DSP_SUCCEEDED(status) && !fExists) ++ hNldr->ovlyTable[i].nExecuteSects++; ++ ++ } else { ++ /* Put in "other" sectins */ ++ status = AddOvlySect(hNldr, &hNldr->ovlyTable[i]. ++ pOtherSects, sectInfo, &fExists, ++ addr, nBytes); ++ if (DSP_SUCCEEDED(status) && !fExists) ++ hNldr->ovlyTable[i].nOtherSects++; ++ ++ } ++ } ++func_end: ++ return status; ++} ++ ++/* ++ * ======== AddOvlyNode ========= ++ * Callback function passed to DCD_GetObjects. ++ */ ++static DSP_STATUS AddOvlyNode(struct DSP_UUID *pUuid, ++ enum DSP_DCDOBJTYPE objType, ++ IN void *handle) ++{ ++ struct NLDR_OBJECT *hNldr = (struct NLDR_OBJECT *)handle; ++ char *pNodeName = NULL; ++ char *pBuf = NULL; ++ u32 uLen; ++ struct DCD_GENERICOBJ objDef; ++ DSP_STATUS status = DSP_SOK; ++ ++ if (objType != DSP_DCDNODETYPE) ++ goto func_end; ++ ++ status = DCD_GetObjectDef(hNldr->hDcdMgr, pUuid, objType, &objDef); ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ /* If overlay node, add to the list */ ++ if (objDef.objData.nodeObj.usLoadType == NLDR_OVLYLOAD) { ++ if (hNldr->ovlyTable == NULL) { ++ hNldr->nOvlyNodes++; ++ } else { ++ /* Add node to table */ ++ hNldr->ovlyTable[hNldr->nNode].uuid = *pUuid; ++ DBC_Require(objDef.objData.nodeObj.ndbProps.acName); ++ uLen = strlen(objDef.objData.nodeObj.ndbProps.acName); ++ pNodeName = objDef.objData.nodeObj.ndbProps.acName; ++ pBuf = MEM_Calloc(uLen + 1, MEM_PAGED); ++ if (pBuf == NULL) { ++ status = DSP_EMEMORY; ++ } else { ++ strncpy(pBuf, pNodeName, uLen); ++ hNldr->ovlyTable[hNldr->nNode].pNodeName = pBuf; ++ hNldr->nNode++; ++ } ++ } ++ } ++ /* These were allocated in DCD_GetObjectDef */ ++ if (objDef.objData.nodeObj.pstrCreatePhaseFxn) ++ MEM_Free(objDef.objData.nodeObj.pstrCreatePhaseFxn); ++ ++ if (objDef.objData.nodeObj.pstrExecutePhaseFxn) ++ MEM_Free(objDef.objData.nodeObj.pstrExecutePhaseFxn); ++ ++ if (objDef.objData.nodeObj.pstrDeletePhaseFxn) ++ MEM_Free(objDef.objData.nodeObj.pstrDeletePhaseFxn); ++ ++ if (objDef.objData.nodeObj.pstrIAlgName) ++ MEM_Free(objDef.objData.nodeObj.pstrIAlgName); ++ ++func_end: ++ return status; ++} ++ ++/* ++ * ======== AddOvlySect ======== ++ */ ++static DSP_STATUS AddOvlySect(struct NLDR_OBJECT *hNldr, ++ struct OvlySect **pList, ++ struct DBLL_SectInfo *pSectInfo, bool *pExists, ++ u32 addr, u32 nBytes) ++{ ++ struct OvlySect *pNewSect = NULL; ++ struct OvlySect *pLastSect; ++ struct OvlySect *pSect; ++ DSP_STATUS status = DSP_SOK; ++ ++ pSect = pLastSect = *pList; ++ *pExists = false; ++ while (pSect) { ++ /* ++ * Make sure section has not already been added. Multiple ++ * 'write' calls may be made to load the section. ++ */ ++ if (pSect->loadAddr == addr) { ++ /* Already added */ ++ *pExists = true; ++ break; ++ } ++ pLastSect = pSect; ++ pSect = pSect->pNextSect; ++ } ++ ++ if (!pSect) { ++ /* New section */ ++ pNewSect = MEM_Calloc(sizeof(struct OvlySect), MEM_PAGED); ++ if (pNewSect == NULL) { ++ status = DSP_EMEMORY; ++ } else { ++ pNewSect->loadAddr = addr; ++ pNewSect->runAddr = pSectInfo->runAddr + ++ (addr - pSectInfo->loadAddr); ++ pNewSect->size = nBytes; ++ pNewSect->page = pSectInfo->type; ++ } ++ ++ /* Add to the list */ ++ if (DSP_SUCCEEDED(status)) { ++ if (*pList == NULL) { ++ /* First in the list */ ++ *pList = pNewSect; ++ } else { ++ pLastSect->pNextSect = pNewSect; ++ } ++ } ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== fakeOvlyWrite ======== ++ */ ++static s32 fakeOvlyWrite(void *handle, u32 dspAddr, void *buf, u32 nBytes, ++ s32 mtype) ++{ ++ return (s32)nBytes; ++} ++ ++/* ++ * ======== FreeSects ======== ++ */ ++static void FreeSects(struct NLDR_OBJECT *hNldr, struct OvlySect *pPhaseSects, ++ u16 nAlloc) ++{ ++ struct OvlySect *pSect = pPhaseSects; ++ u16 i = 0; ++ bool fRet; ++ ++ while (pSect && i < nAlloc) { ++ /* 'Deallocate' */ ++ /* segid - page not supported yet */ ++ /* Reserved memory */ ++ fRet = RMM_free(hNldr->rmm, 0, pSect->runAddr, pSect->size, ++ true); ++ DBC_Assert(fRet); ++ pSect = pSect->pNextSect; ++ i++; ++ } ++} ++ ++/* ++ * ======== GetSymbolValue ======== ++ * Find symbol in library's base image. If not there, check dependent ++ * libraries. ++ */ ++static bool GetSymbolValue(void *handle, void *pArg, void *rmmHandle, ++ char *name, struct DBLL_Symbol **sym) ++{ ++ struct NLDR_OBJECT *hNldr = (struct NLDR_OBJECT *)handle; ++ struct NLDR_NODEOBJECT *hNldrNode = (struct NLDR_NODEOBJECT *)rmmHandle; ++ struct LibNode *root = (struct LibNode *)pArg; ++ u16 i; ++ bool status = false; ++ ++ /* check the base image */ ++ status = hNldr->dbllFxns.getAddrFxn(hNldr->baseLib, name, sym); ++ if (!status) ++ status = hNldr->dbllFxns.getCAddrFxn(hNldr->baseLib, name, sym); ++ ++ /* ++ * Check in root lib itself. If the library consists of ++ * multiple object files linked together, some symbols in the ++ * library may need to be resolved. ++ */ ++ if (!status) { ++ status = hNldr->dbllFxns.getAddrFxn(root->lib, name, sym); ++ if (!status) { ++ status = ++ hNldr->dbllFxns.getCAddrFxn(root->lib, name, sym); ++ } ++ } ++ ++ /* ++ * Check in root lib's dependent libraries, but not dependent ++ * libraries' dependents. ++ */ ++ if (!status) { ++ for (i = 0; i < root->nDepLibs; i++) { ++ status = hNldr->dbllFxns.getAddrFxn(root->pDepLibs[i]. ++ lib, name, sym); ++ if (!status) { ++ status = hNldr->dbllFxns.getCAddrFxn(root-> ++ pDepLibs[i].lib, name, sym); ++ } ++ if (status) { ++ /* Symbol found */ ++ break; ++ } ++ } ++ } ++ /* ++ * Check in persistent libraries ++ */ ++ if (!status) { ++ for (i = 0; i < hNldrNode->nPersLib; i++) { ++ status = hNldr->dbllFxns.getAddrFxn(hNldrNode-> ++ persLib[i].lib, name, sym); ++ if (!status) { ++ status = hNldr->dbllFxns.getCAddrFxn ++ (hNldrNode->persLib[i].lib, name, sym); ++ } ++ if (status) { ++ /* Symbol found */ ++ break; ++ } ++ } ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== LoadLib ======== ++ * Recursively load library and all its dependent libraries. The library ++ * we're loading is specified by a uuid. ++ */ ++static DSP_STATUS LoadLib(struct NLDR_NODEOBJECT *hNldrNode, ++ struct LibNode *root, struct DSP_UUID uuid, ++ bool rootPersistent, struct DBLL_LibraryObj **libPath, ++ enum NLDR_PHASE phase, u16 depth) ++{ ++ struct NLDR_OBJECT *hNldr = hNldrNode->pNldr; ++ u16 nLibs = 0; /* Number of dependent libraries */ ++ u16 nPLibs = 0; /* Number of persistent libraries */ ++ u16 nLoaded = 0; /* Number of dep. libraries loaded */ ++ u16 i; ++ u32 entry; ++ u32 dwBufSize = NLDR_MAXPATHLENGTH; ++ DBLL_Flags flags = DBLL_SYMB | DBLL_CODE | DBLL_DATA | DBLL_DYNAMIC; ++ struct DBLL_Attrs newAttrs; ++ char *pszFileName = NULL; ++ struct DSP_UUID *depLibUUIDs = NULL; ++ bool *persistentDepLibs = NULL; ++ DSP_STATUS status = DSP_SOK; ++ bool fStatus = false; ++ struct LibNode *pDepLib; ++ ++ if (depth > MAXDEPTH) { ++ /* Error */ ++ DBC_Assert(false); ++ } ++ root->lib = NULL; ++ /* Allocate a buffer for library file name of size DBL_MAXPATHLENGTH */ ++ pszFileName = MEM_Calloc(DBLL_MAXPATHLENGTH, MEM_PAGED); ++ if (pszFileName == NULL) ++ status = DSP_EMEMORY; ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Get the name of the library */ ++ if (depth == 0) { ++ status = DCD_GetLibraryName(hNldrNode->pNldr->hDcdMgr, ++ &uuid, pszFileName, &dwBufSize, phase, ++ hNldrNode->pfPhaseSplit); ++ } else { ++ /* Dependent libraries are registered with a phase */ ++ status = DCD_GetLibraryName(hNldrNode->pNldr->hDcdMgr, ++ &uuid, pszFileName, &dwBufSize, NLDR_NOPHASE, ++ NULL); ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Open the library, don't load symbols */ ++ status = hNldr->dbllFxns.openFxn(hNldr->dbll, pszFileName, ++ DBLL_NOLOAD, &root->lib); ++ } ++ /* Done with file name */ ++ if (pszFileName) ++ MEM_Free(pszFileName); ++ ++ /* Check to see if library not already loaded */ ++ if (DSP_SUCCEEDED(status) && rootPersistent) { ++ fStatus = findInPersistentLibArray(hNldrNode, root->lib); ++ /* Close library */ ++ if (fStatus) { ++ hNldr->dbllFxns.closeFxn(root->lib); ++ return DSP_SALREADYLOADED; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Check for circular dependencies. */ ++ for (i = 0; i < depth; i++) { ++ if (root->lib == libPath[i]) { ++ /* This condition could be checked by a ++ * tool at build time. */ ++ status = DSP_EDYNLOAD; ++ } ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Add library to current path in dependency tree */ ++ libPath[depth] = root->lib; ++ depth++; ++ /* Get number of dependent libraries */ ++ status = DCD_GetNumDepLibs(hNldrNode->pNldr->hDcdMgr, &uuid, ++ &nLibs, &nPLibs, phase); ++ } ++ DBC_Assert(nLibs >= nPLibs); ++ if (DSP_SUCCEEDED(status)) { ++ if (!(*hNldrNode->pfPhaseSplit)) ++ nPLibs = 0; ++ ++ /* nLibs = #of dependent libraries */ ++ root->nDepLibs = nLibs - nPLibs; ++ if (nLibs > 0) { ++ depLibUUIDs = MEM_Calloc(sizeof(struct DSP_UUID) * ++ nLibs, MEM_PAGED); ++ persistentDepLibs = ++ MEM_Calloc(sizeof(bool) * nLibs, MEM_PAGED); ++ if (!depLibUUIDs || !persistentDepLibs) ++ status = DSP_EMEMORY; ++ ++ if (root->nDepLibs > 0) { ++ /* Allocate arrays for dependent lib UUIDs, ++ * lib nodes */ ++ root->pDepLibs = MEM_Calloc ++ (sizeof(struct LibNode) * ++ (root->nDepLibs), MEM_PAGED); ++ if (!(root->pDepLibs)) ++ status = DSP_EMEMORY; ++ ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Get the dependent library UUIDs */ ++ status = DCD_GetDepLibs(hNldrNode->pNldr-> ++ hDcdMgr, &uuid, nLibs, depLibUUIDs, ++ persistentDepLibs, phase); ++ } ++ } ++ } ++ ++ /* ++ * Recursively load dependent libraries. ++ */ ++ if (DSP_SUCCEEDED(status) && persistentDepLibs) { ++ for (i = 0; i < nLibs; i++) { ++ /* If root library is NOT persistent, and dep library ++ * is, then record it. If root library IS persistent, ++ * the deplib is already included */ ++ if (!rootPersistent && persistentDepLibs[i] && ++ *hNldrNode->pfPhaseSplit) { ++ if ((hNldrNode->nPersLib) > MAXLIBS) { ++ status = DSP_EDYNLOAD; ++ break; ++ } ++ ++ /* Allocate library outside of phase */ ++ pDepLib = &hNldrNode->persLib[hNldrNode-> ++ nPersLib]; ++ } else { ++ if (rootPersistent) ++ persistentDepLibs[i] = true; ++ ++ ++ /* Allocate library within phase */ ++ pDepLib = &root->pDepLibs[nLoaded]; ++ } ++ ++ if (depLibUUIDs) { ++ status = LoadLib(hNldrNode, pDepLib, ++ depLibUUIDs[i], ++ persistentDepLibs[i], libPath, ++ phase, ++ depth); ++ } else { ++ status = DSP_EMEMORY; ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ if ((status != DSP_SALREADYLOADED) && ++ !rootPersistent && persistentDepLibs[i] && ++ *hNldrNode->pfPhaseSplit) { ++ (hNldrNode->nPersLib)++; ++ } else { ++ if (!persistentDepLibs[i] || ++ !(*hNldrNode->pfPhaseSplit)) { ++ nLoaded++; ++ } ++ } ++ } else { ++ break; ++ } ++ } ++ } ++ ++ /* Now we can load the root library */ ++ if (DSP_SUCCEEDED(status)) { ++ newAttrs = hNldr->dbllAttrs; ++ newAttrs.symArg = root; ++ newAttrs.rmmHandle = hNldrNode; ++ newAttrs.wHandle = hNldrNode->pPrivRef; ++ newAttrs.baseImage = false; ++ ++ status = hNldr->dbllFxns.loadFxn(root->lib, flags, &newAttrs, ++ &entry); ++ } ++ ++ /* ++ * In case of failure, unload any dependent libraries that ++ * were loaded, and close the root library. ++ * (Persistent libraries are unloaded from the very top) ++ */ ++ if (DSP_FAILED(status)) { ++ if (phase != NLDR_EXECUTE) { ++ for (i = 0; i < hNldrNode->nPersLib; i++) ++ UnloadLib(hNldrNode, &hNldrNode->persLib[i]); ++ ++ hNldrNode->nPersLib = 0; ++ } ++ for (i = 0; i < nLoaded; i++) ++ UnloadLib(hNldrNode, &root->pDepLibs[i]); ++ ++ if (root->lib) ++ hNldr->dbllFxns.closeFxn(root->lib); ++ ++ } ++ ++ /* Going up one node in the dependency tree */ ++ depth--; ++ ++ if (depLibUUIDs) { ++ MEM_Free(depLibUUIDs); ++ depLibUUIDs = NULL; ++ } ++ ++ if (persistentDepLibs) { ++ MEM_Free(persistentDepLibs); ++ persistentDepLibs = NULL; ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== LoadOvly ======== ++ */ ++static DSP_STATUS LoadOvly(struct NLDR_NODEOBJECT *hNldrNode, ++ enum NLDR_PHASE phase) ++{ ++ struct NLDR_OBJECT *hNldr = hNldrNode->pNldr; ++ struct OvlyNode *pONode = NULL; ++ struct OvlySect *pPhaseSects = NULL; ++ struct OvlySect *pOtherSects = NULL; ++ u16 i; ++ u16 nAlloc = 0; ++ u16 nOtherAlloc = 0; ++ u16 *pRefCount = NULL; ++ u16 *pOtherRef = NULL; ++ u32 nBytes; ++ struct OvlySect *pSect; ++ DSP_STATUS status = DSP_SOK; ++ ++ /* Find the node in the table */ ++ for (i = 0; i < hNldr->nOvlyNodes; i++) { ++ if (IsEqualUUID(hNldrNode->uuid, hNldr->ovlyTable[i].uuid)) { ++ /* Found it */ ++ pONode = &(hNldr->ovlyTable[i]); ++ break; ++ } ++ } ++ ++ DBC_Assert(i < hNldr->nOvlyNodes); ++ switch (phase) { ++ case NLDR_CREATE: ++ pRefCount = &(pONode->createRef); ++ pOtherRef = &(pONode->otherRef); ++ pPhaseSects = pONode->pCreateSects; ++ pOtherSects = pONode->pOtherSects; ++ break; ++ ++ case NLDR_EXECUTE: ++ pRefCount = &(pONode->executeRef); ++ pPhaseSects = pONode->pExecuteSects; ++ break; ++ ++ case NLDR_DELETE: ++ pRefCount = &(pONode->deleteRef); ++ pPhaseSects = pONode->pDeleteSects; ++ break; ++ ++ default: ++ DBC_Assert(false); ++ break; ++ } ++ ++ DBC_Assert(pRefCount != NULL); ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ if (pRefCount == NULL) ++ goto func_end; ++ ++ if (*pRefCount != 0) ++ goto func_end; ++ ++ /* 'Allocate' memory for overlay sections of this phase */ ++ pSect = pPhaseSects; ++ while (pSect) { ++ /* allocate */ /* page not supported yet */ ++ /* reserve */ /* align */ ++ status = RMM_alloc(hNldr->rmm, 0, pSect->size, 0, ++ &(pSect->runAddr), true); ++ if (DSP_SUCCEEDED(status)) { ++ pSect = pSect->pNextSect; ++ nAlloc++; ++ } else { ++ break; ++ } ++ } ++ if (pOtherRef && *pOtherRef == 0) { ++ /* 'Allocate' memory for other overlay sections ++ * (create phase) */ ++ if (DSP_SUCCEEDED(status)) { ++ pSect = pOtherSects; ++ while (pSect) { ++ /* page not supported */ /* align */ ++ /* reserve */ ++ status = RMM_alloc(hNldr->rmm, 0, pSect->size, ++ 0, &(pSect->runAddr), true); ++ if (DSP_SUCCEEDED(status)) { ++ pSect = pSect->pNextSect; ++ nOtherAlloc++; ++ } else { ++ break; ++ } ++ } ++ } ++ } ++ if (*pRefCount == 0) { ++ if (DSP_SUCCEEDED(status)) { ++ /* Load sections for this phase */ ++ pSect = pPhaseSects; ++ while (pSect && DSP_SUCCEEDED(status)) { ++ nBytes = (*hNldr->ovlyFxn)(hNldrNode->pPrivRef, ++ pSect->runAddr, pSect->loadAddr, ++ pSect->size, pSect->page); ++ if (nBytes != pSect->size) ++ status = DSP_EFAIL; ++ ++ pSect = pSect->pNextSect; ++ } ++ } ++ } ++ if (pOtherRef && *pOtherRef == 0) { ++ if (DSP_SUCCEEDED(status)) { ++ /* Load other sections (create phase) */ ++ pSect = pOtherSects; ++ while (pSect && DSP_SUCCEEDED(status)) { ++ nBytes = (*hNldr->ovlyFxn)(hNldrNode->pPrivRef, ++ pSect->runAddr, pSect->loadAddr, ++ pSect->size, pSect->page); ++ if (nBytes != pSect->size) ++ status = DSP_EFAIL; ++ ++ pSect = pSect->pNextSect; ++ } ++ } ++ } ++ if (DSP_FAILED(status)) { ++ /* 'Deallocate' memory */ ++ FreeSects(hNldr, pPhaseSects, nAlloc); ++ FreeSects(hNldr, pOtherSects, nOtherAlloc); ++ } ++func_end: ++ if (DSP_SUCCEEDED(status) && (pRefCount != NULL)) { ++ *pRefCount += 1; ++ if (pOtherRef) ++ *pOtherRef += 1; ++ ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== RemoteAlloc ======== ++ */ ++static DSP_STATUS RemoteAlloc(void **pRef, u16 space, u32 size, ++ u32 align, u32 *dspAddr, ++ OPTIONAL s32 segmentId, OPTIONAL s32 req, ++ bool reserve) ++{ ++ struct NLDR_NODEOBJECT *hNode = (struct NLDR_NODEOBJECT *)pRef; ++ struct NLDR_OBJECT *hNldr; ++ struct RMM_TargetObj *rmm; ++ u16 memPhaseBit = MAXFLAGS; ++ u16 segid = 0; ++ u16 i; ++ u16 memType; ++ u32 nWords; ++ struct RMM_Addr *pRmmAddr = (struct RMM_Addr *)dspAddr; ++ bool fReq = false; ++ DSP_STATUS status = DSP_EMEMORY; /* Set to fail */ ++ DBC_Require(MEM_IsValidHandle(hNode, NLDR_NODESIGNATURE)); ++ DBC_Require(space == DBLL_CODE || space == DBLL_DATA || ++ space == DBLL_BSS); ++ hNldr = hNode->pNldr; ++ rmm = hNldr->rmm; ++ /* Convert size to DSP words */ ++ nWords = (size + hNldr->usDSPWordSize - 1) / hNldr->usDSPWordSize; ++ /* Modify memory 'align' to account for DSP cache line size */ ++ align = findLcm(GEM_CACHE_LINE_SIZE, align); ++ GT_1trace(NLDR_debugMask, GT_7CLASS, ++ "RemoteAlloc: memory align to 0x%x \n", align); ++ if (segmentId != -1) { ++ pRmmAddr->segid = segmentId; ++ segid = segmentId; ++ fReq = req; ++ } else { ++ switch (hNode->phase) { ++ case NLDR_CREATE: ++ memPhaseBit = CREATEDATAFLAGBIT; ++ break; ++ case NLDR_DELETE: ++ memPhaseBit = DELETEDATAFLAGBIT; ++ break; ++ case NLDR_EXECUTE: ++ memPhaseBit = EXECUTEDATAFLAGBIT; ++ break; ++ default: ++ DBC_Assert(false); ++ break; ++ } ++ if (space == DBLL_CODE) ++ memPhaseBit++; ++ ++ if (memPhaseBit < MAXFLAGS) ++ segid = hNode->segId[memPhaseBit]; ++ ++ /* Determine if there is a memory loading requirement */ ++ if ((hNode->codeDataFlagMask >> memPhaseBit) & 0x1) ++ fReq = true; ++ ++ } ++ memType = (space == DBLL_CODE) ? DYNM_CODE : DYNM_DATA; ++ ++ /* Find an appropriate segment based on space */ ++ if (segid == NULLID) { ++ /* No memory requirements of preferences */ ++ DBC_Assert(!fReq); ++ goto func_cont; ++ } ++ if (segid <= MAXSEGID) { ++ DBC_Assert(segid < hNldr->nSegs); ++ /* Attempt to allocate from segid first. */ ++ pRmmAddr->segid = segid; ++ status = RMM_alloc(rmm, segid, nWords, align, dspAddr, false); ++ if (DSP_FAILED(status)) { ++ GT_1trace(NLDR_debugMask, GT_6CLASS, ++ "RemoteAlloc:Unable allocate " ++ "from segment %d.\n", segid); ++ } ++ } else { ++ /* segid > MAXSEGID ==> Internal or external memory */ ++ DBC_Assert(segid == MEMINTERNALID || segid == MEMEXTERNALID); ++ /* Check for any internal or external memory segment, ++ * depending on segid.*/ ++ memType |= segid == MEMINTERNALID ? ++ DYNM_INTERNAL : DYNM_EXTERNAL; ++ for (i = 0; i < hNldr->nSegs; i++) { ++ if ((hNldr->segTable[i] & memType) != memType) ++ continue; ++ ++ status = RMM_alloc(rmm, i, nWords, align, dspAddr, ++ false); ++ if (DSP_SUCCEEDED(status)) { ++ /* Save segid for freeing later */ ++ pRmmAddr->segid = i; ++ break; ++ } ++ } ++ } ++func_cont: ++ /* Haven't found memory yet, attempt to find any segment that works */ ++ if (status == DSP_EMEMORY && !fReq) { ++ GT_0trace(NLDR_debugMask, GT_6CLASS, ++ "RemoteAlloc: Preferred segment " ++ "unavailable, trying another segment.\n"); ++ for (i = 0; i < hNldr->nSegs; i++) { ++ /* All bits of memType must be set */ ++ if ((hNldr->segTable[i] & memType) != memType) ++ continue; ++ ++ status = RMM_alloc(rmm, i, nWords, align, dspAddr, ++ false); ++ if (DSP_SUCCEEDED(status)) { ++ /* Save segid */ ++ pRmmAddr->segid = i; ++ break; ++ } ++ } ++ } ++ ++ return status; ++} ++ ++static DSP_STATUS RemoteFree(void **pRef, u16 space, u32 dspAddr, ++ u32 size, bool reserve) ++{ ++ struct NLDR_OBJECT *hNldr = (struct NLDR_OBJECT *)pRef; ++ struct RMM_TargetObj *rmm; ++ u32 nWords; ++ DSP_STATUS status = DSP_EMEMORY; /* Set to fail */ ++ ++ DBC_Require(MEM_IsValidHandle(hNldr, NLDR_SIGNATURE)); ++ ++ rmm = hNldr->rmm; ++ ++ /* Convert size to DSP words */ ++ nWords = (size + hNldr->usDSPWordSize - 1) / hNldr->usDSPWordSize; ++ ++ if (RMM_free(rmm, space, dspAddr, nWords, reserve)) ++ status = DSP_SOK; ++ ++ return status; ++} ++ ++/* ++ * ======== UnloadLib ======== ++ */ ++static void UnloadLib(struct NLDR_NODEOBJECT *hNldrNode, struct LibNode *root) ++{ ++ struct DBLL_Attrs newAttrs; ++ struct NLDR_OBJECT *hNldr = hNldrNode->pNldr; ++ u16 i; ++ ++ DBC_Assert(root != NULL); ++ ++ /* Unload dependent libraries */ ++ for (i = 0; i < root->nDepLibs; i++) ++ UnloadLib(hNldrNode, &root->pDepLibs[i]); ++ ++ root->nDepLibs = 0; ++ ++ newAttrs = hNldr->dbllAttrs; ++ newAttrs.rmmHandle = hNldr->rmm; ++ newAttrs.wHandle = hNldrNode->pPrivRef; ++ newAttrs.baseImage = false; ++ newAttrs.symArg = root; ++ ++ if (root->lib) { ++ /* Unload the root library */ ++ hNldr->dbllFxns.unloadFxn(root->lib, &newAttrs); ++ hNldr->dbllFxns.closeFxn(root->lib); ++ } ++ ++ /* Free dependent library list */ ++ if (root->pDepLibs) { ++ MEM_Free(root->pDepLibs); ++ root->pDepLibs = NULL; ++ } ++} ++ ++/* ++ * ======== UnloadOvly ======== ++ */ ++static void UnloadOvly(struct NLDR_NODEOBJECT *hNldrNode, enum NLDR_PHASE phase) ++{ ++ struct NLDR_OBJECT *hNldr = hNldrNode->pNldr; ++ struct OvlyNode *pONode = NULL; ++ struct OvlySect *pPhaseSects = NULL; ++ struct OvlySect *pOtherSects = NULL; ++ u16 i; ++ u16 nAlloc = 0; ++ u16 nOtherAlloc = 0; ++ u16 *pRefCount = NULL; ++ u16 *pOtherRef = NULL; ++ DSP_STATUS status = DSP_SOK; ++ ++ /* Find the node in the table */ ++ for (i = 0; i < hNldr->nOvlyNodes; i++) { ++ if (IsEqualUUID(hNldrNode->uuid, hNldr->ovlyTable[i].uuid)) { ++ /* Found it */ ++ pONode = &(hNldr->ovlyTable[i]); ++ break; ++ } ++ } ++ ++ DBC_Assert(i < hNldr->nOvlyNodes); ++ switch (phase) { ++ case NLDR_CREATE: ++ pRefCount = &(pONode->createRef); ++ pPhaseSects = pONode->pCreateSects; ++ nAlloc = pONode->nCreateSects; ++ break; ++ case NLDR_EXECUTE: ++ pRefCount = &(pONode->executeRef); ++ pPhaseSects = pONode->pExecuteSects; ++ nAlloc = pONode->nExecuteSects; ++ break; ++ case NLDR_DELETE: ++ pRefCount = &(pONode->deleteRef); ++ pOtherRef = &(pONode->otherRef); ++ pPhaseSects = pONode->pDeleteSects; ++ /* 'Other' overlay sections are unloaded in the delete phase */ ++ pOtherSects = pONode->pOtherSects; ++ nAlloc = pONode->nDeleteSects; ++ nOtherAlloc = pONode->nOtherSects; ++ break; ++ default: ++ DBC_Assert(false); ++ break; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ DBC_Assert(pRefCount && (*pRefCount > 0)); ++ if (pRefCount && (*pRefCount > 0)) { ++ *pRefCount -= 1; ++ if (pOtherRef) { ++ DBC_Assert(*pOtherRef > 0); ++ *pOtherRef -= 1; ++ } ++ } ++ } ++ if (pRefCount && (*pRefCount == 0)) { ++ /* 'Deallocate' memory */ ++ FreeSects(hNldr, pPhaseSects, nAlloc); ++ } ++ if (pOtherRef && *pOtherRef == 0) ++ FreeSects(hNldr, pOtherSects, nOtherAlloc); ++ ++} ++ ++/* ++ * ======== findInPersistentLibArray ======== ++ */ ++static bool findInPersistentLibArray(struct NLDR_NODEOBJECT *hNldrNode, ++ struct DBLL_LibraryObj *lib) ++{ ++ s32 i = 0; ++ ++ for (i = 0; i < hNldrNode->nPersLib; i++) { ++ if (lib == hNldrNode->persLib[i].lib) ++ return true; ++ ++ } ++ ++ return false; ++} ++ ++/* ++ * ================ Find LCM (Least Common Multiplier === ++ */ ++static u32 findLcm(u32 a, u32 b) ++{ ++ u32 retVal; ++ ++ retVal = a * b / findGcf(a, b); ++ ++ return retVal; ++} ++ ++/* ++ * ================ Find GCF (Greatest Common Factor ) === ++ */ ++static u32 findGcf(u32 a, u32 b) ++{ ++ u32 c; ++ ++ /* Get the GCF (Greatest common factor between the numbers, ++ * using Euclidian Algo */ ++ while ((c = (a % b))) { ++ a = b; ++ b = c; ++ } ++ return b; ++} ++ +diff --git a/drivers/dsp/bridge/rmgr/node.c b/drivers/dsp/bridge/rmgr/node.c +new file mode 100644 +index 0000000..178b802 +--- /dev/null ++++ b/drivers/dsp/bridge/rmgr/node.c +@@ -0,0 +1,3544 @@ ++/* ++ * node.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== node.c ======== ++ * ++ * Description: ++ * DSP/BIOS Bridge Node Manager. ++ * ++ * Public Functions: ++ * NODE_Allocate ++ * NODE_AllocMsgBuf ++ * NODE_ChangePriority ++ * NODE_Connect ++ * NODE_Create ++ * NODE_CreateMgr ++ * NODE_Delete ++ * NODE_DeleteMgr ++ * NODE_EnumNodes ++ * NODE_Exit ++ * NODE_FreeMsgBuf ++ * NODE_GetAttr ++ * NODE_GetChannelId ++ * NODE_GetMessage ++ * NODE_GetStrmMgr ++ * NODE_Init ++ * NODE_OnExit ++ * NODE_Pause ++ * NODE_PutMessage ++ * NODE_RegisterNotify ++ * NODE_Run ++ * NODE_Terminate ++ * ++ *! Revision History: ++ *! ================= ++ *! 12-Apr-2004 hp Compile IVA only for 24xx ++ *! 09-Feb-2004 vp Updated to support IVA. ++ *! 07-Apr-2003 map Eliminated references to old DLDR ++ *! 26-Mar-2003 vp Commented the call to DSP deep sleep in Node_Delete ++ *! function. ++ *! 18-Feb-2003 vp Code review updates. ++ *! 06-Feb-2003 kc Fixed FreeStream to release streams correctly. ++ *! 23-Jan-2003 map Removed call to DISP_DoCinit within Write() ++ *! 03-Jan-2003 map Only unload code after phase has executed if ++ *! overlay or split dynload phases ++ *! 18-Oct-2002 vp Ported to Linux platform. ++ *! 06-Nov-2002 map Fixed NODE_Run on NODE_PAUSED bug ++ *! 12-Oct-2002 map Fixed DeleteNode bug in NODE_Create ++ *! 11-Sep-2002 rr DeleteNode frees the memory for strmConnect and dcd obj ++ *! 29-Aug-2002 map Modified Ovly and Write to use ARM-side copy ++ *! 22-May-2002 sg Changed use of cbData for PWR calls. ++ *! 17-May-2002 jeh Removed LoadLoaderFxns(). Get address of RMS_cinit() ++ *! function. Call DISP_DoCinit() from Write(), if .cinit. ++ *! 13-May-2002 sg Added timeout to wake/sleep calls. ++ *! 02-May-2002 sg Added wake/sleep of DSP to support "nap" mode. ++ *! 18-Apr-2002 jeh Use dynamic loader if compile flag is set. ++ *! 13-Feb-2002 jeh Get uSysStackSize from DSP_NDBPROPS. ++ *! 07-Jan-2002 ag STRMMODE_ZEROCOPY(shared memory buffer swap) enabled. ++ *! 17-Dec-2001 ag STRMMODE_RDMA(DDMA) enabled. ++ *! 12-Dec-2001 ag Check for valid stream mode in NODE_Connect(). ++ *! 04-Dec-2001 jeh Check for node sufficiently connected in NODE_Create(). ++ *! 15-Nov-2001 jeh Removed DBC_Require(pNode->hXlator != NULL) from ++ *! NODE_AllocMsgBuf(), and check node type != NODE_DEVICE. ++ *! 11-Sep-2001 ag Zero-copy messaging support. ++ *! 28-Aug-2001 jeh Overlay/dynamic loader infrastructure added. Removed ++ *! NODE_GetDispatcher, excess node states. ++ *! 07-Aug-2001 jeh Removed critical section for dispatcher. ++ *! 26-Jul-2001 jeh Get ZL dll name through CFG. ++ *! 05-Jun-2001 jeh Assume DSP_STRMATTRS.uBufsize in GPP bytes. ++ *! 11-May-2001 jeh Some code review cleanup. ++ *! 13-Feb-2001 kc: DSP/BIOS Bridge name updates. ++ *! 15-Dec-2000 sg Convert IALG_Fxn address from byte addr to word addr. ++ *! 04-Dec-2000 jeh Call MSG Get and Put functions. ++ *! 04-Dec-2000 ag Added SM support for node messaging. ++ *! 10-Nov-2000 rr: NODE_MIN/MAX Priority is defined in dspdefs.h. ++ *! 27-Oct-2000 jeh Added NODE_AllocMsgBuf(), NODE_FreeMsgBuf(). ++ *! 11-Oct-2000 jeh Changed NODE_EnumNodeInfo to NODE_EnumNodes. Added ++ *! NODE_CloseOrphans(). Remove NODE_RegisterNotifyAllNodes ++ *! 19-Jun-2000 jeh Created. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Resource Manager */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Link Driver */ ++#include ++#include ++ ++/* ----------------------------------- Others */ ++#include ++#ifdef DEBUG ++#include ++#include ++#endif ++ ++/* ----------------------------------- This */ ++#include ++#include ++ ++/* Static/Dynamic Loader includes */ ++#include ++#include ++ ++#ifndef RES_CLEANUP_DISABLE ++#include ++#include ++#include ++#include ++#endif ++ ++ ++#define NODE_SIGNATURE 0x45444f4e /* "EDON" */ ++#define NODEMGR_SIGNATURE 0x52474d4e /* "RGMN" */ ++ ++#define HOSTPREFIX "/host" ++#define PIPEPREFIX "/dbpipe" ++ ++#define MaxInputs(h) ((h)->dcdProps.objData.nodeObj.ndbProps.uNumInputStreams) ++#define MaxOutputs(h) ((h)->dcdProps.objData.nodeObj.ndbProps.uNumOutputStreams) ++ ++#define NODE_GetPriority(h) ((h)->nPriority) ++#define NODE_SetPriority(hNode, nPriority) ((hNode)->nPriority = nPriority) ++#define NODE_SetState(hNode, state) ((hNode)->nState = state) ++ ++#define MAXPIPES 100 /* Max # of /pipe connections (CSL limit) */ ++#define MAXDEVSUFFIXLEN 2 /* Max(Log base 10 of MAXPIPES, MAXSTREAMS) */ ++ ++#define PIPENAMELEN (sizeof(PIPEPREFIX) + MAXDEVSUFFIXLEN) ++#define HOSTNAMELEN (sizeof(HOSTPREFIX) + MAXDEVSUFFIXLEN) ++ ++#define MAXDEVNAMELEN 32 /* DSP_NDBPROPS.acName size */ ++#define CREATEPHASE 1 ++#define EXECUTEPHASE 2 ++#define DELETEPHASE 3 ++ ++/* Define default STRM parameters */ ++/* ++ * TBD: Put in header file, make global DSP_STRMATTRS with defaults, ++ * or make defaults configurable. ++ */ ++#define DEFAULTBUFSIZE 32 ++#define DEFAULTNBUFS 2 ++#define DEFAULTSEGID 0 ++#define DEFAULTALIGNMENT 0 ++#define DEFAULTTIMEOUT 10000 ++ ++#define RMSQUERYSERVER 0 ++#define RMSCONFIGURESERVER 1 ++#define RMSCREATENODE 2 ++#define RMSEXECUTENODE 3 ++#define RMSDELETENODE 4 ++#define RMSCHANGENODEPRIORITY 5 ++#define RMSREADMEMORY 6 ++#define RMSWRITEMEMORY 7 ++#define RMSCOPY 8 ++#define MAXTIMEOUT 2000 ++ ++#define NUMRMSFXNS 9 ++ ++#define PWR_TIMEOUT 500 /* default PWR timeout in msec */ ++ ++#define STACKSEGLABEL "L1DSRAM_HEAP" /* Label for DSP Stack Segment Address */ ++ ++/* ++ * ======== NODE_MGR ======== ++ */ ++struct NODE_MGR { ++ u32 dwSignature; /* For object validation */ ++ struct DEV_OBJECT *hDevObject; /* Device object */ ++ struct WMD_DRV_INTERFACE *pIntfFxns; /* Function interface to WMD */ ++ struct DCD_MANAGER *hDcdMgr; /* Proc/Node data manager */ ++ struct DISP_OBJECT *hDisp; /* Node dispatcher */ ++ struct LST_LIST *nodeList; /* List of all allocated nodes */ ++ u32 uNumNodes; /* Number of nodes in nodeList */ ++ u32 uNumCreated; /* Number of nodes *created* on DSP */ ++ struct GB_TMap *pipeMap; /* Pipe connection bit map */ ++ struct GB_TMap *pipeDoneMap; /* Pipes that are half free */ ++ struct GB_TMap *chnlMap; /* Channel allocation bit map */ ++ struct GB_TMap *dmaChnlMap; /* DMA Channel allocation bit map */ ++ struct GB_TMap *zChnlMap; /* Zero-Copy Channel alloc bit map */ ++ struct NTFY_OBJECT *hNtfy; /* Manages registered notifications */ ++ struct SYNC_CSOBJECT *hSync; /* For critical sections */ ++ u32 ulFxnAddrs[NUMRMSFXNS]; /* RMS function addresses */ ++ struct MSG_MGR *hMsg; ++ ++ /* Processor properties needed by Node Dispatcher */ ++ u32 ulNumChnls; /* Total number of channels */ ++ u32 ulChnlOffset; /* Offset of chnl ids rsvd for RMS */ ++ u32 ulChnlBufSize; /* Buffer size for data to RMS */ ++ DSP_PROCFAMILY procFamily; /* eg, 5000 */ ++ DSP_PROCTYPE procType; /* eg, 5510 */ ++ u32 uDSPWordSize; /* Size of DSP word on host bytes */ ++ u32 uDSPDataMauSize; /* Size of DSP data MAU */ ++ u32 uDSPMauSize; /* Size of MAU */ ++ s32 nMinPri; /* Minimum runtime priority for node */ ++ s32 nMaxPri; /* Maximum runtime priority for node */ ++ ++ struct STRM_MGR *hStrmMgr; /* STRM manager */ ++ ++ /* Loader properties */ ++ struct NLDR_OBJECT *hNldr; /* Handle to loader */ ++ struct NLDR_FXNS nldrFxns; /* Handle to loader functions */ ++ bool fLoaderInit; /* Loader Init function succeeded? */ ++}; ++ ++/* ++ * ======== CONNECTTYPE ======== ++ */ ++enum CONNECTTYPE { ++ NOTCONNECTED = 0, ++ NODECONNECT, ++ HOSTCONNECT, ++ DEVICECONNECT, ++} ; ++ ++/* ++ * ======== STREAM ======== ++ */ ++struct STREAM { ++ enum CONNECTTYPE type; /* Type of stream connection */ ++ u32 devId; /* pipe or channel id */ ++}; ++ ++/* ++ * ======== NODE_OBJECT ======== ++ */ ++struct NODE_OBJECT { ++ struct LST_ELEM listElem; ++ u32 dwSignature; /* For object validation */ ++ struct NODE_MGR *hNodeMgr; /* The manager of this node */ ++ struct PROC_OBJECT *hProcessor; /* Back pointer to processor */ ++ struct DSP_UUID nodeId; /* Node's ID */ ++ s32 nPriority; /* Node's current priority */ ++ u32 uTimeout; /* Timeout for blocking NODE calls */ ++ u32 uHeapSize; /* Heap Size */ ++ u32 uDSPHeapVirtAddr; /* Heap Size */ ++ u32 uGPPHeapVirtAddr; /* Heap Size */ ++ enum NODE_TYPE nType; /* Type of node: message, task, etc */ ++ enum NODE_STATE nState; /* NODE_ALLOCATED, NODE_CREATED, ... */ ++ u32 uNumInputs; /* Current number of inputs */ ++ u32 uNumOutputs; /* Current number of outputs */ ++ u32 uMaxInputIndex; /* Current max input stream index */ ++ u32 uMaxOutputIndex; /* Current max output stream index */ ++ struct STREAM *inputs; /* Node's input streams */ ++ struct STREAM *outputs; /* Node's output streams */ ++ struct NODE_CREATEARGS createArgs; /* Args for node create function */ ++ NODE_ENV nodeEnv; /* Environment returned by RMS */ ++ struct DCD_GENERICOBJ dcdProps; /* Node properties from DCD */ ++ struct DSP_CBDATA *pArgs; /* Optional args to pass to node */ ++ struct NTFY_OBJECT *hNtfy; /* Manages registered notifications */ ++ char *pstrDevName; /* device name, if device node */ ++ struct SYNC_OBJECT *hSyncDone; /* Synchronize NODE_Terminate */ ++ s32 nExitStatus; /* execute function return status */ ++ ++ /* Information needed for NODE_GetAttr() */ ++ DSP_HNODE hDeviceOwner; /* If dev node, task that owns it */ ++ u32 uNumGPPInputs; /* Current # of from GPP streams */ ++ u32 uNumGPPOutputs; /* Current # of to GPP streams */ ++ /* Current stream connections */ ++ struct DSP_STREAMCONNECT *streamConnect; ++ ++ /* Message queue */ ++ struct MSG_QUEUE *hMsgQueue; ++ ++ /* These fields used for SM messaging */ ++ struct CMM_XLATOROBJECT *hXlator; /* Node's SM address translator */ ++ ++ /* Handle to pass to dynamic loader */ ++ struct NLDR_NODEOBJECT *hNldrNode; ++ bool fLoaded; /* Code is (dynamically) loaded */ ++ bool fPhaseSplit; /* Phases split in many libs or ovly */ ++ ++} ; ++ ++/* Default buffer attributes */ ++static struct DSP_BUFFERATTR NODE_DFLTBUFATTRS = { ++ 0, /* cbStruct */ ++ 1, /* uSegment */ ++ 0, /* uAlignment */ ++}; ++ ++static void DeleteNode(struct NODE_OBJECT *hNode); ++static void DeleteNodeMgr(struct NODE_MGR *hNodeMgr); ++static void FillStreamConnect(struct NODE_OBJECT *hNode1, ++ struct NODE_OBJECT *hNode2, u32 uStream1, ++ u32 uStream2); ++static void FillStreamDef(struct NODE_OBJECT *hNode, ++ struct NODE_STRMDEF *pstrmDef, ++ struct DSP_STRMATTR *pAttrs); ++static void FreeStream(struct NODE_MGR *hNodeMgr, struct STREAM stream); ++static DSP_STATUS GetFxnAddress(struct NODE_OBJECT *hNode, u32 *pulFxnAddr, ++ u32 uPhase); ++static DSP_STATUS GetNodeProps(struct DCD_MANAGER *hDcdMgr, ++ struct NODE_OBJECT *hNode, ++ CONST struct DSP_UUID *pNodeId, ++ struct DCD_GENERICOBJ *pdcdProps); ++static DSP_STATUS GetProcProps(struct NODE_MGR *hNodeMgr, ++ struct DEV_OBJECT *hDevObject); ++static DSP_STATUS GetRMSFxns(struct NODE_MGR *hNodeMgr); ++static u32 Ovly(void *pPrivRef, u32 ulDspRunAddr, u32 ulDspLoadAddr, ++ u32 ulNumBytes, u32 nMemSpace); ++static u32 Write(void *pPrivRef, u32 ulDspAddr, void *pBuf, ++ u32 ulNumBytes, u32 nMemSpace); ++ ++#if GT_TRACE ++static struct GT_Mask NODE_debugMask = { NULL, NULL }; /* GT trace variable */ ++#endif ++ ++#ifdef DSP_DMM_DEBUG ++extern u32 DMM_MemMapDump(struct DMM_OBJECT *hDmmMgr); ++#endif ++ ++static u32 cRefs; /* module reference count */ ++ ++/* Dynamic loader functions. */ ++static struct NLDR_FXNS nldrFxns = { ++ NLDR_Allocate, ++ NLDR_Create, ++ NLDR_Delete, ++ NLDR_Exit, ++ NLDR_Free, ++ NLDR_GetFxnAddr, ++ NLDR_Init, ++ NLDR_Load, ++ NLDR_Unload, ++}; ++ ++enum NODE_STATE NODE_GetState(HANDLE hNode) ++{ ++ struct NODE_OBJECT *pNode = (struct NODE_OBJECT *)hNode; ++ if (!MEM_IsValidHandle(pNode, NODE_SIGNATURE)) { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_GetState:hNode 0x%x\n", pNode); ++ return -1; ++ } else ++ return pNode->nState; ++ ++} ++ ++/* ++ * ======== NODE_Allocate ======== ++ * Purpose: ++ * Allocate GPP resources to manage a node on the DSP. ++ */ ++DSP_STATUS NODE_Allocate(struct PROC_OBJECT *hProcessor, ++ IN CONST struct DSP_UUID *pNodeId, ++ OPTIONAL IN CONST struct DSP_CBDATA *pArgs, ++ OPTIONAL IN CONST struct DSP_NODEATTRIN *pAttrIn, ++ OUT struct NODE_OBJECT **phNode) ++{ ++ struct NODE_MGR *hNodeMgr; ++ struct DEV_OBJECT *hDevObject; ++ struct NODE_OBJECT *pNode = NULL; ++ enum NODE_TYPE nodeType = NODE_TASK; ++ struct NODE_MSGARGS *pmsgArgs; ++ struct NODE_TASKARGS *ptaskArgs; ++ u32 uNumStreams; ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ DSP_STATUS status = DSP_SOK; ++ struct CMM_OBJECT *hCmmMgr = NULL; /* Shared memory manager hndl */ ++ u32 procId; ++ char *label; ++ u32 pulValue; ++ u32 dynextBase; ++ u32 offSet = 0; ++ u32 ulStackSegAddr, ulStackSegVal; ++ u32 ulGppMemBase; ++ struct CFG_HOSTRES hostRes; ++ u32 pMappedAddr = 0; ++ u32 mapAttrs = 0x0; ++ struct DSP_PROCESSORSTATE procStatus; ++#ifdef DSP_DMM_DEBUG ++ struct DMM_OBJECT *hDmmMgr; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProcessor; ++#endif ++ ++#ifndef RES_CLEANUP_DISABLE ++ HANDLE hDrvObject; ++ HANDLE nodeRes; ++ u32 hProcess; ++ struct PROCESS_CONTEXT *pPctxt = NULL; ++ DSP_STATUS res_status = DSP_SOK; ++#endif ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(hProcessor != NULL); ++ DBC_Require(phNode != NULL); ++ DBC_Require(pNodeId != NULL); ++ ++ GT_5trace(NODE_debugMask, GT_ENTER, "NODE_Allocate: \thProcessor: " ++ "0x%x\tpNodeId: 0x%x\tpArgs: 0x%x\tpAttrIn: " ++ "0x%x\tphNode: 0x%x\n", hProcessor, pNodeId, pArgs, pAttrIn, ++ phNode); ++ ++ *phNode = NULL; ++ ++ status = PROC_GetProcessorId(hProcessor, &procId); ++ ++ status = PROC_GetDevObject(hProcessor, &hDevObject); ++ if (DSP_SUCCEEDED(status)) { ++ status = DEV_GetNodeManager(hDevObject, &hNodeMgr); ++ if (hNodeMgr == NULL) ++ status = DSP_EFAIL; ++ ++ } ++ if (procId != DSP_UNIT) ++ goto func_cont; ++ ++ if (DSP_FAILED(status)) ++ goto func_cont; ++ ++ status = PROC_GetState(hProcessor, &procStatus, ++ sizeof(struct DSP_PROCESSORSTATE)); ++ if (DSP_FAILED(status)) ++ goto func_end; ++ /* If processor is in error state then don't attempt ++ to send the message */ ++ if (procStatus.iState == PROC_ERROR) { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_Allocate: proc Status 0x%x\n", ++ procStatus.iState); ++ status = DSP_EFAIL; ++ goto func_end; ++ } ++ ++ /* Assuming that 0 is not a valid function address */ ++ if (hNodeMgr->ulFxnAddrs[0] == 0) { ++ /* No RMS on target - we currently can't handle this */ ++ GT_0trace(NODE_debugMask, GT_5CLASS, "No RMS functions in base " ++ "image. Node allocation fails.\n"); ++ status = DSP_EFAIL; ++ } else { ++ /* Validate pAttrIn fields, if non-NULL */ ++ if (pAttrIn) { ++ /* Check if pAttrIn->iPriority is within range */ ++ if (pAttrIn->iPriority < hNodeMgr->nMinPri || ++ pAttrIn->iPriority > hNodeMgr->nMaxPri) ++ status = DSP_ERANGE; ++ } ++ } ++func_cont: ++ /* Allocate node object and fill in */ ++ if (DSP_FAILED(status)) ++ goto func_cont2; ++ ++ MEM_AllocObject(pNode, struct NODE_OBJECT, NODE_SIGNATURE); ++ if (pNode == NULL) { ++ status = DSP_EMEMORY; ++ goto func_cont1; ++ } ++ pNode->hNodeMgr = hNodeMgr; ++ /* This critical section protects GetNodeProps */ ++ status = SYNC_EnterCS(hNodeMgr->hSync); ++ if (procId != DSP_UNIT) ++ goto func_cont3; ++ ++ /* Get DSP_NDBPROPS from node database */ ++ status = GetNodeProps(hNodeMgr->hDcdMgr, pNode, pNodeId, ++ &(pNode->dcdProps)); ++ if (DSP_FAILED(status)) ++ goto func_cont3; ++ ++ pNode->nodeId = *pNodeId; ++ pNode->hProcessor = hProcessor; ++ pNode->nType = pNode->dcdProps.objData.nodeObj.ndbProps.uNodeType; ++ pNode->uTimeout = pNode->dcdProps.objData.nodeObj.ndbProps.uTimeout; ++ pNode->nPriority = pNode->dcdProps.objData.nodeObj.ndbProps.iPriority; ++ ++ /* Currently only C64 DSP builds support Node Dynamic * heaps */ ++ /* Allocate memory for node heap */ ++ pNode->createArgs.asa.taskArgs.uHeapSize = 0; ++ pNode->createArgs.asa.taskArgs.uDSPHeapAddr = 0; ++ pNode->createArgs.asa.taskArgs.uDSPHeapResAddr = 0; ++ pNode->createArgs.asa.taskArgs.uGPPHeapAddr = 0; ++ if (!pAttrIn) ++ goto func_cont3; ++ ++ /* Check if we have a user allocated node heap */ ++ if (!(pAttrIn->pGPPVirtAddr)) ++ goto func_cont3; ++ ++ /* check for page aligned Heap size */ ++ if (((pAttrIn->uHeapSize) & (PG_SIZE_4K - 1))) { ++ GT_1trace(NODE_debugMask, GT_7CLASS, ++ "NODE_Allocate: node heap page size" ++ " not aligned to 4K page, size=0x%x \n", ++ pAttrIn->uHeapSize); ++ status = DSP_EINVALIDARG; ++ } else { ++ pNode->createArgs.asa.taskArgs.uHeapSize = pAttrIn->uHeapSize; ++ pNode->createArgs.asa.taskArgs.uGPPHeapAddr = ++ (u32)pAttrIn->pGPPVirtAddr; ++ } ++ if (DSP_FAILED(status)) ++ goto func_cont3; ++ ++ status = PROC_ReserveMemory(hProcessor, ++ pNode->createArgs.asa.taskArgs.uHeapSize + PAGE_SIZE, ++ (void **)&(pNode->createArgs.asa.taskArgs. ++ uDSPHeapResAddr)); ++ if (DSP_FAILED(status)) { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_Allocate:Failed to reserve " ++ "memory for Heap: 0x%x\n", status); ++ } else { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_Allocate: DSPProcessor_Reserve" ++ " Memory successful: 0x%x\n", status); ++ } ++#ifdef DSP_DMM_DEBUG ++ status = DMM_GetHandle(pProcObject, &hDmmMgr); ++ if (DSP_SUCCEEDED(status)) ++ DMM_MemMapDump(hDmmMgr); ++#endif ++ if (DSP_FAILED(status)) ++ goto func_cont3; ++ ++ mapAttrs |= DSP_MAPLITTLEENDIAN; ++ mapAttrs |= DSP_MAPELEMSIZE32; ++ mapAttrs |= DSP_MAPVIRTUALADDR; ++ status = PROC_Map(hProcessor, (void *)pAttrIn->pGPPVirtAddr, ++ pNode->createArgs.asa.taskArgs.uHeapSize, ++ (void *)pNode->createArgs.asa.taskArgs.uDSPHeapResAddr, ++ (void **)&pMappedAddr, mapAttrs); ++ if (DSP_FAILED(status)) { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_Allocate: Failed to map memory" ++ " for Heap: 0x%x\n", status); ++ } else { ++ pNode->createArgs.asa.taskArgs.uDSPHeapAddr = ++ (u32) pMappedAddr; ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_Allocate:DSPProcessor_Map" ++ " successful: 0x%x\n", status); ++ } ++ ++func_cont3: ++ (void)SYNC_LeaveCS(hNodeMgr->hSync); ++func_cont1: ++ if (pAttrIn != NULL) { ++ /* Overrides of NBD properties */ ++ pNode->uTimeout = pAttrIn->uTimeout; ++ pNode->nPriority = pAttrIn->iPriority; ++ } ++func_cont2: ++ /* Create object to manage notifications */ ++ if (DSP_SUCCEEDED(status)) ++ status = NTFY_Create(&pNode->hNtfy); ++ ++ if (DSP_SUCCEEDED(status)) { ++ nodeType = NODE_GetType(pNode); ++ /* Allocate DSP_STREAMCONNECT array for device, task, and ++ * dais socket nodes. */ ++ if (nodeType != NODE_MESSAGE) { ++ uNumStreams = MaxInputs(pNode) + MaxOutputs(pNode); ++ pNode->streamConnect = MEM_Calloc(uNumStreams * ++ sizeof(struct DSP_STREAMCONNECT), ++ MEM_PAGED); ++ if (uNumStreams > 0 && pNode->streamConnect == NULL) ++ status = DSP_EMEMORY; ++ ++ } ++ if (DSP_SUCCEEDED(status) && (nodeType == NODE_TASK || ++ nodeType == NODE_DAISSOCKET)) { ++ /* Allocate arrays for maintainig stream connections */ ++ pNode->inputs = ++ MEM_Calloc(MaxInputs(pNode) * ++ sizeof(struct STREAM), MEM_PAGED); ++ pNode->outputs = ++ MEM_Calloc(MaxOutputs(pNode) * ++ sizeof(struct STREAM), MEM_PAGED); ++ ptaskArgs = &(pNode->createArgs.asa.taskArgs); ++ ptaskArgs->strmInDef = ++ MEM_Calloc(MaxInputs(pNode) * ++ sizeof(struct NODE_STRMDEF), ++ MEM_PAGED); ++ ptaskArgs->strmOutDef = ++ MEM_Calloc(MaxOutputs(pNode) * ++ sizeof(struct NODE_STRMDEF), ++ MEM_PAGED); ++ if ((MaxInputs(pNode) > 0 && (pNode->inputs == NULL || ++ ptaskArgs->strmInDef == NULL)) || ++ (MaxOutputs(pNode) > 0 && (pNode->outputs == NULL || ++ ptaskArgs->strmOutDef == NULL))) ++ status = DSP_EMEMORY; ++ } ++ } ++ if (DSP_SUCCEEDED(status) && (nodeType != NODE_DEVICE)) { ++ /* Create an event that will be posted when RMS_EXIT is ++ * received. */ ++ status = SYNC_OpenEvent(&pNode->hSyncDone, NULL); ++ if (DSP_SUCCEEDED(status)) { ++ /*Get the shared mem mgr for this nodes dev object */ ++ status = CMM_GetHandle(hProcessor, &hCmmMgr); ++ if (DSP_FAILED(status)) { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_Allocate: Failed to" ++ " get CMM Mgr handle: 0x%x\n", status); ++ } else { ++ /* Allocate a SM addr translator for this node ++ * w/ deflt attr */ ++ status = CMM_XlatorCreate(&pNode->hXlator, ++ hCmmMgr, NULL); ++ if (DSP_FAILED(status)) { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_Allocate: Failed" ++ " to create SM translator: 0x%x\n", ++ status); ++ } ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Fill in message args */ ++ if ((pArgs != NULL) && (pArgs->cbData > 0)) { ++ pmsgArgs = &(pNode->createArgs.asa.msgArgs); ++ pmsgArgs->pData = MEM_Calloc(pArgs->cbData, ++ MEM_PAGED); ++ if (pmsgArgs->pData == NULL) { ++ status = DSP_EMEMORY; ++ } else { ++ pmsgArgs->uArgLength = pArgs->cbData; ++ memcpy(pmsgArgs->pData, pArgs->cData, ++ pArgs->cbData); ++ } ++ } ++ } ++ } ++ ++ if (DSP_SUCCEEDED(status) && nodeType != NODE_DEVICE) { ++ /* Create a message queue for this node */ ++ pIntfFxns = hNodeMgr->pIntfFxns; ++ status = (*pIntfFxns->pfnMsgCreateQueue)(hNodeMgr->hMsg, ++ &pNode->hMsgQueue, 0, ++ pNode->createArgs.asa.msgArgs.uMaxMessages, ++ pNode); ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Create object for dynamic loading */ ++ ++ status = hNodeMgr->nldrFxns.pfnAllocate(hNodeMgr->hNldr, ++ (void *) pNode, ++ &pNode->dcdProps.objData.nodeObj, ++ &pNode->hNldrNode, ++ &pNode->fPhaseSplit); ++ if (DSP_FAILED(status)) { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_Allocate: Failed to " ++ "allocate NLDR node: 0x%x\n", status); ++ } ++ } ++ ++ /* Comapare value read from Node Properties and check if it is same as ++ * STACKSEGLABEL, if yes read the Address of STACKSEGLABEL, calculate ++ * GPP Address, Read the value in that address and override the ++ * uStackSeg value in task args */ ++ if (DSP_SUCCEEDED(status) && ++ (char *)pNode->dcdProps.objData.nodeObj.ndbProps.uStackSegName != ++ NULL) { ++ label = MEM_Calloc(sizeof(STACKSEGLABEL)+1, MEM_PAGED); ++ strncpy(label, STACKSEGLABEL, sizeof(STACKSEGLABEL)+1); ++ ++ if (strcmp((char *)pNode->dcdProps.objData.nodeObj. ++ ndbProps.uStackSegName, label) == 0) { ++ status = hNodeMgr->nldrFxns.pfnGetFxnAddr(pNode-> ++ hNldrNode, "DYNEXT_BEG", &dynextBase); ++ if (DSP_FAILED(status)) { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_Allocate: Failed to get Address for " ++ "DYNEXT_BEG: 0x%x\n", status); ++ } ++ ++ status = hNodeMgr->nldrFxns.pfnGetFxnAddr(pNode-> ++ hNldrNode, "L1DSRAM_HEAP", &pulValue); ++ ++ if (DSP_FAILED(status)) { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_Allocate: Failed to get Address for " ++ "L1DSRAM_HEAP: 0x%x\n", status); ++ } ++ ++ status = CFG_GetHostResources((struct CFG_DEVNODE *) ++ DRV_GetFirstDevExtension(), &hostRes); ++ ++ if (DSP_FAILED(status)) { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_Allocate: Failed to get host resource " ++ "0x%x\n", status); ++ } ++ ++ ulGppMemBase = hostRes.dwMemBase[1]; ++ offSet = pulValue - dynextBase; ++ ulStackSegAddr = ulGppMemBase + offSet; ++ ulStackSegVal = (u32)*((REG_UWORD32 *) ++ ((u32)(ulStackSegAddr))); ++ ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "StackSegVal =0x%x\n", ulStackSegVal); ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "ulStackSegAddr = 0x%x\n", ulStackSegAddr); ++ ++ pNode->createArgs.asa.taskArgs.uStackSeg = ++ ulStackSegVal; ++ ++ } ++ ++ if (label) ++ MEM_Free(label); ++ ++ } ++ ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Add the node to the node manager's list of allocated ++ * nodes. */ ++ LST_InitElem((struct LST_ELEM *)pNode); ++ NODE_SetState(pNode, NODE_ALLOCATED); ++ ++ status = SYNC_EnterCS(hNodeMgr->hSync); ++ ++ if (DSP_SUCCEEDED(status)) { ++ LST_PutTail(hNodeMgr->nodeList, ++ (struct LST_ELEM *) pNode); ++ ++(hNodeMgr->uNumNodes); ++ } ++ ++ /* Exit critical section */ ++ (void) SYNC_LeaveCS(hNodeMgr->hSync); ++ ++ /* Preset this to assume phases are split ++ * (for overlay and dll) */ ++ pNode->fPhaseSplit = true; ++ ++ if (DSP_SUCCEEDED(status)) ++ *phNode = pNode; ++ ++ ++ /* Notify all clients registered for DSP_NODESTATECHANGE. */ ++ PROC_NotifyAllClients(hProcessor, DSP_NODESTATECHANGE); ++ } else { ++ /* Cleanup */ ++ if (pNode) ++ DeleteNode(pNode); ++ ++ } ++ ++#ifndef RES_CLEANUP_DISABLE ++ if (DSP_SUCCEEDED(status)) { ++ /* Return PID instead of process handle */ ++ hProcess = current->pid; ++ ++ res_status = CFG_GetObject((u32 *)&hDrvObject, ++ REG_DRV_OBJECT); ++ if (DSP_SUCCEEDED(res_status)) { ++ DRV_GetProcContext(hProcess, ++ (struct DRV_OBJECT *)hDrvObject, ++ &pPctxt, *phNode, 0); ++ if (pPctxt == NULL) { ++ DRV_InsertProcContext( ++ (struct DRV_OBJECT *)hDrvObject, ++ &pPctxt); ++ if (pPctxt != NULL) { ++ DRV_ProcUpdatestate(pPctxt, ++ PROC_RES_ALLOCATED); ++ DRV_ProcSetPID(pPctxt, hProcess); ++ pPctxt->hProcessor = ++ (DSP_HPROCESSOR)hProcessor; ++ } ++ } ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Return PID instead of process handle */ ++ hProcess = current->pid; ++ res_status = CFG_GetObject((u32 *)&hDrvObject, ++ REG_DRV_OBJECT); ++ if (DSP_SUCCEEDED(res_status)) { ++ DRV_GetProcContext(hProcess, ++ (struct DRV_OBJECT *)hDrvObject, ++ &pPctxt, *phNode, 0); ++ if (pPctxt != NULL) { ++ DRV_InsertNodeResElement(*phNode, &nodeRes, ++ pPctxt); ++ DRV_ProcNodeUpdateHeapStatus(nodeRes, true); ++ DRV_ProcNodeUpdateStatus(nodeRes, true); ++ } ++ } ++ } ++#endif ++ DBC_Ensure((DSP_FAILED(status) && (*phNode == NULL)) || ++ (DSP_SUCCEEDED(status) ++ && MEM_IsValidHandle((*phNode), NODE_SIGNATURE))); ++func_end: ++ return status; ++} ++ ++/* ++ * ======== NODE_AllocMsgBuf ======== ++ * Purpose: ++ * Allocates buffer for zero copy messaging. ++ */ ++DBAPI NODE_AllocMsgBuf(struct NODE_OBJECT *hNode, u32 uSize, ++ OPTIONAL IN OUT struct DSP_BUFFERATTR *pAttr, ++ OUT u8 **pBuffer) ++{ ++ struct NODE_OBJECT *pNode = (struct NODE_OBJECT *)hNode; ++ DSP_STATUS status = DSP_SOK; ++ bool bVirtAddr = false; ++ bool bSetInfo; ++ u32 procId; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pBuffer != NULL); ++ ++ DBC_Require(uSize > 0); ++ ++ GT_4trace(NODE_debugMask, GT_ENTER, ++ "NODE_AllocMsgBuf: hNode: 0x%x\tuSize:" ++ " 0x%x\tpAttr: 0x%x\tpBuffer: %d\n", pNode, uSize, pAttr, ++ pBuffer); ++ ++ if (!MEM_IsValidHandle(pNode, NODE_SIGNATURE)) ++ status = DSP_EHANDLE; ++ ++ if (NODE_GetType(pNode) == NODE_DEVICE) ++ status = DSP_ENODETYPE; ++ ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ if (pAttr == NULL) ++ pAttr = &NODE_DFLTBUFATTRS; /* set defaults */ ++ ++ status = PROC_GetProcessorId(pNode->hProcessor, &procId); ++ if (procId != DSP_UNIT) { ++ DBC_Assert(NULL); ++ goto func_end; ++ } ++ /* If segment ID includes MEM_SETVIRTUALSEGID then pBuffer is a ++ * virt address, so set this info in this node's translator ++ * object for future ref. If MEM_GETVIRTUALSEGID then retrieve ++ * virtual address from node's translator. */ ++ if ((pAttr->uSegment & MEM_SETVIRTUALSEGID) || ++ (pAttr->uSegment & MEM_GETVIRTUALSEGID)) { ++ bVirtAddr = true; ++ bSetInfo = (pAttr->uSegment & MEM_SETVIRTUALSEGID) ? ++ true : false; ++ pAttr->uSegment &= ~MEM_MASKVIRTUALSEGID; /* clear mask bits */ ++ /* Set/get this node's translators virtual address base/size */ ++ status = CMM_XlatorInfo(pNode->hXlator, pBuffer, uSize, ++ pAttr->uSegment, bSetInfo); ++ if (DSP_FAILED(status)) { ++ GT_1trace(NODE_debugMask, GT_7CLASS, ++ "NODE_AllocMsgBuf " ++ "failed: 0x%lx\n", status); ++ } ++ } ++ if (DSP_SUCCEEDED(status) && (!bVirtAddr)) { ++ if (pAttr->uSegment != 1) { ++ /* Node supports single SM segment only. */ ++ status = DSP_EBADSEGID; ++ } ++ /* Arbitrary SM buffer alignment not supported for host side ++ * allocs, but guaranteed for the following alignment ++ * values. */ ++ switch (pAttr->uAlignment) { ++ case 0: ++ case 1: ++ case 2: ++ case 4: ++ break; ++ default: ++ /* alignment value not suportted */ ++ status = DSP_EALIGNMENT; ++ break; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* allocate physical buffer from segId in node's ++ * translator */ ++ (void)CMM_XlatorAllocBuf(pNode->hXlator, pBuffer, ++ uSize); ++ if (*pBuffer == NULL) { ++ GT_0trace(NODE_debugMask, GT_7CLASS, ++ "NODE_AllocMsgBuf: " ++ "ERROR: Out of shared memory.\n"); ++ status = DSP_EMEMORY; ++ } ++ } ++ } ++func_end: ++ return status; ++} ++ ++/* ++ * ======== NODE_ChangePriority ======== ++ * Purpose: ++ * Change the priority of a node in the allocated state, or that is ++ * currently running or paused on the target. ++ */ ++DSP_STATUS NODE_ChangePriority(struct NODE_OBJECT *hNode, s32 nPriority) ++{ ++ struct NODE_OBJECT *pNode = (struct NODE_OBJECT *)hNode; ++ struct NODE_MGR *hNodeMgr = NULL; ++ enum NODE_TYPE nodeType; ++ enum NODE_STATE state; ++ DSP_STATUS status = DSP_SOK; ++ u32 procId; ++ ++ DBC_Require(cRefs > 0); ++ ++ GT_2trace(NODE_debugMask, GT_ENTER, "NODE_ChangePriority: " ++ "hNode: 0x%x\tnPriority: %d\n", hNode, nPriority); ++ ++ if (!MEM_IsValidHandle(hNode, NODE_SIGNATURE)) { ++ GT_1trace(NODE_debugMask, GT_7CLASS, ++ "Invalid NODE Handle: 0x%x\n", hNode); ++ status = DSP_EHANDLE; ++ } else { ++ hNodeMgr = hNode->hNodeMgr; ++ nodeType = NODE_GetType(hNode); ++ if (nodeType != NODE_TASK && nodeType != NODE_DAISSOCKET) ++ status = DSP_ENODETYPE; ++ else if (nPriority < hNodeMgr->nMinPri || ++ nPriority > hNodeMgr->nMaxPri) ++ status = DSP_ERANGE; ++ } ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ /* Enter critical section */ ++ status = SYNC_EnterCS(hNodeMgr->hSync); ++ if (DSP_FAILED(status)) ++ goto func_cont; ++ ++ state = NODE_GetState(hNode); ++ if (state == NODE_ALLOCATED || state == NODE_PAUSED) { ++ NODE_SetPriority(hNode, nPriority); ++ } else { ++ if (state != NODE_RUNNING) { ++ status = DSP_EWRONGSTATE; ++ goto func_cont; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = PROC_GetProcessorId(pNode->hProcessor, ++ &procId); ++ if (procId == DSP_UNIT) { ++ status = DISP_NodeChangePriority(hNodeMgr-> ++ hDisp, hNode, ++ hNodeMgr->ulFxnAddrs[RMSCHANGENODEPRIORITY], ++ hNode->nodeEnv, nPriority); ++ } ++ if (DSP_SUCCEEDED(status)) ++ NODE_SetPriority(hNode, nPriority); ++ ++ } ++ } ++func_cont: ++ /* Leave critical section */ ++ (void)SYNC_LeaveCS(hNodeMgr->hSync); ++func_end: ++ return status; ++} ++ ++/* ++ * ======== NODE_Connect ======== ++ * Purpose: ++ * Connect two nodes on the DSP, or a node on the DSP to the GPP. ++ */ ++DSP_STATUS NODE_Connect(struct NODE_OBJECT *hNode1, u32 uStream1, ++ struct NODE_OBJECT *hNode2, ++ u32 uStream2, OPTIONAL IN struct DSP_STRMATTR *pAttrs, ++ OPTIONAL IN struct DSP_CBDATA *pConnParam) ++{ ++ struct NODE_MGR *hNodeMgr; ++ char *pstrDevName = NULL; ++ enum NODE_TYPE node1Type = NODE_TASK; ++ enum NODE_TYPE node2Type = NODE_TASK; ++ struct NODE_STRMDEF *pstrmDef; ++ struct NODE_STRMDEF *pInput = NULL; ++ struct NODE_STRMDEF *pOutput = NULL; ++ struct NODE_OBJECT *hDevNode; ++ struct NODE_OBJECT *hNode; ++ struct STREAM *pStream; ++ GB_BitNum pipeId = GB_NOBITS; ++ GB_BitNum chnlId = GB_NOBITS; ++ CHNL_MODE uMode; ++ u32 dwLength; ++ DSP_STATUS status = DSP_SOK; ++ DBC_Require(cRefs > 0); ++ GT_5trace(NODE_debugMask, GT_ENTER, ++ "NODE_Connect: hNode1: 0x%x\tuStream1:" ++ " %d\thNode2: 0x%x\tuStream2: %d\tpAttrs: 0x%x\n", hNode1, ++ uStream1, hNode2, uStream2, pAttrs); ++ if (DSP_SUCCEEDED(status)) { ++ if ((hNode1 != (struct NODE_OBJECT *) DSP_HGPPNODE && ++ !MEM_IsValidHandle(hNode1, NODE_SIGNATURE)) || ++ (hNode2 != (struct NODE_OBJECT *) DSP_HGPPNODE && ++ !MEM_IsValidHandle(hNode2, NODE_SIGNATURE))) ++ status = DSP_EHANDLE; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* The two nodes must be on the same processor */ ++ if (hNode1 != (struct NODE_OBJECT *)DSP_HGPPNODE && ++ hNode2 != (struct NODE_OBJECT *)DSP_HGPPNODE && ++ hNode1->hNodeMgr != hNode2->hNodeMgr) ++ status = DSP_EFAIL; ++ /* Cannot connect a node to itself */ ++ if (hNode1 == hNode2) ++ status = DSP_EFAIL; ++ ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* NODE_GetType() will return NODE_GPP if hNode = ++ * DSP_HGPPNODE. */ ++ node1Type = NODE_GetType(hNode1); ++ node2Type = NODE_GetType(hNode2); ++ /* Check stream indices ranges */ ++ if ((node1Type != NODE_GPP && node1Type != NODE_DEVICE && ++ uStream1 >= MaxOutputs(hNode1)) || (node2Type != NODE_GPP && ++ node2Type != NODE_DEVICE && uStream2 >= MaxInputs(hNode2))) ++ status = DSP_EVALUE; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* ++ * Only the following types of connections are allowed: ++ * task/dais socket < == > task/dais socket ++ * task/dais socket < == > device ++ * task/dais socket < == > GPP ++ * ++ * ie, no message nodes, and at least one task or dais ++ * socket node. ++ */ ++ if (node1Type == NODE_MESSAGE || node2Type == NODE_MESSAGE || ++ (node1Type != NODE_TASK && node1Type != NODE_DAISSOCKET && ++ node2Type != NODE_TASK && node2Type != NODE_DAISSOCKET)) ++ status = DSP_EFAIL; ++ } ++ /* ++ * Check stream mode. Default is STRMMODE_PROCCOPY. ++ */ ++ if (DSP_SUCCEEDED(status) && pAttrs) { ++ if (pAttrs->lMode != STRMMODE_PROCCOPY) ++ status = DSP_ESTRMMODE; /* illegal stream mode */ ++ ++ } ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ if (node1Type != NODE_GPP) { ++ hNodeMgr = hNode1->hNodeMgr; ++ } else { ++ DBC_Assert(hNode2 != (struct NODE_OBJECT *)DSP_HGPPNODE); ++ hNodeMgr = hNode2->hNodeMgr; ++ } ++ /* Enter critical section */ ++ status = SYNC_EnterCS(hNodeMgr->hSync); ++ if (DSP_FAILED(status)) ++ goto func_cont; ++ ++ /* Nodes must be in the allocated state */ ++ if (node1Type != NODE_GPP && NODE_GetState(hNode1) != NODE_ALLOCATED) ++ status = DSP_EWRONGSTATE; ++ ++ if (node2Type != NODE_GPP && NODE_GetState(hNode2) != NODE_ALLOCATED) ++ status = DSP_EWRONGSTATE; ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Check that stream indices for task and dais socket nodes ++ * are not already be used. (Device nodes checked later) */ ++ if (node1Type == NODE_TASK || node1Type == NODE_DAISSOCKET) { ++ pOutput = &(hNode1->createArgs.asa.taskArgs. ++ strmOutDef[uStream1]); ++ if (pOutput->szDevice != NULL) ++ status = DSP_EALREADYCONNECTED; ++ ++ } ++ if (node2Type == NODE_TASK || node2Type == NODE_DAISSOCKET) { ++ pInput = &(hNode2->createArgs.asa.taskArgs. ++ strmInDef[uStream2]); ++ if (pInput->szDevice != NULL) ++ status = DSP_EALREADYCONNECTED; ++ ++ } ++ } ++ /* Connecting two task nodes? */ ++ if (DSP_SUCCEEDED(status) && ((node1Type == NODE_TASK || ++ node1Type == NODE_DAISSOCKET) && (node2Type == NODE_TASK || ++ node2Type == NODE_DAISSOCKET))) { ++ /* Find available pipe */ ++ pipeId = GB_findandset(hNodeMgr->pipeMap); ++ if (pipeId == GB_NOBITS) { ++ status = DSP_ENOMORECONNECTIONS; ++ } else { ++ hNode1->outputs[uStream1].type = NODECONNECT; ++ hNode2->inputs[uStream2].type = NODECONNECT; ++ hNode1->outputs[uStream1].devId = pipeId; ++ hNode2->inputs[uStream2].devId = pipeId; ++ pOutput->szDevice = MEM_Calloc(PIPENAMELEN + 1, ++ MEM_PAGED); ++ pInput->szDevice = MEM_Calloc(PIPENAMELEN + 1, ++ MEM_PAGED); ++ if (pOutput->szDevice == NULL || ++ pInput->szDevice == NULL) { ++ /* Undo the connection */ ++ if (pOutput->szDevice) ++ MEM_Free(pOutput->szDevice); ++ ++ if (pInput->szDevice) ++ MEM_Free(pInput->szDevice); ++ ++ pOutput->szDevice = NULL; ++ pInput->szDevice = NULL; ++ GB_clear(hNodeMgr->pipeMap, pipeId); ++ status = DSP_EMEMORY; ++ } else { ++ /* Copy "/dbpipe" name to device names */ ++ sprintf(pOutput->szDevice, "%s%d", ++ PIPEPREFIX, pipeId); ++ strcpy(pInput->szDevice, pOutput->szDevice); ++ } ++ } ++ } ++ /* Connecting task node to host? */ ++ if (DSP_SUCCEEDED(status) && (node1Type == NODE_GPP || ++ node2Type == NODE_GPP)) { ++ if (node1Type == NODE_GPP) { ++ uMode = CHNL_MODETODSP; ++ } else { ++ DBC_Assert(node2Type == NODE_GPP); ++ uMode = CHNL_MODEFROMDSP; ++ } ++ /* Reserve a channel id. We need to put the name "/host" ++ * in the node's createArgs, but the host ++ * side channel will not be opened until DSPStream_Open is ++ * called for this node. */ ++ if (pAttrs) { ++ if (pAttrs->lMode == STRMMODE_RDMA) { ++ chnlId = GB_findandset(hNodeMgr->dmaChnlMap); ++ /* dma chans are 2nd transport chnl set ++ * ids(e.g. 16-31)*/ ++ (chnlId != GB_NOBITS) ? ++ (chnlId = chnlId + hNodeMgr->ulNumChnls) : ++ chnlId; ++ } else if (pAttrs->lMode == STRMMODE_ZEROCOPY) { ++ chnlId = GB_findandset(hNodeMgr->zChnlMap); ++ /* zero-copy chans are 3nd transport set ++ * (e.g. 32-47) */ ++ (chnlId != GB_NOBITS) ? (chnlId = chnlId + ++ (2 * hNodeMgr->ulNumChnls)) : chnlId; ++ } else { /* must be PROCCOPY */ ++ DBC_Assert(pAttrs->lMode == STRMMODE_PROCCOPY); ++ chnlId = GB_findandset(hNodeMgr->chnlMap); ++ /* e.g. 0-15 */ ++ } ++ } else { ++ /* default to PROCCOPY */ ++ chnlId = GB_findandset(hNodeMgr->chnlMap); ++ } ++ if (chnlId == GB_NOBITS) { ++ status = DSP_ENOMORECONNECTIONS; ++ goto func_cont2; ++ } ++ pstrDevName = MEM_Calloc(HOSTNAMELEN + 1, MEM_PAGED); ++ if (pstrDevName != NULL) ++ goto func_cont2; ++ ++ if (pAttrs) { ++ if (pAttrs->lMode == STRMMODE_RDMA) { ++ GB_clear(hNodeMgr->dmaChnlMap, chnlId - ++ hNodeMgr->ulNumChnls); ++ } else if (pAttrs->lMode == STRMMODE_ZEROCOPY) { ++ GB_clear(hNodeMgr->zChnlMap, chnlId - ++ (2*hNodeMgr->ulNumChnls)); ++ } else { ++ DBC_Assert(pAttrs->lMode == STRMMODE_PROCCOPY); ++ GB_clear(hNodeMgr->chnlMap, chnlId); ++ } ++ } else { ++ GB_clear(hNodeMgr->chnlMap, chnlId); ++ } ++ status = DSP_EMEMORY; ++func_cont2: ++ if (DSP_SUCCEEDED(status)) { ++ if (hNode1 == (struct NODE_OBJECT *) DSP_HGPPNODE) { ++ hNode2->inputs[uStream2].type = HOSTCONNECT; ++ hNode2->inputs[uStream2].devId = chnlId; ++ pInput->szDevice = pstrDevName; ++ } else { ++ hNode1->outputs[uStream1].type = HOSTCONNECT; ++ hNode1->outputs[uStream1].devId = chnlId; ++ pOutput->szDevice = pstrDevName; ++ } ++ sprintf(pstrDevName, "%s%d", HOSTPREFIX, chnlId); ++ } ++ } ++ /* Connecting task node to device node? */ ++ if (DSP_SUCCEEDED(status) && ((node1Type == NODE_DEVICE) || ++ (node2Type == NODE_DEVICE))) { ++ if (node2Type == NODE_DEVICE) { ++ /* node1 == > device */ ++ hDevNode = hNode2; ++ hNode = hNode1; ++ pStream = &(hNode1->outputs[uStream1]); ++ pstrmDef = pOutput; ++ } else { ++ /* device == > node2 */ ++ hDevNode = hNode1; ++ hNode = hNode2; ++ pStream = &(hNode2->inputs[uStream2]); ++ pstrmDef = pInput; ++ } ++ /* Set up create args */ ++ pStream->type = DEVICECONNECT; ++ dwLength = strlen(hDevNode->pstrDevName); ++ if (pConnParam != NULL) { ++ pstrmDef->szDevice = MEM_Calloc(dwLength + 1 + ++ (u32) pConnParam->cbData, ++ MEM_PAGED); ++ } else { ++ pstrmDef->szDevice = MEM_Calloc(dwLength + 1, ++ MEM_PAGED); ++ } ++ if (pstrmDef->szDevice == NULL) { ++ status = DSP_EMEMORY; ++ } else { ++ /* Copy device name */ ++ strncpy(pstrmDef->szDevice, hDevNode->pstrDevName, ++ dwLength); ++ if (pConnParam != NULL) { ++ strncat(pstrmDef->szDevice, ++ (char *)pConnParam->cData, ++ (u32)pConnParam->cbData); ++ } ++ hDevNode->hDeviceOwner = hNode; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Fill in create args */ ++ if (node1Type == NODE_TASK || node1Type == NODE_DAISSOCKET) { ++ hNode1->createArgs.asa.taskArgs.uNumOutputs++; ++ FillStreamDef(hNode1, pOutput, pAttrs); ++ } ++ if (node2Type == NODE_TASK || node2Type == NODE_DAISSOCKET) { ++ hNode2->createArgs.asa.taskArgs.uNumInputs++; ++ FillStreamDef(hNode2, pInput, pAttrs); ++ } ++ /* Update hNode1 and hNode2 streamConnect */ ++ if (node1Type != NODE_GPP && node1Type != NODE_DEVICE) { ++ hNode1->uNumOutputs++; ++ if (uStream1 > hNode1->uMaxOutputIndex) ++ hNode1->uMaxOutputIndex = uStream1; ++ ++ } ++ if (node2Type != NODE_GPP && node2Type != NODE_DEVICE) { ++ hNode2->uNumInputs++; ++ if (uStream2 > hNode2->uMaxInputIndex) ++ hNode2->uMaxInputIndex = uStream2; ++ ++ } ++ FillStreamConnect(hNode1, hNode2, uStream1, uStream2); ++ } ++func_cont: ++ /* end of SYNC_EnterCS */ ++ /* Exit critical section */ ++ (void)SYNC_LeaveCS(hNodeMgr->hSync); ++func_end: ++ return status; ++} ++ ++/* ++ * ======== NODE_Create ======== ++ * Purpose: ++ * Create a node on the DSP by remotely calling the node's create function. ++ */ ++DSP_STATUS NODE_Create(struct NODE_OBJECT *hNode) ++{ ++ struct NODE_OBJECT *pNode = (struct NODE_OBJECT *)hNode; ++ struct NODE_MGR *hNodeMgr; ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ u32 ulCreateFxn; ++ enum NODE_TYPE nodeType; ++ DSP_STATUS status = DSP_SOK; ++ DSP_STATUS status1 = DSP_SOK; ++ bool bJustWokeDSP = false; ++ struct DSP_CBDATA cbData; ++ u32 procId = 255; ++ struct DSP_PROCESSORSTATE procStatus; ++ struct PROC_OBJECT *hProcessor; ++#if defined(CONFIG_BRIDGE_DVFS) && !defined(CONFIG_CPU_FREQ) ++ struct dspbridge_platform_data *pdata = ++ omap_dspbridge_dev->dev.platform_data; ++#endif ++ ++ DBC_Require(cRefs > 0); ++ GT_1trace(NODE_debugMask, GT_ENTER, "NODE_Create: hNode: 0x%x\n", ++ hNode); ++ hProcessor = hNode->hProcessor; ++ status = PROC_GetState(hProcessor, &procStatus, ++ sizeof(struct DSP_PROCESSORSTATE)); ++ if (DSP_FAILED(status)) ++ goto func_end; ++ /* If processor is in error state then don't attempt to create ++ new node */ ++ if (procStatus.iState == PROC_ERROR) { ++ GT_1trace(NODE_debugMask, GT_4CLASS, "NODE_Create:" ++ " proc Status 0x%x\n", procStatus.iState); ++ status = DSP_EFAIL; ++ goto func_end; ++ } ++ /* create struct DSP_CBDATA struct for PWR calls */ ++ cbData.cbData = PWR_TIMEOUT; ++ nodeType = NODE_GetType(hNode); ++ hNodeMgr = hNode->hNodeMgr; ++ pIntfFxns = hNodeMgr->pIntfFxns; ++ /* Get access to node dispatcher */ ++ status = SYNC_EnterCS(hNodeMgr->hSync); ++ if (DSP_FAILED(status)) ++ goto func_cont; ++ ++ /* Check node state */ ++ if (NODE_GetState(hNode) != NODE_ALLOCATED) ++ status = DSP_EWRONGSTATE; ++ ++ if (DSP_SUCCEEDED(status)) ++ status = PROC_GetProcessorId(pNode->hProcessor, &procId); ++ ++ if (DSP_FAILED(status)) ++ goto func_cont2; ++ ++ if (procId != DSP_UNIT) ++ goto func_cont2; ++ ++ /* Make sure streams are properly connected */ ++ if ((hNode->uNumInputs && hNode->uMaxInputIndex > ++ hNode->uNumInputs - 1) || ++ (hNode->uNumOutputs && hNode->uMaxOutputIndex > ++ hNode->uNumOutputs - 1)) ++ status = DSP_ENOTCONNECTED; ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* If node's create function is not loaded, load it */ ++ /* Boost the OPP level to max level that DSP can be requested */ ++#if defined(CONFIG_BRIDGE_DVFS) && !defined(CONFIG_CPU_FREQ) ++ if (pdata->cpu_set_freq) { ++ (*pdata->cpu_set_freq)(pdata->mpu_speed[VDD1_OPP3]); ++ ++ if (pdata->dsp_get_opp) { ++ GT_1trace(NODE_debugMask, GT_4CLASS, "opp level" ++ "after setting to VDD1_OPP3 is %d\n", ++ (*pdata->dsp_get_opp)()); ++ } ++ } ++#endif ++ status = hNodeMgr->nldrFxns.pfnLoad(hNode->hNldrNode, ++ NLDR_CREATE); ++ /* Get address of node's create function */ ++ if (DSP_SUCCEEDED(status)) { ++ hNode->fLoaded = true; ++ if (nodeType != NODE_DEVICE) { ++ status = GetFxnAddress(hNode, &ulCreateFxn, ++ CREATEPHASE); ++ } ++ } else { ++ GT_1trace(NODE_debugMask, GT_ENTER, ++ "NODE_Create: failed to load" ++ " create code: 0x%x\n", status); ++ } ++ /* Request the lowest OPP level*/ ++#if defined(CONFIG_BRIDGE_DVFS) && !defined(CONFIG_CPU_FREQ) ++ if (pdata->cpu_set_freq) { ++ (*pdata->cpu_set_freq)(pdata->mpu_speed[VDD1_OPP1]); ++ ++ if (pdata->dsp_get_opp) { ++ GT_1trace(NODE_debugMask, GT_4CLASS, "opp level" ++ "after setting to VDD1_OPP1 is %d\n", ++ (*pdata->dsp_get_opp)()); ++ } ++ } ++#endif ++ /* Get address of iAlg functions, if socket node */ ++ if (DSP_SUCCEEDED(status)) { ++ if (nodeType == NODE_DAISSOCKET) { ++ status = hNodeMgr->nldrFxns.pfnGetFxnAddr ++ (hNode->hNldrNode, hNode->dcdProps. ++ objData.nodeObj.pstrIAlgName, ++ &hNode->createArgs.asa.taskArgs. ++ ulDaisArg); ++ } ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ if (nodeType != NODE_DEVICE) { ++ status = DISP_NodeCreate(hNodeMgr->hDisp, hNode, ++ hNodeMgr->ulFxnAddrs[RMSCREATENODE], ++ ulCreateFxn, &(hNode->createArgs), ++ &(hNode->nodeEnv)); ++ if (DSP_SUCCEEDED(status)) { ++ /* Set the message queue id to the node env ++ * pointer */ ++ pIntfFxns = hNodeMgr->pIntfFxns; ++ (*pIntfFxns->pfnMsgSetQueueId)(hNode->hMsgQueue, ++ hNode->nodeEnv); ++ } ++ } ++ } ++ /* Phase II/Overlays: Create, execute, delete phases possibly in ++ * different files/sections. */ ++ if (hNode->fLoaded && hNode->fPhaseSplit) { ++ /* If create code was dynamically loaded, we can now unload ++ * it. */ ++ status1 = hNodeMgr->nldrFxns.pfnUnload(hNode->hNldrNode, ++ NLDR_CREATE); ++ hNode->fLoaded = false; ++ } ++ if (DSP_FAILED(status1)) { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_Create: Failed to unload " ++ "create code: 0x%x\n", status1); ++ } ++func_cont2: ++ /* Update node state and node manager state */ ++ if (DSP_SUCCEEDED(status)) { ++ NODE_SetState(hNode, NODE_CREATED); ++ hNodeMgr->uNumCreated++; ++ goto func_cont; ++ } ++ if (status != DSP_EWRONGSTATE) { ++ /* Put back in NODE_ALLOCATED state if error occurred */ ++ NODE_SetState(hNode, NODE_ALLOCATED); ++ } ++ if (procId == DSP_UNIT) { ++ /* If node create failed, see if should sleep DSP now */ ++ if (bJustWokeDSP == true) { ++ /* Check to see if partial create happened on DSP */ ++ if (hNode->nodeEnv == (u32)NULL) { ++ /* No environment allocated on DSP, re-sleep ++ * DSP now */ ++ PROC_Ctrl(hNode->hProcessor, WMDIOCTL_DEEPSLEEP, ++ &cbData); ++ } else { ++ /* Increment count, sleep later when node fully ++ * deleted */ ++ hNodeMgr->uNumCreated++; ++ } ++ } ++ } ++func_cont: ++ /* Free access to node dispatcher */ ++ (void)SYNC_LeaveCS(hNodeMgr->hSync); ++func_end: ++ if (DSP_SUCCEEDED(status)) { ++ PROC_NotifyClients(hNode->hProcessor, DSP_NODESTATECHANGE); ++ NTFY_Notify(hNode->hNtfy, DSP_NODESTATECHANGE); ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== NODE_CreateMgr ======== ++ * Purpose: ++ * Create a NODE Manager object. ++ */ ++DSP_STATUS NODE_CreateMgr(OUT struct NODE_MGR **phNodeMgr, ++ struct DEV_OBJECT *hDevObject) ++{ ++ u32 i; ++ struct NODE_MGR *pNodeMgr = NULL; ++ struct DISP_ATTRS dispAttrs; ++ char *szZLFile = ""; ++ struct NLDR_ATTRS nldrAttrs; ++ DSP_STATUS status = DSP_SOK; ++ u32 devType; ++ DBC_Require(cRefs > 0); ++ DBC_Require(phNodeMgr != NULL); ++ DBC_Require(hDevObject != NULL); ++ GT_2trace(NODE_debugMask, GT_ENTER, "NODE_CreateMgr: phNodeMgr: 0x%x\t" ++ "hDevObject: 0x%x\n", phNodeMgr, hDevObject); ++ *phNodeMgr = NULL; ++ /* Allocate Node manager object */ ++ MEM_AllocObject(pNodeMgr, struct NODE_MGR, NODEMGR_SIGNATURE); ++ if (pNodeMgr) { ++ pNodeMgr->hDevObject = hDevObject; ++ pNodeMgr->nodeList = LST_Create(); ++ pNodeMgr->pipeMap = GB_create(MAXPIPES); ++ pNodeMgr->pipeDoneMap = GB_create(MAXPIPES); ++ if (pNodeMgr->nodeList == NULL || pNodeMgr->pipeMap == NULL || ++ pNodeMgr->pipeDoneMap == NULL) { ++ status = DSP_EMEMORY; ++ GT_0trace(NODE_debugMask, GT_6CLASS, ++ "NODE_CreateMgr: Memory " ++ "allocation failed\n"); ++ } else { ++ status = NTFY_Create(&pNodeMgr->hNtfy); ++ } ++ pNodeMgr->uNumCreated = 0; ++ } else { ++ GT_0trace(NODE_debugMask, GT_6CLASS, ++ "NODE_CreateMgr: Memory allocation failed\n"); ++ status = DSP_EMEMORY; ++ } ++ /* get devNodeType */ ++ if (DSP_SUCCEEDED(status)) ++ status = DEV_GetDevType(hDevObject, &devType); ++ ++ /* Create the DCD Manager */ ++ if (DSP_SUCCEEDED(status)) { ++ status = DCD_CreateManager(szZLFile, &pNodeMgr->hDcdMgr); ++ if (DSP_SUCCEEDED(status)) ++ status = GetProcProps(pNodeMgr, hDevObject); ++ ++ } ++ /* Create NODE Dispatcher */ ++ if (DSP_SUCCEEDED(status)) { ++ dispAttrs.ulChnlOffset = pNodeMgr->ulChnlOffset; ++ dispAttrs.ulChnlBufSize = pNodeMgr->ulChnlBufSize; ++ dispAttrs.procFamily = pNodeMgr->procFamily; ++ dispAttrs.procType = pNodeMgr->procType; ++ status = DISP_Create(&pNodeMgr->hDisp, hDevObject, &dispAttrs); ++ } ++ /* Create a STRM Manager */ ++ if (DSP_SUCCEEDED(status)) ++ status = STRM_Create(&pNodeMgr->hStrmMgr, hDevObject); ++ ++ if (DSP_SUCCEEDED(status)) { ++ DEV_GetIntfFxns(hDevObject, &pNodeMgr->pIntfFxns); ++ /* Get MSG queue manager */ ++ DEV_GetMsgMgr(hDevObject, &pNodeMgr->hMsg); ++ status = SYNC_InitializeCS(&pNodeMgr->hSync); ++ if (DSP_FAILED(status)) ++ status = DSP_EMEMORY; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ pNodeMgr->chnlMap = GB_create(pNodeMgr->ulNumChnls); ++ /* dma chnl map. ulNumChnls is # per transport */ ++ pNodeMgr->dmaChnlMap = GB_create(pNodeMgr->ulNumChnls); ++ pNodeMgr->zChnlMap = GB_create(pNodeMgr->ulNumChnls); ++ if ((pNodeMgr->chnlMap == NULL) || ++ (pNodeMgr->dmaChnlMap == NULL) || ++ (pNodeMgr->zChnlMap == NULL)) { ++ status = DSP_EMEMORY; ++ } else { ++ /* Block out reserved channels */ ++ for (i = 0; i < pNodeMgr->ulChnlOffset; i++) ++ GB_set(pNodeMgr->chnlMap, i); ++ ++ /* Block out channels reserved for RMS */ ++ GB_set(pNodeMgr->chnlMap, pNodeMgr->ulChnlOffset); ++ GB_set(pNodeMgr->chnlMap, pNodeMgr->ulChnlOffset + 1); ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* NO RM Server on the IVA */ ++ if (devType != IVA_UNIT) { ++ /* Get addresses of any RMS functions loaded */ ++ status = GetRMSFxns(pNodeMgr); ++ if (DSP_FAILED(status)) { ++ GT_1trace(NODE_debugMask, GT_6CLASS, ++ "NODE_CreateMgr: Failed to" ++ " get RMS functions: status = 0x%x", status); ++ } ++ } ++ } ++ ++ /* Get loader functions and create loader */ ++ if (DSP_SUCCEEDED(status)) { ++ GT_0trace(NODE_debugMask, GT_1CLASS, ++ "NODE_CreateMgr: using dynamic loader\n"); ++ pNodeMgr->nldrFxns = nldrFxns; /* Dynamic loader functions */ ++ } ++ if (DSP_SUCCEEDED(status)) { ++ nldrAttrs.pfnOvly = Ovly; ++ nldrAttrs.pfnWrite = Write; ++ nldrAttrs.usDSPWordSize = pNodeMgr->uDSPWordSize; ++ nldrAttrs.usDSPMauSize = pNodeMgr->uDSPMauSize; ++ pNodeMgr->fLoaderInit = pNodeMgr->nldrFxns.pfnInit(); ++ status = pNodeMgr->nldrFxns.pfnCreate(&pNodeMgr->hNldr, ++ hDevObject, &nldrAttrs); ++ if (DSP_FAILED(status)) { ++ GT_1trace(NODE_debugMask, GT_6CLASS, ++ "NODE_CreateMgr: Failed to " ++ "create loader: status = 0x%x\n", status); ++ } ++ } ++ if (DSP_SUCCEEDED(status)) ++ *phNodeMgr = pNodeMgr; ++ else ++ DeleteNodeMgr(pNodeMgr); ++ ++ DBC_Ensure((DSP_FAILED(status) && (*phNodeMgr == NULL)) || ++ (DSP_SUCCEEDED(status) && ++ MEM_IsValidHandle((*phNodeMgr), NODEMGR_SIGNATURE))); ++ ++ return status; ++} ++ ++/* ++ * ======== NODE_Delete ======== ++ * Purpose: ++ * Delete a node on the DSP by remotely calling the node's delete function. ++ * Loads the node's delete function if necessary. Free GPP side resources ++ * after node's delete function returns. ++ */ ++DSP_STATUS NODE_Delete(struct NODE_OBJECT *hNode) ++{ ++ struct NODE_OBJECT *pNode = (struct NODE_OBJECT *)hNode; ++ struct NODE_MGR *hNodeMgr; ++ struct PROC_OBJECT *hProcessor; ++ struct DISP_OBJECT *hDisp; ++ u32 ulDeleteFxn; ++ enum NODE_TYPE nodeType; ++ enum NODE_STATE state; ++ DSP_STATUS status = DSP_SOK; ++ DSP_STATUS status1 = DSP_SOK; ++ struct DSP_CBDATA cbData; ++ u32 procId; ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ ++#ifndef RES_CLEANUP_DISABLE ++ u32 hProcess; ++ HANDLE nodeRes; ++ HANDLE hDrvObject; ++ struct PROCESS_CONTEXT *pCtxt = NULL; ++ DSP_STATUS res_status = DSP_SOK; ++#endif ++ struct DSP_PROCESSORSTATE procStatus; ++ DBC_Require(cRefs > 0); ++ GT_1trace(NODE_debugMask, GT_ENTER, "NODE_Delete: hNode: 0x%x\n", ++ hNode); ++ if (!MEM_IsValidHandle(hNode, NODE_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ goto func_end; ++ } ++ /* create struct DSP_CBDATA struct for PWR call */ ++ cbData.cbData = PWR_TIMEOUT; ++ hNodeMgr = hNode->hNodeMgr; ++ hProcessor = hNode->hProcessor; ++ hDisp = hNodeMgr->hDisp; ++ nodeType = NODE_GetType(hNode); ++ pIntfFxns = hNodeMgr->pIntfFxns; ++ /* Enter critical section */ ++ status = SYNC_EnterCS(hNodeMgr->hSync); ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ state = NODE_GetState(hNode); ++ /* Execute delete phase code for non-device node in all cases ++ * except when the node was only allocated. Delete phase must be ++ * executed even if create phase was executed, but failed. ++ * If the node environment pointer is non-NULL, the delete phase ++ * code must be executed. */ ++ if (!(state == NODE_ALLOCATED && hNode->nodeEnv == (u32)NULL) && ++ nodeType != NODE_DEVICE) { ++ status = PROC_GetProcessorId(pNode->hProcessor, &procId); ++ if (DSP_FAILED(status)) ++ goto func_cont1; ++ ++ if (procId == DSP_UNIT || procId == IVA_UNIT) { ++ /* If node has terminated, execute phase code will ++ * have already been unloaded in NODE_OnExit(). If the ++ * node is PAUSED, the execute phase is loaded, and it ++ * is now ok to unload it. If the node is running, we ++ * will unload the execute phase only after deleting ++ * the node. */ ++ if (state == NODE_PAUSED && hNode->fLoaded && ++ hNode->fPhaseSplit) { ++ /* Ok to unload execute code as long as node ++ * is not * running */ ++ status1 = hNodeMgr->nldrFxns.pfnUnload(hNode-> ++ hNldrNode, NLDR_EXECUTE); ++ hNode->fLoaded = false; ++ NODE_SetState(hNode, NODE_DONE); ++ } ++ /* Load delete phase code if not loaded or if haven't ++ * * unloaded EXECUTE phase */ ++ if ((!(hNode->fLoaded) || (state == NODE_RUNNING)) && ++ hNode->fPhaseSplit) { ++ status = hNodeMgr->nldrFxns.pfnLoad(hNode-> ++ hNldrNode, NLDR_DELETE); ++ if (DSP_SUCCEEDED(status)) { ++ hNode->fLoaded = true; ++ } else { ++ GT_1trace(NODE_debugMask, GT_ENTER, ++ "NODE_Delete: failed to " ++ "load delete code: 0x%x\n", ++ status); ++ } ++ } ++ } ++func_cont1: ++ if (DSP_SUCCEEDED(status)) { ++ /* Unblock a thread trying to terminate the node */ ++ (void)SYNC_SetEvent(hNode->hSyncDone); ++ if (procId == DSP_UNIT) { ++ /* ulDeleteFxn = address of node's delete ++ * function */ ++ status = GetFxnAddress(hNode, &ulDeleteFxn, ++ DELETEPHASE); ++ } else if (procId == IVA_UNIT) ++ ulDeleteFxn = (u32)hNode->nodeEnv; ++ if (DSP_SUCCEEDED(status)) { ++ status = PROC_GetState(hProcessor, &procStatus, ++ sizeof(struct DSP_PROCESSORSTATE)); ++ GT_1trace(NODE_debugMask, GT_4CLASS, ++ "NODE_Delete: proc Status " ++ "0x%x\n", procStatus.iState); ++ if (procStatus.iState != PROC_ERROR) { ++ status = DISP_NodeDelete(hDisp, hNode, ++ hNodeMgr->ulFxnAddrs[RMSDELETENODE], ++ ulDeleteFxn, hNode->nodeEnv); ++ } else ++ NODE_SetState(hNode, NODE_DONE); ++ ++ /* Unload execute, if not unloaded, and delete ++ * function */ ++ if (state == NODE_RUNNING && ++ hNode->fPhaseSplit) { ++ status1 = hNodeMgr->nldrFxns.pfnUnload( ++ hNode->hNldrNode, NLDR_EXECUTE); ++ } ++ if (DSP_FAILED(status1)) { ++ GT_1trace(NODE_debugMask, GT_ENTER, ++ "NODE_Delete: failed to" ++ "unload execute code: 0x%x\n", ++ status1); ++ } ++ status1 = hNodeMgr->nldrFxns.pfnUnload( ++ hNode->hNldrNode, NLDR_DELETE); ++ hNode->fLoaded = false; ++ if (DSP_FAILED(status1)) { ++ GT_1trace(NODE_debugMask, GT_ENTER, ++ "NODE_Delete: failed to" ++ "unload delete code: 0x%x\n", ++ status1); ++ } ++ } ++ } ++ } ++ /* Free host side resources even if a failure occurred */ ++ /* Remove node from hNodeMgr->nodeList */ ++ LST_RemoveElem(hNodeMgr->nodeList, (struct LST_ELEM *) hNode); ++ hNodeMgr->uNumNodes--; ++ /* Decrement count of nodes created on DSP */ ++ if ((state != NODE_ALLOCATED) || ((state == NODE_ALLOCATED) && ++ (hNode->nodeEnv != (u32) NULL))) ++ hNodeMgr->uNumCreated--; ++ /* Free host-side resources allocated by NODE_Create() ++ * DeleteNode() fails if SM buffers not freed by client! */ ++#ifndef RES_CLEANUP_DISABLE ++ /* Return PID instead of process handle */ ++ hProcess = current->pid; ++ res_status = CFG_GetObject((u32 *)&hDrvObject, REG_DRV_OBJECT); ++ if (DSP_FAILED(res_status)) ++ goto func_cont; ++ DRV_GetProcContext(0, (struct DRV_OBJECT *)hDrvObject, ++ &pCtxt, hNode, 0); ++ if (pCtxt == NULL) ++ goto func_cont; ++ if (DRV_GetNodeResElement(hNode, &nodeRes, pCtxt) != DSP_ENOTFOUND) { ++ GT_0trace(NODE_debugMask, GT_5CLASS, "\nNODE_Delete12:\n"); ++ DRV_ProcNodeUpdateStatus(nodeRes, false); ++ } ++#endif ++func_cont: ++ GT_0trace(NODE_debugMask, GT_ENTER, "\nNODE_Delete13:\n "); ++ DeleteNode(hNode); ++#ifndef RES_CLEANUP_DISABLE ++ GT_0trace(NODE_debugMask, GT_5CLASS, "\nNODE_Delete2:\n "); ++ if (pCtxt != NULL) ++ DRV_RemoveNodeResElement(nodeRes, (HANDLE)pCtxt); ++#endif ++ GT_0trace(NODE_debugMask, GT_ENTER, "\nNODE_Delete3:\n "); ++ /* Exit critical section */ ++ (void)SYNC_LeaveCS(hNodeMgr->hSync); ++ PROC_NotifyClients(hProcessor, DSP_NODESTATECHANGE); ++func_end: ++ return status; ++} ++ ++/* ++ * ======== NODE_DeleteMgr ======== ++ * Purpose: ++ * Delete the NODE Manager. ++ */ ++DSP_STATUS NODE_DeleteMgr(struct NODE_MGR *hNodeMgr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(hNodeMgr, NODEMGR_SIGNATURE)); ++ ++ GT_1trace(NODE_debugMask, GT_ENTER, "NODE_DeleteMgr: hNodeMgr: 0x%x\n", ++ hNodeMgr); ++ DeleteNodeMgr(hNodeMgr); ++ ++ return status; ++} ++ ++/* ++ * ======== NODE_EnumNodes ======== ++ * Purpose: ++ * Enumerate currently allocated nodes. ++ */ ++DSP_STATUS NODE_EnumNodes(struct NODE_MGR *hNodeMgr, IN DSP_HNODE *aNodeTab, ++ u32 uNodeTabSize, OUT u32 *puNumNodes, ++ OUT u32 *puAllocated) ++{ ++ struct NODE_OBJECT *hNode; ++ u32 i; ++ DSP_STATUS status = DSP_SOK; ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(hNodeMgr, NODEMGR_SIGNATURE)); ++ DBC_Require(aNodeTab != NULL || uNodeTabSize == 0); ++ DBC_Require(puNumNodes != NULL); ++ DBC_Require(puAllocated != NULL); ++ GT_5trace(NODE_debugMask, GT_ENTER, "NODE_EnumNodes: hNodeMgr: 0x%x\t" ++ "aNodeTab: %d\tuNodeTabSize: 0x%x\tpuNumNodes: 0x%x\t" ++ "puAllocated\n", hNodeMgr, aNodeTab, uNodeTabSize, puNumNodes, ++ puAllocated); ++ /* Enter critical section */ ++ status = SYNC_EnterCS(hNodeMgr->hSync); ++ if (DSP_SUCCEEDED(status)) { ++ if (hNodeMgr->uNumNodes > uNodeTabSize) { ++ *puAllocated = hNodeMgr->uNumNodes; ++ *puNumNodes = 0; ++ status = DSP_ESIZE; ++ } else { ++ hNode = (struct NODE_OBJECT *)LST_First(hNodeMgr-> ++ nodeList); ++ for (i = 0; i < hNodeMgr->uNumNodes; i++) { ++ DBC_Assert(MEM_IsValidHandle(hNode, ++ NODE_SIGNATURE)); ++ aNodeTab[i] = hNode; ++ hNode = (struct NODE_OBJECT *)LST_Next ++ (hNodeMgr->nodeList, ++ (struct LST_ELEM *)hNode); ++ } ++ *puAllocated = *puNumNodes = hNodeMgr->uNumNodes; ++ } ++ } ++ /* end of SYNC_EnterCS */ ++ /* Exit critical section */ ++ (void)SYNC_LeaveCS(hNodeMgr->hSync); ++ return status; ++} ++ ++/* ++ * ======== NODE_Exit ======== ++ * Purpose: ++ * Discontinue usage of NODE module. ++ */ ++void NODE_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ cRefs--; ++ ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "Entered NODE_Exit, ref count: 0x%x\n", cRefs); ++ ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== NODE_FreeMsgBuf ======== ++ * Purpose: ++ * Frees the message buffer. ++ */ ++DSP_STATUS NODE_FreeMsgBuf(struct NODE_OBJECT *hNode, IN u8 *pBuffer, ++ OPTIONAL struct DSP_BUFFERATTR *pAttr) ++{ ++ struct NODE_OBJECT *pNode = (struct NODE_OBJECT *)hNode; ++ DSP_STATUS status = DSP_SOK; ++ u32 procId; ++ DBC_Require(cRefs > 0); ++ DBC_Require(pBuffer != NULL); ++ DBC_Require(pNode != NULL); ++ DBC_Require(pNode->hXlator != NULL); ++ GT_3trace(NODE_debugMask, GT_ENTER, "NODE_FreeMsgBuf: hNode: 0x%x\t" ++ "pBuffer: 0x%x\tpAttr: 0x%x\n", hNode, pBuffer, pAttr); ++ if (!MEM_IsValidHandle(hNode, NODE_SIGNATURE)) ++ status = DSP_EHANDLE; ++ ++ status = PROC_GetProcessorId(pNode->hProcessor, &procId); ++ if (procId == DSP_UNIT) { ++ if (DSP_SUCCEEDED(status)) { ++ if (pAttr == NULL) { ++ /* set defaults */ ++ pAttr = &NODE_DFLTBUFATTRS; ++ } ++ /* Node supports single SM segment only */ ++ if (pAttr->uSegment != 1) ++ status = DSP_EBADSEGID; ++ ++ /* pBuffer is clients Va. */ ++ status = CMM_XlatorFreeBuf(pNode->hXlator, pBuffer); ++ if (DSP_FAILED(status)) ++ status = DSP_EFAIL; ++ else ++ status = DSP_SOK; ++ ++ } ++ } else { ++ DBC_Assert(NULL); /* BUG */ ++ } ++ return status; ++} ++ ++/* ++ * ======== NODE_GetAttr ======== ++ * Purpose: ++ * Copy the current attributes of the specified node into a DSP_NODEATTR ++ * structure. ++ */ ++DSP_STATUS NODE_GetAttr(struct NODE_OBJECT *hNode, ++ OUT struct DSP_NODEATTR *pAttr, u32 uAttrSize) ++{ ++ struct NODE_MGR *hNodeMgr; ++ DSP_STATUS status = DSP_SOK; ++ DBC_Require(cRefs > 0); ++ DBC_Require(pAttr != NULL); ++ DBC_Require(uAttrSize >= sizeof(struct DSP_NODEATTR)); ++ GT_3trace(NODE_debugMask, GT_ENTER, "NODE_GetAttr: hNode: " ++ "0x%x\tpAttr: 0x%x \tuAttrSize: 0x%x\n", hNode, pAttr, ++ uAttrSize); ++ if (!MEM_IsValidHandle(hNode, NODE_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ } else { ++ hNodeMgr = hNode->hNodeMgr; ++ /* Enter hNodeMgr critical section (since we're accessing ++ * data that could be changed by NODE_ChangePriority() and ++ * NODE_Connect(). */ ++ status = SYNC_EnterCS(hNodeMgr->hSync); ++ if (DSP_SUCCEEDED(status)) { ++ pAttr->cbStruct = sizeof(struct DSP_NODEATTR); ++ /* DSP_NODEATTRIN */ ++ pAttr->inNodeAttrIn.cbStruct = ++ sizeof(struct DSP_NODEATTRIN); ++ pAttr->inNodeAttrIn.iPriority = hNode->nPriority; ++ pAttr->inNodeAttrIn.uTimeout = hNode->uTimeout; ++ pAttr->inNodeAttrIn.uHeapSize = ++ hNode->createArgs.asa.taskArgs.uHeapSize; ++ pAttr->inNodeAttrIn.pGPPVirtAddr = (void *) ++ hNode->createArgs.asa.taskArgs.uGPPHeapAddr; ++ pAttr->uInputs = hNode->uNumGPPInputs; ++ pAttr->uOutputs = hNode->uNumGPPOutputs; ++ /* DSP_NODEINFO */ ++ GetNodeInfo(hNode, &(pAttr->iNodeInfo)); ++ } ++ /* end of SYNC_EnterCS */ ++ /* Exit critical section */ ++ (void)SYNC_LeaveCS(hNodeMgr->hSync); ++ } ++ return status; ++} ++ ++/* ++ * ======== NODE_GetChannelId ======== ++ * Purpose: ++ * Get the channel index reserved for a stream connection between the ++ * host and a node. ++ */ ++DSP_STATUS NODE_GetChannelId(struct NODE_OBJECT *hNode, u32 uDir, u32 uIndex, ++ OUT u32 *pulId) ++{ ++ enum NODE_TYPE nodeType; ++ DSP_STATUS status = DSP_EVALUE; ++ DBC_Require(cRefs > 0); ++ DBC_Require(uDir == DSP_TONODE || uDir == DSP_FROMNODE); ++ DBC_Require(pulId != NULL); ++ GT_4trace(NODE_debugMask, GT_ENTER, "NODE_GetChannelId: hNode: " ++ "0x%x\tuDir: %d\tuIndex: %d\tpulId: 0x%x\n", hNode, uDir, ++ uIndex, pulId); ++ if (!MEM_IsValidHandle(hNode, NODE_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ return status; ++ } ++ nodeType = NODE_GetType(hNode); ++ if (nodeType != NODE_TASK && nodeType != NODE_DAISSOCKET) { ++ status = DSP_ENODETYPE; ++ return status; ++ } ++ if (uDir == DSP_TONODE) { ++ if (uIndex < MaxInputs(hNode)) { ++ if (hNode->inputs[uIndex].type == HOSTCONNECT) { ++ *pulId = hNode->inputs[uIndex].devId; ++ status = DSP_SOK; ++ } ++ } ++ } else { ++ DBC_Assert(uDir == DSP_FROMNODE); ++ if (uIndex < MaxOutputs(hNode)) { ++ if (hNode->outputs[uIndex].type == HOSTCONNECT) { ++ *pulId = hNode->outputs[uIndex].devId; ++ status = DSP_SOK; ++ } ++ } ++ } ++ return status; ++} ++ ++/* ++ * ======== NODE_GetMessage ======== ++ * Purpose: ++ * Retrieve a message from a node on the DSP. ++ */ ++DSP_STATUS NODE_GetMessage(struct NODE_OBJECT *hNode, OUT struct DSP_MSG *pMsg, ++ u32 uTimeout) ++{ ++ struct NODE_MGR *hNodeMgr; ++ enum NODE_TYPE nodeType; ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ DSP_STATUS status = DSP_SOK; ++ void *pTmpBuf; ++ struct DSP_PROCESSORSTATE procStatus; ++ struct PROC_OBJECT *hProcessor; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pMsg != NULL); ++ GT_3trace(NODE_debugMask, GT_ENTER, ++ "NODE_GetMessage: hNode: 0x%x\tpMsg: " ++ "0x%x\tuTimeout: 0x%x\n", hNode, pMsg, uTimeout); ++ hProcessor = hNode->hProcessor; ++ status = PROC_GetState(hProcessor, &procStatus, ++ sizeof(struct DSP_PROCESSORSTATE)); ++ if (DSP_FAILED(status)) ++ goto func_end; ++ /* If processor is in error state then don't attempt to get the ++ message */ ++ if (procStatus.iState == PROC_ERROR) { ++ GT_1trace(NODE_debugMask, GT_4CLASS, "NODE_GetMessage:" ++ " proc Status 0x%x\n", procStatus.iState); ++ status = DSP_EFAIL; ++ goto func_end; ++ } ++ if (!MEM_IsValidHandle(hNode, NODE_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ goto func_end; ++ } ++ hNodeMgr = hNode->hNodeMgr; ++ nodeType = NODE_GetType(hNode); ++ if (nodeType != NODE_MESSAGE && nodeType != NODE_TASK && ++ nodeType != NODE_DAISSOCKET) { ++ status = DSP_ENODETYPE; ++ goto func_end; ++ } ++ /* This function will block unless a message is available. Since ++ * DSPNode_RegisterNotify() allows notification when a message ++ * is available, the system can be designed so that ++ * DSPNode_GetMessage() is only called when a message is ++ * available. */ ++ pIntfFxns = hNodeMgr->pIntfFxns; ++ status = (*pIntfFxns->pfnMsgGet)(hNode->hMsgQueue, pMsg, uTimeout); ++ /* Check if message contains SM descriptor */ ++ if (DSP_FAILED(status) || !(pMsg->dwCmd & DSP_RMSBUFDESC)) ++ goto func_end; ++ ++ /* Translate DSP byte addr to GPP Va. */ ++ pTmpBuf = CMM_XlatorTranslate(hNode->hXlator, ++ (void *)(pMsg->dwArg1 * hNode->hNodeMgr->uDSPWordSize), ++ CMM_DSPPA2PA); ++ if (pTmpBuf != NULL) { ++ /* now convert this GPP Pa to Va */ ++ pTmpBuf = CMM_XlatorTranslate(hNode->hXlator, pTmpBuf, ++ CMM_PA2VA); ++ if (pTmpBuf != NULL) { ++ /* Adjust SM size in msg */ ++ pMsg->dwArg1 = (u32) pTmpBuf; ++ pMsg->dwArg2 *= hNode->hNodeMgr->uDSPWordSize; ++ } else { ++ GT_0trace(NODE_debugMask, GT_7CLASS, "NODE_GetMessage: " ++ "Failed SM translation!\n"); ++ status = DSP_ETRANSLATE; ++ } ++ } else { ++ GT_0trace(NODE_debugMask, GT_7CLASS, "NODE_GetMessage: Failed " ++ "SM Pa/Pa translation!\n"); ++ status = DSP_ETRANSLATE; ++ } ++func_end: ++ return status; ++} ++ ++/* ++ * ======== NODE_GetNldrObj ======== ++ */ ++DSP_STATUS NODE_GetNldrObj(struct NODE_MGR *hNodeMgr, ++ struct NLDR_OBJECT **phNldrObj) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct NODE_MGR *pNodeMgr = hNodeMgr; ++ DBC_Require(phNldrObj != NULL); ++ GT_2trace(NODE_debugMask, GT_ENTER, ++ "Entered NODE_GetNldrObj, hNodeMgr: " ++ "0x%x\n\tphNldrObj: 0x%x\n", hNodeMgr, phNldrObj); ++ if (!MEM_IsValidHandle(hNodeMgr, NODEMGR_SIGNATURE)) ++ status = DSP_EHANDLE; ++ else ++ *phNldrObj = pNodeMgr->hNldr; ++ ++ GT_2trace(NODE_debugMask, GT_ENTER, ++ "Exit NODE_GetNldrObj: status 0x%x\n\t" ++ "phNldrObj: 0x%x\n", status, *phNldrObj); ++ DBC_Ensure(DSP_SUCCEEDED(status) || ((phNldrObj != NULL) && ++ (*phNldrObj == NULL))); ++ return status; ++} ++ ++/* ++ * ======== NODE_GetStrmMgr ======== ++ * Purpose: ++ * Returns the Stream manager. ++ */ ++DSP_STATUS NODE_GetStrmMgr(struct NODE_OBJECT *hNode, ++ struct STRM_MGR **phStrmMgr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ ++ if (!MEM_IsValidHandle(hNode, NODE_SIGNATURE)) ++ status = DSP_EHANDLE; ++ else ++ *phStrmMgr = hNode->hNodeMgr->hStrmMgr; ++ ++ return status; ++} ++ ++/* ++ * ======== NODE_GetLoadType ======== ++ */ ++enum NLDR_LOADTYPE NODE_GetLoadType(struct NODE_OBJECT *hNode) ++{ ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(hNode, NODE_SIGNATURE)); ++ if (!MEM_IsValidHandle(hNode, NODE_SIGNATURE)) { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_GetLoadType: Failed. hNode:" ++ " 0x%x\n", hNode); ++ return -1; ++ } else ++ return hNode->dcdProps.objData.nodeObj.usLoadType; ++} ++ ++/* ++ * ======== NODE_GetTimeout ======== ++ * Purpose: ++ * Returns the timeout value for this node. ++ */ ++u32 NODE_GetTimeout(struct NODE_OBJECT *hNode) ++{ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(hNode, NODE_SIGNATURE)); ++ if (!MEM_IsValidHandle(hNode, NODE_SIGNATURE)) { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_GetTimeout: Failed. hNode:" ++ " 0x%x\n", hNode); ++ return 0; ++ } else ++ return hNode->uTimeout; ++} ++ ++/* ++ * ======== NODE_GetType ======== ++ * Purpose: ++ * Returns the node type. ++ */ ++enum NODE_TYPE NODE_GetType(struct NODE_OBJECT *hNode) ++{ ++ enum NODE_TYPE nodeType; ++ ++ if (hNode == (struct NODE_OBJECT *) DSP_HGPPNODE) ++ nodeType = NODE_GPP; ++ else { ++ if (!MEM_IsValidHandle(hNode, NODE_SIGNATURE)) ++ nodeType = -1; ++ else ++ nodeType = hNode->nType; ++ } ++ return nodeType; ++} ++ ++/* ++ * ======== NODE_Init ======== ++ * Purpose: ++ * Initialize the NODE module. ++ */ ++bool NODE_Init(void) ++{ ++ bool fRetVal = true; ++ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++ DBC_Assert(!NODE_debugMask.flags); ++ GT_create(&NODE_debugMask, "NO"); /* "NO" for NOde */ ++ } ++ ++ if (fRetVal) ++ cRefs++; ++ ++ GT_1trace(NODE_debugMask, GT_5CLASS, "NODE_Init(), ref count: 0x%x\n", ++ cRefs); ++ ++ DBC_Ensure((fRetVal && (cRefs > 0)) || (!fRetVal && (cRefs >= 0))); ++ return fRetVal; ++} ++ ++/* ++ * ======== NODE_OnExit ======== ++ * Purpose: ++ * Gets called when RMS_EXIT is received for a node. ++ */ ++void NODE_OnExit(struct NODE_OBJECT *hNode, s32 nStatus) ++{ ++ DBC_Assert(MEM_IsValidHandle(hNode, NODE_SIGNATURE)); ++ /* Set node state to done */ ++ NODE_SetState(hNode, NODE_DONE); ++ hNode->nExitStatus = nStatus; ++ if (hNode->fLoaded && hNode->fPhaseSplit) { ++ (void)hNode->hNodeMgr->nldrFxns.pfnUnload(hNode->hNldrNode, ++ NLDR_EXECUTE); ++ hNode->fLoaded = false; ++ } ++ /* Unblock call to NODE_Terminate */ ++ (void) SYNC_SetEvent(hNode->hSyncDone); ++ /* Notify clients */ ++ PROC_NotifyClients(hNode->hProcessor, DSP_NODESTATECHANGE); ++ NTFY_Notify(hNode->hNtfy, DSP_NODESTATECHANGE); ++} ++ ++/* ++ * ======== NODE_Pause ======== ++ * Purpose: ++ * Suspend execution of a node currently running on the DSP. ++ */ ++DSP_STATUS NODE_Pause(struct NODE_OBJECT *hNode) ++{ ++ struct NODE_OBJECT *pNode = (struct NODE_OBJECT *)hNode; ++ enum NODE_TYPE nodeType; ++ enum NODE_STATE state; ++ struct NODE_MGR *hNodeMgr; ++ DSP_STATUS status = DSP_SOK; ++ u32 procId; ++ struct DSP_PROCESSORSTATE procStatus; ++ struct PROC_OBJECT *hProcessor; ++ ++ DBC_Require(cRefs > 0); ++ ++ GT_1trace(NODE_debugMask, GT_ENTER, "NODE_Pause: hNode: 0x%x\n", hNode); ++ ++ if (!MEM_IsValidHandle(hNode, NODE_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ } else { ++ nodeType = NODE_GetType(hNode); ++ if (nodeType != NODE_TASK && nodeType != NODE_DAISSOCKET) ++ status = DSP_ENODETYPE; ++ ++ } ++ ++ status = PROC_GetProcessorId(pNode->hProcessor, &procId); ++ ++ if (procId == IVA_UNIT) ++ status = DSP_ENOTIMPL; ++ ++ if (DSP_SUCCEEDED(status)) { ++ hNodeMgr = hNode->hNodeMgr; ++ ++ /* Enter critical section */ ++ status = SYNC_EnterCS(hNodeMgr->hSync); ++ ++ if (DSP_SUCCEEDED(status)) { ++ state = NODE_GetState(hNode); ++ /* Check node state */ ++ if (state != NODE_RUNNING) ++ status = DSP_EWRONGSTATE; ++ ++ hProcessor = hNode->hProcessor; ++ status = PROC_GetState(hProcessor, &procStatus, ++ sizeof(struct DSP_PROCESSORSTATE)); ++ if (DSP_FAILED(status)) ++ goto func_end; ++ /* If processor is in error state then don't attempt ++ to send the message */ ++ if (procStatus.iState == PROC_ERROR) { ++ GT_1trace(NODE_debugMask, GT_4CLASS, ++ "NODE_Pause: proc Status 0x%x\n", ++ procStatus.iState); ++ status = DSP_EFAIL; ++ goto func_end; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = DISP_NodeChangePriority(hNodeMgr-> ++ hDisp, hNode, ++ hNodeMgr->ulFxnAddrs[RMSCHANGENODEPRIORITY], ++ hNode->nodeEnv, NODE_SUSPENDEDPRI); ++ } ++ ++ /* Update state */ ++ if (DSP_SUCCEEDED(status)) { ++ NODE_SetState(hNode, NODE_PAUSED); ++ } else { ++ GT_1trace(NODE_debugMask, GT_6CLASS, ++ "NODE_Pause: Failed. hNode:" ++ " 0x%x\n", hNode); ++ } ++ } ++ /* End of SYNC_EnterCS */ ++ /* Leave critical section */ ++ (void)SYNC_LeaveCS(hNodeMgr->hSync); ++ if (DSP_SUCCEEDED(status)) { ++ PROC_NotifyClients(hNode->hProcessor, ++ DSP_NODESTATECHANGE); ++ NTFY_Notify(hNode->hNtfy, DSP_NODESTATECHANGE); ++ } ++ } ++func_end: ++ return status; ++} ++ ++/* ++ * ======== NODE_PutMessage ======== ++ * Purpose: ++ * Send a message to a message node, task node, or XDAIS socket node. This ++ * function will block until the message stream can accommodate the ++ * message, or a timeout occurs. ++ */ ++DSP_STATUS NODE_PutMessage(struct NODE_OBJECT *hNode, ++ IN CONST struct DSP_MSG *pMsg, u32 uTimeout) ++{ ++ struct NODE_MGR *hNodeMgr = NULL; ++ enum NODE_TYPE nodeType; ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ enum NODE_STATE state; ++ DSP_STATUS status = DSP_SOK; ++ void *pTmpBuf; ++ struct DSP_MSG newMsg; ++ struct DSP_PROCESSORSTATE procStatus; ++ struct PROC_OBJECT *hProcessor; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pMsg != NULL); ++ GT_3trace(NODE_debugMask, GT_ENTER, ++ "NODE_PutMessage: hNode: 0x%x\tpMsg: " ++ "0x%x\tuTimeout: 0x%x\n", hNode, pMsg, uTimeout); ++ hProcessor = hNode->hProcessor; ++ status = PROC_GetState(hProcessor, &procStatus, ++ sizeof(struct DSP_PROCESSORSTATE)); ++ if (DSP_FAILED(status)) ++ goto func_end; ++ /* If processor is in bad state then don't attempt sending the ++ message */ ++ if (procStatus.iState == PROC_ERROR) { ++ GT_1trace(NODE_debugMask, GT_4CLASS, "NODE_PutMessage:" ++ " proc Status 0x%x\n", procStatus.iState); ++ status = DSP_EFAIL; ++ goto func_end; ++ } ++ if (!MEM_IsValidHandle(hNode, NODE_SIGNATURE)) ++ status = DSP_EHANDLE; ++ else { ++ hNodeMgr = hNode->hNodeMgr; ++ nodeType = NODE_GetType(hNode); ++ if (nodeType != NODE_MESSAGE && nodeType != NODE_TASK && ++ nodeType != NODE_DAISSOCKET) ++ status = DSP_ENODETYPE; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Check node state. Can't send messages to a node after ++ * we've sent the RMS_EXIT command. There is still the ++ * possibility that NODE_Terminate can be called after we've ++ * checked the state. Could add another SYNC object to ++ * prevent this (can't use hNodeMgr->hSync, since we don't ++ * want to block other NODE functions). However, the node may ++ * still exit on its own, before this message is sent. */ ++ status = SYNC_EnterCS(hNodeMgr->hSync); ++ if (DSP_SUCCEEDED(status)) { ++ state = NODE_GetState(hNode); ++ if (state == NODE_TERMINATING || state == NODE_DONE) ++ status = DSP_EWRONGSTATE; ++ ++ } ++ /* end of SYNC_EnterCS */ ++ (void)SYNC_LeaveCS(hNodeMgr->hSync); ++ } ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ /* assign pMsg values to new msg */ ++ newMsg = *pMsg; ++ /* Now, check if message contains a SM buffer descriptor */ ++ if (pMsg->dwCmd & DSP_RMSBUFDESC) { ++ /* Translate GPP Va to DSP physical buf Ptr. */ ++ pTmpBuf = CMM_XlatorTranslate(hNode->hXlator, ++ (void *)newMsg.dwArg1, CMM_VA2DSPPA); ++ if (pTmpBuf != NULL) { ++ /* got translation, convert to MAUs in msg */ ++ if (hNode->hNodeMgr->uDSPWordSize != 0) { ++ newMsg.dwArg1 = ++ (u32)pTmpBuf / ++ hNode->hNodeMgr->uDSPWordSize; ++ /* MAUs */ ++ newMsg.dwArg2 /= hNode->hNodeMgr->uDSPWordSize; ++ } else { ++ GT_0trace(NODE_debugMask, GT_7CLASS, ++ "NODE_PutMessage: " ++ "uDSPWordSize is zero!\n"); ++ status = DSP_EFAIL; /* bad DSPWordSize */ ++ } ++ } else { /* failed to translate buffer address */ ++ GT_0trace(NODE_debugMask, GT_7CLASS, ++ "NODE_PutMessage: Failed to" ++ " translate SM address\n"); ++ status = DSP_ETRANSLATE; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ pIntfFxns = hNodeMgr->pIntfFxns; ++ status = (*pIntfFxns->pfnMsgPut)(hNode->hMsgQueue, ++ &newMsg, uTimeout); ++ } ++func_end: ++ return status; ++} ++ ++/* ++ * ======== NODE_RegisterNotify ======== ++ * Purpose: ++ * Register to be notified on specific events for this node. ++ */ ++DSP_STATUS NODE_RegisterNotify(struct NODE_OBJECT *hNode, u32 uEventMask, ++ u32 uNotifyType, ++ struct DSP_NOTIFICATION *hNotification) ++{ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(hNotification != NULL); ++ ++ GT_4trace(NODE_debugMask, GT_ENTER, ++ "NODE_RegisterNotify: hNode: 0x%x\t" ++ "uEventMask: 0x%x\tuNotifyType: 0x%x\thNotification: 0x%x\n", ++ hNode, uEventMask, uNotifyType, hNotification); ++ ++ if (!MEM_IsValidHandle(hNode, NODE_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ } else { ++ /* Check if event mask is a valid node related event */ ++ if (uEventMask & ~(DSP_NODESTATECHANGE | ++ DSP_NODEMESSAGEREADY)) ++ status = DSP_EVALUE; ++ ++ /* Check if notify type is valid */ ++ if (uNotifyType != DSP_SIGNALEVENT) ++ status = DSP_EVALUE; ++ ++ /* Only one Notification can be registered at a ++ * time - Limitation */ ++ if (uEventMask == (DSP_NODESTATECHANGE | ++ DSP_NODEMESSAGEREADY)) ++ status = DSP_EVALUE; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ if (uEventMask == DSP_NODESTATECHANGE) { ++ status = NTFY_Register(hNode->hNtfy, hNotification, ++ uEventMask & DSP_NODESTATECHANGE, uNotifyType); ++ } else { ++ /* Send Message part of event mask to MSG */ ++ pIntfFxns = hNode->hNodeMgr->pIntfFxns; ++ status = (*pIntfFxns->pfnMsgRegisterNotify) ++ (hNode->hMsgQueue, ++ uEventMask & DSP_NODEMESSAGEREADY, uNotifyType, ++ hNotification); ++ } ++ ++ } ++ return status; ++} ++ ++/* ++ * ======== NODE_Run ======== ++ * Purpose: ++ * Start execution of a node's execute phase, or resume execution of a node ++ * that has been suspended (via NODE_NodePause()) on the DSP. Load the ++ * node's execute function if necessary. ++ */ ++DSP_STATUS NODE_Run(struct NODE_OBJECT *hNode) ++{ ++ struct NODE_OBJECT *pNode = (struct NODE_OBJECT *)hNode; ++ struct NODE_MGR *hNodeMgr; ++ enum NODE_TYPE nodeType; ++ enum NODE_STATE state; ++ u32 ulExecuteFxn; ++ u32 ulFxnAddr; ++ DSP_STATUS status = DSP_SOK; ++ u32 procId; ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ struct DSP_PROCESSORSTATE procStatus; ++ struct PROC_OBJECT *hProcessor; ++ ++ DBC_Require(cRefs > 0); ++ GT_1trace(NODE_debugMask, GT_ENTER, "NODE_Run: hNode: 0x%x\n", hNode); ++ hProcessor = hNode->hProcessor; ++ status = PROC_GetState(hProcessor, &procStatus, ++ sizeof(struct DSP_PROCESSORSTATE)); ++ if (DSP_FAILED(status)) ++ goto func_end; ++ /* If processor is in error state then don't attempt to run the node */ ++ if (procStatus.iState == PROC_ERROR) { ++ GT_1trace(NODE_debugMask, GT_4CLASS, "NODE_Run:" ++ " proc Status 0x%x\n", procStatus.iState); ++ status = DSP_EFAIL; ++ goto func_end; ++ } ++ if (!MEM_IsValidHandle(hNode, NODE_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ } else { ++ nodeType = NODE_GetType(hNode); ++ if (nodeType == NODE_DEVICE) ++ status = DSP_ENODETYPE; ++ } ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ hNodeMgr = hNode->hNodeMgr; ++ pIntfFxns = hNodeMgr->pIntfFxns; ++ /* Enter critical section */ ++ status = SYNC_EnterCS(hNodeMgr->hSync); ++ if (DSP_FAILED(status)) ++ goto func_cont; ++ ++ state = NODE_GetState(hNode); ++ if (state != NODE_CREATED && state != NODE_PAUSED) ++ status = DSP_EWRONGSTATE; ++ ++ if (DSP_SUCCEEDED(status)) ++ status = PROC_GetProcessorId(pNode->hProcessor, &procId); ++ ++ if (DSP_FAILED(status)) ++ goto func_cont1; ++ ++ if ((procId != DSP_UNIT) && (procId != IVA_UNIT)) ++ goto func_cont1; ++ ++ if (state == NODE_CREATED) { ++ /* If node's execute function is not loaded, load it */ ++ if (!(hNode->fLoaded) && hNode->fPhaseSplit) { ++ status = hNodeMgr->nldrFxns.pfnLoad(hNode->hNldrNode, ++ NLDR_EXECUTE); ++ if (DSP_SUCCEEDED(status)) { ++ hNode->fLoaded = true; ++ } else { ++ GT_1trace(NODE_debugMask, GT_ENTER, ++ "NODE_Run: failed to load " ++ "execute code:0x%x\n", status); ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Get address of node's execute function */ ++ if (procId == IVA_UNIT) ++ ulExecuteFxn = (u32) hNode->nodeEnv; ++ else { ++ status = GetFxnAddress(hNode, &ulExecuteFxn, ++ EXECUTEPHASE); ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ ulFxnAddr = hNodeMgr->ulFxnAddrs[RMSEXECUTENODE]; ++ status = DISP_NodeRun(hNodeMgr->hDisp, hNode, ulFxnAddr, ++ ulExecuteFxn, hNode->nodeEnv); ++ } ++ } else if (state == NODE_PAUSED) { ++ ulFxnAddr = hNodeMgr->ulFxnAddrs[RMSCHANGENODEPRIORITY]; ++ status = DISP_NodeChangePriority(hNodeMgr->hDisp, hNode, ++ ulFxnAddr, hNode->nodeEnv, ++ NODE_GetPriority(hNode)); ++ } else { ++ /* We should never get here */ ++ DBC_Assert(false); ++ } ++func_cont1: ++ /* Update node state. */ ++ if (DSP_SUCCEEDED(status)) ++ NODE_SetState(hNode, NODE_RUNNING); ++ else /* Set state back to previous value */ ++ NODE_SetState(hNode, state); ++ /*End of SYNC_EnterCS */ ++ /* Exit critical section */ ++func_cont: ++ (void)SYNC_LeaveCS(hNodeMgr->hSync); ++ if (DSP_SUCCEEDED(status)) { ++ PROC_NotifyClients(hNode->hProcessor, ++ DSP_NODESTATECHANGE); ++ NTFY_Notify(hNode->hNtfy, DSP_NODESTATECHANGE); ++ } ++func_end: ++ return status; ++} ++ ++/* ++ * ======== NODE_Terminate ======== ++ * Purpose: ++ * Signal a node running on the DSP that it should exit its execute phase ++ * function. ++ */ ++DSP_STATUS NODE_Terminate(struct NODE_OBJECT *hNode, OUT DSP_STATUS *pStatus) ++{ ++ struct NODE_OBJECT *pNode = (struct NODE_OBJECT *)hNode; ++ struct NODE_MGR *hNodeMgr = NULL; ++ enum NODE_TYPE nodeType; ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ enum NODE_STATE state; ++ struct DSP_MSG msg, killmsg; ++ DSP_STATUS status = DSP_SOK; ++ u32 procId, killTimeOut; ++ struct DEH_MGR *hDehMgr; ++ struct DSP_PROCESSORSTATE procStatus; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pStatus != NULL); ++ ++ GT_1trace(NODE_debugMask, GT_ENTER, ++ "NODE_Terminate: hNode: 0x%x\n", hNode); ++ ++ if (pNode->hProcessor == NULL) { ++ GT_1trace(NODE_debugMask, GT_4CLASS, ++ "NODE_Terminate: pNode->hProcessor = 0x%x\n", ++ pNode->hProcessor); ++ goto func_end; ++ } ++ status = PROC_GetProcessorId(pNode->hProcessor, &procId); ++ ++ if (DSP_SUCCEEDED(status)) { ++ hNodeMgr = hNode->hNodeMgr; ++ ++ if (!MEM_IsValidHandle(hNode, NODE_SIGNATURE)) ++ status = DSP_EHANDLE; ++ else { ++ nodeType = NODE_GetType(hNode); ++ if (nodeType != NODE_TASK && nodeType != ++ NODE_DAISSOCKET) ++ status = DSP_ENODETYPE; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Check node state */ ++ status = SYNC_EnterCS(hNodeMgr->hSync); ++ if (DSP_SUCCEEDED(status)) { ++ state = NODE_GetState(hNode); ++ if (state != NODE_RUNNING) { ++ status = DSP_EWRONGSTATE; ++ /* Set the exit status if node terminated on ++ * its own. */ ++ if (state == NODE_DONE) ++ *pStatus = hNode->nExitStatus; ++ ++ } else { ++ NODE_SetState(hNode, NODE_TERMINATING); ++ } ++ } ++ /* end of SYNC_EnterCS */ ++ (void)SYNC_LeaveCS(hNodeMgr->hSync); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* ++ * Send exit message. Do not change state to NODE_DONE ++ * here. That will be done in callback. ++ */ ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_Terminate: env = 0x%x\n", hNode->nodeEnv); ++ ++ status = PROC_GetState(pNode->hProcessor, &procStatus, ++ sizeof(struct DSP_PROCESSORSTATE)); ++ if (DSP_FAILED(status)) ++ goto func_cont; ++ /* If processor is in error state then don't attempt to send ++ * A kill task command */ ++ if (procStatus.iState == PROC_ERROR) { ++ GT_1trace(NODE_debugMask, GT_4CLASS, "NODE_Terminate:" ++ " proc Status 0x%x\n", procStatus.iState); ++ status = DSP_EFAIL; ++ goto func_cont; ++ } ++ ++ msg.dwCmd = RMS_EXIT; ++ msg.dwArg1 = hNode->nodeEnv; ++ killmsg.dwCmd = RMS_KILLTASK; ++ killmsg.dwArg1 = hNode->nodeEnv; ++ pIntfFxns = hNodeMgr->pIntfFxns; ++ ++ if (hNode->uTimeout > MAXTIMEOUT) ++ killTimeOut = MAXTIMEOUT; ++ else ++ killTimeOut = (hNode->uTimeout)*2; ++ ++ status = (*pIntfFxns->pfnMsgPut)(hNode->hMsgQueue, &msg, ++ hNode->uTimeout); ++ if (DSP_SUCCEEDED(status)) { ++ /* Wait on synchronization object that will be ++ * posted in the callback on receiving RMS_EXIT ++ * message, or by NODE_Delete. Check for valid hNode, ++ * in case posted by NODE_Delete(). */ ++ status = SYNC_WaitOnEvent(hNode->hSyncDone, ++ killTimeOut/2); ++ if (DSP_FAILED(status)) { ++ if (status == DSP_ETIMEOUT) { ++ status = (*pIntfFxns->pfnMsgPut) ++ (hNode->hMsgQueue, &killmsg, ++ hNode->uTimeout); ++ if (DSP_SUCCEEDED(status)) { ++ status = SYNC_WaitOnEvent ++ (hNode->hSyncDone, ++ killTimeOut/2); ++ if (DSP_FAILED(status)) { ++ /* Here it goes the part ++ * of the simulation of ++ * the DSP exception */ ++ DEV_GetDehMgr(hNodeMgr-> ++ hDevObject, &hDehMgr); ++ if (hDehMgr) { ++ (*pIntfFxns-> ++ pfnDehNotify)(hDehMgr, ++ DSP_SYSERROR, ++ DSP_EXCEPTIONABORT); ++ status = DSP_EFAIL; ++ } ++ } else ++ status = DSP_SOK; ++ } ++ } else ++ status = DSP_EFAIL; ++ } else /* Convert SYNC status to DSP status */ ++ status = DSP_SOK; ++ } ++ } ++func_cont: ++ if (DSP_SUCCEEDED(status)) { ++ /* Enter CS before getting exit status, in case node was ++ * deleted. */ ++ status = SYNC_EnterCS(hNodeMgr->hSync); ++ /* Make sure node wasn't deleted while we blocked */ ++ if (!MEM_IsValidHandle(hNode, NODE_SIGNATURE)) { ++ status = DSP_EFAIL; ++ } else { ++ *pStatus = hNode->nExitStatus; ++ GT_1trace(NODE_debugMask, GT_ENTER, ++ "NODE_Terminate: env = 0x%x " ++ "succeeded.\n", hNode->nodeEnv); ++ } ++ (void)SYNC_LeaveCS(hNodeMgr->hSync); ++ } /*End of SYNC_EnterCS */ ++func_end: ++ return status; ++} ++ ++/* ++ * ======== DeleteNode ======== ++ * Purpose: ++ * Free GPP resources allocated in NODE_Allocate() or NODE_Connect(). ++ */ ++static void DeleteNode(struct NODE_OBJECT *hNode) ++{ ++ struct NODE_MGR *hNodeMgr; ++ struct CMM_XLATOROBJECT *hXlator; ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ u32 i; ++ enum NODE_TYPE nodeType; ++ struct STREAM stream; ++ struct NODE_MSGARGS msgArgs; ++ struct NODE_TASKARGS taskArgs; ++#ifdef DSP_DMM_DEBUG ++ struct DMM_OBJECT *hDmmMgr; ++ struct PROC_OBJECT *pProcObject = ++ (struct PROC_OBJECT *)hNode->hProcessor; ++#endif ++ DSP_STATUS status; ++ DBC_Require(MEM_IsValidHandle(hNode, NODE_SIGNATURE)); ++ hNodeMgr = hNode->hNodeMgr; ++ if (!MEM_IsValidHandle(hNodeMgr, NODEMGR_SIGNATURE)) ++ return; ++ hXlator = hNode->hXlator; ++ nodeType = NODE_GetType(hNode); ++ if (nodeType != NODE_DEVICE) { ++ msgArgs = hNode->createArgs.asa.msgArgs; ++ if (msgArgs.pData) ++ MEM_Free(msgArgs.pData); ++ ++ /* Free MSG queue */ ++ if (hNode->hMsgQueue) { ++ pIntfFxns = hNodeMgr->pIntfFxns; ++ (*pIntfFxns->pfnMsgDeleteQueue) (hNode->hMsgQueue); ++ hNode->hMsgQueue = NULL; ++ ++ } ++ if (hNode->hSyncDone) ++ (void) SYNC_CloseEvent(hNode->hSyncDone); ++ ++ /* Free all stream info */ ++ if (hNode->inputs) { ++ for (i = 0; i < MaxInputs(hNode); i++) { ++ stream = hNode->inputs[i]; ++ FreeStream(hNodeMgr, stream); ++ } ++ MEM_Free(hNode->inputs); ++ hNode->inputs = NULL; ++ } ++ if (hNode->outputs) { ++ for (i = 0; i < MaxOutputs(hNode); i++) { ++ stream = hNode->outputs[i]; ++ FreeStream(hNodeMgr, stream); ++ } ++ MEM_Free(hNode->outputs); ++ hNode->outputs = NULL; ++ } ++ taskArgs = hNode->createArgs.asa.taskArgs; ++ if (taskArgs.strmInDef) { ++ for (i = 0; i < MaxInputs(hNode); i++) { ++ if (taskArgs.strmInDef[i].szDevice) { ++ MEM_Free(taskArgs.strmInDef[i]. ++ szDevice); ++ taskArgs.strmInDef[i].szDevice = NULL; ++ } ++ } ++ MEM_Free(taskArgs.strmInDef); ++ taskArgs.strmInDef = NULL; ++ } ++ if (taskArgs.strmOutDef) { ++ for (i = 0; i < MaxOutputs(hNode); i++) { ++ if (taskArgs.strmOutDef[i].szDevice) { ++ MEM_Free(taskArgs.strmOutDef[i]. ++ szDevice); ++ taskArgs.strmOutDef[i].szDevice = NULL; ++ } ++ } ++ MEM_Free(taskArgs.strmOutDef); ++ taskArgs.strmOutDef = NULL; ++ } ++ if (taskArgs.uDSPHeapResAddr) { ++ status = PROC_UnMap(hNode->hProcessor, ++ (void *)taskArgs.uDSPHeapAddr); ++ if (DSP_SUCCEEDED(status)) { ++ GT_0trace(NODE_debugMask, GT_5CLASS, ++ "DSPProcessor_UnMap succeeded.\n"); ++ } else { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "DSPProcessor_UnMap failed." ++ " Status = 0x%x\n", (u32)status); ++ } ++ status = PROC_UnReserveMemory(hNode->hProcessor, ++ (void *)taskArgs.uDSPHeapResAddr); ++ if (DSP_SUCCEEDED(status)) { ++ GT_0trace(NODE_debugMask, GT_5CLASS, ++ "DSPProcessor_UnReserveMemory " ++ "succeeded.\n"); ++ } else { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "DSPProcessor_UnReserveMemory " ++ "failed. Status = 0x%x\n", ++ (u32)status); ++ } ++#ifdef DSP_DMM_DEBUG ++ status = DMM_GetHandle(pProcObject, &hDmmMgr); ++ if (DSP_SUCCEEDED(status)) ++ DMM_MemMapDump(hDmmMgr); ++#endif ++ } ++ } ++ if (nodeType != NODE_MESSAGE) { ++ if (hNode->streamConnect) { ++ MEM_Free(hNode->streamConnect); ++ hNode->streamConnect = NULL; ++ } ++ } ++ if (hNode->pstrDevName) { ++ MEM_Free(hNode->pstrDevName); ++ hNode->pstrDevName = NULL; ++ } ++ ++ if (hNode->hNtfy) { ++ NTFY_Delete(hNode->hNtfy); ++ hNode->hNtfy = NULL; ++ } ++ ++ /* These were allocated in DCD_GetObjectDef (via NODE_Allocate) */ ++ if (hNode->dcdProps.objData.nodeObj.pstrCreatePhaseFxn) { ++ MEM_Free(hNode->dcdProps.objData.nodeObj.pstrCreatePhaseFxn); ++ hNode->dcdProps.objData.nodeObj.pstrCreatePhaseFxn = NULL; ++ } ++ ++ if (hNode->dcdProps.objData.nodeObj.pstrExecutePhaseFxn) { ++ MEM_Free(hNode->dcdProps.objData.nodeObj.pstrExecutePhaseFxn); ++ hNode->dcdProps.objData.nodeObj.pstrExecutePhaseFxn = NULL; ++ } ++ ++ if (hNode->dcdProps.objData.nodeObj.pstrDeletePhaseFxn) { ++ MEM_Free(hNode->dcdProps.objData.nodeObj.pstrDeletePhaseFxn); ++ hNode->dcdProps.objData.nodeObj.pstrDeletePhaseFxn = NULL; ++ } ++ ++ if (hNode->dcdProps.objData.nodeObj.pstrIAlgName) { ++ MEM_Free(hNode->dcdProps.objData.nodeObj.pstrIAlgName); ++ hNode->dcdProps.objData.nodeObj.pstrIAlgName = NULL; ++ } ++ ++ /* Free all SM address translator resources */ ++ if (hXlator) { ++ (void) CMM_XlatorDelete(hXlator, TRUE); /* force free */ ++ hXlator = NULL; ++ } ++ ++ if (hNode->hNldrNode) { ++ hNodeMgr->nldrFxns.pfnFree(hNode->hNldrNode); ++ hNode->hNldrNode = NULL; ++ } ++ ++ MEM_FreeObject(hNode); ++ hNode = NULL; ++} ++ ++/* ++ * ======== DeleteNodeMgr ======== ++ * Purpose: ++ * Frees the node manager. ++ */ ++static void DeleteNodeMgr(struct NODE_MGR *hNodeMgr) ++{ ++ struct NODE_OBJECT *hNode; ++ ++ if (MEM_IsValidHandle(hNodeMgr, NODEMGR_SIGNATURE)) { ++ /* Free resources */ ++ if (hNodeMgr->hDcdMgr) ++ DCD_DestroyManager(hNodeMgr->hDcdMgr); ++ ++ /* Remove any elements remaining in lists */ ++ if (hNodeMgr->nodeList) { ++ while ((hNode = ++ (struct NODE_OBJECT *)LST_GetHead(hNodeMgr-> ++ nodeList))) ++ DeleteNode(hNode); ++ ++ DBC_Assert(LST_IsEmpty(hNodeMgr->nodeList)); ++ LST_Delete(hNodeMgr->nodeList); ++ } ++ if (hNodeMgr->hNtfy) ++ NTFY_Delete(hNodeMgr->hNtfy); ++ ++ if (hNodeMgr->pipeMap) ++ GB_delete(hNodeMgr->pipeMap); ++ ++ if (hNodeMgr->pipeDoneMap) ++ GB_delete(hNodeMgr->pipeDoneMap); ++ ++ if (hNodeMgr->chnlMap) ++ GB_delete(hNodeMgr->chnlMap); ++ ++ if (hNodeMgr->dmaChnlMap) ++ GB_delete(hNodeMgr->dmaChnlMap); ++ ++ if (hNodeMgr->zChnlMap) ++ GB_delete(hNodeMgr->zChnlMap); ++ ++ if (hNodeMgr->hDisp) ++ DISP_Delete(hNodeMgr->hDisp); ++ ++ if (hNodeMgr->hSync) ++ SYNC_DeleteCS(hNodeMgr->hSync); ++ ++ if (hNodeMgr->hStrmMgr) ++ STRM_Delete(hNodeMgr->hStrmMgr); ++ ++ /* Delete the loader */ ++ if (hNodeMgr->hNldr) ++ hNodeMgr->nldrFxns.pfnDelete(hNodeMgr->hNldr); ++ ++ if (hNodeMgr->fLoaderInit) ++ hNodeMgr->nldrFxns.pfnExit(); ++ ++ MEM_FreeObject(hNodeMgr); ++ } ++} ++ ++/* ++ * ======== FillStreamConnect ======== ++ * Purpose: ++ * Fills stream information. ++ */ ++static void FillStreamConnect(struct NODE_OBJECT *hNode1, ++ struct NODE_OBJECT *hNode2, ++ u32 uStream1, u32 uStream2) ++{ ++ u32 uStrmIndex; ++ struct DSP_STREAMCONNECT *pStrm1 = NULL; ++ struct DSP_STREAMCONNECT *pStrm2 = NULL; ++ enum NODE_TYPE node1Type = NODE_TASK; ++ enum NODE_TYPE node2Type = NODE_TASK; ++ ++ node1Type = NODE_GetType(hNode1); ++ node2Type = NODE_GetType(hNode2); ++ if (hNode1 != (struct NODE_OBJECT *)DSP_HGPPNODE) { ++ ++ if (node1Type != NODE_DEVICE) { ++ uStrmIndex = hNode1->uNumInputs + ++ hNode1->uNumOutputs - 1; ++ pStrm1 = &(hNode1->streamConnect[uStrmIndex]); ++ pStrm1->cbStruct = sizeof(struct DSP_STREAMCONNECT); ++ pStrm1->uThisNodeStreamIndex = uStream1; ++ } ++ ++ if (hNode2 != (struct NODE_OBJECT *)DSP_HGPPNODE) { ++ /* NODE == > NODE */ ++ if (node1Type != NODE_DEVICE) { ++ pStrm1->hConnectedNode = hNode2; ++ pStrm1->uiConnectedNodeID = hNode2->nodeId; ++ pStrm1->uConnectedNodeStreamIndex = uStream2; ++ pStrm1->lType = CONNECTTYPE_NODEOUTPUT; ++ } ++ if (node2Type != NODE_DEVICE) { ++ uStrmIndex = hNode2->uNumInputs + ++ hNode2->uNumOutputs - 1; ++ pStrm2 = &(hNode2->streamConnect[uStrmIndex]); ++ pStrm2->cbStruct = ++ sizeof(struct DSP_STREAMCONNECT); ++ pStrm2->uThisNodeStreamIndex = uStream2; ++ pStrm2->hConnectedNode = hNode1; ++ pStrm2->uiConnectedNodeID = hNode1->nodeId; ++ pStrm2->uConnectedNodeStreamIndex = uStream1; ++ pStrm2->lType = CONNECTTYPE_NODEINPUT; ++ } ++ } else if (node1Type != NODE_DEVICE) ++ pStrm1->lType = CONNECTTYPE_GPPOUTPUT; ++ } else { ++ /* GPP == > NODE */ ++ DBC_Assert(hNode2 != (struct NODE_OBJECT *)DSP_HGPPNODE); ++ uStrmIndex = hNode2->uNumInputs + hNode2->uNumOutputs - 1; ++ pStrm2 = &(hNode2->streamConnect[uStrmIndex]); ++ pStrm2->cbStruct = sizeof(struct DSP_STREAMCONNECT); ++ pStrm2->uThisNodeStreamIndex = uStream2; ++ pStrm2->lType = CONNECTTYPE_GPPINPUT; ++ } ++} ++ ++/* ++ * ======== FillStreamDef ======== ++ * Purpose: ++ * Fills Stream attributes. ++ */ ++static void FillStreamDef(struct NODE_OBJECT *hNode, ++ struct NODE_STRMDEF *pstrmDef, ++ struct DSP_STRMATTR *pAttrs) ++{ ++ struct NODE_MGR *hNodeMgr = hNode->hNodeMgr; ++ ++ if (pAttrs != NULL) { ++ pstrmDef->uNumBufs = pAttrs->uNumBufs; ++ pstrmDef->uBufsize = pAttrs->uBufsize / hNodeMgr-> ++ uDSPDataMauSize; ++ pstrmDef->uSegid = pAttrs->uSegid; ++ pstrmDef->uAlignment = pAttrs->uAlignment; ++ pstrmDef->uTimeout = pAttrs->uTimeout; ++ } else { ++ pstrmDef->uNumBufs = DEFAULTNBUFS; ++ pstrmDef->uBufsize = DEFAULTBUFSIZE / hNodeMgr-> ++ uDSPDataMauSize; ++ pstrmDef->uSegid = DEFAULTSEGID; ++ pstrmDef->uAlignment = DEFAULTALIGNMENT; ++ pstrmDef->uTimeout = DEFAULTTIMEOUT; ++ } ++} ++ ++/* ++ * ======== FreeStream ======== ++ * Purpose: ++ * Updates the channel mask and frees the pipe id. ++ */ ++static void FreeStream(struct NODE_MGR *hNodeMgr, struct STREAM stream) ++{ ++ /* Free up the pipe id unless other node has not yet been deleted. */ ++ if (stream.type == NODECONNECT) { ++ if (GB_test(hNodeMgr->pipeDoneMap, stream.devId)) { ++ /* The other node has already been deleted */ ++ GB_clear(hNodeMgr->pipeDoneMap, stream.devId); ++ GB_clear(hNodeMgr->pipeMap, stream.devId); ++ } else { ++ /* The other node has not been deleted yet */ ++ GB_set(hNodeMgr->pipeDoneMap, stream.devId); ++ } ++ } else if (stream.type == HOSTCONNECT) { ++ if (stream.devId < hNodeMgr->ulNumChnls) { ++ GB_clear(hNodeMgr->chnlMap, stream.devId); ++ } else if (stream.devId < (2 * hNodeMgr->ulNumChnls)) { ++ /* dsp-dma */ ++ GB_clear(hNodeMgr->dmaChnlMap, stream.devId - ++ (1 * hNodeMgr->ulNumChnls)); ++ } else if (stream.devId < (3 * hNodeMgr->ulNumChnls)) { ++ /* zero-copy */ ++ GB_clear(hNodeMgr->zChnlMap, stream.devId - ++ (2 * hNodeMgr->ulNumChnls)); ++ } ++ } ++} ++ ++/* ++ * ======== GetFxnAddress ======== ++ * Purpose: ++ * Retrieves the address for create, execute or delete phase for a node. ++ */ ++static DSP_STATUS GetFxnAddress(struct NODE_OBJECT *hNode, u32 *pulFxnAddr, ++ u32 uPhase) ++{ ++ char *pstrFxnName = NULL; ++ struct NODE_MGR *hNodeMgr = hNode->hNodeMgr; ++ DSP_STATUS status = DSP_SOK; ++ DBC_Require(NODE_GetType(hNode) == NODE_TASK || ++ NODE_GetType(hNode) == NODE_DAISSOCKET || ++ NODE_GetType(hNode) == NODE_MESSAGE); ++ ++ switch (uPhase) { ++ case CREATEPHASE: ++ pstrFxnName = hNode->dcdProps.objData.nodeObj. ++ pstrCreatePhaseFxn; ++ break; ++ case EXECUTEPHASE: ++ pstrFxnName = hNode->dcdProps.objData.nodeObj. ++ pstrExecutePhaseFxn; ++ break; ++ case DELETEPHASE: ++ pstrFxnName = hNode->dcdProps.objData.nodeObj. ++ pstrDeletePhaseFxn; ++ break; ++ default: ++ /* Should never get here */ ++ DBC_Assert(false); ++ break; ++ } ++ ++ status = hNodeMgr->nldrFxns.pfnGetFxnAddr(hNode->hNldrNode, pstrFxnName, ++ pulFxnAddr); ++ ++ return status; ++} ++ ++/* ++ * ======== GetNodeInfo ======== ++ * Purpose: ++ * Retrieves the node information. ++ */ ++void GetNodeInfo(struct NODE_OBJECT *hNode, struct DSP_NODEINFO *pNodeInfo) ++{ ++ u32 i; ++ ++ DBC_Require(MEM_IsValidHandle(hNode, NODE_SIGNATURE)); ++ DBC_Require(pNodeInfo != NULL); ++ ++ pNodeInfo->cbStruct = sizeof(struct DSP_NODEINFO); ++ pNodeInfo->nbNodeDatabaseProps = hNode->dcdProps.objData.nodeObj. ++ ndbProps; ++ pNodeInfo->uExecutionPriority = hNode->nPriority; ++ pNodeInfo->hDeviceOwner = hNode->hDeviceOwner; ++ pNodeInfo->uNumberStreams = hNode->uNumInputs + hNode->uNumOutputs; ++ pNodeInfo->uNodeEnv = hNode->nodeEnv; ++ ++ pNodeInfo->nsExecutionState = NODE_GetState(hNode); ++ ++ /* Copy stream connect data */ ++ for (i = 0; i < hNode->uNumInputs + hNode->uNumOutputs; i++) ++ pNodeInfo->scStreamConnection[i] = hNode->streamConnect[i]; ++ ++} ++ ++/* ++ * ======== GetNodeProps ======== ++ * Purpose: ++ * Retrieve node properties. ++ */ ++static DSP_STATUS GetNodeProps(struct DCD_MANAGER *hDcdMgr, ++ struct NODE_OBJECT *hNode, ++ CONST struct DSP_UUID *pNodeId, ++ struct DCD_GENERICOBJ *pdcdProps) ++{ ++ u32 uLen; ++ struct NODE_MSGARGS *pMsgArgs; ++ struct NODE_TASKARGS *pTaskArgs; ++ enum NODE_TYPE nodeType = NODE_TASK; ++ struct DSP_NDBPROPS *pndbProps = &(pdcdProps->objData.nodeObj.ndbProps); ++ DSP_STATUS status = DSP_SOK; ++#ifdef DEBUG ++ char szUuid[MAXUUIDLEN]; ++#endif ++ ++ status = DCD_GetObjectDef(hDcdMgr, (struct DSP_UUID *)pNodeId, ++ DSP_DCDNODETYPE, pdcdProps); ++ ++ if (DSP_SUCCEEDED(status)) { ++ hNode->nType = nodeType = pndbProps->uNodeType; ++ ++#ifdef DEBUG ++ /* Create UUID value to set in registry. */ ++ UUID_UuidToString((struct DSP_UUID *)pNodeId, szUuid, ++ MAXUUIDLEN); ++ DBG_Trace(DBG_LEVEL7, "\n** (node) UUID: %s\n", szUuid); ++#endif ++ ++ /* Fill in message args that come from NDB */ ++ if (nodeType != NODE_DEVICE) { ++ pMsgArgs = &(hNode->createArgs.asa.msgArgs); ++ pMsgArgs->uSegid = pdcdProps->objData.nodeObj.uMsgSegid; ++ pMsgArgs->uNotifyType = pdcdProps->objData.nodeObj. ++ uMsgNotifyType; ++ pMsgArgs->uMaxMessages = pndbProps->uMessageDepth; ++#ifdef DEBUG ++ DBG_Trace(DBG_LEVEL7, ++ "** (node) Max Number of Messages: 0x%x\n", ++ pMsgArgs->uMaxMessages); ++#endif ++ } else { ++ /* Copy device name */ ++ DBC_Require(pndbProps->acName); ++ uLen = strlen(pndbProps->acName); ++ DBC_Assert(uLen < MAXDEVNAMELEN); ++ hNode->pstrDevName = MEM_Calloc(uLen + 1, MEM_PAGED); ++ if (hNode->pstrDevName == NULL) { ++ status = DSP_EMEMORY; ++ } else { ++ strncpy(hNode->pstrDevName, ++ pndbProps->acName, uLen); ++ } ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Fill in create args that come from NDB */ ++ if (nodeType == NODE_TASK || nodeType == NODE_DAISSOCKET) { ++ pTaskArgs = &(hNode->createArgs.asa.taskArgs); ++ pTaskArgs->nPriority = pndbProps->iPriority; ++ pTaskArgs->uStackSize = pndbProps->uStackSize; ++ pTaskArgs->uSysStackSize = pndbProps->uSysStackSize; ++ pTaskArgs->uStackSeg = pndbProps->uStackSeg; ++#ifdef DEBUG ++ DBG_Trace(DBG_LEVEL7, ++ "** (node) Priority: 0x%x\n" "** (node) Stack" ++ " Size: 0x%x words\n" "** (node) System Stack" ++ " Size: 0x%x words\n" "** (node) Stack" ++ " Segment: 0x%x\n\n", ++ "** (node) profile count : 0x%x \n \n", ++ pTaskArgs->nPriority, pTaskArgs->uStackSize, ++ pTaskArgs->uSysStackSize, ++ pTaskArgs->uStackSeg, ++ pndbProps->uCountProfiles); ++#endif ++ } ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== GetProcProps ======== ++ * Purpose: ++ * Retrieve the processor properties. ++ */ ++static DSP_STATUS GetProcProps(struct NODE_MGR *hNodeMgr, ++ struct DEV_OBJECT *hDevObject) ++{ ++ struct CFG_DEVNODE *hDevNode; ++ struct CFG_HOSTRES hostRes; ++ DSP_STATUS status = DSP_SOK; ++ ++ status = DEV_GetDevNode(hDevObject, &hDevNode); ++ if (DSP_SUCCEEDED(status)) ++ status = CFG_GetHostResources(hDevNode, &hostRes); ++ ++ if (DSP_SUCCEEDED(status)) { ++ hNodeMgr->ulChnlOffset = hostRes.dwChnlOffset; ++ hNodeMgr->ulChnlBufSize = hostRes.dwChnlBufSize; ++ hNodeMgr->ulNumChnls = hostRes.dwNumChnls; ++ ++ /* ++ * PROC will add an API to get DSP_PROCESSORINFO. ++ * Fill in default values for now. ++ */ ++ /* TODO -- Instead of hard coding, take from registry */ ++ hNodeMgr->procFamily = 6000; ++ hNodeMgr->procType = 6410; ++ hNodeMgr->nMinPri = DSP_NODE_MIN_PRIORITY; ++ hNodeMgr->nMaxPri = DSP_NODE_MAX_PRIORITY; ++ hNodeMgr->uDSPWordSize = DSPWORDSIZE; ++ hNodeMgr->uDSPDataMauSize = DSPWORDSIZE; ++ hNodeMgr->uDSPMauSize = 1; ++ ++ } ++ return status; ++} ++ ++ ++ ++/* ++ * ======== NODE_GetUUIDProps ======== ++ * Purpose: ++ * Fetch Node UUID properties from DCD/DOF file. ++ */ ++DSP_STATUS NODE_GetUUIDProps(DSP_HPROCESSOR hProcessor, ++ IN CONST struct DSP_UUID *pNodeId, ++ OUT struct DSP_NDBPROPS *pNodeProps) ++{ ++ struct NODE_MGR *hNodeMgr = NULL; ++ struct DEV_OBJECT *hDevObject; ++ DSP_STATUS status = DSP_SOK; ++ struct DCD_NODEPROPS dcdNodeProps; ++ struct DSP_PROCESSORSTATE procStatus; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(hProcessor != NULL); ++ DBC_Require(pNodeId != NULL); ++ ++ if (hProcessor == NULL || pNodeId == NULL) { ++ status = DSP_EHANDLE; ++ goto func_end; ++ } ++ status = PROC_GetState(hProcessor, &procStatus, ++ sizeof(struct DSP_PROCESSORSTATE)); ++ if (DSP_FAILED(status)) ++ goto func_end; ++ /* If processor is in error state then don't attempt ++ to send the message */ ++ if (procStatus.iState == PROC_ERROR) { ++ GT_1trace(NODE_debugMask, GT_5CLASS, ++ "NODE_GetUUIDProps: proc Status 0x%x\n", ++ procStatus.iState); ++ status = DSP_EFAIL; ++ goto func_end; ++ } ++ ++ GT_3trace(NODE_debugMask, GT_ENTER, ++ "NODE_GetUUIDProps: " "\thProcessor: " ++ "0x%x\tpNodeId: 0x%x" "\tpNodeProps: 0x%x\n", hProcessor, ++ pNodeId, pNodeProps); ++ ++ status = PROC_GetDevObject(hProcessor, &hDevObject); ++ if (DSP_SUCCEEDED(status) && hDevObject != NULL) { ++ status = DEV_GetNodeManager(hDevObject, &hNodeMgr); ++ if (hNodeMgr == NULL) { ++ status = DSP_EHANDLE; ++ goto func_end; ++ } ++ } ++ ++ /* ++ * Enter the critical section. This is needed because ++ * DCD_GetObjectDef will ultimately end up calling DBLL_open/close, ++ * which needs to be protected in order to not corrupt the zlib manager ++ * (COD). ++ */ ++ status = SYNC_EnterCS(hNodeMgr->hSync); ++ ++ if (DSP_SUCCEEDED(status)) { ++ dcdNodeProps.pstrCreatePhaseFxn = NULL; ++ dcdNodeProps.pstrExecutePhaseFxn = NULL; ++ dcdNodeProps.pstrDeletePhaseFxn = NULL; ++ dcdNodeProps.pstrIAlgName = NULL; ++ ++ status = DCD_GetObjectDef(hNodeMgr->hDcdMgr, ++ (struct DSP_UUID *) pNodeId, ++ DSP_DCDNODETYPE, ++ (struct DCD_GENERICOBJ *) &dcdNodeProps); ++ if (DSP_SUCCEEDED(status)) { ++ *pNodeProps = dcdNodeProps.ndbProps; ++ if (dcdNodeProps.pstrCreatePhaseFxn) ++ MEM_Free(dcdNodeProps.pstrCreatePhaseFxn); ++ ++ if (dcdNodeProps.pstrExecutePhaseFxn) ++ MEM_Free(dcdNodeProps.pstrExecutePhaseFxn); ++ ++ if (dcdNodeProps.pstrDeletePhaseFxn) ++ MEM_Free(dcdNodeProps.pstrDeletePhaseFxn); ++ ++ if (dcdNodeProps.pstrIAlgName) ++ MEM_Free(dcdNodeProps.pstrIAlgName); ++ } ++ /* Leave the critical section, we're done. */ ++ (void)SYNC_LeaveCS(hNodeMgr->hSync); ++ } ++func_end: ++ return status; ++} ++ ++/* ++ * ======== GetRMSFxns ======== ++ * Purpose: ++ * Retrieve the RMS functions. ++ */ ++static DSP_STATUS GetRMSFxns(struct NODE_MGR *hNodeMgr) ++{ ++ s32 i; ++ struct DEV_OBJECT *hDev = hNodeMgr->hDevObject; ++ DSP_STATUS status = DSP_SOK; ++ ++ static char *pszFxns[NUMRMSFXNS] = { ++ "RMS_queryServer", /* RMSQUERYSERVER */ ++ "RMS_configureServer", /* RMSCONFIGURESERVER */ ++ "RMS_createNode", /* RMSCREATENODE */ ++ "RMS_executeNode", /* RMSEXECUTENODE */ ++ "RMS_deleteNode", /* RMSDELETENODE */ ++ "RMS_changeNodePriority", /* RMSCHANGENODEPRIORITY */ ++ "RMS_readMemory", /* RMSREADMEMORY */ ++ "RMS_writeMemory", /* RMSWRITEMEMORY */ ++ "RMS_copy", /* RMSCOPY */ ++ }; ++ ++ for (i = 0; i < NUMRMSFXNS; i++) { ++ status = DEV_GetSymbol(hDev, pszFxns[i], ++ &(hNodeMgr->ulFxnAddrs[i])); ++ if (DSP_FAILED(status)) { ++ if (status == COD_E_SYMBOLNOTFOUND) { ++ /* ++ * May be loaded dynamically (in the future), ++ * but return an error for now. ++ */ ++ GT_1trace(NODE_debugMask, GT_6CLASS, ++ "RMS function: %s " ++ "currently not loaded\n", pszFxns[i]); ++ } else { ++ GT_2trace(NODE_debugMask, GT_6CLASS, ++ "GetRMSFxns: Symbol not " ++ "found: %s\tstatus = 0x%x\n", ++ pszFxns[i], status); ++ break; ++ } ++ } ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== Ovly ======== ++ * Purpose: ++ * Called during overlay.Sends command to RMS to copy a block of data. ++ */ ++static u32 Ovly(void *pPrivRef, u32 ulDspRunAddr, u32 ulDspLoadAddr, ++ u32 ulNumBytes, u32 nMemSpace) ++{ ++ struct NODE_OBJECT *hNode = (struct NODE_OBJECT *)pPrivRef; ++ struct NODE_MGR *hNodeMgr; ++ u32 ulBytes = 0; ++ u32 ulSize; ++ u32 ulTimeout; ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DEV_CONTEXT *hWmdContext; ++ struct WMD_DRV_INTERFACE *pIntfFxns; /* Function interface to WMD */ ++ ++ DBC_Require(MEM_IsValidHandle(hNode, NODE_SIGNATURE)); ++ ++ hNodeMgr = hNode->hNodeMgr; ++ ++ ulSize = ulNumBytes / hNodeMgr->uDSPWordSize; ++ ulTimeout = hNode->uTimeout; ++ ++ /* Call new MemCopy function */ ++ pIntfFxns = hNodeMgr->pIntfFxns; ++ status = DEV_GetWMDContext(hNodeMgr->hDevObject, &hWmdContext); ++ status = (*pIntfFxns->pfnBrdMemCopy)(hWmdContext, ulDspRunAddr, ++ ulDspLoadAddr, ulNumBytes, (u32) nMemSpace); ++ ++ if (DSP_SUCCEEDED(status)) ++ ulBytes = ulNumBytes; ++ ++ return ulBytes; ++} ++ ++/* ++ * ======== Write ======== ++ */ ++static u32 Write(void *pPrivRef, u32 ulDspAddr, void *pBuf, ++ u32 ulNumBytes, u32 nMemSpace) ++{ ++ struct NODE_OBJECT *hNode = (struct NODE_OBJECT *) pPrivRef; ++ struct NODE_MGR *hNodeMgr; ++ u16 memType; ++ u32 ulTimeout; ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DEV_CONTEXT *hWmdContext; ++ struct WMD_DRV_INTERFACE *pIntfFxns; /* Function interface to WMD */ ++ ++ DBC_Require(MEM_IsValidHandle(hNode, NODE_SIGNATURE)); ++ DBC_Require(nMemSpace & DBLL_CODE || nMemSpace & DBLL_DATA); ++ ++ hNodeMgr = hNode->hNodeMgr; ++ ++ ulTimeout = hNode->uTimeout; ++ memType = (nMemSpace & DBLL_CODE) ? RMS_CODE : RMS_DATA; ++ ++ /* Call new MemWrite function */ ++ pIntfFxns = hNodeMgr->pIntfFxns; ++ status = DEV_GetWMDContext(hNodeMgr->hDevObject, &hWmdContext); ++ status = (*pIntfFxns->pfnBrdMemWrite) (hWmdContext, pBuf, ulDspAddr, ++ ulNumBytes, memType); ++ ++ return ulNumBytes; ++} ++ +diff --git a/drivers/dsp/bridge/rmgr/proc.c b/drivers/dsp/bridge/rmgr/proc.c +new file mode 100644 +index 0000000..332e01a +--- /dev/null ++++ b/drivers/dsp/bridge/rmgr/proc.c +@@ -0,0 +1,1985 @@ ++/* ++ * proc.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== proc.c ======== ++ * Description: ++ * Processor interface at the driver level. ++ * ++ * Public Functions: ++ * PROC_Attach ++ * PROC_Ctrl ++ * PROC_Detach ++ * PROC_EnumNodes ++ * PROC_GetResourceInfo ++ * PROC_Exit ++ * PROC_FlushMemory ++ * PROC_GetState ++ * PROC_GetProcessorId ++ * PROC_GetTrace ++ * PROC_Init ++ * PROC_Load ++ * PROC_Map ++ * PROC_NotifyClients ++ * PROC_RegisterNotify ++ * PROC_ReserveMemory ++ * PROC_Start ++ * PROC_UnMap ++ * PROC_UnReserveMemory ++ * PROC_InvalidateMemory ++ ++ *! Revision History ++ *! ======== ======== ++ *! 04-Apr-2007 sh Added PROC_InvalidateMemory API ++ *! 19-Apr-2004 sb Aligned DMM definitions with Symbian ++ *! Used MEM_FlushCache instead of OS specific API ++ *! Integrated Alan's code review updates ++ *! 08-Mar-2004 sb Added the Dynamic Memory Mapping feature ++ *! 08-Mar-2004 vp Added g_pszLastCoff member to PROC_OBJECT. ++ *! This is required for multiprocessor environment. ++ *! 09-Feb-2004 vp Added PROC_GetProcessorID function ++ *! 22-Apr-2003 vp Fixed issue with the string that stores coff file name ++ *! 03-Apr-2003 sb Fix DEH deregistering bug ++ *! 26-Mar-2003 vp Commented the call to DSP deep sleep in PROC_Start function. ++ *! 18-Feb-2003 vp Code review updates. ++ *! 18-Oct-2002 vp Ported to Linux platform. ++ *! 22-May-2002 sg Do IOCTL-to-PWR translation before calling PWR_SleepDSP. ++ *! 14-May-2002 sg Use CSL_Atoi() instead of atoi(). ++ *! 13-May-2002 sg Propagate PWR return codes upwards. ++ *! 07-May-2002 sg Added check for, and call to PWR functions in PROC_Ctrl. ++ *! 02-May-2002 sg Added "nap" mode: put DSP to sleep once booted. ++ *! 01-Apr-2002 jeh Assume word addresses in PROC_GetTrace(). ++ *! 29-Nov-2001 jeh Don't call DEH function if hDehMgr == NULL. ++ *! 05-Nov-2001 kc: Updated PROC_RegisterNotify and PROC_GetState to support ++ *! DEH module. ++ *! 09-Oct-2001 jeh Fix number of bytes calculated in PROC_GetTrace(). ++ *! 11-Sep-2001 jeh Delete MSG manager in PROC_Monitor() to fix memory leak. ++ *! 29-Aug-2001 rr: DCD_AutoRegister and IOOnLoaded moved before COD_LoadBase ++ *! to facilitate the external loading. ++ *! 14-Aug-2001 ag DCD_AutoRegister() now called before IOOnLoaded() fxn. ++ *! 21-Jun-2001 rr: MSG_Create is done only the first time. ++ *! 02-May-2001 jeh Return failure in PROC_Load if IOOnLoaded function returns ++ *! error other than E_NOTIMPL. ++ *! 03-Apr-2001 sg: Changed DSP_DCD_ENOAUTOREGISTER to DSP_EDCDNOAUTOREGISTER. ++ *! 13-Feb-2001 kc: DSP/BIOS Bridge name updates. ++ *! 05-Jan-2001 rr: PROC_LOAD MSG_Create error is checked. ++ *! 15-Dec-2000 rr: IoOnLoaded is checked for WSX_STATUS. We fail to load ++ *! if DEV_Create2 fails; ie, no non-RMS targets can be ++ *! loaded. ++ *! 12-Dec-2000 rr: PROC_Start's DEV_Create2 is checked for WSX_STATUS. ++ *! 28-Nov-2000 jeh Added call to IO OnLoaded function to PROC_Load(). ++ *! 29-Nov-2000 rr: Incorporated code review changes. ++ *! 03-Nov-2000 rr: Auto_Register happens after PROC_Load. ++ *! 06-Oct-2000 rr: Updated to ver 0.9. PROC_Start calls DEV_Create2 and ++ *! WMD_BRD_STOP is always followed by DEV_Destroy2. ++ *! 05-Sep-2000 rr: PROC_GetTrace calculates the Trace symbol for 55 in a ++ *! different way. ++ *! 10-Aug-2000 rr: PROC_NotifyClients, PROC_GetProcessorHandle Added ++ *! 07-Aug-2000 rr: PROC_IDLE/SYNCINIT/UNKNOWN state removed. ++ *! WMD fxns are checked for WSX_STATUS. ++ *! PROC_Attach does not alter the state of the BRD. ++ *! PROC_Run removed. ++ *! 04-Aug-2000 rr: All the functions return DSP_EHANDLE if proc handle is ++ *! invalid ++ *! 27-Jul-2000 rr: PROC_GetTrace and PROC_Load implemented. Updated to ++ *! ver 0.8 API. ++ *! 06-Jul-2000 rr: Created. ++ */ ++ ++/* ------------------------------------ Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++#include ++#include ++/* ----------------------------------- Mini Driver */ ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Resource Manager */ ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Others */ ++#include ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++#include ++ ++#ifndef RES_CLEANUP_DISABLE ++#include ++#endif ++/* ----------------------------------- Defines, Data Structures, Typedefs */ ++#define PROC_SIGNATURE 0x434F5250 /* "PROC" (in reverse). */ ++#define MAXCMDLINELEN 255 ++#define PROC_ENVPROCID "PROC_ID=%d" ++#define MAXPROCIDLEN (8 + 5) ++#define PROC_DFLT_TIMEOUT 10000 /* Time out in milliseconds */ ++#define PWR_TIMEOUT 500 /* Sleep/wake timout in msec */ ++#define EXTEND "_EXT_END" /* Extmem end addr in DSP binary */ ++ ++extern char *iva_img; ++/* The PROC_OBJECT structure. */ ++struct PROC_OBJECT { ++ struct LST_ELEM link; /* Link to next PROC_OBJECT */ ++ u32 dwSignature; /* Used for object validation */ ++ struct DEV_OBJECT *hDevObject; /* Device this PROC represents */ ++ u32 hProcess; /* Process owning this Processor */ ++ struct MGR_OBJECT *hMgrObject; /* Manager Object Handle */ ++ u32 uAttachCount; /* Processor attach count */ ++ u32 uProcessor; /* Processor number */ ++ u32 uTimeout; /* Time out count */ ++ enum DSP_PROCSTATE sState; /* Processor state */ ++ u32 ulUnit; /* DDSP unit number */ ++ bool bIsAlreadyAttached; /* ++ * True if the Device below has ++ * GPP Client attached ++ */ ++ struct NTFY_OBJECT *hNtfy; /* Manages notifications */ ++ struct WMD_DEV_CONTEXT *hWmdContext; /* WMD Context Handle */ ++ struct WMD_DRV_INTERFACE *pIntfFxns; /* Function interface to WMD */ ++ char *g_pszLastCoff; ++} ; ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask PROC_DebugMask = { NULL, NULL }; /* WCD MGR Mask */ ++#endif ++ ++static u32 cRefs; ++ ++struct SYNC_CSOBJECT *hProcLock; /* For critical sections */ ++ ++/* ----------------------------------- Function Prototypes */ ++static DSP_STATUS PROC_Monitor(struct PROC_OBJECT *hProcessor); ++static s32 GetEnvpCount(char **envp); ++static char **PrependEnvp(char **newEnvp, char **envp, s32 cEnvp, s32 cNewEnvp, ++ char *szVar); ++ ++ ++/* ++ * ======== PROC_Attach ======== ++ * Purpose: ++ * Prepare for communication with a particular DSP processor, and return ++ * a handle to the processor object. ++ */ ++DSP_STATUS ++PROC_Attach(u32 uProcessor, OPTIONAL CONST struct DSP_PROCESSORATTRIN *pAttrIn, ++ OUT DSP_HPROCESSOR *phProcessor) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEV_OBJECT *hDevObject; ++ struct PROC_OBJECT *pProcObject = NULL; ++ struct MGR_OBJECT *hMgrObject = NULL; ++ struct DRV_OBJECT *hDrvObject = NULL; ++ u32 devType; ++ ++#ifndef RES_CLEANUP_DISABLE ++ HANDLE hDRVObject; ++ u32 hProcess; ++ DSP_STATUS res_status = DSP_SOK; ++ struct PROCESS_CONTEXT *pPctxt = NULL; ++#endif ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phProcessor != NULL); ++ ++ GT_3trace(PROC_DebugMask, GT_ENTER, "Entered PROC_Attach, args:\n\t" ++ "uProcessor: 0x%x\n\tpAttrIn: 0x%x\n\tphProcessor:" ++ "0x%x\n", uProcessor, pAttrIn, phProcessor); ++ /* Get the Driver and Manager Object Handles */ ++ status = CFG_GetObject((u32 *)&hDrvObject, REG_DRV_OBJECT); ++ if (DSP_SUCCEEDED(status)) { ++ status = CFG_GetObject((u32 *)&hMgrObject, REG_MGR_OBJECT); ++ if (DSP_FAILED(status)) { ++ /* don't propogate CFG errors from this PROC function */ ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Attach: DSP_FAILED to get" ++ "the Manager Object.\n", status); ++ } ++ } else { ++ /* don't propogate CFG errors from this PROC function */ ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Attach: failed to get the" ++ " DriverObject, 0x%x!\n", status); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Get the Device Object */ ++ status = DRV_GetDevObject(uProcessor, hDrvObject, &hDevObject); ++ if (DSP_FAILED(status)) { ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Attach: failed to get" ++ " DevObject, 0x%x!\n", status); ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = DEV_GetDevType(hDevObject, &devType); ++ if (DSP_FAILED(status)) { ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Attach: failed to get" ++ " DevType, 0x%x!\n", status); ++ } ++ } ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ /* If we made it this far, create the Proceesor object: */ ++ MEM_AllocObject(pProcObject, struct PROC_OBJECT, PROC_SIGNATURE); ++ /* Fill out the Processor Object: */ ++ if (pProcObject == NULL) { ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Attach:Out of memeory \n"); ++ status = DSP_EFAIL; ++ goto func_end; ++ } ++ pProcObject->hDevObject = hDevObject; ++ pProcObject->hMgrObject = hMgrObject; ++ pProcObject->uProcessor = devType; ++ /* Get Caller Process and store it */ ++ /* Return PID instead of process handle */ ++ pProcObject->hProcess = current->pid; ++ ++ if (pAttrIn) ++ pProcObject->uTimeout = pAttrIn->uTimeout; ++ else ++ pProcObject->uTimeout = PROC_DFLT_TIMEOUT; ++ ++ status = DEV_GetIntfFxns(hDevObject, &pProcObject->pIntfFxns); ++ if (DSP_SUCCEEDED(status)) { ++ status = DEV_GetWMDContext(hDevObject, ++ &pProcObject->hWmdContext); ++ if (DSP_FAILED(status)) { ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Attach Could not" ++ " get the WMD Context.\n", status); ++ MEM_FreeObject(pProcObject); ++ } ++ } else { ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Attach Could not get" ++ " the DEV_ Interface fxns.\n", status); ++ MEM_FreeObject(pProcObject); ++ } ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ /* Create the Notification Object */ ++ /* This is created with no event mask, no notify mask ++ * and no valid handle to the notification. They all get ++ * filled up when PROC_RegisterNotify is called */ ++ status = NTFY_Create(&pProcObject->hNtfy); ++ if (DSP_SUCCEEDED(status)) { ++ /* Insert the Processor Object into the DEV List. ++ * Return handle to this Processor Object: ++ * Find out if the Device is already attached to a ++ * Processor. If so, return AlreadyAttached status */ ++ LST_InitElem(&pProcObject->link); ++ status = DEV_InsertProcObject(pProcObject->hDevObject, ++ (u32)pProcObject, ++ &pProcObject->bIsAlreadyAttached); ++ if (DSP_SUCCEEDED(status)) { ++ if (pProcObject->bIsAlreadyAttached) { ++ status = DSP_SALREADYATTACHED; ++ GT_0trace(PROC_DebugMask, GT_1CLASS, ++ "PROC_Attach: Processor " ++ "Already Attached!\n"); ++ } ++ } else { ++ if (pProcObject->hNtfy) ++ NTFY_Delete(pProcObject->hNtfy); ++ ++ MEM_FreeObject(pProcObject); ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Attach: failed to insert " ++ "Proc Object into DEV, 0x%x!\n", status); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ *phProcessor = (DSP_HPROCESSOR)pProcObject; ++ (void)PROC_NotifyClients(pProcObject, ++ DSP_PROCESSORATTACH); ++ GT_0trace(PROC_DebugMask, GT_1CLASS, ++ "PROC_Attach: Processor " ++ "Attach Success!\n"); ++ } ++ } else { ++ /* Don't leak memory if DSP_FAILED */ ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Attach: Could not allocate " ++ "storage for notification \n"); ++ MEM_FreeObject(pProcObject); ++ } ++func_end: ++#ifndef RES_CLEANUP_DISABLE ++ if (DSP_FAILED(status)) ++ goto func_cont; ++ ++ /* Return PID instead of process handle */ ++ hProcess = current->pid; ++ ++ res_status = CFG_GetObject((u32 *)&hDRVObject, REG_DRV_OBJECT); ++ if (DSP_FAILED(res_status)) ++ goto func_cont; ++ ++ DRV_GetProcContext(hProcess, (struct DRV_OBJECT *)hDRVObject, ++ &pPctxt, NULL, 0); ++ if (pPctxt == NULL) { ++ DRV_InsertProcContext((struct DRV_OBJECT *)hDRVObject, &pPctxt); ++ if (pPctxt != NULL) { ++ DRV_ProcUpdatestate(pPctxt, PROC_RES_ALLOCATED); ++ DRV_ProcSetPID(pPctxt, hProcess); ++ } ++ } ++func_cont: ++ /* Return PID instead of process handle */ ++ hProcess = current->pid; ++ ++ res_status = CFG_GetObject((u32 *)&hDRVObject, REG_DRV_OBJECT); ++ if (DSP_SUCCEEDED(res_status)) { ++ DRV_GetProcContext(hProcess, ++ (struct DRV_OBJECT *)hDRVObject, &pPctxt, ++ NULL, 0); ++ if (pPctxt != NULL) ++ pPctxt->hProcessor = (DSP_HPROCESSOR)*phProcessor; ++ ++ } ++#endif ++ DBC_Ensure((status == DSP_EFAIL && *phProcessor == NULL) || ++ (DSP_SUCCEEDED(status) && ++ MEM_IsValidHandle(pProcObject, PROC_SIGNATURE)) || ++ (status == DSP_SALREADYATTACHED && ++ MEM_IsValidHandle(pProcObject, PROC_SIGNATURE))); ++ GT_2trace(PROC_DebugMask, GT_ENTER, "Exiting PROC_Attach, results:\n\t" ++ "status: 0x%x\n\thProcessor: 0x%x\n", status, *phProcessor); ++ ++ return status; ++} ++ ++static DSP_STATUS GetExecFile(struct CFG_DEVNODE *hDevNode, ++ struct DEV_OBJECT *hDevObject, ++ u32 size, char *execFile) ++{ ++ s32 devType; ++ s32 len; ++ ++ DEV_GetDevType(hDevObject, (u32 *) &devType); ++ if (devType == DSP_UNIT) { ++ return CFG_GetExecFile(hDevNode, size, execFile); ++ } else if (devType == IVA_UNIT) { ++ if (iva_img) { ++ len = strlen(iva_img); ++ strncpy(execFile, iva_img, len + 1); ++ return DSP_SOK; ++ } ++ } ++ return DSP_EFILE; ++} ++ ++/* ++ * ======== PROC_AutoStart ======== = ++ * Purpose: ++ * A Particular device gets loaded with the default image ++ * if the AutoStart flag is set. ++ * Parameters: ++ * hDevObject: Handle to the Device ++ * Returns: ++ * DSP_SOK: On Successful Loading ++ * DSP_EFAIL General Failure ++ * Requires: ++ * hDevObject != NULL ++ * Ensures: ++ */ ++DSP_STATUS PROC_AutoStart(struct CFG_DEVNODE *hDevNode, ++ struct DEV_OBJECT *hDevObject) ++{ ++ DSP_STATUS status = DSP_EFAIL; ++ u32 dwAutoStart = 0; /* autostart flag */ ++ struct PROC_OBJECT *pProcObject; ++ struct PROC_OBJECT *hProcObject; ++ char szExecFile[MAXCMDLINELEN]; ++ char *argv[2]; ++ struct MGR_OBJECT *hMgrObject = NULL; ++ s32 devType; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(hDevNode != NULL); ++ DBC_Require(hDevObject != NULL); ++ ++ GT_2trace(PROC_DebugMask, GT_ENTER, ++ "Entered PROC_AutoStart, args:\n\t" ++ "hDevNode: 0x%x\thDevObject: 0x%x\n", hDevNode, hDevObject); ++ /* Create a Dummy PROC Object */ ++ if (DSP_FAILED(CFG_GetObject((u32 *)&hMgrObject, ++ REG_MGR_OBJECT))) { ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_AutoStart: DSP_FAILED to " ++ "Get MGR Object\n"); ++ goto func_end; ++ } ++ MEM_AllocObject(pProcObject, struct PROC_OBJECT, PROC_SIGNATURE); ++ if (pProcObject == NULL) { ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_AutoStart: DSP_FAILED " ++ "to Create a dummy Processor\n"); ++ goto func_end; ++ } ++ GT_0trace(PROC_DebugMask, GT_1CLASS, "NTFY Created \n"); ++ pProcObject->hDevObject = hDevObject; ++ pProcObject->hMgrObject = hMgrObject; ++ hProcObject = pProcObject; ++ if (DSP_SUCCEEDED(DEV_GetIntfFxns(hDevObject, ++ &pProcObject->pIntfFxns))) { ++ if (DSP_SUCCEEDED(DEV_GetWMDContext(hDevObject, ++ &pProcObject->hWmdContext))) { ++ status = DSP_SOK; ++ } else { ++ MEM_FreeObject(hProcObject); ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_AutoStart: Failed " ++ "to get WMD Context \n"); ++ } ++ } else { ++ MEM_FreeObject(hProcObject); ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_AutoStart: Failed to " ++ "get IntFxns \n"); ++ } ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ /* Stop the Device, put it into standby mode */ ++ status = PROC_Stop(hProcObject); ++ if (DSP_FAILED(CFG_GetAutoStart(hDevNode, &dwAutoStart)) || ++ !dwAutoStart) { ++ status = DSP_EFAIL; ++ /* DSP_FAILED to Get s32 Fxn or Wmd Context */ ++ GT_0trace(PROC_DebugMask, GT_1CLASS, "PROC_AutoStart: " ++ "CFG_GetAutoStart DSP_FAILED \n"); ++ goto func_cont; ++ } ++ /* Get the default executable for this board... */ ++ DEV_GetDevType(hDevObject, (u32 *)&devType); ++ pProcObject->uProcessor = devType; ++ if (DSP_SUCCEEDED(GetExecFile(hDevNode, hDevObject, ++ sizeof(szExecFile), szExecFile))) { ++ argv[0] = szExecFile; ++ argv[1] = NULL; ++ /* ...and try to load it: */ ++ status = PROC_Load(hProcObject, 1, (CONST char **)argv, NULL); ++ if (DSP_SUCCEEDED(status)) { ++ status = PROC_Start(hProcObject); ++ if (DSP_SUCCEEDED(status)) { ++ GT_0trace(PROC_DebugMask, GT_1CLASS, ++ "PROC_AutoStart: Processor started " ++ "running\n"); ++ } else { ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_AutoStart: DSP_FAILED To " ++ "Start \n"); ++ } ++ } else { ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_AutoStart: DSP_FAILED to Load\n"); ++ } ++ } else { ++ status = DSP_EFILE; ++ GT_0trace(PROC_DebugMask, GT_7CLASS, "PROC_AutoStart: " ++ "No Exec file found \n"); ++ } ++func_cont: ++ MEM_FreeObject(hProcObject); ++func_end: ++ GT_1trace(PROC_DebugMask, GT_ENTER, ++ "Exiting PROC_AutoStart, status:0x%x\n", status); ++ return status; ++} ++ ++/* ++ * ======== PROC_Ctrl ======== ++ * Purpose: ++ * Pass control information to the GPP device driver managing the ++ * DSP processor. ++ * ++ * This will be an OEM-only function, and not part of the DSP/BIOS Bridge ++ * application developer's API. ++ * Call the WMD_ICOTL Fxn with the Argument This is a Synchronous ++ * Operation. arg can be null. ++ */ ++DSP_STATUS PROC_Ctrl(DSP_HPROCESSOR hProcessor, u32 dwCmd, ++ IN struct DSP_CBDATA *arg) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct PROC_OBJECT *pProcObject = hProcessor; ++ u32 timeout = 0; ++ ++ DBC_Require(cRefs > 0); ++ GT_3trace(PROC_DebugMask, GT_ENTER, ++ "Entered PROC_Ctrl, args:\n\thProcessor:" ++ " 0x%x\n\tdwCmd: 0x%x\n\targ: 0x%x\n", hProcessor, dwCmd, arg); ++ ++ if (MEM_IsValidHandle(pProcObject, PROC_SIGNATURE)) { ++ /* intercept PWR deep sleep command */ ++ if (dwCmd == WMDIOCTL_DEEPSLEEP) { ++ timeout = arg->cbData; ++ status = PWR_SleepDSP(PWR_DEEPSLEEP, timeout); ++ } ++ /* intercept PWR emergency sleep command */ ++ else if (dwCmd == WMDIOCTL_EMERGENCYSLEEP) { ++ timeout = arg->cbData; ++ status = PWR_SleepDSP(PWR_EMERGENCYDEEPSLEEP, timeout); ++ } else if (dwCmd == PWR_DEEPSLEEP) { ++ /* timeout = arg->cbData; */ ++ status = PWR_SleepDSP(PWR_DEEPSLEEP, timeout); ++ } ++ /* intercept PWR wake commands */ ++ else if (dwCmd == WMDIOCTL_WAKEUP) { ++ timeout = arg->cbData; ++ status = PWR_WakeDSP(timeout); ++ } else if (dwCmd == PWR_WAKEUP) { ++ /* timeout = arg->cbData; */ ++ status = PWR_WakeDSP(timeout); ++ } else ++ if (DSP_SUCCEEDED ++ ((*pProcObject->pIntfFxns->pfnDevCntrl) ++ (pProcObject->hWmdContext, dwCmd, arg))) { ++ status = DSP_SOK; ++ } else { ++ status = DSP_EFAIL; ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Ctrl: Failed \n"); ++ } ++ } else { ++ status = DSP_EHANDLE; ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Ctrl: InValid Processor Handle \n"); ++ } ++ GT_1trace(PROC_DebugMask, GT_ENTER, "Exiting PROC_Ctrl, 0x%x\n", ++ status); ++ return status; ++} ++ ++/* ++ * ======== PROC_Detach ======== ++ * Purpose: ++ * Destroys the Processor Object. Removes the notification from the Dev ++ * List. ++ */ ++DSP_STATUS PROC_Detach(DSP_HPROCESSOR hProcessor) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProcessor; ++#ifndef RES_CLEANUP_DISABLE ++ HANDLE hDRVObject; ++ u32 hProcess; ++ DSP_STATUS res_status = DSP_SOK; ++ struct PROCESS_CONTEXT *pPctxt = NULL; ++#endif ++ DBC_Require(cRefs > 0); ++ GT_1trace(PROC_DebugMask, GT_ENTER, "Entered PROC_Detach, args:\n\t" ++ "hProcessor: 0x%x\n", hProcessor); ++ ++ if (MEM_IsValidHandle(pProcObject, PROC_SIGNATURE)) { ++ /* Notify the Client */ ++ NTFY_Notify(pProcObject->hNtfy, DSP_PROCESSORDETACH); ++ /* Remove the notification memory */ ++ if (pProcObject->hNtfy) ++ NTFY_Delete(pProcObject->hNtfy); ++ ++ if (pProcObject->g_pszLastCoff) { ++ MEM_Free(pProcObject->g_pszLastCoff); ++ pProcObject->g_pszLastCoff = NULL; ++ } ++ ++#ifndef RES_CLEANUP_DISABLE ++ /* Return PID instead of process handle */ ++ hProcess = current->pid; ++ ++ res_status = CFG_GetObject((u32 *)&hDRVObject, REG_DRV_OBJECT); ++ if (DSP_SUCCEEDED(res_status)) { ++ DRV_GetProcContext(hProcess, ++ (struct DRV_OBJECT *)hDRVObject, &pPctxt, ++ NULL, 0); ++ if (pPctxt != NULL) { ++ DRV_ProcFreeDMMRes(pPctxt); ++ pPctxt->hProcessor = NULL; ++ } ++ } ++#endif ++ ++ /* Remove the Proc from the DEV List */ ++ (void)DEV_RemoveProcObject(pProcObject->hDevObject, ++ (u32)pProcObject); ++ /* Free the Processor Object */ ++ MEM_FreeObject(pProcObject); ++ } else { ++ status = DSP_EHANDLE; ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Detach: InValid Processor Handle \n"); ++ } ++ GT_1trace(PROC_DebugMask, GT_ENTER, "Exiting PROC_Detach, 0x%x\n", ++ status); ++ return status; ++} ++ ++/* ++ * ======== PROC_EnumNodes ======== ++ * Purpose: ++ * Enumerate and get configuration information about nodes allocated ++ * on a DSP processor. ++ */ ++DSP_STATUS PROC_EnumNodes(DSP_HPROCESSOR hProcessor, OUT DSP_HNODE *aNodeTab, ++ IN u32 uNodeTabSize, OUT u32 *puNumNodes, ++ OUT u32 *puAllocated) ++{ ++ DSP_STATUS status = DSP_EFAIL; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProcessor; ++ struct NODE_MGR *hNodeMgr = NULL; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(aNodeTab != NULL || uNodeTabSize == 0); ++ DBC_Require(puNumNodes != NULL); ++ DBC_Require(puAllocated != NULL); ++ ++ GT_5trace(PROC_DebugMask, GT_ENTER, "Entered PROC_EnumNodes, args:\n\t" ++ "hProcessor: 0x%x\n\taNodeTab: 0x%x\n\tuNodeTabSize: " ++ " 0x%x\n\t puNumNodes 0x%x\n\t puAllocated: 0x%x\n", ++ hProcessor, aNodeTab, uNodeTabSize, puNumNodes, ++ puAllocated); ++ if (MEM_IsValidHandle(pProcObject, PROC_SIGNATURE)) { ++ if (DSP_SUCCEEDED(DEV_GetNodeManager(pProcObject->hDevObject, ++ &hNodeMgr))) { ++ if (hNodeMgr) { ++ status = NODE_EnumNodes(hNodeMgr, aNodeTab, ++ uNodeTabSize, ++ puNumNodes, ++ puAllocated); ++ } ++ } ++ } else { ++ status = DSP_EHANDLE; ++ GT_0trace(PROC_DebugMask, GT_7CLASS, "PROC_EnumNodes: " ++ "InValid Processor Handle \n"); ++ } ++ GT_6trace(PROC_DebugMask, GT_ENTER, "Exit PROC_EnumNodes, args:\n\t" ++ "hProcessor: 0x%x\n\taNodeTab: 0x%x\n\tuNodeTabSize: " ++ " 0x%x\n\t puNumNodes 0x%x\n\t puAllocated: 0x%x\n\t " ++ "status: 0x%x \n", hProcessor, aNodeTab, uNodeTabSize, ++ puNumNodes, puAllocated, status); ++ ++ return status; ++} ++ ++/* ++ * ======== PROC_FlushMemory ======== ++ * Purpose: ++ * Flush cache ++ */ ++DSP_STATUS PROC_FlushMemory(DSP_HPROCESSOR hProcessor, void *pMpuAddr, ++ u32 ulSize, u32 ulFlags) ++{ ++ /* Keep STATUS here for future additions to this function */ ++ DSP_STATUS status = DSP_SOK; ++ enum DSP_FLUSHTYPE FlushMemType = PROC_WRITEBACK_INVALIDATE_MEM; ++ DBC_Require(cRefs > 0); ++ ++ GT_4trace(PROC_DebugMask, GT_ENTER, ++ "Entered PROC_FlushMemory, args:\n\t" ++ "hProcessor: 0x%x pMpuAddr: 0x%x ulSize 0x%x, ulFlags 0x%x\n", ++ hProcessor, pMpuAddr, ulSize, ulFlags); ++ /* Critical section */ ++ (void)SYNC_EnterCS(hProcLock); ++ MEM_FlushCache(pMpuAddr, ulSize, FlushMemType); ++ (void)SYNC_LeaveCS(hProcLock); ++ ++ GT_1trace(PROC_DebugMask, GT_ENTER, "Leaving PROC_FlushMemory [0x%x]", ++ status); ++ return status; ++} ++ ++ ++/* ++ * ======== PROC_InvalidateMemory ======== ++ * Purpose: ++ * Invalidates the memory specified ++ */ ++DSP_STATUS PROC_InvalidateMemory(DSP_HPROCESSOR hProcessor, void *pMpuAddr, ++ u32 ulSize) ++{ ++ /* Keep STATUS here for future additions to this function */ ++ DSP_STATUS status = DSP_SOK; ++ enum DSP_FLUSHTYPE FlushMemType = PROC_INVALIDATE_MEM; ++ DBC_Require(cRefs > 0); ++ GT_3trace(PROC_DebugMask, GT_ENTER, ++ "Entered PROC_InvalidateMemory, args:\n\t" ++ "hProcessor: 0x%x pMpuAddr: 0x%x ulSize 0x%x\n", hProcessor, ++ pMpuAddr, ulSize); ++ (void)SYNC_EnterCS(hProcLock); ++ MEM_FlushCache(pMpuAddr, ulSize, FlushMemType); ++ (void)SYNC_LeaveCS(hProcLock); ++ ++ GT_1trace(PROC_DebugMask, GT_ENTER, ++ "Leaving PROC_InvalidateMemory [0x%x]", status); ++ return status; ++} ++ ++/* ++ * ======== PROC_GetResourceInfo ======== ++ * Purpose: ++ * Enumerate the resources currently available on a processor. ++ */ ++DSP_STATUS PROC_GetResourceInfo(DSP_HPROCESSOR hProcessor, u32 uResourceType, ++ OUT struct DSP_RESOURCEINFO *pResourceInfo, ++ u32 uResourceInfoSize) ++{ ++ DSP_STATUS status = DSP_EFAIL; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProcessor; ++ struct NODE_MGR *hNodeMgr = NULL; ++ struct NLDR_OBJECT *hNldr = NULL; ++ struct RMM_TargetObj *rmm = NULL; ++ struct IO_MGR *hIOMgr = NULL; /* IO manager handle */ ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pResourceInfo != NULL); ++ DBC_Require(uResourceInfoSize >= sizeof(struct DSP_RESOURCEINFO)); ++ ++ GT_4trace(PROC_DebugMask, GT_ENTER, "Entered PROC_GetResourceInfo,\n\t" ++ "hProcessor: 0x%x\n\tuResourceType: 0x%x\n\tpResourceInfo:" ++ " 0x%x\n\t uResourceInfoSize 0x%x\n", hProcessor, ++ uResourceType, pResourceInfo, uResourceInfoSize); ++ if (!MEM_IsValidHandle(pProcObject, PROC_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_GetResourceInfo: InValid " ++ "Processor Handle \n"); ++ goto func_end; ++ } ++ switch (uResourceType) { ++ case DSP_RESOURCE_DYNDARAM: ++ case DSP_RESOURCE_DYNSARAM: ++ case DSP_RESOURCE_DYNEXTERNAL: ++ case DSP_RESOURCE_DYNSRAM: ++ if (DSP_FAILED(DEV_GetNodeManager(pProcObject->hDevObject, ++ &hNodeMgr))) ++ goto func_end; ++ ++ if (DSP_SUCCEEDED(NODE_GetNldrObj(hNodeMgr, &hNldr))) { ++ if (DSP_SUCCEEDED(NLDR_GetRmmManager(hNldr, &rmm))) { ++ DBC_Assert(rmm != NULL); ++ status = DSP_EVALUE; ++ if (RMM_stat(rmm, ++ (enum DSP_MEMTYPE)uResourceType, ++ (struct DSP_MEMSTAT *)&(pResourceInfo-> ++ result.memStat))) ++ status = DSP_SOK; ++ } ++ } ++ break; ++ case DSP_RESOURCE_PROCLOAD: ++ status = DEV_GetIOMgr(pProcObject->hDevObject, &hIOMgr); ++ status = pProcObject->pIntfFxns->pfnIOGetProcLoad(hIOMgr, ++ (struct DSP_PROCLOADSTAT *)&(pResourceInfo-> ++ result.procLoadStat)); ++ if (DSP_FAILED(status)) { ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "Error in procLoadStat function 0x%x\n", status); ++ } ++ break; ++ default: ++ status = DSP_EFAIL; ++ break; ++ } ++func_end: ++ GT_1trace(PROC_DebugMask, GT_ENTER, "Exiting PROC_GetResourceInfo, " ++ "status 0x%x\n", status); ++ return status; ++} ++ ++/* ++ * ======== PROC_Exit ======== ++ * Purpose: ++ * Decrement reference count, and free resources when reference count is ++ * 0. ++ */ ++void PROC_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ if (hProcLock) ++ (void)SYNC_DeleteCS(hProcLock); ++ ++ cRefs--; ++ ++ GT_1trace(PROC_DebugMask, GT_5CLASS, ++ "Entered PROC_Exit, ref count:0x%x\n", cRefs); ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== PROC_GetDevObject ======== ++ * Purpose: ++ * Return the Dev Object handle for a given Processor. ++ * ++ */ ++DSP_STATUS PROC_GetDevObject(DSP_HPROCESSOR hProcessor, ++ struct DEV_OBJECT **phDevObject) ++{ ++ DSP_STATUS status = DSP_EFAIL; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProcessor; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phDevObject != NULL); ++ ++ if (MEM_IsValidHandle(pProcObject, PROC_SIGNATURE)) { ++ *phDevObject = pProcObject->hDevObject; ++ status = DSP_SOK; ++ } else { ++ *phDevObject = NULL; ++ } ++ ++ DBC_Ensure((DSP_SUCCEEDED(status) && *phDevObject != NULL) || ++ (DSP_FAILED(status) && *phDevObject == NULL)); ++ ++ return status; ++} ++ ++/* ++ * ======== PROC_GetState ======== ++ * Purpose: ++ * Report the state of the specified DSP processor. ++ */ ++DSP_STATUS PROC_GetState(DSP_HPROCESSOR hProcessor, ++ OUT struct DSP_PROCESSORSTATE *pProcStatus, ++ u32 uStateInfoSize) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProcessor; ++ BRD_STATUS brdStatus; ++ struct DEH_MGR *hDehMgr; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pProcStatus != NULL); ++ DBC_Require(uStateInfoSize >= sizeof(struct DSP_PROCESSORSTATE)); ++ ++ GT_3trace(PROC_DebugMask, GT_ENTER, "Entering PROC_GetState, args:\n\t" ++ "pProcStatus: 0x%x\n\thProcessor: 0x%x\n\t uStateInfoSize" ++ " 0x%x\n", pProcStatus, hProcessor, uStateInfoSize); ++ if (MEM_IsValidHandle(pProcObject, PROC_SIGNATURE)) { ++ /* First, retrieve BRD state information */ ++ if (DSP_SUCCEEDED((*pProcObject->pIntfFxns->pfnBrdStatus) ++ (pProcObject->hWmdContext, &brdStatus))) { ++ switch (brdStatus) { ++ case BRD_STOPPED: ++ pProcStatus->iState = PROC_STOPPED; ++ break; ++ case BRD_DSP_HIBERNATION: ++ /* Fall through */ ++ case BRD_RUNNING: ++ pProcStatus->iState = PROC_RUNNING; ++ break; ++ case BRD_LOADED: ++ pProcStatus->iState = PROC_LOADED; ++ break; ++ case BRD_ERROR: ++ pProcStatus->iState = PROC_ERROR; ++ break; ++ default: ++ pProcStatus->iState = 0xFF; ++ status = DSP_EFAIL; ++ break; ++ } ++ } else { ++ status = DSP_EFAIL; ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_GetState: General Failure" ++ " to read the PROC Status \n"); ++ } ++ /* Next, retrieve error information, if any */ ++ status = DEV_GetDehMgr(pProcObject->hDevObject, &hDehMgr); ++ if (DSP_SUCCEEDED(status) && hDehMgr) { ++ status = (*pProcObject->pIntfFxns->pfnDehGetInfo) ++ (hDehMgr, &(pProcStatus->errInfo)); ++ if (DSP_FAILED(status)) { ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_GetState: Failed " ++ "retrieve exception info.\n"); ++ } ++ } else { ++ status = DSP_EFAIL; ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_GetState: Failed to " ++ "retrieve DEH handle.\n"); ++ } ++ } else { ++ status = DSP_EHANDLE; ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_GetState:InValid Processor Handle \n"); ++ } ++ GT_2trace(PROC_DebugMask, GT_ENTER, ++ "Exiting PROC_GetState, results:\n\t" ++ "status: 0x%x\n\tpProcStatus: 0x%x\n", status, ++ pProcStatus->iState); ++ return status; ++} ++ ++/* ++ * ======== PROC_GetTrace ======== ++ * Purpose: ++ * Retrieve the current contents of the trace buffer, located on the ++ * Processor. Predefined symbols for the trace buffer must have been ++ * configured into the DSP executable. ++ * Details: ++ * We support using the symbols SYS_PUTCBEG and SYS_PUTCEND to define a ++ * trace buffer, only. Treat it as an undocumented feature. ++ * This call is destructive, meaning the processor is placed in the monitor ++ * state as a result of this function. ++ */ ++DSP_STATUS PROC_GetTrace(DSP_HPROCESSOR hProcessor, u8 *pBuf, u32 uMaxSize) ++{ ++ DSP_STATUS status; ++ status = DSP_ENOTIMPL; ++ return status; ++} ++ ++/* ++ * ======== PROC_Init ======== ++ * Purpose: ++ * Initialize PROC's private state, keeping a reference count on each call ++ */ ++bool PROC_Init(void) ++{ ++ bool fRetval = true; ++ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++ /* Set the Trace mask */ ++ DBC_Assert(!PROC_DebugMask.flags); ++ GT_create(&PROC_DebugMask, "PR"); /* "PR" for Processor */ ++ ++ (void)SYNC_InitializeCS(&hProcLock); ++ } ++ ++ if (fRetval) ++ cRefs++; ++ ++ GT_1trace(PROC_DebugMask, GT_5CLASS, ++ "Entered PROC_Init, ref count:0x%x\n", cRefs); ++ DBC_Ensure((fRetval && (cRefs > 0)) || (!fRetval && (cRefs >= 0))); ++ ++ return fRetval; ++} ++ ++/* ++ * ======== PROC_Load ======== ++ * Purpose: ++ * Reset a processor and load a new base program image. ++ * This will be an OEM-only function, and not part of the DSP/BIOS Bridge ++ * application developer's API. ++ */ ++DSP_STATUS PROC_Load(DSP_HPROCESSOR hProcessor, IN CONST s32 iArgc, ++ IN CONST char **aArgv, IN CONST char **aEnvp) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProcessor; ++ struct IO_MGR *hIOMgr; /* IO manager handle */ ++ struct MSG_MGR *hMsgMgr; ++ struct COD_MANAGER *hCodMgr; /* Code manager handle */ ++ char *pargv0; /* temp argv[0] ptr */ ++ char **newEnvp; /* Updated envp[] array. */ ++ char szProcID[MAXPROCIDLEN]; /* Size of "PROC_ID=" */ ++ s32 cEnvp; /* Num elements in envp[]. */ ++ s32 cNewEnvp; /* " " in newEnvp[] */ ++ s32 nProcID = 0; /* Anticipate MP version. */ ++ struct DCD_MANAGER *hDCDHandle; ++ struct DMM_OBJECT *hDmmMgr; ++ u32 dwExtEnd; ++ u32 uProcId; ++#ifdef DEBUG ++ BRD_STATUS uBrdState; ++#endif ++#ifdef OPT_LOAD_TIME_INSTRUMENTATION ++ struct timeval tv1; ++ struct timeval tv2; ++#endif ++ DBC_Require(cRefs > 0); ++ DBC_Require(iArgc > 0); ++ DBC_Require(aArgv != NULL); ++#ifdef OPT_LOAD_TIME_INSTRUMENTATION ++ do_gettimeofday(&tv1); ++#endif ++#if defined(CONFIG_BRIDGE_DVFS) && !defined(CONFIG_CPU_FREQ) ++ struct dspbridge_platform_data *pdata = ++ omap_dspbridge_dev->dev.platform_data; ++#endif ++ GT_2trace(PROC_DebugMask, GT_ENTER, "Entered PROC_Load, args:\n\t" ++ "hProcessor: 0x%x\taArgv: 0x%x\n", hProcessor, aArgv[0]); ++ /* Call the WMD_BRD_Load Fxn */ ++ if (!MEM_IsValidHandle(pProcObject, PROC_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ GT_0trace(PROC_DebugMask, GT_1CLASS, ++ "PROC_Load: Invalid Processor Handle..\n"); ++ goto func_end; ++ } ++ if (pProcObject->bIsAlreadyAttached) { ++ status = DSP_EATTACHED; ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Load Abort becuase a GPP " ++ "Client is already attached status 0x%x \n", status); ++ goto func_end; ++ } ++ if (DSP_FAILED(DEV_GetCodMgr(pProcObject->hDevObject, &hCodMgr))) { ++ status = DSP_EFAIL; ++ GT_1trace(PROC_DebugMask, GT_7CLASS, "PROC_Load: DSP_FAILED in " ++ "DEV_GetCodMgr status 0x%x \n", status); ++ goto func_end; ++ } ++ status = PROC_Stop(hProcessor); ++ if (DSP_FAILED(status)) { ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Load: DSP_FAILED to Place the" ++ " Processor in Stop Mode(PROC_STOP) status 0x%x \n", ++ status); ++ goto func_end; ++ } ++ /* Place the board in the monitor state. */ ++ status = PROC_Monitor(hProcessor); ++ if (DSP_FAILED(status)) { ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Load: DSP_FAILED to Place the" ++ " Processor in Monitor Mode(PROC_IDLE) status 0x%x\n", ++ status); ++ goto func_end; ++ } ++ /* Save ptr to original argv[0]. */ ++ pargv0 = (char *)aArgv[0]; ++ /*Prepend "PROC_ID="to envp array for target.*/ ++ cEnvp = GetEnvpCount((char **)aEnvp); ++ cNewEnvp = (cEnvp ? (cEnvp + 1) : (cEnvp + 2)); ++ newEnvp = MEM_Calloc(cNewEnvp * sizeof(char **), MEM_PAGED); ++ if (newEnvp) { ++ status = snprintf(szProcID, MAXPROCIDLEN, PROC_ENVPROCID, ++ nProcID); ++ if (status == -1) { ++ GT_0trace(PROC_DebugMask, GT_7CLASS, "PROC_Load: " ++ "Proc ID string overflow \n"); ++ status = DSP_EFAIL; ++ } else { ++ newEnvp = PrependEnvp(newEnvp, (char **)aEnvp, cEnvp, ++ cNewEnvp, szProcID); ++ /* Get the DCD Handle */ ++ status = MGR_GetDCDHandle(pProcObject->hMgrObject, ++ (u32 *)&hDCDHandle); ++ if (DSP_SUCCEEDED(status)) { ++ /* Before proceeding with new load, ++ * check if a previously registered COFF ++ * exists. ++ * If yes, unregister nodes in previously ++ * registered COFF. If any error occurred, ++ * set previously registered COFF to NULL. */ ++ if (pProcObject->g_pszLastCoff != NULL) { ++ status = DCD_AutoUnregister(hDCDHandle, ++ pProcObject->g_pszLastCoff); ++ /* Regardless of auto unregister status, ++ * free previously allocated ++ * memory. */ ++ MEM_Free(pProcObject->g_pszLastCoff); ++ pProcObject->g_pszLastCoff = NULL; ++ } ++ } ++ /* On success, do COD_OpenBase() */ ++ status = COD_OpenBase(hCodMgr, (char *)aArgv[0], ++ COD_SYMB); ++ if (DSP_FAILED(status)) { ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Load: COD_OpenBase " ++ "failed (0x%x)\n", status); ++ } ++ } ++ } else { ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ " PROC_Load:Out of Memory \n"); ++ status = DSP_EMEMORY; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Auto-register data base */ ++ /* Get the DCD Handle */ ++ status = MGR_GetDCDHandle(pProcObject->hMgrObject, ++ (u32 *)&hDCDHandle); ++ if (DSP_SUCCEEDED(status)) { ++ /* Auto register nodes in specified COFF ++ * file. If registration did not fail, ++ * (status = DSP_SOK or DSP_EDCDNOAUTOREGISTER) ++ * save the name of the COFF file for ++ * de-registration in the future. */ ++ status = DCD_AutoRegister(hDCDHandle, (char *)aArgv[0]); ++ if (status == DSP_EDCDNOAUTOREGISTER) { ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Load: No Auto " ++ "Register section. Proceeding..\n"); ++ status = DSP_SOK; ++ } ++ if (DSP_FAILED(status)) { ++ status = DSP_EFAIL; ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Load: Failed to " ++ "Auto Register..\n"); ++ } else { ++ DBC_Assert(pProcObject->g_pszLastCoff == NULL); ++ /* Allocate memory for pszLastCoff */ ++ pProcObject->g_pszLastCoff = MEM_Calloc( ++ (strlen((char *)aArgv[0]) + 1), ++ MEM_PAGED); ++ /* If memory allocated, save COFF file name*/ ++ if (pProcObject->g_pszLastCoff) { ++ strncpy(pProcObject->g_pszLastCoff, ++ (char *)aArgv[0], ++ (strlen((char *)aArgv[0]) + 1)); ++ } ++ } ++ } ++ } ++ /* Update shared memory address and size */ ++ if (DSP_SUCCEEDED(status)) { ++ /* Create the message manager. This must be done ++ * before calling the IOOnLoaded function. */ ++ DEV_GetMsgMgr(pProcObject->hDevObject, &hMsgMgr); ++ if (!hMsgMgr) { ++ status = MSG_Create(&hMsgMgr, pProcObject->hDevObject, ++ (MSG_ONEXIT)NODE_OnExit); ++ DBC_Assert(DSP_SUCCEEDED(status)); ++ DEV_SetMsgMgr(pProcObject->hDevObject, hMsgMgr); ++ } ++ if (status == DSP_ENOTIMPL) { ++ /* It's OK not to have a message manager */ ++ status = DSP_SOK; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Set the Device object's message manager */ ++ status = DEV_GetIOMgr(pProcObject->hDevObject, &hIOMgr); ++ DBC_Assert(DSP_SUCCEEDED(status)); ++ status = (*pProcObject->pIntfFxns->pfnIOOnLoaded)(hIOMgr); ++ if (status == DSP_ENOTIMPL) { ++ /* Ok not to implement this function */ ++ status = DSP_SOK; ++ } else { ++ if (DSP_FAILED(status)) { ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Load: Failed to get shared " ++ "memory or message buffer address " ++ "from COFF status 0x%x\n", status); ++ status = DSP_EFAIL; ++ } ++ } ++ } else { ++ status = DSP_EFAIL; ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Load: DSP_FAILED in " ++ "MSG_Create status 0x%x\n", status); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Now, attempt to load an exec: */ ++ ++ /* Boost the OPP level to Maximum level supported by baseport*/ ++#if defined(CONFIG_BRIDGE_DVFS) && !defined(CONFIG_CPU_FREQ) ++ if (pdata->cpu_set_freq) ++ (*pdata->cpu_set_freq)(pdata->mpu_speed[VDD1_OPP5]); ++#endif ++ status = COD_LoadBase(hCodMgr, iArgc, (char **)aArgv, ++ DEV_BrdWriteFxn, ++ pProcObject->hDevObject, NULL); ++ if (DSP_FAILED(status)) { ++ if (status == COD_E_OPENFAILED) { ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Load:Failure to Load the EXE\n"); ++ } ++ if (status == COD_E_SYMBOLNOTFOUND) { ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Load:Could not parse the file\n"); ++ } else { ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Load: DSP_FAILED in " ++ "COD_Load status 0x%x \n", status); ++ } ++ } ++ /* Requesting the lowest opp supported*/ ++#if defined(CONFIG_BRIDGE_DVFS) && !defined(CONFIG_CPU_FREQ) ++ if (pdata->cpu_set_freq) ++ (*pdata->cpu_set_freq)(pdata->mpu_speed[VDD1_OPP1]); ++#endif ++ ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Update the Processor status to loaded */ ++ status = (*pProcObject->pIntfFxns->pfnBrdSetState) ++ (pProcObject->hWmdContext, BRD_LOADED); ++ if (DSP_SUCCEEDED(status)) { ++ pProcObject->sState = PROC_LOADED; ++ if (pProcObject->hNtfy) { ++ PROC_NotifyClients(pProcObject, ++ DSP_PROCESSORSTATECHANGE); ++ } ++ } else { ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Load, pfnBrdSetState " ++ "failed: 0x%x\n", status); ++ status = DSP_EFAIL; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = PROC_GetProcessorId(hProcessor, &uProcId); ++ if (uProcId == DSP_UNIT) { ++ /* Use all available DSP address space after EXTMEM ++ * for DMM */ ++ if (DSP_SUCCEEDED(status)) { ++ status = COD_GetSymValue(hCodMgr, EXTEND, ++ &dwExtEnd); ++ if (DSP_FAILED(status)) { ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Load: Failed on " ++ "COD_GetSymValue %s.\n", ++ EXTEND); ++ } ++ } ++ /* Reset DMM structs and add an initial free chunk*/ ++ if (DSP_SUCCEEDED(status)) { ++ status = DEV_GetDmmMgr(pProcObject->hDevObject, ++ &hDmmMgr); ++ if (DSP_SUCCEEDED(status)) { ++ /* Set dwExtEnd to DMM START u8 ++ * address */ ++ dwExtEnd = (dwExtEnd + 1) * DSPWORDSIZE; ++ /* DMM memory is from EXT_END */ ++ status = DMM_CreateTables(hDmmMgr, ++ dwExtEnd, DMMPOOLSIZE); ++ } ++ } ++ } ++ } ++ /* Restore the original argv[0] */ ++ MEM_Free(newEnvp); ++ aArgv[0] = pargv0; ++#ifdef DEBUG ++ if (DSP_SUCCEEDED(status)) { ++ if (DSP_SUCCEEDED((*pProcObject->pIntfFxns->pfnBrdStatus) ++ (pProcObject->hWmdContext, &uBrdState))) { ++ GT_0trace(PROC_DebugMask, GT_1CLASS, ++ "PROC_Load: Processor Loaded\n"); ++ DBC_Assert(uBrdState == BRD_LOADED); ++ } ++ } ++#endif ++func_end: ++#ifdef DEBUG ++ if (DSP_FAILED(status)) { ++ GT_0trace(PROC_DebugMask, GT_1CLASS, "PROC_Load: " ++ "Processor Load Failed.\n"); ++ ++ } ++#endif ++ GT_1trace(PROC_DebugMask, GT_ENTER, ++ "Exiting PROC_Load, status: 0x%x\n", status); ++ DBC_Ensure((DSP_SUCCEEDED(status) && pProcObject->sState == PROC_LOADED) ++ || DSP_FAILED(status)); ++#ifdef OPT_LOAD_TIME_INSTRUMENTATION ++ do_gettimeofday(&tv2); ++ if (tv2.tv_usec < tv1.tv_usec) { ++ tv2.tv_usec += 1000000; ++ tv2.tv_sec--; ++ } ++ GT_2trace(PROC_DebugMask, GT_1CLASS, ++ "Proc_Load: time to load %d sec and %d usec \n", ++ tv2.tv_sec - tv1.tv_sec, tv2.tv_usec - tv1.tv_usec); ++#endif ++ return status; ++} ++ ++/* ++ * ======== PROC_Map ======== ++ * Purpose: ++ * Maps a MPU buffer to DSP address space. ++ */ ++DSP_STATUS PROC_Map(DSP_HPROCESSOR hProcessor, void *pMpuAddr, u32 ulSize, ++ void *pReqAddr, void **ppMapAddr, u32 ulMapAttr) ++{ ++ u32 vaAlign; ++ u32 paAlign; ++ struct DMM_OBJECT *hDmmMgr; ++ u32 sizeAlign; ++ DSP_STATUS status = DSP_SOK; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProcessor; ++ ++#ifndef RES_CLEANUP_DISABLE ++ u32 hProcess; ++ HANDLE pCtxt = NULL; ++ HANDLE hDrvObject; ++ HANDLE dmmRes; ++ DSP_STATUS res_status = DSP_SOK; ++#endif ++ ++ GT_6trace(PROC_DebugMask, GT_ENTER, "Entered PROC_Map, args:\n\t" ++ "hProcessor %x, pMpuAddr %x, ulSize %x, pReqAddr %x, " ++ "ulMapAttr %x, ppMapAddr %x\n", hProcessor, pMpuAddr, ulSize, ++ pReqAddr, ulMapAttr, ppMapAddr); ++ /* Calculate the page-aligned PA, VA and size */ ++ vaAlign = PG_ALIGN_LOW((u32) pReqAddr, PG_SIZE_4K); ++ paAlign = PG_ALIGN_LOW((u32) pMpuAddr, PG_SIZE_4K); ++ sizeAlign = PG_ALIGN_HIGH(ulSize + (u32)pMpuAddr - paAlign, ++ PG_SIZE_4K); ++ ++ GT_3trace(PROC_DebugMask, GT_ENTER, "PROC_Map: vaAlign %x, paAlign %x, " ++ "sizeAlign %x\n", vaAlign, paAlign, sizeAlign); ++ ++ /* Critical section */ ++ (void)SYNC_EnterCS(hProcLock); ++ status = DMM_GetHandle(pProcObject, &hDmmMgr); ++ if (DSP_FAILED(status)) { ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Map: Failed to get DMM Mgr " ++ "handle: 0x%x\n", status); ++ } else { ++ status = DMM_MapMemory(hDmmMgr, vaAlign, sizeAlign); ++ } ++ /* Add mapping to the page tables. */ ++ if (DSP_SUCCEEDED(status)) { ++ ++ status = (*pProcObject->pIntfFxns->pfnBrdMemMap) ++ (pProcObject->hWmdContext, paAlign, vaAlign, sizeAlign, ++ ulMapAttr); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Mapped address = MSB of VA | LSB of PA */ ++ *ppMapAddr = (void *) (vaAlign | ((u32) pMpuAddr & ++ (PG_SIZE_4K - 1))); ++ } else { ++ DMM_UnMapMemory(hDmmMgr, vaAlign, &sizeAlign); ++ } ++ (void)SYNC_LeaveCS(hProcLock); ++ ++#ifndef RES_CLEANUP_DISABLE ++ if (DSP_SUCCEEDED(status)) { ++ /* Update the node and stream resource status */ ++ /* Return PID instead of process handle */ ++ hProcess = current->pid; ++ ++ res_status = CFG_GetObject((u32 *)&hDrvObject, ++ REG_DRV_OBJECT); ++ if (DSP_SUCCEEDED(res_status)) { ++ if (DRV_GetProcContext(hProcess, ++ (struct DRV_OBJECT *)hDrvObject, &pCtxt, NULL, ++ (u32)pMpuAddr) != DSP_ENOTFOUND) { ++ DRV_InsertDMMResElement(&dmmRes, pCtxt); ++ DRV_UpdateDMMResElement(dmmRes, (u32)pMpuAddr, ++ ulSize, (u32)pReqAddr, ++ (u32)*ppMapAddr, hProcessor); ++ } ++ } ++ } ++#endif ++ GT_1trace(PROC_DebugMask, GT_ENTER, "Leaving PROC_Map [0x%x]", status); ++ return status; ++} ++ ++/* ++ * ======== PROC_RegisterNotify ======== ++ * Purpose: ++ * Register to be notified of specific processor events. ++ */ ++DSP_STATUS PROC_RegisterNotify(DSP_HPROCESSOR hProcessor, u32 uEventMask, ++ u32 uNotifyType, struct DSP_NOTIFICATION ++ *hNotification) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProcessor; ++ struct DEH_MGR *hDehMgr; ++ ++ DBC_Require(hNotification != NULL); ++ DBC_Require(cRefs > 0); ++ ++ GT_4trace(PROC_DebugMask, GT_ENTER, ++ "Entered PROC_RegisterNotify, args:\n\t" ++ "hProcessor: 0x%x\n\tuEventMask: 0x%x\n\tuNotifyMask:" ++ " 0x%x\n\t hNotification 0x%x\n", hProcessor, uEventMask, ++ uNotifyType, hNotification); ++ ++ /* Check processor handle */ ++ if (!MEM_IsValidHandle(pProcObject, PROC_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_RegsiterNotify Invalid " ++ "ProcessorHandle 0x%x\n", hProcessor); ++ goto func_end; ++ } ++ /* Check if event mask is a valid processor related event */ ++ if (uEventMask & ~(DSP_PROCESSORSTATECHANGE | DSP_PROCESSORATTACH | ++ DSP_PROCESSORDETACH | DSP_PROCESSORRESTART | DSP_MMUFAULT | ++ DSP_SYSERROR)) ++ status = DSP_EVALUE; ++ ++ /* Check if notify type is valid */ ++ if (uNotifyType != DSP_SIGNALEVENT) ++ status = DSP_EVALUE; ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* * If event mask is not DSP_SYSERROR or DSP_MMUFAULT, ++ * then register event immediately. */ ++ if (uEventMask & ~(DSP_SYSERROR | DSP_MMUFAULT)) { ++ status = NTFY_Register(pProcObject->hNtfy, ++ hNotification, uEventMask, uNotifyType); ++ /* * Special case alert, special case alert! ++ * If we're trying to *deregister* (i.e. uEventMask ++ * is 0), a DSP_SYSERROR or DSP_MMUFAULT notification, ++ * we have to deregister with the DEH manager. ++ * There's no way to know, based on uEventMask which ++ * manager the notification event was registered with, ++ * so if we're trying to deregister and NTFY_Register ++ * failed, we'll give the deh manager a shot. ++ */ ++ if ((uEventMask == 0) && DSP_FAILED(status)) { ++ status = DEV_GetDehMgr(pProcObject->hDevObject, ++ &hDehMgr); ++ DBC_Assert(pProcObject->pIntfFxns-> ++ pfnDehRegisterNotify); ++ status = (*pProcObject->pIntfFxns-> ++ pfnDehRegisterNotify) ++ (hDehMgr, uEventMask, uNotifyType, ++ hNotification); ++ } ++ } else { ++ status = DEV_GetDehMgr(pProcObject->hDevObject, ++ &hDehMgr); ++ DBC_Assert(pProcObject->pIntfFxns-> ++ pfnDehRegisterNotify); ++ status = (*pProcObject->pIntfFxns->pfnDehRegisterNotify) ++ (hDehMgr, uEventMask, uNotifyType, ++ hNotification); ++ if (DSP_FAILED(status)) ++ status = DSP_EFAIL; ++ ++ } ++ } ++func_end: ++ return status; ++} ++ ++/* ++ * ======== PROC_ReserveMemory ======== ++ * Purpose: ++ * Reserve a virtually contiguous region of DSP address space. ++ */ ++DSP_STATUS PROC_ReserveMemory(DSP_HPROCESSOR hProcessor, u32 ulSize, ++ void **ppRsvAddr) ++{ ++ struct DMM_OBJECT *hDmmMgr; ++ DSP_STATUS status = DSP_SOK; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProcessor; ++ ++ GT_3trace(PROC_DebugMask, GT_ENTER, ++ "Entered PROC_ReserveMemory, args:\n\t" ++ "hProcessor: 0x%x ulSize: 0x%x ppRsvAddr: 0x%x\n", hProcessor, ++ ulSize, ppRsvAddr); ++ status = DMM_GetHandle(pProcObject, &hDmmMgr); ++ if (DSP_FAILED(status)) { ++ GT_1trace(PROC_DebugMask, GT_7CLASS, "PROC_ReserveMemory: " ++ "Failed to get DMM Mgr handle: 0x%x\n", status); ++ } else ++ status = DMM_ReserveMemory(hDmmMgr, ulSize, (u32 *)ppRsvAddr); ++ ++ GT_1trace(PROC_DebugMask, GT_ENTER, "Leaving PROC_ReserveMemory [0x%x]", ++ status); ++ return status; ++} ++ ++/* ++ * ======== PROC_Start ======== ++ * Purpose: ++ * Start a processor running. ++ */ ++DSP_STATUS PROC_Start(DSP_HPROCESSOR hProcessor) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProcessor; ++ struct COD_MANAGER *hCodMgr; /* Code manager handle */ ++ u32 dwDspAddr; /* Loaded code's entry point. */ ++#ifdef DEBUG ++ BRD_STATUS uBrdState; ++#endif ++ DBC_Require(cRefs > 0); ++ GT_1trace(PROC_DebugMask, GT_ENTER, "Entered PROC_Start, args:\n\t" ++ "hProcessor: 0x%x\n", hProcessor); ++ if (!MEM_IsValidHandle(pProcObject, PROC_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Start :InValid Handle \n"); ++ goto func_end; ++ } ++ /* Call the WMD_BRD_Start */ ++ if (pProcObject->sState != PROC_LOADED) { ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Start :Wrong state \n"); ++ status = DSP_EWRONGSTATE; ++ goto func_end; ++ } ++ status = DEV_GetCodMgr(pProcObject->hDevObject, &hCodMgr); ++ if (DSP_FAILED(status)) { ++ status = DSP_EFAIL; ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "Processor Start DSP_FAILED " ++ "in Getting DEV_GetCodMgr status 0x%x\n", status); ++ goto func_cont; ++ } ++ status = COD_GetEntry(hCodMgr, &dwDspAddr); ++ if (DSP_FAILED(status)) { ++ status = DSP_EFAIL; ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "Processor Start DSP_FAILED in " ++ "Getting COD_GetEntry status 0x%x\n", status); ++ goto func_cont; ++ } ++ status = (*pProcObject->pIntfFxns->pfnBrdStart) ++ (pProcObject->hWmdContext, dwDspAddr); ++ if (DSP_FAILED(status)) { ++ status = DSP_EFAIL; ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Start Failed to Start the board\n"); ++ goto func_cont; ++ } ++ /* Call DEV_Create2 */ ++ status = DEV_Create2(pProcObject->hDevObject); ++ if (DSP_SUCCEEDED(status)) { ++ pProcObject->sState = PROC_RUNNING; ++ /* Deep sleep switces off the peripheral clocks. ++ * we just put the DSP CPU in idle in the idle loop. ++ * so there is no need to send a command to DSP */ ++ ++ if (pProcObject->hNtfy) { ++ PROC_NotifyClients(pProcObject, ++ DSP_PROCESSORSTATECHANGE); ++ } ++ GT_0trace(PROC_DebugMask, GT_1CLASS, "PROC_Start: Processor " ++ "Started and running \n"); ++ } else { ++ /* Failed to Create Node Manager and DISP Object ++ * Stop the Processor from running. Put it in STOPPED State */ ++ (void)(*pProcObject->pIntfFxns->pfnBrdStop)(pProcObject-> ++ hWmdContext); ++ status = DSP_EFAIL; ++ pProcObject->sState = PROC_STOPPED; ++ GT_0trace(PROC_DebugMask, GT_7CLASS, "PROC_Start " ++ "Failed to Create the Node Manager\n"); ++ } ++func_cont: ++#ifdef DEBUG ++ if (DSP_SUCCEEDED(status)) { ++ if (DSP_SUCCEEDED((*pProcObject->pIntfFxns->pfnBrdStatus) ++ (pProcObject->hWmdContext, &uBrdState))) { ++ GT_0trace(PROC_DebugMask, GT_1CLASS, ++ "PROC_Start: Processor State is RUNNING \n"); ++ DBC_Assert(uBrdState != BRD_HIBERNATION); ++ } ++ } ++#endif ++func_end: ++ GT_1trace(PROC_DebugMask, GT_ENTER, ++ "Exiting PROC_Start, status 0x%x\n", status); ++ DBC_Ensure((DSP_SUCCEEDED(status) && pProcObject->sState == ++ PROC_RUNNING) || DSP_FAILED(status)); ++ return status; ++} ++ ++/* ++ * ======== PROC_Stop ======== ++ * Purpose: ++ * Stop a processor running. ++ */ ++DSP_STATUS PROC_Stop(DSP_HPROCESSOR hProcessor) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProcessor; ++ struct MSG_MGR *hMsgMgr; ++ struct NODE_MGR *hNodeMgr; ++ DSP_HNODE hNode; ++ u32 uNodeTabSize = 1; ++ u32 uNumNodes = 0; ++ u32 uNodesAllocated = 0; ++#ifdef DEBUG ++ BRD_STATUS uBrdState; ++#endif ++ DBC_Require(cRefs > 0); ++ GT_1trace(PROC_DebugMask, GT_ENTER, "Entered PROC_Stop, args:\n\t" ++ "hProcessor: 0x%x\n", hProcessor); ++ if (!MEM_IsValidHandle(pProcObject, PROC_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Stop :InValid Handle \n"); ++ goto func_end; ++ } ++ /* check if there are any running nodes */ ++ status = DEV_GetNodeManager(pProcObject->hDevObject, &hNodeMgr); ++ if (DSP_SUCCEEDED(status) && hNodeMgr) { ++ status = NODE_EnumNodes(hNodeMgr, &hNode, uNodeTabSize, ++ &uNumNodes, &uNodesAllocated); ++ if ((status == DSP_ESIZE) || (uNodesAllocated > 0)) { ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "Can't stop device, Active " ++ "nodes = 0x%x \n", uNodesAllocated); ++ return DSP_EWRONGSTATE; ++ } ++ } ++ /* Call the WMD_BRD_Stop */ ++ /* It is OK to stop a device that does n't have nodes OR not started */ ++ status = (*pProcObject->pIntfFxns->pfnBrdStop)(pProcObject-> ++ hWmdContext); ++ if (DSP_SUCCEEDED(status)) { ++ GT_0trace(PROC_DebugMask, GT_1CLASS, ++ "PROC_Stop: Processor Stopped, " ++ "i.e in standby mode \n"); ++ pProcObject->sState = PROC_STOPPED; ++ /* Destory the Node Manager, MSG Manager */ ++ if (DSP_SUCCEEDED(DEV_Destroy2(pProcObject->hDevObject))) { ++ /* Destroy the MSG by calling MSG_Delete */ ++ DEV_GetMsgMgr(pProcObject->hDevObject, &hMsgMgr); ++ if (hMsgMgr) { ++ MSG_Delete(hMsgMgr); ++ DEV_SetMsgMgr(pProcObject->hDevObject, NULL); ++ } ++#ifdef DEBUG ++ if (DSP_SUCCEEDED((*pProcObject->pIntfFxns-> ++ pfnBrdStatus)(pProcObject->hWmdContext, ++ &uBrdState))) { ++ GT_0trace(PROC_DebugMask, GT_1CLASS, ++ "PROC_Monitor:Processor Stopped \n"); ++ DBC_Assert(uBrdState == BRD_STOPPED); ++ } ++#endif ++ } else { ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Stop Couldn't delete node manager \n"); ++ } ++ } else { ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Stop Failed to Stop the processor/device \n"); ++ } ++func_end: ++ GT_1trace(PROC_DebugMask, GT_ENTER, "Exiting PROC_Stop, status 0x%x\n", ++ status); ++ ++ return status; ++} ++ ++/* ++ * ======== PROC_UnMap ======== ++ * Purpose: ++ * Removes a MPU buffer mapping from the DSP address space. ++ */ ++DSP_STATUS PROC_UnMap(DSP_HPROCESSOR hProcessor, void *pMapAddr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProcessor; ++ struct DMM_OBJECT *hDmmMgr; ++ u32 vaAlign; ++ u32 sizeAlign; ++#ifndef RES_CLEANUP_DISABLE ++ u32 hProcess; ++ HANDLE pCtxt = NULL; ++ HANDLE hDrvObject; ++ HANDLE dmmRes; ++ DSP_STATUS res_status = DSP_SOK; ++#endif ++ GT_2trace(PROC_DebugMask, GT_ENTER, ++ "Entered PROC_UnMap, args:\n\thProcessor:" ++ "0x%x pMapAddr: 0x%x\n", hProcessor, pMapAddr); ++ ++ vaAlign = PG_ALIGN_LOW((u32) pMapAddr, PG_SIZE_4K); ++ ++ status = DMM_GetHandle(hProcessor, &hDmmMgr); ++ /* Critical section */ ++ (void)SYNC_EnterCS(hProcLock); ++ if (DSP_FAILED(status)) { ++ GT_1trace(PROC_DebugMask, GT_7CLASS, "PROC_UnMap: " ++ "Failed to get DMM Mgr handle: 0x%x\n", status); ++ } else { ++ /* Update DMM structures. Get the size to unmap. ++ This function returns error if the VA is not mapped */ ++ status = DMM_UnMapMemory(hDmmMgr, (u32) vaAlign, &sizeAlign); ++ } ++ /* Remove mapping from the page tables. */ ++ if (DSP_SUCCEEDED(status)) { ++ status = (*pProcObject->pIntfFxns->pfnBrdMemUnMap) ++ (pProcObject->hWmdContext, vaAlign, sizeAlign); ++ } ++ (void)SYNC_LeaveCS(hProcLock); ++#ifndef RES_CLEANUP_DISABLE ++ GT_1trace(PROC_DebugMask, GT_ENTER, ++ "PROC_UnMap DRV_GetDMMResElement " ++ "pMapAddr:[0x%x]", pMapAddr); ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ /* Update the node and stream resource status */ ++ /* Return PID instead of process handle */ ++ hProcess = current->pid; ++ ++ res_status = CFG_GetObject((u32 *)&hDrvObject, REG_DRV_OBJECT); ++ if (DSP_FAILED(res_status)) ++ goto func_end; ++ ++ DRV_GetProcContext(hProcess, (struct DRV_OBJECT *)hDrvObject, ++ &pCtxt, NULL, (u32)pMapAddr); ++ if (pCtxt != NULL) { ++ if (DRV_GetDMMResElement((u32)pMapAddr, &dmmRes, pCtxt) != ++ DSP_ENOTFOUND) ++ DRV_RemoveDMMResElement(dmmRes, pCtxt); ++ } ++func_end: ++#endif ++ GT_1trace(PROC_DebugMask, GT_ENTER, ++ "Leaving PROC_UnMap [0x%x]", status); ++ return status; ++} ++ ++/* ++ * ======== PROC_UnReserveMemory ======== ++ * Purpose: ++ * Frees a previously reserved region of DSP address space. ++ */ ++DSP_STATUS PROC_UnReserveMemory(DSP_HPROCESSOR hProcessor, void *pRsvAddr) ++{ ++ struct DMM_OBJECT *hDmmMgr; ++ DSP_STATUS status = DSP_SOK; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProcessor; ++ ++ GT_2trace(PROC_DebugMask, GT_ENTER, ++ "Entered PROC_UnReserveMemory, args:\n\t" ++ "hProcessor: 0x%x pRsvAddr: 0x%x\n", hProcessor, pRsvAddr); ++ ++ status = DMM_GetHandle(pProcObject, &hDmmMgr); ++ if (DSP_FAILED(status)) ++ GT_1trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_UnReserveMemory: Failed to get DMM Mgr " ++ "handle: 0x%x\n", status); ++ else ++ status = DMM_UnReserveMemory(hDmmMgr, (u32) pRsvAddr); ++ ++ GT_1trace(PROC_DebugMask, GT_ENTER, ++ "Leaving PROC_UnReserveMemory [0x%x]", ++ status); ++ ++ return status; ++} ++ ++/* ++ * ======== = PROC_Monitor ======== == ++ * Purpose: ++ * Place the Processor in Monitor State. This is an internal ++ * function and a requirement before Processor is loaded. ++ * This does a WMD_BRD_Stop, DEV_Destroy2 and WMD_BRD_Monitor. ++ * In DEV_Destroy2 we delete the node manager. ++ * Parameters: ++ * hProcObject: Handle to Processor Object ++ * Returns: ++ * DSP_SOK: Processor placed in monitor mode. ++ * !DSP_SOK: Failed to place processor in monitor mode. ++ * Requires: ++ * Valid Processor Handle ++ * Ensures: ++ * Success: ProcObject state is PROC_IDLE ++ */ ++static DSP_STATUS PROC_Monitor(struct PROC_OBJECT *hProcObject) ++{ ++ DSP_STATUS status = DSP_EFAIL; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProcObject; ++ struct MSG_MGR *hMsgMgr; ++#ifdef DEBUG ++ BRD_STATUS uBrdState; ++#endif ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(pProcObject, PROC_SIGNATURE)); ++ ++ GT_1trace(PROC_DebugMask, GT_ENTER, "Entered PROC_Monitor, args:\n\t" ++ "hProcessor: 0x%x\n", hProcObject); ++ /* This is needed only when Device is loaded when it is ++ * already 'ACTIVE' */ ++ /* Destory the Node Manager, MSG Manager */ ++ if (DSP_SUCCEEDED(DEV_Destroy2(pProcObject->hDevObject))) { ++ /* Destroy the MSG by calling MSG_Delete */ ++ DEV_GetMsgMgr(pProcObject->hDevObject, &hMsgMgr); ++ if (hMsgMgr) { ++ MSG_Delete(hMsgMgr); ++ DEV_SetMsgMgr(pProcObject->hDevObject, NULL); ++ } ++ } ++ /* Place the Board in the Monitor State */ ++ if (DSP_SUCCEEDED((*pProcObject->pIntfFxns->pfnBrdMonitor) ++ (pProcObject->hWmdContext))) { ++ status = DSP_SOK; ++#ifdef DEBUG ++ if (DSP_SUCCEEDED((*pProcObject->pIntfFxns->pfnBrdStatus) ++ (pProcObject->hWmdContext, &uBrdState))) { ++ GT_0trace(PROC_DebugMask, GT_1CLASS, ++ "PROC_Monitor:Processor in " ++ "Monitor State\n"); ++ DBC_Assert(uBrdState == BRD_IDLE); ++ } ++#endif ++ } else { ++ /* Monitor Failure */ ++ GT_0trace(PROC_DebugMask, GT_7CLASS, ++ "PROC_Monitor: Processor Could not" ++ "be put in Monitor mode \n"); ++ } ++ GT_1trace(PROC_DebugMask, GT_ENTER, ++ "Exiting PROC_Monitor, status 0x%x\n", ++ status); ++#ifdef DEBUG ++ DBC_Ensure((DSP_SUCCEEDED(status) && uBrdState == BRD_IDLE) || ++ DSP_FAILED(status)); ++#endif ++ return status; ++} ++ ++/* ++ * ======== GetEnvpCount ======== ++ * Purpose: ++ * Return the number of elements in the envp array, including the ++ * terminating NULL element. ++ */ ++static s32 GetEnvpCount(char **envp) ++{ ++ s32 cRetval = 0; ++ if (envp) { ++ while (*envp++) ++ cRetval++; ++ ++ cRetval += 1; /* Include the terminating NULL in the count. */ ++ } ++ ++ return cRetval; ++} ++ ++/* ++ * ======== PrependEnvp ======== ++ * Purpose: ++ * Prepend an environment variable=value pair to the new envp array, and ++ * copy in the existing var=value pairs in the old envp array. ++ */ ++static char **PrependEnvp(char **newEnvp, char **envp, s32 cEnvp, s32 cNewEnvp, ++ char *szVar) ++{ ++ char **ppEnvp = newEnvp; ++ ++ DBC_Require(newEnvp); ++ ++ /* Prepend new environ var=value string */ ++ *newEnvp++ = szVar; ++ ++ /* Copy user's environment into our own. */ ++ while (cEnvp--) ++ *newEnvp++ = *envp++; ++ ++ /* Ensure NULL terminates the new environment strings array. */ ++ if (cEnvp == 0) ++ *newEnvp = NULL; ++ ++ return ppEnvp; ++} ++ ++/* ++ * ======== PROC_NotifyClients ======== ++ * Purpose: ++ * Notify the processor the events. ++ */ ++DSP_STATUS PROC_NotifyClients(DSP_HPROCESSOR hProc, u32 uEvents) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProc; ++ ++ DBC_Require(MEM_IsValidHandle(pProcObject, PROC_SIGNATURE)); ++ DBC_Require(IsValidProcEvent(uEvents)); ++ DBC_Require(cRefs > 0); ++ ++ NTFY_Notify(pProcObject->hNtfy, uEvents); ++ GT_0trace(PROC_DebugMask, GT_1CLASS, ++ "PROC_NotifyClients :Signaled. \n"); ++ ++ return status; ++} ++ ++/* ++ * ======== PROC_NotifyAllClients ======== ++ * Purpose: ++ * Notify the processor the events. This includes notifying all clients ++ * attached to a particulat DSP. ++ */ ++DSP_STATUS PROC_NotifyAllClients(DSP_HPROCESSOR hProc, u32 uEvents) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProc; ++ ++ DBC_Require(MEM_IsValidHandle(pProcObject, PROC_SIGNATURE)); ++ DBC_Require(IsValidProcEvent(uEvents)); ++ DBC_Require(cRefs > 0); ++ ++ DEV_NotifyClients(pProcObject->hDevObject, uEvents); ++ ++ GT_0trace(PROC_DebugMask, GT_1CLASS, ++ "PROC_NotifyAllClients :Signaled. \n"); ++ ++ return status; ++} ++ ++/* ++ * ======== PROC_GetProcessorId ======== ++ * Purpose: ++ * Retrieves the processor ID. ++ */ ++DSP_STATUS PROC_GetProcessorId(DSP_HPROCESSOR hProc, u32 *procID) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct PROC_OBJECT *pProcObject = (struct PROC_OBJECT *)hProc; ++ ++ *procID = pProcObject->uProcessor; ++ ++ return status; ++} ++ +diff --git a/drivers/dsp/bridge/rmgr/pwr.c b/drivers/dsp/bridge/rmgr/pwr.c +new file mode 100644 +index 0000000..50a3f79 +--- /dev/null ++++ b/drivers/dsp/bridge/rmgr/pwr.c +@@ -0,0 +1,184 @@ ++/* ++ * pwr.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== PWR.c ======== ++ * PWR API for controlling DSP power states. ++ * ++ * Public Functions: ++ * PWR_SleepDSP ++ * PWR_WakeDSP ++ * ++ *! Revision History ++ *! ================ ++ *! 18-Feb-2003 vp Code review updates. ++ *! 18-Oct-2002 vp Ported to Linux platform. ++ *! 22-May-2002 sg Do PWR-to-IOCTL code mapping in PWR_SleepDSP. ++ *! 29-Apr-2002 sg Initial. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ----------------------------------- Resource Manager */ ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++ ++/* ----------------------------------- Link Driver */ ++#include ++ ++/* ++ * ======== PWR_SleepDSP ======== ++ * Send command to DSP to enter sleep state. ++ */ ++DSP_STATUS PWR_SleepDSP(IN CONST u32 sleepCode, IN CONST u32 timeout) ++{ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ struct WMD_DEV_CONTEXT *dwContext; ++ DSP_STATUS status = DSP_EFAIL; ++ struct DEV_OBJECT *hDevObject = NULL; ++ u32 ioctlcode = 0; ++ u32 arg = timeout; ++ ++ for (hDevObject = (struct DEV_OBJECT *)DRV_GetFirstDevObject(); ++ hDevObject != NULL; ++ hDevObject = ++ (struct DEV_OBJECT *)DRV_GetNextDevObject ++ ((u32)hDevObject)) { ++ if (DSP_FAILED(DEV_GetWMDContext(hDevObject, ++ (struct WMD_DEV_CONTEXT **)&dwContext))) { ++ continue; ++ } ++ if (DSP_FAILED(DEV_GetIntfFxns(hDevObject, ++ (struct WMD_DRV_INTERFACE **)&pIntfFxns))) { ++ continue; ++ } ++ if (sleepCode == PWR_DEEPSLEEP) ++ ioctlcode = WMDIOCTL_DEEPSLEEP; ++ else if (sleepCode == PWR_EMERGENCYDEEPSLEEP) ++ ioctlcode = WMDIOCTL_EMERGENCYSLEEP; ++ else ++ status = DSP_EINVALIDARG; ++ ++ if (status != DSP_EINVALIDARG) { ++ status = (*pIntfFxns->pfnDevCntrl)(dwContext, ++ ioctlcode, (void *)&arg); ++ } ++ } ++ return status; ++} ++ ++/* ++ * ======== PWR_WakeDSP ======== ++ * Send command to DSP to wake it from sleep. ++ */ ++DSP_STATUS PWR_WakeDSP(IN CONST u32 timeout) ++{ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ struct WMD_DEV_CONTEXT *dwContext; ++ DSP_STATUS status = DSP_EFAIL; ++ struct DEV_OBJECT *hDevObject = NULL; ++ u32 arg = timeout; ++ ++ for (hDevObject = (struct DEV_OBJECT *)DRV_GetFirstDevObject(); ++ hDevObject != NULL; ++ hDevObject = (struct DEV_OBJECT *)DRV_GetNextDevObject ++ ((u32)hDevObject)) { ++ if (DSP_SUCCEEDED(DEV_GetWMDContext(hDevObject, ++ (struct WMD_DEV_CONTEXT **)&dwContext))) { ++ if (DSP_SUCCEEDED(DEV_GetIntfFxns(hDevObject, ++ (struct WMD_DRV_INTERFACE **)&pIntfFxns))) { ++ status = (*pIntfFxns->pfnDevCntrl)(dwContext, ++ WMDIOCTL_WAKEUP, (void *)&arg); ++ } ++ } ++ } ++ return status; ++} ++ ++/* ++ * ======== PWR_PM_PreScale======== ++ * Sends pre-notification message to DSP. ++ */ ++DSP_STATUS PWR_PM_PreScale(IN u16 voltage_domain, u32 level) ++{ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ struct WMD_DEV_CONTEXT *dwContext; ++ DSP_STATUS status = DSP_EFAIL; ++ struct DEV_OBJECT *hDevObject = NULL; ++ u32 arg[2]; ++ ++ arg[0] = voltage_domain; ++ arg[1] = level; ++ ++ for (hDevObject = (struct DEV_OBJECT *)DRV_GetFirstDevObject(); ++ hDevObject != NULL; ++ hDevObject = (struct DEV_OBJECT *)DRV_GetNextDevObject ++ ((u32)hDevObject)) { ++ if (DSP_SUCCEEDED(DEV_GetWMDContext(hDevObject, ++ (struct WMD_DEV_CONTEXT **)&dwContext))) { ++ if (DSP_SUCCEEDED(DEV_GetIntfFxns(hDevObject, ++ (struct WMD_DRV_INTERFACE **)&pIntfFxns))) { ++ status = (*pIntfFxns->pfnDevCntrl)(dwContext, ++ WMDIOCTL_PRESCALE_NOTIFY, ++ (void *)&arg); ++ } ++ } ++ } ++ return status; ++} ++ ++/* ++ * ======== PWR_PM_PostScale======== ++ * Sends post-notification message to DSP. ++ */ ++DSP_STATUS PWR_PM_PostScale(IN u16 voltage_domain, u32 level) ++{ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ struct WMD_DEV_CONTEXT *dwContext; ++ DSP_STATUS status = DSP_EFAIL; ++ struct DEV_OBJECT *hDevObject = NULL; ++ u32 arg[2]; ++ ++ arg[0] = voltage_domain; ++ arg[1] = level; ++ ++ for (hDevObject = (struct DEV_OBJECT *)DRV_GetFirstDevObject(); ++ hDevObject != NULL; ++ hDevObject = (struct DEV_OBJECT *)DRV_GetNextDevObject ++ ((u32)hDevObject)) { ++ if (DSP_SUCCEEDED(DEV_GetWMDContext(hDevObject, ++ (struct WMD_DEV_CONTEXT **)&dwContext))) { ++ if (DSP_SUCCEEDED(DEV_GetIntfFxns(hDevObject, ++ (struct WMD_DRV_INTERFACE **)&pIntfFxns))) { ++ status = (*pIntfFxns->pfnDevCntrl)(dwContext, ++ WMDIOCTL_POSTSCALE_NOTIFY, ++ (void *)&arg); ++ } ++ } ++ } ++ return status; ++ ++} ++ ++ +diff --git a/drivers/dsp/bridge/rmgr/rmm.c b/drivers/dsp/bridge/rmgr/rmm.c +new file mode 100644 +index 0000000..575f675 +--- /dev/null ++++ b/drivers/dsp/bridge/rmgr/rmm.c +@@ -0,0 +1,604 @@ ++/* ++ * rmm.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== rmm.c ======== ++ * Description: ++ * ++ * This memory manager provides general heap management and arbitrary ++ * alignment for any number of memory segments. ++ * ++ * Notes: ++ * ++ * Memory blocks are allocated from the end of the first free memory ++ * block large enough to satisfy the request. Alignment requirements ++ * are satisfied by "sliding" the block forward until its base satisfies ++ * the alignment specification; if this is not possible then the next ++ * free block large enough to hold the request is tried. ++ * ++ * Since alignment can cause the creation of a new free block - the ++ * unused memory formed between the start of the original free block ++ * and the start of the allocated block - the memory manager must free ++ * this memory to prevent a memory leak. ++ * ++ * Overlay memory is managed by reserving through RMM_alloc, and freeing ++ * it through RMM_free. The memory manager prevents DSP code/data that is ++ * overlayed from being overwritten as long as the memory it runs at has ++ * been allocated, and not yet freed. ++ * ++ *! Revision History ++ *! ================ ++ *! 18-Feb-2003 vp Code review updates. ++ *! 18-Oct-2002 vp Ported to Linux Platform. ++ *! 24-Sep-2002 map Updated from Code Review ++ *! 25-Jun-2002 jeh Free from segid passed to RMM_free(). ++ *! 24-Apr-2002 jeh Determine segid based on address in RMM_free(). (No way ++ *! to keep track of segid with dynamic loader library.) ++ *! 16-Oct-2001 jeh Based on gen tree rm.c. Added support for overlays. ++ */ ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++#define RMM_TARGSIGNATURE 0x544d4d52 /* "TMMR" */ ++ ++/* ++ * ======== RMM_Header ======== ++ * This header is used to maintain a list of free memory blocks. ++ */ ++struct RMM_Header { ++ struct RMM_Header *next; /* form a free memory link list */ ++ u32 size; /* size of the free memory */ ++ u32 addr; /* DSP address of memory block */ ++} ; ++ ++/* ++ * ======== RMM_OvlySect ======== ++ * Keeps track of memory occupied by overlay section. ++ */ ++struct RMM_OvlySect { ++ struct LST_ELEM listElem; ++ u32 addr; /* Start of memory section */ ++ u32 size; /* Length (target MAUs) of section */ ++ s32 page; /* Memory page */ ++}; ++ ++/* ++ * ======== RMM_TargetObj ======== ++ */ ++struct RMM_TargetObj { ++ u32 dwSignature; ++ struct RMM_Segment *segTab; ++ struct RMM_Header **freeList; ++ u32 numSegs; ++ struct LST_LIST *ovlyList; /* List of overlay memory in use */ ++}; ++ ++#if GT_TRACE ++static struct GT_Mask RMM_debugMask = { NULL, NULL }; /* GT trace variable */ ++#endif ++ ++static u32 cRefs; /* module reference count */ ++ ++static bool allocBlock(struct RMM_TargetObj *target, u32 segid, u32 size, ++ u32 align, u32 *dspAddr); ++static bool freeBlock(struct RMM_TargetObj *target, u32 segid, u32 addr, ++ u32 size); ++ ++/* ++ * ======== RMM_alloc ======== ++ */ ++DSP_STATUS RMM_alloc(struct RMM_TargetObj *target, u32 segid, u32 size, ++ u32 align, u32 *dspAddr, bool reserve) ++{ ++ struct RMM_OvlySect *sect; ++ struct RMM_OvlySect *prevSect = NULL; ++ struct RMM_OvlySect *newSect; ++ u32 addr; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(MEM_IsValidHandle(target, RMM_TARGSIGNATURE)); ++ DBC_Require(dspAddr != NULL); ++ DBC_Require(size > 0); ++ DBC_Require(reserve || (target->numSegs > 0)); ++ DBC_Require(cRefs > 0); ++ ++ GT_6trace(RMM_debugMask, GT_ENTER, ++ "RMM_alloc(0x%lx, 0x%lx, 0x%lx, 0x%lx, " ++ "0x%lx, 0x%lx)\n", target, segid, size, align, dspAddr, ++ reserve); ++ if (!reserve) { ++ if (!allocBlock(target, segid, size, align, dspAddr)) { ++ status = DSP_EMEMORY; ++ } else { ++ /* Increment the number of allocated blocks in this ++ * segment */ ++ target->segTab[segid].number++; ++ } ++ goto func_end; ++ } ++ /* An overlay section - See if block is already in use. If not, ++ * insert into the list in ascending address size. */ ++ addr = *dspAddr; ++ sect = (struct RMM_OvlySect *)LST_First(target->ovlyList); ++ /* Find place to insert new list element. List is sorted from ++ * smallest to largest address. */ ++ while (sect != NULL) { ++ if (addr <= sect->addr) { ++ /* Check for overlap with sect */ ++ if ((addr + size > sect->addr) || (prevSect && ++ (prevSect->addr + prevSect->size > addr))) { ++ status = DSP_EOVERLAYMEMORY; ++ } ++ break; ++ } ++ prevSect = sect; ++ sect = (struct RMM_OvlySect *)LST_Next(target->ovlyList, ++ (struct LST_ELEM *)sect); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* No overlap - allocate list element for new section. */ ++ newSect = MEM_Calloc(sizeof(struct RMM_OvlySect), MEM_PAGED); ++ if (newSect == NULL) { ++ status = DSP_EMEMORY; ++ } else { ++ LST_InitElem((struct LST_ELEM *)newSect); ++ newSect->addr = addr; ++ newSect->size = size; ++ newSect->page = segid; ++ if (sect == NULL) { ++ /* Put new section at the end of the list */ ++ LST_PutTail(target->ovlyList, ++ (struct LST_ELEM *)newSect); ++ } else { ++ /* Put new section just before sect */ ++ LST_InsertBefore(target->ovlyList, ++ (struct LST_ELEM *)newSect, ++ (struct LST_ELEM *)sect); ++ } ++ } ++ } ++func_end: ++ return status; ++} ++ ++/* ++ * ======== RMM_create ======== ++ */ ++DSP_STATUS RMM_create(struct RMM_TargetObj **pTarget, ++ struct RMM_Segment segTab[], u32 numSegs) ++{ ++ struct RMM_Header *hptr; ++ struct RMM_Segment *sptr, *tmp; ++ struct RMM_TargetObj *target; ++ s32 i; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(pTarget != NULL); ++ DBC_Require(numSegs == 0 || segTab != NULL); ++ ++ GT_3trace(RMM_debugMask, GT_ENTER, ++ "RMM_create(0x%lx, 0x%lx, 0x%lx)\n", ++ pTarget, segTab, numSegs); ++ ++ /* Allocate DBL target object */ ++ MEM_AllocObject(target, struct RMM_TargetObj, RMM_TARGSIGNATURE); ++ ++ if (target == NULL) { ++ GT_0trace(RMM_debugMask, GT_6CLASS, ++ "RMM_create: Memory allocation failed\n"); ++ status = DSP_EMEMORY; ++ } ++ if (DSP_FAILED(status)) ++ goto func_cont; ++ ++ target->numSegs = numSegs; ++ if (!(numSegs > 0)) ++ goto func_cont; ++ ++ /* Allocate the memory for freelist from host's memory */ ++ target->freeList = MEM_Calloc(numSegs * sizeof(struct RMM_Header *), ++ MEM_PAGED); ++ if (target->freeList == NULL) { ++ GT_0trace(RMM_debugMask, GT_6CLASS, ++ "RMM_create: Memory allocation failed\n"); ++ status = DSP_EMEMORY; ++ } else { ++ /* Allocate headers for each element on the free list */ ++ for (i = 0; i < (s32) numSegs; i++) { ++ target->freeList[i] = ++ MEM_Calloc(sizeof(struct RMM_Header), ++ MEM_PAGED); ++ if (target->freeList[i] == NULL) { ++ GT_0trace(RMM_debugMask, GT_6CLASS, ++ "RMM_create: Memory " ++ "allocation failed\n"); ++ status = DSP_EMEMORY; ++ break; ++ } ++ } ++ /* Allocate memory for initial segment table */ ++ target->segTab = MEM_Calloc(numSegs * ++ sizeof(struct RMM_Segment), MEM_PAGED); ++ if (target->segTab == NULL) { ++ GT_0trace(RMM_debugMask, GT_6CLASS, ++ "RMM_create: Memory allocation failed\n"); ++ status = DSP_EMEMORY; ++ } else { ++ /* Initialize segment table and free list */ ++ sptr = target->segTab; ++ for (i = 0, tmp = segTab; numSegs > 0; numSegs--, i++) { ++ *sptr = *tmp; ++ hptr = target->freeList[i]; ++ hptr->addr = tmp->base; ++ hptr->size = tmp->length; ++ hptr->next = NULL; ++ tmp++; ++ sptr++; ++ } ++ } ++ } ++func_cont: ++ /* Initialize overlay memory list */ ++ if (DSP_SUCCEEDED(status)) { ++ target->ovlyList = LST_Create(); ++ if (target->ovlyList == NULL) { ++ GT_0trace(RMM_debugMask, GT_6CLASS, ++ "RMM_create: Memory allocation failed\n"); ++ status = DSP_EMEMORY; ++ } ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ *pTarget = target; ++ } else { ++ *pTarget = NULL; ++ if (target) ++ RMM_delete(target); ++ ++ } ++ ++ DBC_Ensure((DSP_SUCCEEDED(status) && MEM_IsValidHandle((*pTarget), ++ RMM_TARGSIGNATURE)) || (DSP_FAILED(status) && *pTarget == ++ NULL)); ++ ++ return status; ++} ++ ++/* ++ * ======== RMM_delete ======== ++ */ ++void RMM_delete(struct RMM_TargetObj *target) ++{ ++ struct RMM_OvlySect *pSect; ++ struct RMM_Header *hptr; ++ struct RMM_Header *next; ++ u32 i; ++ ++ DBC_Require(MEM_IsValidHandle(target, RMM_TARGSIGNATURE)); ++ ++ GT_1trace(RMM_debugMask, GT_ENTER, "RMM_delete(0x%lx)\n", target); ++ ++ if (target->segTab != NULL) ++ MEM_Free(target->segTab); ++ ++ if (target->ovlyList) { ++ while ((pSect = (struct RMM_OvlySect *)LST_GetHead ++ (target->ovlyList))) { ++ MEM_Free(pSect); ++ } ++ DBC_Assert(LST_IsEmpty(target->ovlyList)); ++ LST_Delete(target->ovlyList); ++ } ++ ++ if (target->freeList != NULL) { ++ /* Free elements on freelist */ ++ for (i = 0; i < target->numSegs; i++) { ++ hptr = next = target->freeList[i]; ++ while (next) { ++ hptr = next; ++ next = hptr->next; ++ MEM_Free(hptr); ++ } ++ } ++ MEM_Free(target->freeList); ++ } ++ ++ MEM_FreeObject(target); ++} ++ ++/* ++ * ======== RMM_exit ======== ++ */ ++void RMM_exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ cRefs--; ++ ++ GT_1trace(RMM_debugMask, GT_5CLASS, "RMM_exit() ref count: 0x%x\n", ++ cRefs); ++ ++ if (cRefs == 0) ++ MEM_Exit(); ++ ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== RMM_free ======== ++ */ ++bool RMM_free(struct RMM_TargetObj *target, u32 segid, u32 addr, u32 size, ++ bool reserved) ++ ++{ ++ struct RMM_OvlySect *sect; ++ bool retVal = true; ++ ++ DBC_Require(MEM_IsValidHandle(target, RMM_TARGSIGNATURE)); ++ ++ DBC_Require(reserved || segid < target->numSegs); ++ DBC_Require(reserved || (addr >= target->segTab[segid].base && ++ (addr + size) <= (target->segTab[segid].base + ++ target->segTab[segid].length))); ++ ++ GT_5trace(RMM_debugMask, GT_ENTER, ++ "RMM_free(0x%lx, 0x%lx, 0x%lx, 0x%lx, " ++ "0x%lx)\n", target, segid, addr, size, reserved); ++ /* ++ * Free or unreserve memory. ++ */ ++ if (!reserved) { ++ retVal = freeBlock(target, segid, addr, size); ++ if (retVal) ++ target->segTab[segid].number--; ++ ++ } else { ++ /* Unreserve memory */ ++ sect = (struct RMM_OvlySect *)LST_First(target->ovlyList); ++ while (sect != NULL) { ++ if (addr == sect->addr) { ++ DBC_Assert(size == sect->size); ++ /* Remove from list */ ++ LST_RemoveElem(target->ovlyList, ++ (struct LST_ELEM *)sect); ++ MEM_Free(sect); ++ break; ++ } ++ sect = (struct RMM_OvlySect *)LST_Next(target->ovlyList, ++ (struct LST_ELEM *)sect); ++ } ++ if (sect == NULL) ++ retVal = false; ++ ++ } ++ return retVal; ++} ++ ++/* ++ * ======== RMM_init ======== ++ */ ++bool RMM_init(void) ++{ ++ bool retVal = true; ++ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++ DBC_Assert(!RMM_debugMask.flags); ++ GT_create(&RMM_debugMask, "RM"); /* "RM" for RMm */ ++ ++ retVal = MEM_Init(); ++ ++ if (!retVal) ++ MEM_Exit(); ++ ++ } ++ ++ if (retVal) ++ cRefs++; ++ ++ GT_1trace(RMM_debugMask, GT_5CLASS, ++ "RMM_init(), ref count: 0x%x\n", ++ cRefs); ++ ++ DBC_Ensure((retVal && (cRefs > 0)) || (!retVal && (cRefs >= 0))); ++ ++ return retVal; ++} ++ ++/* ++ * ======== RMM_stat ======== ++ */ ++bool RMM_stat(struct RMM_TargetObj *target, enum DSP_MEMTYPE segid, ++ struct DSP_MEMSTAT *pMemStatBuf) ++{ ++ struct RMM_Header *head; ++ bool retVal = false; ++ u32 maxFreeSize = 0; ++ u32 totalFreeSize = 0; ++ u32 freeBlocks = 0; ++ ++ DBC_Require(pMemStatBuf != NULL); ++ DBC_Assert(target != NULL); ++ ++ if ((u32) segid < target->numSegs) { ++ head = target->freeList[segid]; ++ ++ /* Collect data from freeList */ ++ while (head != NULL) { ++ maxFreeSize = max(maxFreeSize, head->size); ++ totalFreeSize += head->size; ++ freeBlocks++; ++ head = head->next; ++ } ++ ++ /* ulSize */ ++ pMemStatBuf->ulSize = target->segTab[segid].length; ++ ++ /* ulNumFreeBlocks */ ++ pMemStatBuf->ulNumFreeBlocks = freeBlocks; ++ ++ /* ulTotalFreeSize */ ++ pMemStatBuf->ulTotalFreeSize = totalFreeSize; ++ ++ /* ulLenMaxFreeBlock */ ++ pMemStatBuf->ulLenMaxFreeBlock = maxFreeSize; ++ ++ /* ulNumAllocBlocks */ ++ pMemStatBuf->ulNumAllocBlocks = target->segTab[segid].number; ++ ++ retVal = true; ++ } ++ ++ return retVal; ++} ++ ++/* ++ * ======== balloc ======== ++ * This allocation function allocates memory from the lowest addresses ++ * first. ++ */ ++static bool allocBlock(struct RMM_TargetObj *target, u32 segid, u32 size, ++ u32 align, u32 *dspAddr) ++{ ++ struct RMM_Header *head; ++ struct RMM_Header *prevhead = NULL; ++ struct RMM_Header *next; ++ u32 tmpalign; ++ u32 alignbytes; ++ u32 hsize; ++ u32 allocsize; ++ u32 addr; ++ ++ alignbytes = (align == 0) ? 1 : align; ++ prevhead = NULL; ++ head = target->freeList[segid]; ++ ++ do { ++ hsize = head->size; ++ next = head->next; ++ ++ addr = head->addr; /* alloc from the bottom */ ++ ++ /* align allocation */ ++ (tmpalign = (u32) addr % alignbytes); ++ if (tmpalign != 0) ++ tmpalign = alignbytes - tmpalign; ++ ++ allocsize = size + tmpalign; ++ ++ if (hsize >= allocsize) { /* big enough */ ++ if (hsize == allocsize && prevhead != NULL) { ++ prevhead->next = next; ++ MEM_Free(head); ++ } else { ++ head->size = hsize - allocsize; ++ head->addr += allocsize; ++ } ++ ++ /* free up any hole created by alignment */ ++ if (tmpalign) ++ freeBlock(target, segid, addr, tmpalign); ++ ++ *dspAddr = addr + tmpalign; ++ return true; ++ } ++ ++ prevhead = head; ++ head = next; ++ ++ } while (head != NULL); ++ ++ return false; ++} ++ ++/* ++ * ======== freeBlock ======== ++ * TO DO: freeBlock() allocates memory, which could result in failure. ++ * Could allocate an RMM_Header in RMM_alloc(), to be kept in a pool. ++ * freeBlock() could use an RMM_Header from the pool, freeing as blocks ++ * are coalesced. ++ */ ++static bool freeBlock(struct RMM_TargetObj *target, u32 segid, u32 addr, ++ u32 size) ++{ ++ struct RMM_Header *head; ++ struct RMM_Header *thead; ++ struct RMM_Header *rhead; ++ bool retVal = true; ++ ++ /* Create a memory header to hold the newly free'd block. */ ++ rhead = MEM_Calloc(sizeof(struct RMM_Header), MEM_PAGED); ++ if (rhead == NULL) { ++ retVal = false; ++ } else { ++ /* search down the free list to find the right place for addr */ ++ head = target->freeList[segid]; ++ ++ if (addr >= head->addr) { ++ while (head->next != NULL && addr > head->next->addr) ++ head = head->next; ++ ++ thead = head->next; ++ ++ head->next = rhead; ++ rhead->next = thead; ++ rhead->addr = addr; ++ rhead->size = size; ++ } else { ++ *rhead = *head; ++ head->next = rhead; ++ head->addr = addr; ++ head->size = size; ++ thead = rhead->next; ++ } ++ ++ /* join with upper block, if possible */ ++ if (thead != NULL && (rhead->addr + rhead->size) == ++ thead->addr) { ++ head->next = rhead->next; ++ thead->size = size + thead->size; ++ thead->addr = addr; ++ MEM_Free(rhead); ++ rhead = thead; ++ } ++ ++ /* join with the lower block, if possible */ ++ if ((head->addr + head->size) == rhead->addr) { ++ head->next = rhead->next; ++ head->size = head->size + rhead->size; ++ MEM_Free(rhead); ++ } ++ } ++ ++ return retVal; ++} ++ +diff --git a/drivers/dsp/bridge/rmgr/strm.c b/drivers/dsp/bridge/rmgr/strm.c +new file mode 100644 +index 0000000..bd55fd3 +--- /dev/null ++++ b/drivers/dsp/bridge/rmgr/strm.c +@@ -0,0 +1,1066 @@ ++/* ++ * strm.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== strm.c ======== ++ * Description: ++ * DSP/BIOS Bridge Stream Manager. ++ * ++ * Public Functions: ++ * STRM_AllocateBuffer ++ * STRM_Close ++ * STRM_Create ++ * STRM_Delete ++ * STRM_Exit ++ * STRM_FreeBuffer ++ * STRM_GetEventHandle ++ * STRM_GetInfo ++ * STRM_Idle ++ * STRM_Init ++ * STRM_Issue ++ * STRM_Open ++ * STRM_PrepareBuffer ++ * STRM_Reclaim ++ * STRM_RegisterNotify ++ * STRM_Select ++ * STRM_UnprepareBuffer ++ * ++ * Notes: ++ * ++ *! Revision History: ++ *! ================= ++ *! 18-Feb-2003 vp Code review updates. ++ *! 18-Oct-2002 vp Ported to Linux platform. ++ *! 13-Mar-2002 map pStrm init'd to NULL in STRM_Open to prevent error ++ *! 12-Mar-2002 map Changed return var to WSX "wStatus" instead of "status" ++ *! in DEV and CMM function calls to avoid confusion. ++ *! Return DSP_SOK instead of S_OK from API fxns. ++ *! 12-Mar-2002 map Changed FAILED(..) to DSP_FAILED(..) ++ *! 25-Jan-2002 ag Allow neg seg ids(e.g. DSP_SHMSEG0) to denote SM. ++ *! 15-Nov-2001 ag Added STRMMODE & SM for DMA/ZCopy streaming. ++ *! Changed DSP_STREAMINFO to STRM_INFO in STRM_GetInfo(). ++ *! Use strm timeout value for dma flush timeout. ++ *! 09-May-2001 jeh Code review cleanup. ++ *! 06-Feb-2001 kc Updated DBC_Ensure in STRM_Select to check timeout. ++ *! 23-Oct-2000 jeh Allow NULL STRM_ATTRS passed to STRM_Open() for DLL ++ *! tests to pass. ++ *! 25-Sep-2000 jeh Created. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++ ++/* ----------------------------------- Mini Driver */ ++#include ++ ++/* ----------------------------------- Resource Manager */ ++#include ++ ++/* ----------------------------------- Others */ ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++#ifndef RES_CLEANUP_DISABLE ++#include ++#include ++#include ++#endif ++ ++/* ----------------------------------- Defines, Data Structures, Typedefs */ ++#define STRM_SIGNATURE 0x4d525453 /* "MRTS" */ ++#define STRMMGR_SIGNATURE 0x5254534d /* "RTSM" */ ++ ++#define DEFAULTTIMEOUT 10000 ++#define DEFAULTNUMBUFS 2 ++ ++/* ++ * ======== STRM_MGR ======== ++ * The STRM_MGR contains device information needed to open the underlying ++ * channels of a stream. ++ */ ++struct STRM_MGR { ++ u32 dwSignature; ++ struct DEV_OBJECT *hDev; /* Device for this processor */ ++ struct CHNL_MGR *hChnlMgr; /* Channel manager */ ++ struct WMD_DRV_INTERFACE *pIntfFxns; /* Function interface to WMD */ ++ struct SYNC_CSOBJECT *hSync; /* For critical sections */ ++} ; ++ ++/* ++ * ======== STRM_OBJECT ======== ++ * This object is allocated in STRM_Open(). ++ */ ++ struct STRM_OBJECT { ++ u32 dwSignature; ++ struct STRM_MGR *hStrmMgr; ++ struct CHNL_OBJECT *hChnl; ++ u32 uDir; /* DSP_TONODE or DSP_FROMNODE */ ++ u32 uTimeout; ++ u32 uNumBufs; /* Max # of bufs allowed in stream */ ++ u32 uNBufsInStrm; /* Current # of bufs in stream */ ++ u32 ulNBytes; /* bytes transferred since idled */ ++ enum DSP_STREAMSTATE strmState; /* STREAM_IDLE, STREAM_READY, ... */ ++ HANDLE hUserEvent; /* Saved for STRM_GetInfo() */ ++ enum DSP_STRMMODE lMode; /* STRMMODE_[PROCCOPY][ZEROCOPY]... */ ++ u32 uDMAChnlId; /* DMA chnl id */ ++ u32 uDMAPriority; /* DMA priority:DMAPRI_[LOW][HIGH] */ ++ u32 uSegment; /* >0 is SM segment.=0 is local heap */ ++ u32 uAlignment; /* Alignment for stream bufs */ ++ struct CMM_XLATOROBJECT *hXlator; /* Stream's SM address translator */ ++} ; ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask STRM_debugMask = { NULL, NULL }; /* GT trace variable */ ++#endif ++static u32 cRefs; /* module reference count */ ++ ++/* ----------------------------------- Function Prototypes */ ++static DSP_STATUS DeleteStrm(struct STRM_OBJECT *hStrm); ++static void DeleteStrmMgr(struct STRM_MGR *hStrmMgr); ++ ++/* ++ * ======== STRM_AllocateBuffer ======== ++ * Purpose: ++ * Allocates buffers for a stream. ++ */ ++DSP_STATUS STRM_AllocateBuffer(struct STRM_OBJECT *hStrm, u32 uSize, ++ OUT u8 **apBuffer, u32 uNumBufs) ++{ ++ DSP_STATUS status = DSP_SOK; ++ u32 uAllocated = 0; ++ u32 i; ++ #ifndef RES_CLEANUP_DISABLE ++ DSP_STATUS res_status = DSP_SOK; ++ u32 hProcess; ++ HANDLE pCtxt = NULL; ++ HANDLE hDrvObject; ++ HANDLE hSTRMRes; ++ #endif ++ DBC_Require(cRefs > 0); ++ DBC_Require(apBuffer != NULL); ++ ++ GT_4trace(STRM_debugMask, GT_ENTER, "STRM_AllocateBuffer: hStrm: 0x%x\t" ++ "uSize: 0x%x\tapBuffer: 0x%x\tuNumBufs: 0x%x\n", ++ hStrm, uSize, apBuffer, uNumBufs); ++ if (MEM_IsValidHandle(hStrm, STRM_SIGNATURE)) { ++ /* ++ * Allocate from segment specified at time of stream open. ++ */ ++ if (uSize == 0) ++ status = DSP_ESIZE; ++ ++ } ++ if (DSP_FAILED(status)) { ++ status = DSP_EHANDLE; ++ goto func_end; ++ } ++ for (i = 0; i < uNumBufs; i++) { ++ DBC_Assert(hStrm->hXlator != NULL); ++ (void)CMM_XlatorAllocBuf(hStrm->hXlator, &apBuffer[i], uSize); ++ if (apBuffer[i] == NULL) { ++ GT_0trace(STRM_debugMask, GT_7CLASS, ++ "STRM_AllocateBuffer: " ++ "DSP_FAILED to alloc shared memory.\n"); ++ status = DSP_EMEMORY; ++ uAllocated = i; ++ break; ++ } ++ } ++ if (DSP_FAILED(status)) ++ STRM_FreeBuffer(hStrm, apBuffer, uAllocated); ++ ++#ifndef RES_CLEANUP_DISABLE ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ /* Return PID instead of process handle */ ++ hProcess = current->pid; ++ ++ res_status = CFG_GetObject((u32 *)&hDrvObject, REG_DRV_OBJECT); ++ if (DSP_FAILED(res_status)) ++ goto func_end; ++ ++ DRV_GetProcContext(hProcess, (struct DRV_OBJECT *)hDrvObject, ++ &pCtxt, NULL, 0); ++ if (pCtxt != NULL) { ++ if (DRV_GetSTRMResElement(hStrm, &hSTRMRes, pCtxt) != ++ DSP_ENOTFOUND) { ++ DRV_ProcUpdateSTRMRes(uNumBufs, hSTRMRes, pCtxt); ++ } ++ } ++#endif ++func_end: ++ return status; ++} ++ ++/* ++ * ======== STRM_Close ======== ++ * Purpose: ++ * Close a stream opened with STRM_Open(). ++ */ ++DSP_STATUS STRM_Close(struct STRM_OBJECT *hStrm) ++{ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ struct CHNL_INFO chnlInfo; ++ DSP_STATUS status = DSP_SOK; ++ ++ ++#ifndef RES_CLEANUP_DISABLE ++ u32 hProcess; ++ HANDLE pCtxt = NULL; ++ HANDLE hDrvObject; ++ HANDLE hSTRMRes; ++ DSP_STATUS res_status = DSP_SOK; ++#endif ++ ++ ++ DBC_Require(cRefs > 0); ++ ++ GT_1trace(STRM_debugMask, GT_ENTER, "STRM_Close: hStrm: 0x%x\n", hStrm); ++ ++ if (!MEM_IsValidHandle(hStrm, STRM_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ } else { ++ /* Have all buffers been reclaimed? If not, return ++ * DSP_EPENDING */ ++ pIntfFxns = hStrm->hStrmMgr->pIntfFxns; ++ status = (*pIntfFxns->pfnChnlGetInfo) (hStrm->hChnl, &chnlInfo); ++ DBC_Assert(DSP_SUCCEEDED(status)); ++ ++ if (chnlInfo.cIOCs > 0 || chnlInfo.cIOReqs > 0) { ++ status = DSP_EPENDING; ++ } else { ++ ++ status = DeleteStrm(hStrm); ++ ++ if (DSP_FAILED(status)) { ++ /* we already validated the handle. */ ++ DBC_Assert(status != DSP_EHANDLE); ++ ++ /* make sure we return a documented result */ ++ status = DSP_EFAIL; ++ } ++ } ++ } ++#ifndef RES_CLEANUP_DISABLE ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ /* Update the node and stream resource status */ ++ /* Return PID instead of process handle */ ++ hProcess = current->pid; ++ ++ res_status = CFG_GetObject((u32 *)&hDrvObject, REG_DRV_OBJECT); ++ if (DSP_FAILED(res_status)) ++ goto func_end; ++ ++ DRV_GetProcContext(hProcess, (struct DRV_OBJECT *)hDrvObject, ++ &pCtxt, NULL, 0); ++ if (pCtxt != NULL) { ++ if (DRV_GetSTRMResElement(hStrm, &hSTRMRes, pCtxt) != ++ DSP_ENOTFOUND) { ++ DRV_ProcRemoveSTRMResElement(hSTRMRes, pCtxt); ++ } ++ } ++func_end: ++#endif ++ DBC_Ensure(status == DSP_SOK || status == DSP_EHANDLE || ++ status == DSP_EPENDING || status == DSP_EFAIL); ++ ++ return status; ++} ++ ++/* ++ * ======== STRM_Create ======== ++ * Purpose: ++ * Create a STRM manager object. ++ */ ++DSP_STATUS STRM_Create(OUT struct STRM_MGR **phStrmMgr, struct DEV_OBJECT *hDev) ++{ ++ struct STRM_MGR *pStrmMgr; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(phStrmMgr != NULL); ++ DBC_Require(hDev != NULL); ++ ++ GT_2trace(STRM_debugMask, GT_ENTER, "STRM_Create: phStrmMgr: " ++ "0x%x\thDev: 0x%x\n", phStrmMgr, hDev); ++ *phStrmMgr = NULL; ++ /* Allocate STRM manager object */ ++ MEM_AllocObject(pStrmMgr, struct STRM_MGR, STRMMGR_SIGNATURE); ++ if (pStrmMgr == NULL) { ++ status = DSP_EMEMORY; ++ GT_0trace(STRM_debugMask, GT_6CLASS, "STRM_Create: " ++ "MEM_AllocObject() failed!\n "); ++ } else { ++ pStrmMgr->hDev = hDev; ++ } ++ /* Get Channel manager and WMD function interface */ ++ if (DSP_SUCCEEDED(status)) { ++ status = DEV_GetChnlMgr(hDev, &(pStrmMgr->hChnlMgr)); ++ if (DSP_SUCCEEDED(status)) { ++ (void) DEV_GetIntfFxns(hDev, &(pStrmMgr->pIntfFxns)); ++ DBC_Assert(pStrmMgr->pIntfFxns != NULL); ++ } else { ++ GT_1trace(STRM_debugMask, GT_6CLASS, "STRM_Create: " ++ "Failed to get channel manager! status = " ++ "0x%x\n", status); ++ } ++ } ++ if (DSP_SUCCEEDED(status)) ++ status = SYNC_InitializeCS(&pStrmMgr->hSync); ++ ++ if (DSP_SUCCEEDED(status)) ++ *phStrmMgr = pStrmMgr; ++ else ++ DeleteStrmMgr(pStrmMgr); ++ ++ DBC_Ensure(DSP_SUCCEEDED(status) && ++ (MEM_IsValidHandle((*phStrmMgr), STRMMGR_SIGNATURE) || ++ (DSP_FAILED(status) && *phStrmMgr == NULL))); ++ ++ return status; ++} ++ ++/* ++ * ======== STRM_Delete ======== ++ * Purpose: ++ * Delete the STRM Manager Object. ++ */ ++void STRM_Delete(struct STRM_MGR *hStrmMgr) ++{ ++ DBC_Require(cRefs > 0); ++ DBC_Require(MEM_IsValidHandle(hStrmMgr, STRMMGR_SIGNATURE)); ++ ++ GT_1trace(STRM_debugMask, GT_ENTER, "STRM_Delete: hStrmMgr: 0x%x\n", ++ hStrmMgr); ++ ++ DeleteStrmMgr(hStrmMgr); ++ ++ DBC_Ensure(!MEM_IsValidHandle(hStrmMgr, STRMMGR_SIGNATURE)); ++} ++ ++/* ++ * ======== STRM_Exit ======== ++ * Purpose: ++ * Discontinue usage of STRM module. ++ */ ++void STRM_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ cRefs--; ++ ++ GT_1trace(STRM_debugMask, GT_5CLASS, ++ "Entered STRM_Exit, ref count: 0x%x\n", cRefs); ++ ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== STRM_FreeBuffer ======== ++ * Purpose: ++ * Frees the buffers allocated for a stream. ++ */ ++DSP_STATUS STRM_FreeBuffer(struct STRM_OBJECT *hStrm, u8 **apBuffer, ++ u32 uNumBufs) ++{ ++ DSP_STATUS status = DSP_SOK; ++ u32 i = 0; ++ ++ #ifndef RES_CLEANUP_DISABLE ++ DSP_STATUS res_status = DSP_SOK; ++ u32 hProcess; ++ HANDLE pCtxt = NULL; ++ HANDLE hDrvObject; ++ HANDLE hSTRMRes = NULL; ++ #endif ++ DBC_Require(cRefs > 0); ++ DBC_Require(apBuffer != NULL); ++ ++ GT_3trace(STRM_debugMask, GT_ENTER, "STRM_FreeBuffer: hStrm: 0x%x\t" ++ "apBuffer: 0x%x\tuNumBufs: 0x%x\n", hStrm, apBuffer, uNumBufs); ++ ++ if (!MEM_IsValidHandle(hStrm, STRM_SIGNATURE)) ++ status = DSP_EHANDLE; ++ ++ if (DSP_SUCCEEDED(status)) { ++ for (i = 0; i < uNumBufs; i++) { ++ DBC_Assert(hStrm->hXlator != NULL); ++ status = CMM_XlatorFreeBuf(hStrm->hXlator, apBuffer[i]); ++ if (DSP_FAILED(status)) { ++ GT_0trace(STRM_debugMask, GT_7CLASS, ++ "STRM_FreeBuffer: DSP_FAILED" ++ " to free shared memory.\n"); ++ break; ++ } ++ apBuffer[i] = NULL; ++ } ++ } ++#ifndef RES_CLEANUP_DISABLE ++ /* Update the node and stream resource status */ ++ /* Return PID instead of process handle */ ++ hProcess = current->pid; ++ ++ res_status = CFG_GetObject((u32 *)&hDrvObject, REG_DRV_OBJECT); ++ if (DSP_SUCCEEDED(res_status)) { ++ DRV_GetProcContext(hProcess, ++ (struct DRV_OBJECT *)hDrvObject, &pCtxt, ++ NULL, 0); ++ if (pCtxt != NULL) { ++ if (DRV_GetSTRMResElement(hStrm, hSTRMRes, pCtxt) != ++ DSP_ENOTFOUND) { ++ DRV_ProcUpdateSTRMRes(uNumBufs-i, hSTRMRes, ++ pCtxt); ++ } ++ } ++ } ++#endif ++ return status; ++} ++ ++/* ++ * ======== STRM_GetInfo ======== ++ * Purpose: ++ * Retrieves information about a stream. ++ */ ++DSP_STATUS STRM_GetInfo(struct STRM_OBJECT *hStrm, ++ OUT struct STRM_INFO *pStreamInfo, ++ u32 uStreamInfoSize) ++{ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ struct CHNL_INFO chnlInfo; ++ DSP_STATUS status = DSP_SOK; ++ void *pVirtBase = NULL; /* NULL if no SM used */ ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pStreamInfo != NULL); ++ DBC_Require(uStreamInfoSize >= sizeof(struct STRM_INFO)); ++ ++ GT_3trace(STRM_debugMask, GT_ENTER, "STRM_GetInfo: hStrm: 0x%x\t" ++ "pStreamInfo: 0x%x\tuStreamInfoSize: 0x%x\n", hStrm, ++ pStreamInfo, uStreamInfoSize); ++ if (!MEM_IsValidHandle(hStrm, STRM_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ } else { ++ if (uStreamInfoSize < sizeof(struct STRM_INFO)) { ++ /* size of users info */ ++ status = DSP_ESIZE; ++ } ++ } ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ pIntfFxns = hStrm->hStrmMgr->pIntfFxns; ++ status = (*pIntfFxns->pfnChnlGetInfo) (hStrm->hChnl, &chnlInfo); ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ if (hStrm->hXlator) { ++ /* We have a translator */ ++ DBC_Assert(hStrm->uSegment > 0); ++ CMM_XlatorInfo(hStrm->hXlator, (u8 **)&pVirtBase, 0, ++ hStrm->uSegment, false); ++ } ++ pStreamInfo->uSegment = hStrm->uSegment; ++ pStreamInfo->lMode = hStrm->lMode; ++ pStreamInfo->pVirtBase = pVirtBase; ++ pStreamInfo->pUser->uNumberBufsAllowed = hStrm->uNumBufs; ++ pStreamInfo->pUser->uNumberBufsInStream = chnlInfo.cIOCs + ++ chnlInfo.cIOReqs; ++ /* # of bytes transferred since last call to DSPStream_Idle() */ ++ pStreamInfo->pUser->ulNumberBytes = chnlInfo.cPosition; ++ pStreamInfo->pUser->hSyncObjectHandle = chnlInfo.hEvent; ++ /* Determine stream state based on channel state and info */ ++ if (chnlInfo.dwState & CHNL_STATEEOS) { ++ pStreamInfo->pUser->ssStreamState = STREAM_DONE; ++ } else { ++ if (chnlInfo.cIOCs > 0) ++ pStreamInfo->pUser->ssStreamState = STREAM_READY; ++ else if (chnlInfo.cIOReqs > 0) ++ pStreamInfo->pUser->ssStreamState = STREAM_PENDING; ++ else ++ pStreamInfo->pUser->ssStreamState = STREAM_IDLE; ++ ++ } ++func_end: ++ return status; ++} ++ ++/* ++ * ======== STRM_Idle ======== ++ * Purpose: ++ * Idles a particular stream. ++ */ ++DSP_STATUS STRM_Idle(struct STRM_OBJECT *hStrm, bool fFlush) ++{ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ ++ GT_2trace(STRM_debugMask, GT_ENTER, "STRM_Idle: hStrm: 0x%x\t" ++ "fFlush: 0x%x\n", hStrm, fFlush); ++ ++ if (!MEM_IsValidHandle(hStrm, STRM_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ } else { ++ pIntfFxns = hStrm->hStrmMgr->pIntfFxns; ++ ++ status = (*pIntfFxns->pfnChnlIdle) (hStrm->hChnl, ++ hStrm->uTimeout, fFlush); ++ } ++ return status; ++} ++ ++/* ++ * ======== STRM_Init ======== ++ * Purpose: ++ * Initialize the STRM module. ++ */ ++bool STRM_Init(void) ++{ ++ bool fRetVal = true; ++ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++#if GT_TRACE ++ DBC_Assert(!STRM_debugMask.flags); ++ GT_create(&STRM_debugMask, "ST"); /* "ST" for STrm */ ++#endif ++ } ++ ++ if (fRetVal) ++ cRefs++; ++ ++ GT_1trace(STRM_debugMask, GT_5CLASS, "STRM_Init(), ref count: 0x%x\n", ++ cRefs); ++ ++ DBC_Ensure((fRetVal && (cRefs > 0)) || (!fRetVal && (cRefs >= 0))); ++ ++ return fRetVal; ++} ++ ++/* ++ * ======== STRM_Issue ======== ++ * Purpose: ++ * Issues a buffer on a stream ++ */ ++DSP_STATUS STRM_Issue(struct STRM_OBJECT *hStrm, IN u8 *pBuf, u32 ulBytes, ++ u32 ulBufSize, u32 dwArg) ++{ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ DSP_STATUS status = DSP_SOK; ++ void *pTmpBuf = NULL; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pBuf != NULL); ++ ++ GT_4trace(STRM_debugMask, GT_ENTER, "STRM_Issue: hStrm: 0x%x\tpBuf: " ++ "0x%x\tulBytes: 0x%x\tdwArg: 0x%x\n", hStrm, pBuf, ulBytes, ++ dwArg); ++ if (!MEM_IsValidHandle(hStrm, STRM_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ } else { ++ pIntfFxns = hStrm->hStrmMgr->pIntfFxns; ++ ++ if (hStrm->uSegment != 0) { ++ pTmpBuf = CMM_XlatorTranslate(hStrm->hXlator, ++ (void *)pBuf, CMM_VA2DSPPA); ++ if (pTmpBuf == NULL) ++ status = DSP_ETRANSLATE; ++ ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = (*pIntfFxns->pfnChnlAddIOReq) ++ (hStrm->hChnl, pBuf, ulBytes, ulBufSize, ++ (u32) pTmpBuf, dwArg); ++ } ++ if (DSP_FAILED(status)) { ++ if (status == CHNL_E_NOIORPS) ++ status = DSP_ESTREAMFULL; ++ else ++ status = DSP_EFAIL; ++ ++ } ++ } ++ return status; ++} ++ ++/* ++ * ======== STRM_Open ======== ++ * Purpose: ++ * Open a stream for sending/receiving data buffers to/from a task or ++ * XDAIS socket node on the DSP. ++ */ ++DSP_STATUS STRM_Open(struct NODE_OBJECT *hNode, u32 uDir, u32 uIndex, ++ IN struct STRM_ATTR *pAttr, OUT struct STRM_OBJECT **phStrm) ++{ ++ struct STRM_MGR *hStrmMgr; ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ u32 ulChnlId; ++ struct STRM_OBJECT *pStrm = NULL; ++ CHNL_MODE uMode; ++ struct CHNL_ATTRS chnlAttrs; ++ DSP_STATUS status = DSP_SOK; ++ struct CMM_OBJECT *hCmmMgr = NULL; /* Shared memory manager hndl */ ++ ++ #ifndef RES_CLEANUP_DISABLE ++ DSP_STATUS res_status = DSP_SOK; ++ u32 hProcess; ++ HANDLE pCtxt = NULL; ++ HANDLE hDrvObject; ++ HANDLE hSTRMRes; ++ #endif ++ DBC_Require(cRefs > 0); ++ DBC_Require(phStrm != NULL); ++ DBC_Require(pAttr != NULL); ++ GT_5trace(STRM_debugMask, GT_ENTER, ++ "STRM_Open: hNode: 0x%x\tuDir: 0x%x\t" ++ "uIndex: 0x%x\tpAttr: 0x%x\tphStrm: 0x%x\n", ++ hNode, uDir, uIndex, pAttr, phStrm); ++ *phStrm = NULL; ++ if (uDir != DSP_TONODE && uDir != DSP_FROMNODE) { ++ status = DSP_EDIRECTION; ++ } else { ++ /* Get the channel id from the node (set in NODE_Connect()) */ ++ status = NODE_GetChannelId(hNode, uDir, uIndex, &ulChnlId); ++ } ++ if (DSP_SUCCEEDED(status)) ++ status = NODE_GetStrmMgr(hNode, &hStrmMgr); ++ ++ if (DSP_SUCCEEDED(status)) { ++ MEM_AllocObject(pStrm, struct STRM_OBJECT, STRM_SIGNATURE); ++ if (pStrm == NULL) { ++ status = DSP_EMEMORY; ++ GT_0trace(STRM_debugMask, GT_6CLASS, ++ "STRM_Open: MEM_AllocObject() failed!\n "); ++ } else { ++ pStrm->hStrmMgr = hStrmMgr; ++ pStrm->uDir = uDir; ++ pStrm->strmState = STREAM_IDLE; ++ pStrm->hUserEvent = pAttr->hUserEvent; ++ if (pAttr->pStreamAttrIn != NULL) { ++ pStrm->uTimeout = pAttr->pStreamAttrIn-> ++ uTimeout; ++ pStrm->uNumBufs = pAttr->pStreamAttrIn-> ++ uNumBufs; ++ pStrm->lMode = pAttr->pStreamAttrIn->lMode; ++ pStrm->uSegment = pAttr->pStreamAttrIn-> ++ uSegment; ++ pStrm->uAlignment = pAttr->pStreamAttrIn-> ++ uAlignment; ++ pStrm->uDMAChnlId = pAttr->pStreamAttrIn-> ++ uDMAChnlId; ++ pStrm->uDMAPriority = pAttr->pStreamAttrIn-> ++ uDMAPriority; ++ chnlAttrs.uIOReqs = pAttr->pStreamAttrIn-> ++ uNumBufs; ++ } else { ++ pStrm->uTimeout = DEFAULTTIMEOUT; ++ pStrm->uNumBufs = DEFAULTNUMBUFS; ++ pStrm->lMode = STRMMODE_PROCCOPY; ++ pStrm->uSegment = 0; /* local memory */ ++ pStrm->uAlignment = 0; ++ pStrm->uDMAChnlId = 0; ++ pStrm->uDMAPriority = 0; ++ chnlAttrs.uIOReqs = DEFAULTNUMBUFS; ++ } ++ chnlAttrs.hReserved1 = NULL; ++ /* DMA chnl flush timeout */ ++ chnlAttrs.hReserved2 = pStrm->uTimeout; ++ chnlAttrs.hEvent = NULL; ++ if (pAttr->hUserEvent != NULL) ++ chnlAttrs.hEvent = pAttr->hUserEvent; ++ ++ } ++ } ++ if (DSP_FAILED(status)) ++ goto func_cont; ++ ++ if ((pAttr->pVirtBase == NULL) || !(pAttr->ulVirtSize > 0)) ++ goto func_cont; ++ ++ DBC_Assert(pStrm->lMode != STRMMODE_LDMA); /* no System DMA */ ++ /* Get the shared mem mgr for this streams dev object */ ++ status = DEV_GetCmmMgr(hStrmMgr->hDev, &hCmmMgr); ++ if (DSP_FAILED(status)) { ++ GT_1trace(STRM_debugMask, GT_6CLASS, "STRM_Open: Failed to get " ++ "CMM Mgr handle: 0x%x\n", status); ++ } else { ++ /*Allocate a SM addr translator for this strm.*/ ++ status = CMM_XlatorCreate(&pStrm->hXlator, hCmmMgr, NULL); ++ if (DSP_FAILED(status)) { ++ GT_1trace(STRM_debugMask, GT_6CLASS, ++ "STRM_Open: Failed to " ++ "create SM translator: 0x%x\n", status); ++ } else { ++ DBC_Assert(pStrm->uSegment > 0); ++ /* Set translators Virt Addr attributes */ ++ status = CMM_XlatorInfo(pStrm->hXlator, ++ (u8 **)&pAttr->pVirtBase, pAttr->ulVirtSize, ++ pStrm->uSegment, true); ++ if (status != DSP_SOK) { ++ GT_0trace(STRM_debugMask, GT_6CLASS, ++ "STRM_Open: ERROR: " ++ "in setting CMM_XlatorInfo.\n"); ++ } ++ } ++ } ++func_cont: ++ if (DSP_SUCCEEDED(status)) { ++ /* Open channel */ ++ uMode = (uDir == DSP_TONODE) ? ++ CHNL_MODETODSP : CHNL_MODEFROMDSP; ++ pIntfFxns = hStrmMgr->pIntfFxns; ++ status = (*pIntfFxns->pfnChnlOpen) (&(pStrm->hChnl), ++ hStrmMgr->hChnlMgr, uMode, ulChnlId, &chnlAttrs); ++ if (DSP_FAILED(status)) { ++ /* ++ * over-ride non-returnable status codes so we return ++ * something documented ++ */ ++ if (status != DSP_EMEMORY && status != ++ DSP_EINVALIDARG && status != DSP_EFAIL) { ++ /* ++ * We got a status that's not return-able. ++ * Assert that we got something we were ++ * expecting (DSP_EHANDLE isn't acceptable, ++ * hStrmMgr->hChnlMgr better be valid or we ++ * assert here), and then return DSP_EFAIL. ++ */ ++ DBC_Assert(status == CHNL_E_OUTOFSTREAMS || ++ status == CHNL_E_BADCHANID || ++ status == CHNL_E_CHANBUSY || ++ status == CHNL_E_NOIORPS); ++ status = DSP_EFAIL; ++ } ++ GT_2trace(STRM_debugMask, GT_6CLASS, ++ "STRM_Open: Channel open failed, " ++ "chnl id = %d, status = 0x%x\n", ulChnlId, ++ status); ++ } ++ } ++ if (DSP_SUCCEEDED(status)) ++ *phStrm = pStrm; ++ else ++ (void)DeleteStrm(pStrm); ++ ++#ifndef RES_CLEANUP_DISABLE ++ /* Return PID instead of process handle */ ++ hProcess = current->pid; ++ ++ res_status = CFG_GetObject((u32 *)&hDrvObject, REG_DRV_OBJECT); ++ if (DSP_SUCCEEDED(res_status)) { ++ DRV_GetProcContext(hProcess, ++ (struct DRV_OBJECT *)hDrvObject, &pCtxt, ++ hNode, 0); ++ if (pCtxt != NULL) ++ DRV_ProcInsertSTRMResElement(*phStrm, &hSTRMRes, pCtxt); ++ ++ } ++#endif ++ ++ /* ensure we return a documented error code */ ++ DBC_Ensure((DSP_SUCCEEDED(status) && ++ MEM_IsValidHandle((*phStrm), STRM_SIGNATURE)) || ++ (*phStrm == NULL && (status == DSP_EHANDLE || ++ status == DSP_EDIRECTION || status == DSP_EVALUE || ++ status == DSP_EFAIL))); ++ return status; ++} ++ ++/* ++ * ======== STRM_Reclaim ======== ++ * Purpose: ++ * Relcaims a buffer from a stream. ++ */ ++DSP_STATUS STRM_Reclaim(struct STRM_OBJECT *hStrm, OUT u8 **pBufPtr, ++ u32 *pulBytes, u32 *pulBufSize, u32 *pdwArg) ++{ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ struct CHNL_IOC chnlIOC; ++ DSP_STATUS status = DSP_SOK; ++ void *pTmpBuf = NULL; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pBufPtr != NULL); ++ DBC_Require(pulBytes != NULL); ++ DBC_Require(pdwArg != NULL); ++ ++ GT_4trace(STRM_debugMask, GT_ENTER, ++ "STRM_Reclaim: hStrm: 0x%x\tpBufPtr: 0x%x" ++ "\tpulBytes: 0x%x\tpdwArg: 0x%x\n", hStrm, pBufPtr, pulBytes, ++ pdwArg); ++ ++ if (!MEM_IsValidHandle(hStrm, STRM_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ goto func_end; ++ } ++ pIntfFxns = hStrm->hStrmMgr->pIntfFxns; ++ ++ status = (*pIntfFxns->pfnChnlGetIOC)(hStrm->hChnl, hStrm->uTimeout, ++ &chnlIOC); ++ if (DSP_FAILED(status)) { ++ GT_1trace(STRM_debugMask, GT_6CLASS, ++ "STRM_Reclaim: GetIOC failed! " ++ "Status = 0x%x\n", status); ++ } else { ++ *pulBytes = chnlIOC.cBytes; ++ if (pulBufSize) ++ *pulBufSize = chnlIOC.cBufSize; ++ ++ *pdwArg = chnlIOC.dwArg; ++ if (!CHNL_IsIOComplete(chnlIOC)) { ++ if (CHNL_IsTimedOut(chnlIOC)) { ++ status = DSP_ETIMEOUT; ++ } else { ++ /* Allow reclaims after idle to succeed */ ++ if (!CHNL_IsIOCancelled(chnlIOC)) ++ status = DSP_EFAIL; ++ ++ } ++ } ++ /* Translate zerocopy buffer if channel not canceled. */ ++ if (DSP_SUCCEEDED(status) && (!CHNL_IsIOCancelled(chnlIOC)) && ++ (hStrm->lMode == STRMMODE_ZEROCOPY)) { ++ /* ++ * This is a zero-copy channel so chnlIOC.pBuf ++ * contains the DSP address of SM. We need to ++ * translate it to a virtual address for the user ++ * thread to access. ++ * Note: Could add CMM_DSPPA2VA to CMM in the future. ++ */ ++ pTmpBuf = CMM_XlatorTranslate(hStrm->hXlator, ++ chnlIOC.pBuf, CMM_DSPPA2PA); ++ if (pTmpBuf != NULL) { ++ /* now convert this GPP Pa to Va */ ++ pTmpBuf = CMM_XlatorTranslate(hStrm->hXlator, ++ pTmpBuf, CMM_PA2VA); ++ } ++ if (pTmpBuf == NULL) { ++ GT_0trace(STRM_debugMask, GT_7CLASS, ++ "STRM_Reclaim: Failed " ++ "SM translation!\n"); ++ status = DSP_ETRANSLATE; ++ } ++ chnlIOC.pBuf = pTmpBuf; ++ } ++ *pBufPtr = chnlIOC.pBuf; ++ } ++func_end: ++ /* ensure we return a documented return code */ ++ DBC_Ensure(DSP_SUCCEEDED(status) || status == DSP_EHANDLE || ++ status == DSP_ETIMEOUT || status == DSP_ETRANSLATE || ++ status == DSP_EFAIL); ++ return status; ++} ++ ++/* ++ * ======== STRM_RegisterNotify ======== ++ * Purpose: ++ * Register to be notified on specific events for this stream. ++ */ ++DSP_STATUS STRM_RegisterNotify(struct STRM_OBJECT *hStrm, u32 uEventMask, ++ u32 uNotifyType, struct DSP_NOTIFICATION ++ *hNotification) ++{ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(hNotification != NULL); ++ ++ GT_4trace(STRM_debugMask, GT_ENTER, ++ "STRM_RegisterNotify: hStrm: 0x%x\t" ++ "uEventMask: 0x%x\tuNotifyType: 0x%x\thNotification: 0x%x\n", ++ hStrm, uEventMask, uNotifyType, hNotification); ++ if (!MEM_IsValidHandle(hStrm, STRM_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ } else if ((uEventMask & ~((DSP_STREAMIOCOMPLETION) | ++ DSP_STREAMDONE)) != 0) { ++ status = DSP_EVALUE; ++ } else { ++ if (uNotifyType != DSP_SIGNALEVENT) ++ status = DSP_ENOTIMPL; ++ ++ } ++ if (DSP_SUCCEEDED(status)) { ++ pIntfFxns = hStrm->hStrmMgr->pIntfFxns; ++ ++ status = (*pIntfFxns->pfnChnlRegisterNotify)(hStrm->hChnl, ++ uEventMask, uNotifyType, hNotification); ++ } ++ /* ensure we return a documented return code */ ++ DBC_Ensure(DSP_SUCCEEDED(status) || status == DSP_EHANDLE || ++ status == DSP_ETIMEOUT || status == DSP_ETRANSLATE || ++ status == DSP_ENOTIMPL || status == DSP_EFAIL); ++ return status; ++} ++ ++/* ++ * ======== STRM_Select ======== ++ * Purpose: ++ * Selects a ready stream. ++ */ ++DSP_STATUS STRM_Select(IN struct STRM_OBJECT **aStrmTab, u32 nStrms, ++ OUT u32 *pMask, u32 uTimeout) ++{ ++ u32 uIndex; ++ struct CHNL_INFO chnlInfo; ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ struct SYNC_OBJECT **hSyncEvents = NULL; ++ u32 i; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(cRefs > 0); ++ DBC_Require(aStrmTab != NULL); ++ DBC_Require(pMask != NULL); ++ DBC_Require(nStrms > 0); ++ ++ GT_4trace(STRM_debugMask, GT_ENTER, ++ "STRM_Select: aStrmTab: 0x%x \tnStrms: " ++ "0x%x\tpMask: 0x%x\tuTimeout: 0x%x\n", aStrmTab, ++ nStrms, pMask, uTimeout); ++ *pMask = 0; ++ for (i = 0; i < nStrms; i++) { ++ if (!MEM_IsValidHandle(aStrmTab[i], STRM_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ break; ++ } ++ } ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ /* Determine which channels have IO ready */ ++ for (i = 0; i < nStrms; i++) { ++ pIntfFxns = aStrmTab[i]->hStrmMgr->pIntfFxns; ++ status = (*pIntfFxns->pfnChnlGetInfo)(aStrmTab[i]->hChnl, ++ &chnlInfo); ++ if (DSP_FAILED(status)) { ++ break; ++ } else { ++ if (chnlInfo.cIOCs > 0) ++ *pMask |= (1 << i); ++ ++ } ++ } ++ if (DSP_SUCCEEDED(status) && uTimeout > 0 && *pMask == 0) { ++ /* Non-zero timeout */ ++ hSyncEvents = (struct SYNC_OBJECT **)MEM_Alloc(nStrms * ++ sizeof(struct SYNC_OBJECT *), MEM_PAGED); ++ if (hSyncEvents == NULL) { ++ status = DSP_EMEMORY; ++ } else { ++ for (i = 0; i < nStrms; i++) { ++ pIntfFxns = aStrmTab[i]->hStrmMgr->pIntfFxns; ++ status = (*pIntfFxns->pfnChnlGetInfo) ++ (aStrmTab[i]->hChnl, &chnlInfo); ++ if (DSP_FAILED(status)) ++ break; ++ else ++ hSyncEvents[i] = chnlInfo.hSyncEvent; ++ ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = SYNC_WaitOnMultipleEvents(hSyncEvents, nStrms, ++ uTimeout, &uIndex); ++ if (DSP_SUCCEEDED(status)) { ++ /* Since we waited on the event, we have to ++ * reset it */ ++ SYNC_SetEvent(hSyncEvents[uIndex]); ++ *pMask = 1 << uIndex; ++ } ++ } ++ } ++func_end: ++ if (hSyncEvents) ++ MEM_Free(hSyncEvents); ++ ++ DBC_Ensure((DSP_SUCCEEDED(status) && (*pMask != 0 || uTimeout == 0)) || ++ (DSP_FAILED(status) && *pMask == 0)); ++ ++ return status; ++} ++ ++/* ++ * ======== DeleteStrm ======== ++ * Purpose: ++ * Frees the resources allocated for a stream. ++ */ ++static DSP_STATUS DeleteStrm(struct STRM_OBJECT *hStrm) ++{ ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ DSP_STATUS status = DSP_SOK; ++ ++ if (MEM_IsValidHandle(hStrm, STRM_SIGNATURE)) { ++ if (hStrm->hChnl) { ++ pIntfFxns = hStrm->hStrmMgr->pIntfFxns; ++ /* Channel close can fail only if the channel handle ++ * is invalid. */ ++ status = (*pIntfFxns->pfnChnlClose) (hStrm->hChnl); ++ /* Free all SM address translator resources */ ++ if (DSP_SUCCEEDED(status)) { ++ if (hStrm->hXlator) { ++ /* force free */ ++ (void)CMM_XlatorDelete(hStrm->hXlator, ++ true); ++ } ++ } ++ } ++ MEM_FreeObject(hStrm); ++ } else { ++ status = DSP_EHANDLE; ++ } ++ return status; ++} ++ ++/* ++ * ======== DeleteStrmMgr ======== ++ * Purpose: ++ * Frees stream manager. ++ */ ++static void DeleteStrmMgr(struct STRM_MGR *hStrmMgr) ++{ ++ if (MEM_IsValidHandle(hStrmMgr, STRMMGR_SIGNATURE)) { ++ ++ if (hStrmMgr->hSync) ++ SYNC_DeleteCS(hStrmMgr->hSync); ++ ++ MEM_FreeObject(hStrmMgr); ++ } ++} ++ +diff --git a/drivers/dsp/bridge/services/cfg.c b/drivers/dsp/bridge/services/cfg.c +new file mode 100644 +index 0000000..67656bf +--- /dev/null ++++ b/drivers/dsp/bridge/services/cfg.c +@@ -0,0 +1,483 @@ ++/* ++ * cfg.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== cfgce.c ======== ++ * Purpose: ++ * Implementation of platform specific config services. ++ * ++ * Private Functions: ++ * CFG_Exit ++ * CFG_GetAutoStart ++ * CFG_GetDevObject ++ * CFG_GetDSPResources ++ * CFG_GetExecFile ++ * CFG_GetHostResources ++ * CFG_GetObject ++ * CFG_Init ++ * CFG_SetDevObject ++ * CFG_SetObject ++ * ++ * ++ *! Revision History: ++ *! ================ ++ *! 26-Arp-2004 hp Support for handling more than one Device. ++ *! 26-Feb-2003 kc Removed unused CFG fxns. ++ *! 10-Nov-2000 rr: CFG_GetBoardName local var initialized. ++ *! 30-Oct-2000 kc: Changed local var. names to use Hungarian notation. ++ *! 10-Aug-2000 rr: Cosmetic changes. ++ *! 26-Jul-2000 rr: Added CFG_GetDCDName. CFG_Get/SetObject(based on a flag) ++ *! replaces CFG_GetMgrObject & CFG_SetMgrObject. ++ *! 17-Jul-2000 rr: Added CFG_GetMgrObject & CFG_SetMgrObject. ++ *! 03-Feb-2000 rr: Module init/exit is handled by SERVICES Init/Exit. ++ *! GT Changes. ++ *! 31-Jan-2000 rr: Comments and bugfixes: modified after code review ++ *! 07-Jan-2000 rr: CFG_GetBoardName Ensure class checks strlen of the ++ *! read value from the registry against the passed in BufSize; ++ *! CFG_GetZLFile,CFG_GetWMDFileName and ++ *! CFG_GetExecFile also modified same way. ++ *! 06-Jan-2000 rr: CFG_GetSearchPath & CFG_GetWinBRIDGEDir removed. ++ *! 09-Dec-1999 rr: CFG_SetDevObject stores the DevNodeString pointer. ++ *! 03-Dec-1999 rr: CFG_GetDevObject reads stored DevObject from Registry. ++ *! CFG_GetDevNode reads the Devnodestring from the registry. ++ *! CFG_SetDevObject stores the registry path as ++ *! DevNodestring in the registry. ++ *! 02-Dec-1999 rr: CFG_debugMask is declared static now. stdwin.h included ++ *! 22-Nov-1999 kc: Added windows.h to remove warnings. ++ *! 25-Oct-1999 rr: CFG_GetHostResources reads the HostResource structure ++ *! from the registry which was set by the DRV Request ++ *! Resources. ++ *! 15-Oct-1999 rr: Changes in CFG_SetPrivateDword & HostResources reflecting ++ *! changes for drv.h resource structure and wsxreg.h new ++ *! entry(DevObject) Hard coded entries removed for those items ++ *! 08-Oct-1999 rr: CFG_SetPrivateDword modified. it sets devobject into the ++ *! registry. CFG_Get HostResources modified for opening up ++ *! two mem winodws. ++ *! 24-Sep-1999 rr: CFG_GetHostResources uses hardcoded Registry calls,uses NT ++ *! type of Resource Structure. ++ *! 19-Jul-1999 a0216266: Stubbed from cfgnt.c. ++ */ ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++ ++/* ----------------------------------- Others */ ++#include ++ ++/* ----------------------------------- This */ ++#include ++#include ++ ++struct DRV_EXT { ++ struct LST_ELEM link; ++ char szString[MAXREGPATHLENGTH]; ++}; ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask CFG_debugMask = { NULL, NULL }; /* CFG debug Mask */ ++#endif ++ ++/* ++ * ======== CFG_Exit ======== ++ * Purpose: ++ * Discontinue usage of the CFG module. ++ */ ++void CFG_Exit(void) ++{ ++ GT_0trace(CFG_debugMask, GT_5CLASS, "Entered CFG_Exit\n"); ++} ++ ++/* ++ * ======== CFG_GetAutoStart ======== ++ * Purpose: ++ * Retreive the autostart mask, if any, for this board. ++ */ ++DSP_STATUS CFG_GetAutoStart(struct CFG_DEVNODE *hDevNode, ++ OUT u32 *pdwAutoStart) ++{ ++ DSP_STATUS status = DSP_SOK; ++ u32 dwBufSize; ++ GT_2trace(CFG_debugMask, GT_ENTER, ++ "Entered CFG_GetAutoStart: \n\thDevNode:" ++ "0x%x\n\tpdwAutoStart: 0x%x\n", hDevNode, pdwAutoStart); ++ dwBufSize = sizeof(*pdwAutoStart); ++ if (!hDevNode) ++ status = CFG_E_INVALIDHDEVNODE; ++ if (!pdwAutoStart) ++ status = CFG_E_INVALIDPOINTER; ++ if (DSP_SUCCEEDED(status)) { ++ status = REG_GetValue(NULL, (char *)hDevNode, AUTOSTART, ++ (u8 *)pdwAutoStart, &dwBufSize); ++ if (DSP_FAILED(status)) ++ status = CFG_E_RESOURCENOTAVAIL; ++ } ++#ifdef DEBUG ++ if (DSP_SUCCEEDED(status)) { ++ GT_0trace(CFG_debugMask, GT_1CLASS, ++ "CFG_GetAutoStart SUCCESS \n"); ++ } else { ++ GT_0trace(CFG_debugMask, GT_6CLASS, ++ "CFG_GetAutoStart Failed \n"); ++ } ++#endif ++ DBC_Ensure((status == DSP_SOK && ++ (*pdwAutoStart == 0 || *pdwAutoStart == 1)) ++ || status != DSP_SOK); ++ return status; ++} ++ ++/* ++ * ======== CFG_GetDevObject ======== ++ * Purpose: ++ * Retrieve the Device Object handle for a given devnode. ++ */ ++DSP_STATUS CFG_GetDevObject(struct CFG_DEVNODE *hDevNode, OUT u32 *pdwValue) ++{ ++ DSP_STATUS status = DSP_SOK; ++ u32 dwBufSize; ++ GT_2trace(CFG_debugMask, GT_ENTER, "Entered CFG_GetDevObject, args: " ++ "\n\thDevNode: 0x%x\n\tpdwValue: 0x%x\n", hDevNode, ++ *pdwValue); ++ if (!hDevNode) ++ status = CFG_E_INVALIDHDEVNODE; ++ ++ if (!pdwValue) ++ status = CFG_E_INVALIDHDEVNODE; ++ ++ dwBufSize = sizeof(pdwValue); ++ if (DSP_SUCCEEDED(status)) { ++ ++ /* check the device string and then call the REG_SetValue*/ ++ if (!(strcmp((char *)((struct DRV_EXT *)hDevNode)->szString, ++ "TIOMAP1510"))) { ++ GT_0trace(CFG_debugMask, GT_1CLASS, ++ "Fetching DSP Device from " ++ "Registry \n"); ++ status = REG_GetValue(NULL, (char *)hDevNode, ++ "DEVICE_DSP", ++ (u8 *)pdwValue, &dwBufSize); ++ } else { ++ GT_0trace(CFG_debugMask, GT_6CLASS, ++ "Failed to Identify the Device to Fetch \n"); ++ } ++ } ++#ifdef DEBUG ++ if (DSP_SUCCEEDED(status)) { ++ GT_1trace(CFG_debugMask, GT_1CLASS, ++ "CFG_GetDevObject SUCCESS DevObject" ++ ": 0x%x\n ", *pdwValue); ++ } else { ++ GT_0trace(CFG_debugMask, GT_6CLASS, ++ "CFG_GetDevObject Failed \n"); ++ } ++#endif ++ return status; ++} ++ ++/* ++ * ======== CFG_GetDSPResources ======== ++ * Purpose: ++ * Get the DSP resources available to a given device. ++ */ ++DSP_STATUS CFG_GetDSPResources(struct CFG_DEVNODE *hDevNode, ++ OUT struct CFG_DSPRES *pDSPResTable) ++{ ++ DSP_STATUS status = DSP_SOK; /* return value */ ++ u32 dwResSize; ++ GT_2trace(CFG_debugMask, GT_ENTER, ++ "Entered CFG_GetDSPResources, args: " ++ "\n\thDevNode: 0x%x\n\tpDSPResTable: 0x%x\n", ++ hDevNode, pDSPResTable); ++ if (!hDevNode) { ++ status = CFG_E_INVALIDHDEVNODE; ++ } else if (!pDSPResTable) { ++ status = CFG_E_INVALIDPOINTER; ++ } else { ++ status = REG_GetValue(NULL, CONFIG, DSPRESOURCES, ++ (u8 *)pDSPResTable, ++ &dwResSize); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ GT_0trace(CFG_debugMask, GT_1CLASS, ++ "CFG_GetDSPResources SUCCESS\n"); ++ } else { ++ status = CFG_E_RESOURCENOTAVAIL; ++ GT_0trace(CFG_debugMask, GT_6CLASS, ++ "CFG_GetDSPResources Failed \n"); ++ } ++#ifdef DEBUG ++ /* assert that resource values are reasonable */ ++ DBC_Assert(pDSPResTable->uChipType < 256); ++ DBC_Assert(pDSPResTable->uWordSize > 0); ++ DBC_Assert(pDSPResTable->uWordSize < 32); ++ DBC_Assert(pDSPResTable->cChips > 0); ++ DBC_Assert(pDSPResTable->cChips < 256); ++#endif ++ return status; ++} ++ ++/* ++ * ======== CFG_GetExecFile ======== ++ * Purpose: ++ * Retreive the default executable, if any, for this board. ++ */ ++DSP_STATUS CFG_GetExecFile(struct CFG_DEVNODE *hDevNode, u32 ulBufSize, ++ OUT char *pstrExecFile) ++{ ++ DSP_STATUS status = DSP_SOK; ++ u32 cExecSize = ulBufSize; ++ GT_3trace(CFG_debugMask, GT_ENTER, ++ "Entered CFG_GetExecFile:\n\tthDevNode: " ++ "0x%x\n\tulBufSize: 0x%x\n\tpstrExecFile: 0x%x\n", hDevNode, ++ ulBufSize, pstrExecFile); ++ if (!hDevNode) ++ status = CFG_E_INVALIDHDEVNODE; ++ ++ if (!pstrExecFile) ++ status = CFG_E_INVALIDPOINTER; ++ ++ if (DSP_SUCCEEDED(status)) { ++ status = REG_GetValue(NULL, (char *)hDevNode, DEFEXEC, ++ (u8 *)pstrExecFile, &cExecSize); ++ if (DSP_FAILED(status)) ++ status = CFG_E_RESOURCENOTAVAIL; ++ else if (cExecSize > ulBufSize) ++ status = DSP_ESIZE; ++ ++ } ++#ifdef DEBUG ++ if (DSP_SUCCEEDED(status)) { ++ GT_1trace(CFG_debugMask, GT_1CLASS, ++ "CFG_GetExecFile SUCCESS Exec File" ++ "name : %s\n ", pstrExecFile); ++ } else { ++ GT_0trace(CFG_debugMask, GT_6CLASS, ++ "CFG_GetExecFile Failed \n"); ++ } ++#endif ++ DBC_Ensure(((status == DSP_SOK) && ++ (strlen(pstrExecFile) <= ulBufSize)) || (status != DSP_SOK)); ++ return status; ++} ++ ++/* ++ * ======== CFG_GetHostResources ======== ++ * Purpose: ++ * Get the Host allocated resources assigned to a given device. ++ */ ++DSP_STATUS CFG_GetHostResources(struct CFG_DEVNODE *hDevNode, ++ OUT struct CFG_HOSTRES *pHostResTable) ++{ ++ DSP_STATUS status = DSP_SOK; ++ u32 dwBufSize; ++ GT_2trace(CFG_debugMask, GT_ENTER, ++ "Entered CFG_GetHostResources, args:\n\t" ++ "pHostResTable: 0x%x\n\thDevNode: 0x%x\n", ++ pHostResTable, hDevNode); ++ if (!hDevNode) ++ status = CFG_E_INVALIDHDEVNODE; ++ ++ if (!pHostResTable) ++ status = CFG_E_INVALIDPOINTER; ++ ++ if (DSP_SUCCEEDED(status)) { ++ dwBufSize = sizeof(struct CFG_HOSTRES); ++ if (DSP_FAILED(REG_GetValue(NULL, (char *)hDevNode, ++ CURRENTCONFIG, ++ (u8 *)pHostResTable, &dwBufSize))) { ++ status = CFG_E_RESOURCENOTAVAIL; ++ } ++ } ++#ifdef DEBUG ++ if (DSP_SUCCEEDED(status)) { ++ GT_0trace(CFG_debugMask, GT_1CLASS, ++ "CFG_GetHostResources SUCCESS \n"); ++ } else { ++ GT_0trace(CFG_debugMask, GT_6CLASS, ++ "CFG_GetHostResources Failed \n"); ++ } ++#endif ++ return status; ++} ++ ++/* ++ * ======== CFG_GetObject ======== ++ * Purpose: ++ * Retrieve the Object handle from the Registry ++ */ ++DSP_STATUS CFG_GetObject(OUT u32 *pdwValue, u32 dwType) ++{ ++ DSP_STATUS status = DSP_EINVALIDARG; ++ u32 dwBufSize; ++ DBC_Require(pdwValue != NULL); ++ GT_1trace(CFG_debugMask, GT_ENTER, ++ "Entered CFG_GetObject, args:pdwValue: " ++ "0x%x\n", *pdwValue); ++ dwBufSize = sizeof(pdwValue); ++ switch (dwType) { ++ case (REG_DRV_OBJECT): ++ status = REG_GetValue(NULL, CONFIG, DRVOBJECT, ++ (u8 *)pdwValue, ++ &dwBufSize); ++ break; ++ case (REG_MGR_OBJECT): ++ status = REG_GetValue(NULL, CONFIG, MGROBJECT, ++ (u8 *)pdwValue, ++ &dwBufSize); ++ break; ++ default: ++ break; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ GT_1trace(CFG_debugMask, GT_1CLASS, ++ "CFG_GetObject SUCCESS DrvObject: " ++ "0x%x\n ", *pdwValue); ++ } else { ++ status = CFG_E_RESOURCENOTAVAIL; ++ *pdwValue = 0; ++ GT_0trace(CFG_debugMask, GT_6CLASS, "CFG_GetObject Failed \n"); ++ } ++ DBC_Ensure((DSP_SUCCEEDED(status) && *pdwValue != 0) || ++ (DSP_FAILED(status) && *pdwValue == 0)); ++ return status; ++} ++ ++/* ++ * ======== CFG_Init ======== ++ * Purpose: ++ * Initialize the CFG module's private state. ++ */ ++bool CFG_Init(void) ++{ ++ struct CFG_DSPRES dspResources; ++ GT_create(&CFG_debugMask, "CF"); /* CF for ConFig */ ++ GT_0trace(CFG_debugMask, GT_5CLASS, "Entered CFG_Init\n"); ++ GT_0trace(CFG_debugMask, GT_5CLASS, "Intializing DSP Registry Info \n"); ++ ++ dspResources.uChipType = DSPTYPE_64; ++ dspResources.cChips = 1; ++ dspResources.uWordSize = DSPWORDSIZE; ++ dspResources.cMemTypes = 0; ++ dspResources.aMemDesc[0].uMemType = 0; ++ dspResources.aMemDesc[0].ulMin = 0; ++ dspResources.aMemDesc[0].ulMax = 0; ++ if (DSP_SUCCEEDED(REG_SetValue(NULL, CONFIG, DSPRESOURCES, REG_BINARY, ++ (u8 *)&dspResources, sizeof(struct CFG_DSPRES)))) { ++ GT_0trace(CFG_debugMask, GT_5CLASS, ++ "Initialized DSP resources in " ++ "Registry \n"); ++ } else ++ GT_0trace(CFG_debugMask, GT_5CLASS, ++ "Failed to Initialize DSP resources" ++ " in Registry \n"); ++ return true; ++} ++ ++/* ++ * ======== CFG_SetDevObject ======== ++ * Purpose: ++ * Store the Device Object handle and devNode pointer for a given devnode. ++ */ ++DSP_STATUS CFG_SetDevObject(struct CFG_DEVNODE *hDevNode, u32 dwValue) ++{ ++ DSP_STATUS status = DSP_SOK; ++ u32 dwBuffSize; ++ GT_2trace(CFG_debugMask, GT_ENTER, ++ "Entered CFG_SetDevObject, args: \n\t" ++ "hDevNode: 0x%x\n\tdwValue: 0x%x\n", hDevNode, dwValue); ++ if (!hDevNode) ++ status = CFG_E_INVALIDHDEVNODE; ++ ++ dwBuffSize = sizeof(dwValue); ++ if (DSP_SUCCEEDED(status)) { ++ /* Store the WCD device object in the Registry */ ++ ++ if (!(strcmp((char *)hDevNode, "TIOMAP1510"))) { ++ GT_0trace(CFG_debugMask, GT_1CLASS, ++ "Registering the DSP Device \n"); ++ status = REG_SetValue(NULL, (char *)hDevNode, ++ "DEVICE_DSP", REG_DWORD,\ ++ (u8 *)&dwValue, dwBuffSize); ++ if (DSP_SUCCEEDED(status)) { ++ dwBuffSize = sizeof(hDevNode); ++ status = REG_SetValue(NULL, ++ (char *)hDevNode, "DEVNODESTRING_DSP", ++ REG_DWORD, (u8 *)&hDevNode, ++ dwBuffSize); ++ } ++ } else { ++ GT_0trace(CFG_debugMask, GT_6CLASS, ++ "Failed to Register Device \n"); ++ } ++ } ++#ifdef DEBUG ++ if (DSP_SUCCEEDED(status)) { ++ GT_0trace(CFG_debugMask, GT_1CLASS, ++ "CFG_SetDevObject SUCCESS \n"); ++ } else { ++ GT_0trace(CFG_debugMask, GT_6CLASS, ++ "CFG_SetDevObject Failed \n"); ++ } ++#endif ++ return status; ++} ++ ++/* ++ * ======== CFG_SetObject ======== ++ * Purpose: ++ * Store the Driver Object handle ++ */ ++DSP_STATUS CFG_SetObject(u32 dwValue, u32 dwType) ++{ ++ DSP_STATUS status = DSP_EINVALIDARG; ++ u32 dwBuffSize; ++ GT_1trace(CFG_debugMask, GT_ENTER, ++ "Entered CFG_SetObject, args: dwValue: " ++ "0x%x\n", dwValue); ++ dwBuffSize = sizeof(dwValue); ++ switch (dwType) { ++ case (REG_DRV_OBJECT): ++ status = REG_SetValue(NULL, CONFIG, DRVOBJECT, REG_DWORD, ++ (u8 *)&dwValue, dwBuffSize); ++ break; ++ case (REG_MGR_OBJECT): ++ status = REG_SetValue(NULL, CONFIG, MGROBJECT, REG_DWORD, ++ (u8 *) &dwValue, dwBuffSize); ++ break; ++ default: ++ break; ++ } ++#ifdef DEBUG ++ if (DSP_SUCCEEDED(status)) ++ GT_0trace(CFG_debugMask, GT_1CLASS, "CFG_SetObject SUCCESS \n"); ++ else ++ GT_0trace(CFG_debugMask, GT_6CLASS, "CFG_SetObject Failed \n"); ++ ++#endif ++ return status; ++} +diff --git a/drivers/dsp/bridge/services/clk.c b/drivers/dsp/bridge/services/clk.c +new file mode 100644 +index 0000000..b499b14 +--- /dev/null ++++ b/drivers/dsp/bridge/services/clk.c +@@ -0,0 +1,375 @@ ++/* ++ * clk.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== clk.c ======== ++ * Purpose: ++ * Clock and Timer services. ++ * ++ * Public Functions: ++ * CLK_Exit ++ * CLK_Init ++ * CLK_Enable ++ * CLK_Disable ++ * CLK_GetRate ++ * CLK_Set_32KHz ++ *! Revision History: ++ *! ================ ++ *! 08-May-2007 rg: moved all clock functions from sync module. ++ * And added CLK_Set_32KHz, CLK_Set_SysClk. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++#include ++ ++ ++/* ----------------------------------- Defines, Data Structures, Typedefs */ ++ ++typedef volatile unsigned long REG_UWORD32; ++ ++#define SSI_Base 0x48058000 ++ ++#define SSI_BASE IO_ADDRESS(SSI_Base) ++ ++ ++struct SERVICES_Clk_t { ++ struct clk *clk_handle; ++ const char *clk_name; ++ int id; ++}; ++ ++/* The row order of the below array needs to match with the clock enumerations ++ * 'SERVICES_ClkId' provided in the header file.. any changes in the ++ * enumerations needs to be fixed in the array as well */ ++static struct SERVICES_Clk_t SERVICES_Clks[] = { ++ {NULL, "iva2_ck", -1}, ++ {NULL, "mailboxes_ick", -1}, ++ {NULL, "gpt5_fck", -1}, ++ {NULL, "gpt5_ick", -1}, ++ {NULL, "gpt6_fck", -1}, ++ {NULL, "gpt6_ick", -1}, ++ {NULL, "gpt7_fck", -1}, ++ {NULL, "gpt7_ick", -1}, ++ {NULL, "gpt8_fck", -1}, ++ {NULL, "gpt8_ick", -1}, ++ {NULL, "wdt_fck", 3}, ++ {NULL, "wdt_ick", 3}, ++ {NULL, "mcbsp_fck", 1}, ++ {NULL, "mcbsp_ick", 1}, ++ {NULL, "mcbsp_fck", 2}, ++ {NULL, "mcbsp_ick", 2}, ++ {NULL, "mcbsp_fck", 3}, ++ {NULL, "mcbsp_ick", 3}, ++ {NULL, "mcbsp_fck", 4}, ++ {NULL, "mcbsp_ick", 4}, ++ {NULL, "mcbsp_fck", 5}, ++ {NULL, "mcbsp_ick", 5}, ++ {NULL, "ssi_ssr_sst_fck", -1}, ++ {NULL, "ssi_ick", -1}, ++ {NULL, "omap_32k_fck", -1}, ++ {NULL, "sys_ck", -1}, ++ {NULL, ""} ++}; ++ ++/* Generic TIMER object: */ ++struct TIMER_OBJECT { ++ struct timer_list timer; ++}; ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask CLK_debugMask = { NULL, NULL }; /* GT trace variable */ ++#endif ++ ++/* ++ * ======== CLK_Exit ======== ++ * Purpose: ++ * Cleanup CLK module. ++ */ ++void CLK_Exit(void) ++{ ++ int i = 0; ++ ++ GT_0trace(CLK_debugMask, GT_5CLASS, "CLK_Exit\n"); ++ /* Relinquish the clock handles */ ++ while (i < SERVICESCLK_NOT_DEFINED) { ++ if (SERVICES_Clks[i].clk_handle) ++ clk_put(SERVICES_Clks[i].clk_handle); ++ ++ SERVICES_Clks[i].clk_handle = NULL; ++ i++; ++ } ++ ++} ++ ++/* ++ * ======== CLK_Init ======== ++ * Purpose: ++ * Initialize CLK module. ++ */ ++bool CLK_Init(void) ++{ ++ static struct platform_device dspbridge_device; ++ struct clk *clk_handle; ++ int i = 0; ++ GT_create(&CLK_debugMask, "CK"); /* CK for CLK */ ++ GT_0trace(CLK_debugMask, GT_5CLASS, "CLK_Init\n"); ++ ++ dspbridge_device.dev.bus = &platform_bus_type; ++ ++ /* Get the clock handles from base port and store locally */ ++ while (i < SERVICESCLK_NOT_DEFINED) { ++ /* get the handle from BP */ ++ dspbridge_device.id = SERVICES_Clks[i].id; ++ ++ clk_handle = clk_get(&dspbridge_device.dev, ++ SERVICES_Clks[i].clk_name); ++ ++ if (!clk_handle) { ++ GT_2trace(CLK_debugMask, GT_7CLASS, ++ "CLK_Init: failed to get Clk handle %s, " ++ "CLK dev id = %d\n", ++ SERVICES_Clks[i].clk_name, ++ SERVICES_Clks[i].id); ++ /* should we fail here?? */ ++ } else { ++ GT_2trace(CLK_debugMask, GT_7CLASS, ++ "CLK_Init: PASS and Clk handle %s, " ++ "CLK dev id = %d\n", ++ SERVICES_Clks[i].clk_name, ++ SERVICES_Clks[i].id); ++ } ++ SERVICES_Clks[i].clk_handle = clk_handle; ++ i++; ++ } ++ ++ return true; ++} ++ ++/* ++ * ======== CLK_Enable ======== ++ * Purpose: ++ * Enable Clock . ++ * ++*/ ++DSP_STATUS CLK_Enable(IN enum SERVICES_ClkId clk_id) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct clk *pClk; ++ ++ DBC_Require(clk_id < SERVICESCLK_NOT_DEFINED); ++ GT_2trace(CLK_debugMask, GT_6CLASS, "CLK_Enable: CLK %s, " ++ "CLK dev id = %d\n", SERVICES_Clks[clk_id].clk_name, ++ SERVICES_Clks[clk_id].id); ++ ++ pClk = SERVICES_Clks[clk_id].clk_handle; ++ if (pClk) { ++ if (clk_enable(pClk) == 0x0) { ++ /* Success ? */ ++ } else { ++ pr_err("CLK_Enable: failed to Enable CLK %s, " ++ "CLK dev id = %d\n", ++ SERVICES_Clks[clk_id].clk_name, ++ SERVICES_Clks[clk_id].id); ++ status = DSP_EFAIL; ++ } ++ } else { ++ pr_err("CLK_Enable: failed to get CLK %s, CLK dev id = %d\n", ++ SERVICES_Clks[clk_id].clk_name, ++ SERVICES_Clks[clk_id].id); ++ status = DSP_EFAIL; ++ } ++ /* The SSI module need to configured not to have the Forced idle for ++ * master interface. If it is set to forced idle, the SSI module is ++ * transitioning to standby thereby causing the client in the DSP hang ++ * waiting for the SSI module to be active after enabling the clocks ++ */ ++ if (clk_id == SERVICESCLK_ssi_fck) ++ SSI_Clk_Prepare(true); ++ ++ return status; ++} ++/* ++ * ======== CLK_Set_32KHz ======== ++ * Purpose: ++ * To Set parent of a clock to 32KHz. ++ */ ++ ++DSP_STATUS CLK_Set_32KHz(IN enum SERVICES_ClkId clk_id) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct clk *pClk; ++ struct clk *pClkParent; ++ enum SERVICES_ClkId sys_32k_id = SERVICESCLK_sys_32k_ck; ++ pClkParent = SERVICES_Clks[sys_32k_id].clk_handle; ++ ++ DBC_Require(clk_id < SERVICESCLK_NOT_DEFINED); ++ GT_2trace(CLK_debugMask, GT_6CLASS, "CLK_Set_32KHz: CLK %s, " ++ "CLK dev id = %d is setting to 32KHz \n", ++ SERVICES_Clks[clk_id].clk_name, ++ SERVICES_Clks[clk_id].id); ++ pClk = SERVICES_Clks[clk_id].clk_handle; ++ if (pClk) { ++ if (!(clk_set_parent(pClk, pClkParent) == 0x0)) { ++ GT_2trace(CLK_debugMask, GT_7CLASS, "CLK_Set_32KHz: " ++ "Failed to set to 32KHz %s, CLK dev id = %d\n", ++ SERVICES_Clks[clk_id].clk_name, ++ SERVICES_Clks[clk_id].id); ++ status = DSP_EFAIL; ++ } ++ } ++ return status; ++} ++ ++/* ++ * ======== CLK_Disable ======== ++ * Purpose: ++ * Disable the clock. ++ * ++*/ ++DSP_STATUS CLK_Disable(IN enum SERVICES_ClkId clk_id) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct clk *pClk; ++ s32 clkUseCnt; ++ ++ DBC_Require(clk_id < SERVICESCLK_NOT_DEFINED); ++ GT_2trace(CLK_debugMask, GT_6CLASS, "CLK_Disable: CLK %s, " ++ "CLK dev id = %d\n", SERVICES_Clks[clk_id].clk_name, ++ SERVICES_Clks[clk_id].id); ++ ++ pClk = SERVICES_Clks[clk_id].clk_handle; ++ ++ clkUseCnt = CLK_Get_UseCnt(clk_id); ++ if (clkUseCnt == -1) { ++ pr_err("CLK_Disable: failed to get CLK Use count for CLK %s," ++ "CLK dev id = %d\n", ++ SERVICES_Clks[clk_id].clk_name, ++ SERVICES_Clks[clk_id].id); ++ } else if (clkUseCnt == 0) { ++ pr_err("CLK_Disable: CLK %s, CLK dev id= %d is already" ++ "disabled\n", ++ SERVICES_Clks[clk_id].clk_name, ++ SERVICES_Clks[clk_id].id); ++ return status; ++ } ++ if (clk_id == SERVICESCLK_ssi_ick) ++ SSI_Clk_Prepare(false); ++ ++ if (pClk) { ++ clk_disable(pClk); ++ } else { ++ pr_err("CLK_Disable: failed to get CLK %s," ++ "CLK dev id = %d\n", ++ SERVICES_Clks[clk_id].clk_name, ++ SERVICES_Clks[clk_id].id); ++ status = DSP_EFAIL; ++ } ++ return status; ++} ++ ++/* ++ * ======== CLK_GetRate ======== ++ * Purpose: ++ * GetClock Speed. ++ * ++ */ ++ ++DSP_STATUS CLK_GetRate(IN enum SERVICES_ClkId clk_id, u32 *speedKhz) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct clk *pClk; ++ u32 clkSpeedHz; ++ ++ DBC_Require(clk_id < SERVICESCLK_NOT_DEFINED); ++ *speedKhz = 0x0; ++ ++ GT_2trace(CLK_debugMask, GT_7CLASS, "CLK_GetRate: CLK %s, " ++ "CLK dev Id = %d \n", SERVICES_Clks[clk_id].clk_name, ++ SERVICES_Clks[clk_id].id); ++ pClk = SERVICES_Clks[clk_id].clk_handle; ++ if (pClk) { ++ clkSpeedHz = clk_get_rate(pClk); ++ *speedKhz = clkSpeedHz / 1000; ++ GT_2trace(CLK_debugMask, GT_6CLASS, ++ "CLK_GetRate: clkSpeedHz = %d , " ++ "speedinKhz=%d\n", clkSpeedHz, *speedKhz); ++ } else { ++ GT_2trace(CLK_debugMask, GT_7CLASS, ++ "CLK_GetRate: failed to get CLK %s, " ++ "CLK dev Id = %d\n", SERVICES_Clks[clk_id].clk_name, ++ SERVICES_Clks[clk_id].id); ++ status = DSP_EFAIL; ++ } ++ return status; ++} ++ ++s32 CLK_Get_UseCnt(IN enum SERVICES_ClkId clk_id) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct clk *pClk; ++ s32 useCount = -1; ++ DBC_Require(clk_id < SERVICESCLK_NOT_DEFINED); ++ ++ pClk = SERVICES_Clks[clk_id].clk_handle; ++ ++ if (pClk) { ++ useCount = pClk->usecount; /* FIXME: usecount shouldn't be used */ ++ } else { ++ GT_2trace(CLK_debugMask, GT_7CLASS, ++ "CLK_GetRate: failed to get CLK %s, " ++ "CLK dev Id = %d\n", SERVICES_Clks[clk_id].clk_name, ++ SERVICES_Clks[clk_id].id); ++ status = DSP_EFAIL; ++ } ++ return useCount; ++} ++ ++void SSI_Clk_Prepare(bool FLAG) ++{ ++ u32 ssi_sysconfig; ++ ssi_sysconfig = __raw_readl((SSI_BASE) + 0x10); ++ ++ if (FLAG) { ++ /* Set Autoidle, SIDLEMode to smart idle, and MIDLEmode to ++ * no idle ++ */ ++ ssi_sysconfig = 0x1011; ++ } else { ++ /* Set Autoidle, SIDLEMode to forced idle, and MIDLEmode to ++ * forced idle ++ */ ++ ssi_sysconfig = 0x1; ++ } ++ __raw_writel((u32)ssi_sysconfig, SSI_BASE + 0x10); ++} +diff --git a/drivers/dsp/bridge/services/csl.c b/drivers/dsp/bridge/services/csl.c +new file mode 100644 +index 0000000..dd33c2d +--- /dev/null ++++ b/drivers/dsp/bridge/services/csl.c +@@ -0,0 +1,173 @@ ++/* ++ * csl.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== cslce.c ======== ++ * Purpose: ++ * Provides platform independent C Standard library functions. ++ * ++ * Public Functions: ++ * CSL_Atoi ++ * CSL_Exit ++ * CSL_Init ++ * CSL_NumToAscii ++ * CSL_Strtokr ++ * ++ *! Revision History: ++ *! ================ ++ *! 07-Aug-2002 jeh: Added CSL_Strtokr(). ++ *! 21-Sep-2001 jeh: Added CSL_Strncmp(). Alphabetized functions. ++ *! 22-Nov-2000 map: Added CSL_Atoi and CSL_Strtok ++ *! 19-Nov-2000 kc: Added CSL_ByteSwap. ++ *! 09-Nov-2000 kc: Added CSL_Strncat. ++ *! 03-Feb-2000 rr: Module init/exit is handled by SERVICES Init/Exit. ++ *! GT Changes. ++ *! 15-Dec-1999 ag: Removed incorrect assertion CSL_NumToAscii() ++ *! 29-Oct-1999 kc: Added CSL_Wstrlen for UNICODE strings. ++ *! 30-Sep-1999 ag: Removed DBC assertion (!CSL_DebugMask.flags) in ++ * CSP_Init(). ++ *! 20-Sep-1999 ag: Added CSL_WcharToAnsi(). ++ *! Removed call to GT_set(). ++ *! 19-Jan-1998 cr: Code review cleanup. ++ *! 29-Dec-1997 cr: Made platform independant, using MS CRT code, and ++ *! combined csl32.c csl95.c and cslnt.c into csl.c. Also ++ *! changed CSL_lowercase to CSL_Uppercase. ++ *! 21-Aug-1997 gp: Fix to CSL_strcpyn to initialize Source string, the NT way. ++ *! 25-Jun-1997 cr: Created from csl95, added CSL_strcmp. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* Is character c in the string pstrDelim? */ ++#define IsDelimiter(c, pstrDelim) ((c != '\0') && \ ++ (strchr(pstrDelim, c) != NULL)) ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask CSL_DebugMask = { NULL, NULL }; /* GT trace var. */ ++#endif ++ ++/* ++ * ======== CSL_Exit ======== ++ * Purpose: ++ * Discontinue usage of the CSL module. ++ */ ++void CSL_Exit(void) ++{ ++ GT_0trace(CSL_DebugMask, GT_5CLASS, "CSL_Exit\n"); ++} ++ ++/* ++ * ======== CSL_Init ======== ++ * Purpose: ++ * Initialize the CSL module's private state. ++ */ ++bool CSL_Init(void) ++{ ++ GT_create(&CSL_DebugMask, "CS"); ++ ++ GT_0trace(CSL_DebugMask, GT_5CLASS, "CSL_Init\n"); ++ ++ return true; ++} ++ ++/* ++ * ======== CSL_NumToAscii ======== ++ * Purpose: ++ * Convert a 1 or 2 digit number to a 2 digit string. ++ */ ++void CSL_NumToAscii(OUT char *pstrNumber, u32 dwNum) ++{ ++ char tens; ++ ++ DBC_Require(dwNum < 100); ++ ++ if (dwNum < 100) { ++ tens = (char) dwNum / 10; ++ dwNum = dwNum % 10; ++ ++ if (tens) { ++ pstrNumber[0] = tens + '0'; ++ pstrNumber[1] = (char) dwNum + '0'; ++ pstrNumber[2] = '\0'; ++ } else { ++ pstrNumber[0] = (char) dwNum + '0'; ++ pstrNumber[1] = '\0'; ++ } ++ } else { ++ pstrNumber[0] = '\0'; ++ } ++} ++ ++ ++ ++ ++/* ++ * ======= CSL_Strtokr ======= ++ * Purpose: ++ * Re-entrant version of strtok. ++ */ ++char *CSL_Strtokr(IN char *pstrSrc, IN CONST char *szSeparators, ++ OUT char **ppstrLast) ++{ ++ char *pstrTemp; ++ char *pstrToken; ++ ++ DBC_Require(szSeparators != NULL); ++ DBC_Require(ppstrLast != NULL); ++ DBC_Require(pstrSrc != NULL || *ppstrLast != NULL); ++ ++ /* ++ * Set string location to beginning (pstrSrc != NULL) or to the ++ * beginning of the next token. ++ */ ++ pstrTemp = (pstrSrc != NULL) ? pstrSrc : *ppstrLast; ++ if (*pstrTemp == '\0') { ++ pstrToken = NULL; ++ } else { ++ pstrToken = pstrTemp; ++ while (*pstrTemp != '\0' && !IsDelimiter(*pstrTemp, ++ szSeparators)) { ++ pstrTemp++; ++ } ++ if (*pstrTemp != '\0') { ++ while (IsDelimiter(*pstrTemp, szSeparators)) { ++ /* TODO: Shouldn't we do this for ++ * only 1 char?? */ ++ *pstrTemp = '\0'; ++ pstrTemp++; ++ } ++ } ++ ++ /* Location in string for next call */ ++ *ppstrLast = pstrTemp; ++ } ++ ++ return pstrToken; ++} +diff --git a/drivers/dsp/bridge/services/dbg.c b/drivers/dsp/bridge/services/dbg.c +new file mode 100644 +index 0000000..5e1773f +--- /dev/null ++++ b/drivers/dsp/bridge/services/dbg.c +@@ -0,0 +1,119 @@ ++/* ++ * dbg.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dbgce.c ======== ++ * Purpose: ++ * Provide debugging services for DSP/BIOS Bridge Mini Drivers. ++ * ++ * Public Functions: ++ * DBG_Exit ++ * DBG_Init ++ * DBG_Trace ++ * ++ * Notes: ++ * Requires gt.h. ++ * ++ * This implementation does not create GT masks on a per WMD basis. ++ * There is currently no facility for a WMD to alter the GT mask. ++ * ++ *! Revision History: ++ *! ================ ++ *! 15-Feb-2000 rr: DBG_Trace prints based on the DebugZones. ++ *! 03-Feb-2000 rr: Module init/exit is handled by SERVICES Init/Exit. ++ *! GT Changes. ++ *! 29-Oct-1999 kc: Cleaned up for code review. ++ *! 10-Oct-1997 cr: Added DBG_Printf service. ++ *! 28-May-1997 cr: Added reference counting. ++ *! 23-May-1997 cr: Updated DBG_Trace to new gt interface. ++ *! 29-May-1996 gp: Removed WCD_ prefix. ++ *! 20-May-1996 gp: Remove DEBUG conditional compilation. ++ *! 15-May-1996 gp: Created. ++ */ ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask DBG_debugMask = { NULL, NULL }; /* GT trace var. */ ++#endif ++ ++#if (defined(DEBUG) || defined (DDSP_DEBUG_PRODUCT)) && GT_TRACE ++ ++/* ++ * ======== DBG_Init ======== ++ * Purpose: ++ * Ensures trace capability is set up for link drivers. ++ */ ++bool DBG_Init(void) ++{ ++ GT_create(&DBG_debugMask, "WD"); /* for WmD (link driver) debug */ ++ ++ GT_0trace(DBG_debugMask, GT_5CLASS, "DBG_Init\n"); ++ ++ return true; ++} ++ ++/* ++ * ======== DBG_Trace ======== ++ * Purpose: ++ * Output a trace message to the debugger, if the given trace level ++ * is unmasked. ++ */ ++DSP_STATUS DBG_Trace(u8 bLevel, char *pstrFormat, ...) ++{ ++ s32 arg1, arg2, arg3, arg4, arg5, arg6; ++ va_list va; ++ ++ va_start(va, pstrFormat); ++ ++ arg1 = va_arg(va, s32); ++ arg2 = va_arg(va, s32); ++ arg3 = va_arg(va, s32); ++ arg4 = va_arg(va, s32); ++ arg5 = va_arg(va, s32); ++ arg6 = va_arg(va, s32); ++ ++ va_end(va); ++ ++ if (bLevel & *(DBG_debugMask).flags) ++ printk(pstrFormat, arg1, arg2, arg3, arg4, arg5, arg6); ++ ++ return DSP_SOK; ++} ++ ++/* ++ * ======== DBG_Exit ======== ++ * Purpose: ++ * Discontinue usage of the DBG module. ++ */ ++void DBG_Exit(void) ++{ ++ GT_0trace(DBG_debugMask, GT_5CLASS, "DBG_Exit\n"); ++} ++ ++#endif /* (defined(DEBUG) || defined(DDSP_DEBUG_PRODUCT)) && GT_TRACE */ +diff --git a/drivers/dsp/bridge/services/dpc.c b/drivers/dsp/bridge/services/dpc.c +new file mode 100644 +index 0000000..bd608d1 +--- /dev/null ++++ b/drivers/dsp/bridge/services/dpc.c +@@ -0,0 +1,274 @@ ++/* ++ * dpc.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== dpcce.c ======== ++ * Purpose: ++ * Deferred Procedure Call(DPC) Services. ++ * ++ * ++ * Public Functions: ++ * DPC_Create ++ * DPC_Destroy ++ * DPC_Exit ++ * DPC_Init ++ * DPC_Schedule ++ * ++ *! Revision History: ++ *! ================ ++ *! 28-Mar-2001 ag: Added #ifdef CHNL_NOIPCINTR to set DPC thread priority ++ *! to THREAD_PRIORITY_IDLE for polling IPC. ++ *! 03-Feb-2000 rr: Module init/exit is handled by SERVICES Init/Exit. ++ *! GT Changes. ++ *! 31-Jan-2000 rr: Changes after code review.Terminate thread,handle ++ *! modified.DPC_Destroy frees the DPC_Object only on ++ *! Successful termination of the thread and the handle. ++ *! 06-Jan-1999 ag: Format cleanup for code review. ++ *! Removed DPC_[Lower|Raise]IRQL[From|To]DispatchLevel. ++ *! 10-Dec-1999 ag: Added SetProcPermissions in DPC_DeferredProcedure(). ++ *! (Needed to access client(s) CHNL buffers). ++ *! 19-Sep-1999 a0216266: Stubbed from dpcnt.c. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ----------------------------------- Defines, Data Structures, Typedefs */ ++#define SIGNATURE 0x5f435044 /* "DPC_" (in reverse). */ ++ ++/* The DPC object, passed to our priority event callback routine: */ ++struct DPC_OBJECT { ++ u32 dwSignature; /* Used for object validation. */ ++ void *pRefData; /* Argument for client's DPC. */ ++ DPC_PROC pfnDPC; /* Client's DPC. */ ++ u32 numRequested; /* Number of requested DPC's. */ ++ u32 numScheduled; /* Number of executed DPC's. */ ++ struct tasklet_struct dpc_tasklet; ++ ++#ifdef DEBUG ++ u32 cEntryCount; /* Number of times DPC reentered. */ ++ u32 numRequestedMax; /* Keep track of max pending DPC's. */ ++#endif ++ ++ spinlock_t dpc_lock; ++}; ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask DPC_DebugMask = { NULL, NULL }; /* DPC Debug Mask */ ++#endif ++ ++/* ----------------------------------- Function Prototypes */ ++static void DPC_DeferredProcedure(IN unsigned long pDeferredContext); ++ ++/* ++ * ======== DPC_Create ======== ++ * Purpose: ++ * Create a DPC object, allowing a client's own DPC procedure to be ++ * scheduled for a call with client reference data. ++ */ ++DSP_STATUS DPC_Create(OUT struct DPC_OBJECT **phDPC, DPC_PROC pfnDPC, ++ void *pRefData) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DPC_OBJECT *pDPCObject = NULL; ++ ++ if ((phDPC != NULL) && (pfnDPC != NULL)) { ++ /* ++ * Allocate a DPC object to store information allowing our DPC ++ * callback to dispatch to the client's DPC. ++ */ ++ MEM_AllocObject(pDPCObject, struct DPC_OBJECT, SIGNATURE); ++ if (pDPCObject != NULL) { ++ tasklet_init(&pDPCObject->dpc_tasklet, ++ DPC_DeferredProcedure, ++ (u32) pDPCObject); ++ /* Fill out our DPC Object: */ ++ pDPCObject->pRefData = pRefData; ++ pDPCObject->pfnDPC = pfnDPC; ++ pDPCObject->numRequested = 0; ++ pDPCObject->numScheduled = 0; ++#ifdef DEBUG ++ pDPCObject->numRequestedMax = 0; ++ pDPCObject->cEntryCount = 0; ++#endif ++ spin_lock_init(&pDPCObject->dpc_lock); ++ *phDPC = pDPCObject; ++ } else { ++ GT_0trace(DPC_DebugMask, GT_6CLASS, ++ "DPC_Create: DSP_EMEMORY\n"); ++ status = DSP_EMEMORY; ++ } ++ } else { ++ GT_0trace(DPC_DebugMask, GT_6CLASS, ++ "DPC_Create: DSP_EPOINTER\n"); ++ status = DSP_EPOINTER; ++ } ++ DBC_Ensure((DSP_FAILED(status) && (!phDPC || (phDPC && *phDPC == NULL))) ++ || DSP_SUCCEEDED(status)); ++ return status; ++} ++ ++/* ++ * ======== DPC_Destroy ======== ++ * Purpose: ++ * Cancel the last scheduled DPC, and deallocate a DPC object previously ++ * allocated with DPC_Create(). Frees the Object only if the thread ++ * and the event terminated successfuly. ++ */ ++DSP_STATUS DPC_Destroy(struct DPC_OBJECT *hDPC) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DPC_OBJECT *pDPCObject = (struct DPC_OBJECT *)hDPC; ++ ++ if (MEM_IsValidHandle(hDPC, SIGNATURE)) { ++ ++ /* Free our DPC object: */ ++ if (DSP_SUCCEEDED(status)) { ++ tasklet_kill(&pDPCObject->dpc_tasklet); ++ MEM_FreeObject(pDPCObject); ++ pDPCObject = NULL; ++ GT_0trace(DPC_DebugMask, GT_2CLASS, ++ "DPC_Destroy: SUCCESS\n"); ++ } ++ } else { ++ GT_0trace(DPC_DebugMask, GT_6CLASS, ++ "DPC_Destroy: DSP_EHANDLE\n"); ++ status = DSP_EHANDLE; ++ } ++ DBC_Ensure((DSP_SUCCEEDED(status) && pDPCObject == NULL) ++ || DSP_FAILED(status)); ++ return status; ++} ++ ++/* ++ * ======== DPC_Exit ======== ++ * Purpose: ++ * Discontinue usage of the DPC module. ++ */ ++void DPC_Exit(void) ++{ ++ GT_0trace(DPC_DebugMask, GT_5CLASS, "Entered DPC_Exit\n"); ++} ++ ++/* ++ * ======== DPC_Init ======== ++ * Purpose: ++ * Initialize the DPC module's private state. ++ */ ++bool DPC_Init(void) ++{ ++ GT_create(&DPC_DebugMask, "DP"); ++ ++ GT_0trace(DPC_DebugMask, GT_5CLASS, "Entered DPC_Init\n"); ++ ++ return true; ++} ++ ++/* ++ * ======== DPC_Schedule ======== ++ * Purpose: ++ * Schedule a deferred procedure call to be executed at a later time. ++ * Latency and order of DPC execution is platform specific. ++ */ ++DSP_STATUS DPC_Schedule(struct DPC_OBJECT *hDPC) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DPC_OBJECT *pDPCObject = (struct DPC_OBJECT *)hDPC; ++ unsigned long flags; ++ ++ GT_1trace(DPC_DebugMask, GT_ENTER, "DPC_Schedule hDPC %x\n", hDPC); ++ if (MEM_IsValidHandle(hDPC, SIGNATURE)) { ++ /* Increment count of DPC's pending. Needs to be protected ++ * from ISRs since this function is called from process ++ * context also. */ ++ spin_lock_irqsave(&hDPC->dpc_lock, flags); ++ pDPCObject->numRequested++; ++ spin_unlock_irqrestore(&hDPC->dpc_lock, flags); ++ tasklet_schedule(&(hDPC->dpc_tasklet)); ++#ifdef DEBUG ++ if (pDPCObject->numRequested > pDPCObject->numScheduled + ++ pDPCObject->numRequestedMax) { ++ pDPCObject->numRequestedMax = pDPCObject->numRequested - ++ pDPCObject->numScheduled; ++ } ++#endif ++ /* If an interrupt occurs between incrementing numRequested and the ++ * assertion below, then DPC will get executed while returning from ++ * ISR, which will complete all requests and make numRequested equal ++ * to numScheduled, firing this assertion. This happens only when ++ * DPC is being scheduled in process context */ ++ } else { ++ GT_0trace(DPC_DebugMask, GT_6CLASS, ++ "DPC_Schedule: DSP_EHANDLE\n"); ++ status = DSP_EHANDLE; ++ } ++ GT_1trace(DPC_DebugMask, GT_ENTER, "DPC_Schedule status %x\n", status); ++ return status; ++} ++ ++/* ++ * ======== DeferredProcedure ======== ++ * Purpose: ++ * Main DPC routine. This is called by host OS DPC callback ++ * mechanism with interrupts enabled. ++ */ ++static void DPC_DeferredProcedure(IN unsigned long pDeferredContext) ++{ ++ struct DPC_OBJECT *pDPCObject = (struct DPC_OBJECT *)pDeferredContext; ++ /* read numRequested in local variable */ ++ u32 requested; ++ u32 serviced; ++ ++ DBC_Require(pDPCObject != NULL); ++ requested = pDPCObject->numRequested; ++ serviced = pDPCObject->numScheduled; ++ ++ GT_1trace(DPC_DebugMask, GT_ENTER, "> DPC_DeferredProcedure " ++ "pDeferredContext=%x\n", pDeferredContext); ++ /* Rollover taken care of using != instead of < */ ++ if (serviced != requested) { ++ if (pDPCObject->pfnDPC != NULL) { ++ /* Process pending DPC's: */ ++ do { ++ /* Call client's DPC: */ ++ (*(pDPCObject->pfnDPC))(pDPCObject->pRefData); ++ serviced++; ++ } while (serviced != requested); ++ } ++ pDPCObject->numScheduled = requested; ++ } ++ GT_2trace(DPC_DebugMask, GT_ENTER, ++ "< DPC_DeferredProcedure requested %d" ++ " serviced %d\n", requested, serviced); ++} ++ +diff --git a/drivers/dsp/bridge/services/kfile.c b/drivers/dsp/bridge/services/kfile.c +new file mode 100644 +index 0000000..ba1d26f +--- /dev/null ++++ b/drivers/dsp/bridge/services/kfile.c +@@ -0,0 +1,338 @@ ++/* ++ * kfile.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== kfilece.c ======== ++ * Purpose: ++ * This module provides file i/o services. ++ * ++ * Public Functions: ++ * KFILE_Close ++ * KFILE_Exit ++ * KFILE_Init ++ * KFILE_Open ++ * KFILE_Read ++ * KFILE_Seek ++ * KFILE_Tell ++ * ++ *! Revision History ++ *! ================ ++ *! 03-Feb-2000 rr: Module init/exit is handled by SERVICES Init/Exit. ++ *! GT Changes. ++ *! 22-Nov-1999 kc: Added changes from code review. ++ *! 12-Nov-1999 kc: Enabled CSL for UNICODE/ANSI string conversions. ++ *! 30-Sep-1999 ag: Changed KFILE_Read() GT level from _ENTER to _4CLASS. ++ *! Removed GT_set(). ++ *! 25-Aug-1999 ag: Changed MEM_Calloc allocation type to MEM_PAGED. ++ *! 13-Jul-1999 a0216266(ww - TID): Stubbed from kfilent.c. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ----------------------------------- Defines, Data Structures, Typedefs */ ++#define SIGNATURE 0x4c49464b /* hex code of KFIL (reversed) */ ++#define MAXFILENAMELENGTH 256 ++#define GENERAL_FAILURE 0xffffffff /* SetFilePointer error */ ++ ++/* The KFILE_FileObj abstracts the true file handle from a KFILE handle. */ ++struct KFILE_FileObj { ++ u32 dwSignature; ++ __kernel_pid_t owner_pid; /* PID of process that opened this file */ ++ char *fileName ; ++ bool isOpen ; ++ u32 size ; ++ u32 curPos ; ++ long hInternal; /* internal handle of file */ ++ struct file *fileDesc; ++ ++}; ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask KFILE_debugMask = { NULL, NULL }; /* Debug mask */ ++#endif ++ ++/* ++ * ======== KFILE_Close ======== ++ * Purpose: ++ * This function closes a file's stream. ++ */ ++s32 KFILE_Close(struct KFILE_FileObj *hFile) ++{ ++ s32 cRetVal = 0; /* 0 indicates success */ ++ s32 fRetVal = 0; ++ __kernel_pid_t curr_pid; ++ ++ GT_1trace(KFILE_debugMask, GT_ENTER, "KFILE_Close: hFile 0x%x\n", ++ hFile); ++ ++ /* Check for valid handle */ ++ if (MEM_IsValidHandle(hFile, SIGNATURE)) { ++ /* Close file only if opened by the same process (id). Otherwise ++ * Linux closes all open file handles when process exits.*/ ++ /* Return PID instead of process handle */ ++ curr_pid = (__kernel_pid_t)current->pid; ++ fRetVal = filp_close(hFile->fileDesc, NULL) ; ++ if (fRetVal) { ++ cRetVal = E_KFILE_ERROR; ++ GT_1trace(KFILE_debugMask, GT_6CLASS, ++ "KFILE_Close: sys_close " ++ "returned %d\n", fRetVal); ++ } ++ MEM_FreeObject(hFile); ++ } else { ++ cRetVal = E_KFILE_INVALIDHANDLE; ++ GT_0trace(KFILE_debugMask, GT_6CLASS, "KFILE_Close: " ++ "invalid file handle\n"); ++ } ++ return cRetVal; ++} ++ ++/* ++ * ======== KFILE_Exit ======== ++ * Purpose: ++ * Decrement reference count, and free resources when reference count ++ * is 0. ++ */ ++void KFILE_Exit(void) ++{ ++ GT_0trace(KFILE_debugMask, GT_5CLASS, "KFILE_Exit\n"); ++} ++ ++/* ++ * ======== KFILE_Init ======== ++ */ ++bool KFILE_Init(void) ++{ ++ GT_create(&KFILE_debugMask, "KF"); /* "KF" for KFile */ ++ ++ GT_0trace(KFILE_debugMask, GT_5CLASS, "KFILE_Init\n"); ++ ++ return true; ++} ++ ++/* ++ * ======== KFILE_Open ======== ++ * Purpose: ++ * Open a file for reading ONLY ++ */ ++struct KFILE_FileObj *KFILE_Open(CONST char *pszFileName, CONST char *pszMode) ++{ ++ struct KFILE_FileObj *hFile; /* file handle */ ++ DSP_STATUS status; ++ mm_segment_t fs; ++ ++ struct file*fileDesc = NULL; ++ DBC_Require(pszMode != NULL); ++ DBC_Require(pszFileName != NULL); ++ ++ GT_2trace(KFILE_debugMask, GT_ENTER, ++ "KFILE_Open: pszFileName %s, pszMode " ++ "%s\n", pszFileName, pszMode); ++ ++ /* create a KFILE object */ ++ MEM_AllocObject(hFile, struct KFILE_FileObj, SIGNATURE); ++ ++ if (hFile) { ++ fs = get_fs(); ++ set_fs(get_ds()); ++ /* Third argument is mode (permissions). Ignored unless creating file */ ++ fileDesc = filp_open(pszFileName, O_RDONLY, 0); ++ if ((IS_ERR(fileDesc)) || (fileDesc == NULL) || ++ (fileDesc->f_op == NULL) || (fileDesc->f_op->read == NULL) ++ || (fileDesc->f_op->llseek == NULL)) { ++ status = DSP_EFILE; ++ } else { ++ hFile->fileDesc = fileDesc; ++ hFile->fileName = (char *)pszFileName; ++ hFile->isOpen = true; ++ hFile->curPos = 0; ++ hFile->size = fileDesc->f_op->llseek(fileDesc, 0, ++ SEEK_END); ++ fileDesc->f_op->llseek(fileDesc, 0, SEEK_SET); ++ /* Return PID instead of process handle */ ++ hFile->owner_pid = current->pid; ++ ++ status = DSP_SOK; ++ } ++ set_fs(fs); ++ if (DSP_FAILED(status)) { ++ /* free memory, and clear handle */ ++ MEM_FreeObject(hFile); ++ hFile = NULL; ++ } ++ } else { ++ GT_0trace(KFILE_debugMask, GT_6CLASS, ++ "KFILE_Open: MEM_AllocObject failed\n"); ++ status = DSP_EMEMORY; ++ } ++ return hFile; ++} ++ ++/* ++ * ======== KFILE_Read ======== ++ * Purpose: ++ * Reads a specified number of bytes into a buffer. ++ */ ++s32 ++KFILE_Read(void __user*pBuffer, s32 cSize, s32 cCount, ++ struct KFILE_FileObj *hFile) ++{ ++ u32 dwBytesRead = 0; ++ s32 cRetVal = 0; ++ mm_segment_t fs; ++ ++ DBC_Require(pBuffer != NULL); ++ ++ GT_4trace(KFILE_debugMask, GT_4CLASS, ++ "KFILE_Read: buffer 0x%x, cSize 0x%x," ++ "cCount 0x%x, hFile 0x%x\n", pBuffer, cSize, cCount, hFile); ++ ++ /* check for valid file handle */ ++ if (MEM_IsValidHandle(hFile, SIGNATURE)) { ++ if ((cSize > 0) && (cCount > 0) && pBuffer) { ++ /* read from file */ ++ fs = get_fs(); ++ set_fs(get_ds()); ++ dwBytesRead = hFile->fileDesc->f_op->read(hFile-> ++ fileDesc, pBuffer, cSize *cCount, ++ &(hFile->fileDesc->f_pos)); ++ set_fs(fs); ++ if (dwBytesRead) { ++ cRetVal = dwBytesRead / cSize; ++ hFile->curPos += dwBytesRead; ++ DBC_Assert((dwBytesRead / cSize) <= \ ++ (u32)cCount); ++ } else { ++ cRetVal = E_KFILE_ERROR; ++ GT_0trace(KFILE_debugMask, GT_6CLASS, ++ "KFILE_Read: sys_read() failed\n"); ++ } ++ } else { ++ cRetVal = DSP_EINVALIDARG; ++ GT_0trace(KFILE_debugMask, GT_6CLASS, ++ "KFILE_Read: Invalid argument(s)\n"); ++ } ++ } else { ++ cRetVal = E_KFILE_INVALIDHANDLE; ++ GT_0trace(KFILE_debugMask, GT_6CLASS, ++ "KFILE_Read: invalid file handle\n"); ++ } ++ ++ return cRetVal; ++} ++ ++/* ++ * ======== KFILE_Seek ======== ++ * Purpose: ++ * Sets the file position indicator. NOTE: we don't support seeking ++ * beyond the boundaries of a file. ++ */ ++s32 KFILE_Seek(struct KFILE_FileObj *hFile, s32 lOffset, s32 cOrigin) ++{ ++ s32 cRetVal = 0; /* 0 for success */ ++ u32 dwCurPos = 0; ++ ++ struct file *fileDesc = NULL; ++ ++ GT_3trace(KFILE_debugMask, GT_ENTER, "KFILE_Seek: hFile 0x%x, " ++ "lOffset 0x%x, cOrigin 0x%x\n", ++ hFile, lOffset, cOrigin); ++ ++ /* check for valid file handle */ ++ if (MEM_IsValidHandle(hFile, SIGNATURE)) { ++ /* based on the origin flag, move the internal pointer */ ++ ++ fileDesc = hFile->fileDesc; ++ switch (cOrigin) { ++ case KFILE_SEEK_SET: ++ dwCurPos = hFile->fileDesc->f_op->llseek(hFile-> ++ fileDesc, lOffset, SEEK_SET); ++ cRetVal = ((dwCurPos >= 0) ? 0 : E_KFILE_ERROR); ++ break; ++ ++ case KFILE_SEEK_CUR: ++ dwCurPos = hFile->fileDesc->f_op->llseek(hFile-> ++ fileDesc, lOffset, SEEK_CUR); ++ cRetVal = ((dwCurPos >= 0) ? 0 : E_KFILE_ERROR); ++ break; ++ case KFILE_SEEK_END: ++ dwCurPos = hFile->fileDesc->f_op->llseek(hFile-> ++ fileDesc, lOffset, SEEK_END); ++ cRetVal = ((dwCurPos >= 0) ? 0 : E_KFILE_ERROR); ++ break; ++ default: ++ cRetVal = E_KFILE_BADORIGINFLAG; ++ GT_0trace(KFILE_debugMask, GT_6CLASS, ++ "KFILE_Seek:bad origin flag\n"); ++ break; ++ } ++ } else { ++ cRetVal = E_KFILE_INVALIDHANDLE; ++ GT_0trace(KFILE_debugMask, GT_6CLASS, ++ "KFILE_Seek:invalid file handle\n"); ++ } ++ return cRetVal; ++} ++ ++/* ++ * ======== KFILE_Tell ======== ++ * Purpose: ++ * Reports the current value of the position indicator. We did not ++ * consider 64 bit long file size, which implies a 4GB file limit ++ * (2 to 32 power). ++ */ ++s32 KFILE_Tell(struct KFILE_FileObj *hFile) ++{ ++ u32 dwCurPos = 0; ++ s32 lRetVal = E_KFILE_ERROR; ++ ++ GT_1trace(KFILE_debugMask, GT_ENTER, "KFILE_Tell: hFile 0x%x\n", hFile); ++ ++ if (MEM_IsValidHandle(hFile, SIGNATURE)) { ++ ++ /* Get current position. */ ++ dwCurPos = hFile->fileDesc->f_op->llseek(hFile->fileDesc, 0, ++ SEEK_CUR); ++ if (dwCurPos >= 0) ++ lRetVal = dwCurPos; ++ ++ } else { ++ lRetVal = E_KFILE_INVALIDHANDLE; ++ GT_0trace(KFILE_debugMask, GT_6CLASS, ++ "KFILE_Seek:invalid file handle\n"); ++ } ++ return lRetVal; ++} ++ +diff --git a/drivers/dsp/bridge/services/list.c b/drivers/dsp/bridge/services/list.c +new file mode 100644 +index 0000000..7fa3e76 +--- /dev/null ++++ b/drivers/dsp/bridge/services/list.c +@@ -0,0 +1,285 @@ ++/* ++ * list.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== listce.c ======== ++ * Purpose ++ * Provides standard circular list handling functions. ++ * ++ * Public Functions: ++ * LST_Create ++ * LST_Delete ++ * LST_Exit ++ * LST_First ++ * LST_GetHead ++ * LST_Init ++ * LST_InitElem ++ * LST_InsertBefore ++ * LST_Next ++ * LST_PutTail ++ * LST_RemoveElem ++ * ++ *! Revision History ++ *! ================ ++ *! 06-Mar-2002 jeh Don't set element self to NULL in LST_RemoveElem(). ++ *! 10-Aug-2000 ag: Added LST_InsertBefore(). ++ *! 03-Feb-2000 rr: Module init/exit is handled by SERVICES Init/Exit. ++ *! GT Changes. ++ *! 22-Nov-1999 kc: Added changes from code review. ++ *! 10-Aug-1999 kc: Based on wsx-c18. ++ *! 16-Jun-1997 gp: Removed unnecessary enabling/disabling of interrupts around ++ *! list manipulation code. ++ *! 22-Oct-1996 gp: Added LST_RemoveElem, and LST_First/LST_Next iterators. ++ *! 10-Aug-1996 gp: Acquired from SMM for WinSPOX v. 1.1; renamed identifiers. ++ */ ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask LST_debugMask = { NULL, NULL }; /* GT trace var. */ ++#endif ++ ++/* ++ * ======== LST_Create ======== ++ * Purpose: ++ * Allocates and initializes a circular list. ++ */ ++struct LST_LIST *LST_Create(void) ++{ ++ struct LST_LIST *pList; ++ ++ GT_0trace(LST_debugMask, GT_ENTER, "LST_Create: entered\n"); ++ ++ pList = (struct LST_LIST *) MEM_Calloc(sizeof(struct LST_LIST), ++ MEM_NONPAGED); ++ if (pList != NULL) { ++ pList->head.next = &pList->head; ++ pList->head.prev = &pList->head; ++ pList->head.self = NULL; ++ } ++ ++ return pList; ++} ++ ++/* ++ * ======== LST_Delete ======== ++ * Purpose: ++ * Removes a list by freeing its control structure's memory space. ++ */ ++void LST_Delete(struct LST_LIST *pList) ++{ ++ DBC_Require(pList != NULL); ++ ++ GT_1trace(LST_debugMask, GT_ENTER, "LST_Delete: pList 0x%x\n", pList); ++ ++ MEM_Free(pList); ++} ++ ++/* ++ * ======== LST_Exit ======== ++ * Purpose: ++ * Discontinue usage of the LST module. ++ */ ++void LST_Exit(void) ++{ ++ GT_0trace(LST_debugMask, GT_5CLASS, "LST_Exit\n"); ++} ++ ++/* ++ * ======== LST_First ======== ++ * Purpose: ++ * Returns a pointer to the first element of the list, or NULL if the ++ * list is empty. ++ */ ++struct LST_ELEM *LST_First(struct LST_LIST *pList) ++{ ++ struct LST_ELEM *pElem = NULL; ++ ++ DBC_Require(pList != NULL); ++ ++ GT_1trace(LST_debugMask, GT_ENTER, "LST_First: pList 0x%x\n", pList); ++ ++ if (!LST_IsEmpty(pList)) ++ pElem = pList->head.next; ++ ++ return pElem; ++} ++ ++/* ++ * ======== LST_GetHead ======== ++ * Purpose: ++ * "Pops" the head off the list and returns a pointer to it. ++ */ ++struct LST_ELEM *LST_GetHead(struct LST_LIST *pList) ++{ ++ struct LST_ELEM *pElem; ++ ++ DBC_Require(pList != NULL); ++ ++ GT_1trace(LST_debugMask, GT_ENTER, "LST_GetHead: pList 0x%x\n", pList); ++ ++ if (LST_IsEmpty(pList)) ++ return NULL; ++ ++ /* pElem is always valid because the list cannot be empty ++ * at this point */ ++ pElem = pList->head.next; ++ pList->head.next = pElem->next; ++ pElem->next->prev = &pList->head; ++ ++ return pElem->self; ++} ++ ++/* ++ * ======== LST_Init ======== ++ * Purpose: ++ * Initialize LST module private state. ++ */ ++bool LST_Init(void) ++{ ++ GT_create(&LST_debugMask, "LS"); /* LS for LSt module */ ++ ++ GT_0trace(LST_debugMask, GT_5CLASS, "LST_Init\n"); ++ ++ return true; ++} ++ ++/* ++ * ======== LST_InitElem ======== ++ * Purpose: ++ * Initializes a list element to default (cleared) values ++ */ ++void LST_InitElem(struct LST_ELEM *pElem) ++{ ++ DBC_Require(pElem != NULL); ++ ++ GT_1trace(LST_debugMask, GT_ENTER, "LST_InitElem: pElem 0x%x\n", pElem); ++ ++ if (pElem) { ++ pElem->next = NULL; ++ pElem->prev = NULL; ++ pElem->self = pElem; ++ } ++} ++ ++/* ++ * ======== LST_InsertBefore ======== ++ * Purpose: ++ * Insert the element before the existing element. ++ */ ++void LST_InsertBefore(struct LST_LIST *pList, struct LST_ELEM *pElem, ++ struct LST_ELEM *pElemExisting) ++{ ++ DBC_Require(pList != NULL); ++ DBC_Require(pElem != NULL); ++ DBC_Require(pElemExisting != NULL); ++ ++ GT_3trace(LST_debugMask, GT_ENTER, "LST_InsertBefore: pList 0x%x, " ++ "pElem 0x%x pElemExisting 0x%x\n", pList, pElem, ++ pElemExisting); ++ ++ pElemExisting->prev->next = pElem; ++ pElem->prev = pElemExisting->prev; ++ pElem->next = pElemExisting; ++ pElemExisting->prev = pElem; ++} ++ ++/* ++ * ======== LST_Next ======== ++ * Purpose: ++ * Returns a pointer to the next element of the list, or NULL if the ++ * next element is the head of the list or the list is empty. ++ */ ++struct LST_ELEM *LST_Next(struct LST_LIST *pList, struct LST_ELEM *pCurElem) ++{ ++ struct LST_ELEM *pNextElem = NULL; ++ ++ DBC_Require(pList != NULL); ++ DBC_Require(pCurElem != NULL); ++ ++ GT_2trace(LST_debugMask, GT_ENTER, ++ "LST_Next: pList 0x%x, pCurElem 0x%x\n", ++ pList, pCurElem); ++ ++ if (!LST_IsEmpty(pList)) { ++ if (pCurElem->next != &pList->head) ++ pNextElem = pCurElem->next; ++ } ++ ++ return pNextElem; ++} ++ ++/* ++ * ======== LST_PutTail ======== ++ * Purpose: ++ * Adds the specified element to the tail of the list ++ */ ++void LST_PutTail(struct LST_LIST *pList, struct LST_ELEM *pElem) ++{ ++ DBC_Require(pList != NULL); ++ DBC_Require(pElem != NULL); ++ ++ GT_2trace(LST_debugMask, GT_ENTER, ++ "LST_PutTail: pList 0x%x, pElem 0x%x\n", ++ pList, pElem); ++ ++ pElem->prev = pList->head.prev; ++ pElem->next = &pList->head; ++ pList->head.prev = pElem; ++ pElem->prev->next = pElem; ++ ++ DBC_Ensure(!LST_IsEmpty(pList)); ++} ++ ++/* ++ * ======== LST_RemoveElem ======== ++ * Purpose: ++ * Removes (unlinks) the given element from the list, if the list is not ++ * empty. Does not free the list element. ++ */ ++void LST_RemoveElem(struct LST_LIST *pList, struct LST_ELEM *pCurElem) ++{ ++ DBC_Require(pList != NULL); ++ DBC_Require(pCurElem != NULL); ++ ++ GT_2trace(LST_debugMask, GT_ENTER, ++ "LST_RemoveElem: pList 0x%x, pCurElem " ++ "0x%x\n", pList, pCurElem); ++ ++ if (!LST_IsEmpty(pList)) { ++ pCurElem->prev->next = pCurElem->next; ++ pCurElem->next->prev = pCurElem->prev; ++ ++ /* set elem fields to NULL to prevent illegal references */ ++ pCurElem->next = NULL; ++ pCurElem->prev = NULL; ++ } ++} ++ +diff --git a/drivers/dsp/bridge/services/mem.c b/drivers/dsp/bridge/services/mem.c +new file mode 100644 +index 0000000..0a10304 +--- /dev/null ++++ b/drivers/dsp/bridge/services/mem.c +@@ -0,0 +1,599 @@ ++/* ++ * mem.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== mem.c ======== ++ * Purpose: ++ * Implementation of platform specific memory services. ++ * ++ * Public Functions: ++ * MEM_Alloc ++ * MEM_AllocPhysMem ++ * MEM_Calloc ++ * MEM_Exit ++ * MEM_FlushCache ++ * MEM_Free ++ * MEM_FreePhysMem ++ * MEM_Init ++ * MEM_ExtPhysPoolInit ++ * ++ *! Revision History: ++ *! ================= ++ *! 18-Jan-2004 hp: Added support for External physical memory pool ++ *! 19-Apr-2004 sb: Added Alloc/Free PhysMem, FlushCache, VirtualToPhysical ++ *! 01-Sep-2001 ag: Code cleanup. ++ *! 02-May-2001 ag: MEM_[UnMap]LinearAddress revamped to align Phys to Virt. ++ *! Set PAGE_PHYSICAL if phy addr <= 512MB. Opposite uSoft doc! ++ *! 29-Aug-2000 rr: MEM_LinearAddress does not check for 512MB for non-x86. ++ *! 28-Mar-2000 rr: MEM_LinearAddress changed.Handles address larger than 512MB ++ *! 03-Feb-2000 rr: Module init/exit is handled by SERVICES Init/Exit. ++ *! GT Changes. ++ *! 22-Nov-1999 kc: Added changes from code review. ++ *! 16-Aug-1999 kc: modified for WinCE. ++ *! 20-Mar-1999 ag: SP 4 fix in MEM_UMBCalloc(). ++ *! Mdl offset now ORed not added to userBuf. ++ *! 23-Dec-1997 cr: Code review changes. ++ *! 08-Dec-1997 cr: Prepared for code review. ++ *! 24-Jun-1997 cr: Created. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++#include ++ ++/* ----------------------------------- Defines */ ++#define MEM_512MB 0x1fffffff ++#define memInfoSign 0x464E494D /* "MINF" (in reverse). */ ++ ++#ifdef DEBUG ++#define MEM_CHECK /* Use to detect source of memory leaks */ ++#endif ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask MEM_debugMask = { NULL, NULL }; /* GT trace variable */ ++#endif ++ ++static u32 cRefs; /* module reference count */ ++ ++static bool extPhysMemPoolEnabled; ++ ++struct extPhysMemPool { ++ u32 physMemBase; ++ u32 physMemSize; ++ u32 virtMemBase; ++ u32 nextPhysAllocPtr; ++}; ++ ++static struct extPhysMemPool extMemPool; ++ ++/* Information about each element allocated on heap */ ++struct memInfo { ++ struct LST_ELEM link; /* Must be first */ ++ size_t size; ++ void *caller; ++ u32 dwSignature; /* Should be last */ ++}; ++ ++#ifdef MEM_CHECK ++ ++/* ++ * This structure holds a linked list to all memory elements allocated on ++ * heap by DSP/BIOS Bridge. This is used to report memory leaks and free ++ * such elements while removing the DSP/BIOS Bridge driver ++ */ ++struct memMan { ++ struct LST_LIST lst; ++ spinlock_t lock; ++}; ++ ++static struct memMan mMan; ++ ++/* ++ * These functions are similar to LST_PutTail and LST_RemoveElem and are ++ * duplicated here to make MEM independent of LST ++ */ ++static inline void MLST_PutTail(struct LST_LIST *pList, struct LST_ELEM *pElem) ++{ ++ pElem->prev = pList->head.prev; ++ pElem->next = &pList->head; ++ pList->head.prev = pElem; ++ pElem->prev->next = pElem; ++ pElem->self = pElem; ++} ++ ++static inline void MLST_RemoveElem(struct LST_LIST *pList, ++ struct LST_ELEM *pCurElem) ++{ ++ pCurElem->prev->next = pCurElem->next; ++ pCurElem->next->prev = pCurElem->prev; ++ pCurElem->next = NULL; ++ pCurElem->prev = NULL; ++} ++ ++static void MEM_Check(void) ++{ ++ struct memInfo *pMem; ++ struct LST_ELEM *last = &mMan.lst.head; ++ struct LST_ELEM *curr = mMan.lst.head.next; ++ ++ if (!LST_IsEmpty(&mMan.lst)) { ++ GT_0trace(MEM_debugMask, GT_7CLASS, "*** MEMORY LEAK ***\n"); ++ GT_0trace(MEM_debugMask, GT_7CLASS, ++ "Addr Size Caller\n"); ++ while (curr != last) { ++ pMem = (struct memInfo *)curr; ++ curr = curr->next; ++ if ((u32)pMem > PAGE_OFFSET && ++ MEM_IsValidHandle(pMem, memInfoSign)) { ++ GT_3trace(MEM_debugMask, GT_7CLASS, ++ "%lx %d\t [<%p>]\n", ++ (u32) pMem + sizeof(struct memInfo), ++ pMem->size, pMem->caller); ++ MLST_RemoveElem(&mMan.lst, ++ (struct LST_ELEM *) pMem); ++ kfree(pMem); ++ } else { ++ GT_1trace(MEM_debugMask, GT_7CLASS, ++ "Invalid allocation or " ++ "Buffer underflow at %x\n", ++ (u32)pMem + sizeof(struct memInfo)); ++ break; ++ } ++ } ++ } ++ DBC_Ensure(LST_IsEmpty(&mMan.lst)); ++} ++ ++#endif ++ ++void MEM_ExtPhysPoolInit(u32 poolPhysBase, u32 poolSize) ++{ ++ u32 poolVirtBase; ++ ++ /* get the virtual address for the physical memory pool passed */ ++ poolVirtBase = (u32)ioremap(poolPhysBase, poolSize); ++ ++ if ((void **)poolVirtBase == NULL) { ++ GT_0trace(MEM_debugMask, GT_7CLASS, ++ "[PHYS_POOL]Mapping External " ++ "physical memory to virt failed \n"); ++ extPhysMemPoolEnabled = false; ++ } else { ++ extMemPool.physMemBase = poolPhysBase; ++ extMemPool.physMemSize = poolSize; ++ extMemPool.virtMemBase = poolVirtBase; ++ extMemPool.nextPhysAllocPtr = poolPhysBase; ++ extPhysMemPoolEnabled = true; ++ GT_3trace(MEM_debugMask, GT_1CLASS, ++ "ExtMemory Pool details " "Pool" ++ "Physical mem base = %0x " "Pool Physical mem size " ++ "= %0x" "Pool Virtual mem base = %0x \n", ++ poolPhysBase, poolSize, poolVirtBase); ++ } ++} ++ ++static void MEM_ExtPhysPoolRelease(void) ++{ ++ GT_0trace(MEM_debugMask, GT_1CLASS, ++ "Releasing External memory pool \n"); ++ if (extPhysMemPoolEnabled) { ++ iounmap((void *)(extMemPool.virtMemBase)); ++ extPhysMemPoolEnabled = false; ++ } ++} ++ ++/* ++ * ======== MEM_ExtPhysMemAlloc ======== ++ * Purpose: ++ * Allocate physically contiguous, uncached memory from external memory pool ++ */ ++ ++static void *MEM_ExtPhysMemAlloc(u32 bytes, u32 align, OUT u32 *pPhysAddr) ++{ ++ u32 newAllocPtr; ++ u32 offset; ++ u32 virtAddr; ++ ++ GT_2trace(MEM_debugMask, GT_1CLASS, ++ "Ext Memory Allocation" "bytes=0x%x , " ++ "align=0x%x \n", bytes, align); ++ if (align == 0) { ++ GT_0trace(MEM_debugMask, GT_7CLASS, ++ "ExtPhysical Memory Allocation " ++ "No alignment request in allocation call !! \n"); ++ align = 1; ++ } ++ if (bytes > ((extMemPool.physMemBase + extMemPool.physMemSize) ++ - extMemPool.nextPhysAllocPtr)) { ++ GT_1trace(MEM_debugMask, GT_7CLASS, ++ "ExtPhysical Memory Allocation " ++ "unable to allocate memory for bytes = 0x%x \n", ++ bytes); ++ pPhysAddr = NULL; ++ return NULL; ++ } else { ++ offset = (extMemPool.nextPhysAllocPtr & (align - 1)); ++ if (offset == 0) ++ newAllocPtr = extMemPool.nextPhysAllocPtr; ++ else ++ newAllocPtr = (extMemPool.nextPhysAllocPtr) + ++ (align - offset); ++ if ((newAllocPtr + bytes) <= ++ (extMemPool.physMemBase + extMemPool.physMemSize)) { ++ /* we can allocate */ ++ *pPhysAddr = newAllocPtr; ++ extMemPool.nextPhysAllocPtr = newAllocPtr + bytes; ++ virtAddr = extMemPool.virtMemBase + (newAllocPtr - ++ extMemPool.physMemBase); ++ GT_2trace(MEM_debugMask, GT_1CLASS, ++ "Ext Memory Allocation succedded " ++ "phys address=0x%x , virtaddress=0x%x \n", ++ newAllocPtr, virtAddr); ++ return (void *)virtAddr; ++ } else { ++ *pPhysAddr = 0; ++ return NULL; ++ } ++ } ++} ++ ++/* ++ * ======== MEM_Alloc ======== ++ * Purpose: ++ * Allocate memory from the paged or non-paged pools. ++ */ ++void *MEM_Alloc(u32 cBytes, enum MEM_POOLATTRS type) ++{ ++ struct memInfo *pMem = NULL; ++ ++ GT_2trace(MEM_debugMask, GT_ENTER, ++ "MEM_Alloc: cBytes 0x%x\ttype 0x%x\n", cBytes, type); ++ if (cBytes > 0) { ++ switch (type) { ++ case MEM_NONPAGED: ++ /* If non-paged memory required, see note at top of file. */ ++ case MEM_PAGED: ++#ifndef MEM_CHECK ++ pMem = kmalloc(cBytes, ++ (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL); ++#else ++ pMem = kmalloc(cBytes + sizeof(struct memInfo), ++ (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL); ++ if (pMem) { ++ pMem->size = cBytes; ++ pMem->caller = __builtin_return_address(0); ++ pMem->dwSignature = memInfoSign; ++ ++ spin_lock(&mMan.lock); ++ MLST_PutTail(&mMan.lst, ++ (struct LST_ELEM *)pMem); ++ spin_unlock(&mMan.lock); ++ ++ pMem = (void *)((u32)pMem + ++ sizeof(struct memInfo)); ++ } ++#endif ++ break; ++ case MEM_LARGEVIRTMEM: ++#ifndef MEM_CHECK ++ /* FIXME - Replace with 'vmalloc' after BP fix */ ++ pMem = __vmalloc(cBytes, ++ (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL, PAGE_KERNEL); ++#else ++ /* FIXME - Replace with 'vmalloc' after BP fix */ ++ pMem = __vmalloc((cBytes + sizeof(struct memInfo)), ++ (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL, PAGE_KERNEL); ++ if (pMem) { ++ pMem->size = cBytes; ++ pMem->caller = __builtin_return_address(0); ++ pMem->dwSignature = memInfoSign; ++ ++ spin_lock(&mMan.lock); ++ MLST_PutTail(&mMan.lst, ++ (struct LST_ELEM *) pMem); ++ spin_unlock(&mMan.lock); ++ ++ pMem = (void *)((u32)pMem + ++ sizeof(struct memInfo)); ++ } ++#endif ++ break; ++ ++ default: ++ GT_0trace(MEM_debugMask, GT_6CLASS, ++ "MEM_Alloc: unexpected " ++ "MEM_POOLATTRS value\n"); ++ break; ++ } ++ } ++ ++ return pMem; ++} ++ ++/* ++ * ======== MEM_AllocPhysMem ======== ++ * Purpose: ++ * Allocate physically contiguous, uncached memory ++ */ ++void *MEM_AllocPhysMem(u32 cBytes, u32 ulAlign, OUT u32 *pPhysicalAddress) ++{ ++ void *pVaMem = NULL; ++ dma_addr_t paMem; ++ ++ DBC_Require(cRefs > 0); ++ ++ GT_2trace(MEM_debugMask, GT_ENTER, ++ "MEM_AllocPhysMem: cBytes 0x%x\tulAlign" ++ "0x%x\n", cBytes, ulAlign); ++ ++ if (cBytes > 0) { ++ if (extPhysMemPoolEnabled) { ++ pVaMem = MEM_ExtPhysMemAlloc(cBytes, ulAlign, ++ (u32 *)&paMem); ++ } else ++ pVaMem = dma_alloc_coherent(NULL, cBytes, &paMem, ++ (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL); ++ if (pVaMem == NULL) { ++ *pPhysicalAddress = 0; ++ GT_1trace(MEM_debugMask, GT_6CLASS, ++ "MEM_AllocPhysMem failed: " ++ "0x%x\n", pVaMem); ++ } else { ++ *pPhysicalAddress = paMem; ++ } ++ } ++ return pVaMem; ++} ++ ++/* ++ * ======== MEM_Calloc ======== ++ * Purpose: ++ * Allocate zero-initialized memory from the paged or non-paged pools. ++ */ ++void *MEM_Calloc(u32 cBytes, enum MEM_POOLATTRS type) ++{ ++ struct memInfo *pMem = NULL; ++ ++ GT_2trace(MEM_debugMask, GT_ENTER, ++ "MEM_Calloc: cBytes 0x%x\ttype 0x%x\n", ++ cBytes, type); ++ ++ if (cBytes > 0) { ++ switch (type) { ++ case MEM_NONPAGED: ++ /* If non-paged memory required, see note at top of file. */ ++ case MEM_PAGED: ++#ifndef MEM_CHECK ++ pMem = kmalloc(cBytes, ++ (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL); ++ if (pMem) ++ memset(pMem, 0, cBytes); ++ ++#else ++ pMem = kmalloc(cBytes + sizeof(struct memInfo), ++ (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL); ++ if (pMem) { ++ memset((void *)((u32)pMem + ++ sizeof(struct memInfo)), 0, cBytes); ++ pMem->size = cBytes; ++ pMem->caller = __builtin_return_address(0); ++ pMem->dwSignature = memInfoSign; ++ spin_lock(&mMan.lock); ++ MLST_PutTail(&mMan.lst, ++ (struct LST_ELEM *) pMem); ++ spin_unlock(&mMan.lock); ++ pMem = (void *)((u32)pMem + ++ sizeof(struct memInfo)); ++ } ++#endif ++ break; ++ case MEM_LARGEVIRTMEM: ++#ifndef MEM_CHECK ++ /* FIXME - Replace with 'vmalloc' after BP fix */ ++ pMem = __vmalloc(cBytes, ++ (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL, PAGE_KERNEL); ++ if (pMem) ++ memset(pMem, 0, cBytes); ++ ++#else ++ /* FIXME - Replace with 'vmalloc' after BP fix */ ++ pMem = __vmalloc(cBytes + sizeof(struct memInfo), ++ (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL, PAGE_KERNEL); ++ if (pMem) { ++ memset((void *)((u32)pMem + ++ sizeof(struct memInfo)), 0, cBytes); ++ pMem->size = cBytes; ++ pMem->caller = __builtin_return_address(0); ++ pMem->dwSignature = memInfoSign; ++ spin_lock(&mMan.lock); ++ MLST_PutTail(&mMan.lst, (struct LST_ELEM *) ++ pMem); ++ spin_unlock(&mMan.lock); ++ pMem = (void *)((u32)pMem + ++ sizeof(struct memInfo)); ++ } ++#endif ++ break; ++ default: ++ GT_1trace(MEM_debugMask, GT_6CLASS, ++ "MEM_Calloc: unexpected " ++ "MEM_POOLATTRS value 0x%x\n", type); ++ break; ++ } ++ } ++ ++ return pMem; ++} ++ ++/* ++ * ======== MEM_Exit ======== ++ * Purpose: ++ * Discontinue usage of the MEM module. ++ */ ++void MEM_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ GT_1trace(MEM_debugMask, GT_5CLASS, "MEM_Exit: cRefs 0x%x\n", cRefs); ++ ++ cRefs--; ++#ifdef MEM_CHECK ++ if (cRefs == 0) ++ MEM_Check(); ++ ++#endif ++ MEM_ExtPhysPoolRelease(); ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== MEM_FlushCache ======== ++ * Purpose: ++ * Flush cache ++ */ ++void MEM_FlushCache(void *pMemBuf, u32 cBytes, s32 FlushType) ++{ ++ DBC_Require(cRefs > 0); ++ ++ switch (FlushType) { ++ /* invalidate only */ ++ case PROC_INVALIDATE_MEM: ++ dmac_inv_range(pMemBuf, pMemBuf + cBytes); ++ outer_inv_range(__pa((u32)pMemBuf), __pa((u32)pMemBuf + ++ cBytes)); ++ break; ++ /* writeback only */ ++ case PROC_WRITEBACK_MEM: ++ dmac_clean_range(pMemBuf, pMemBuf + cBytes); ++ outer_clean_range(__pa((u32)pMemBuf), __pa((u32)pMemBuf + ++ cBytes)); ++ break; ++ /* writeback and invalidate */ ++ case PROC_WRITEBACK_INVALIDATE_MEM: ++ dmac_flush_range(pMemBuf, pMemBuf + cBytes); ++ outer_flush_range(__pa((u32)pMemBuf), __pa((u32)pMemBuf + ++ cBytes)); ++ break; ++ default: ++ GT_1trace(MEM_debugMask, GT_6CLASS, "MEM_FlushCache: invalid " ++ "FlushMemType 0x%x\n", FlushType); ++ break; ++ } ++ ++} ++ ++ ++/* ++ * ======== MEM_Free ======== ++ * Purpose: ++ * Free the given block of system memory. ++ */ ++void MEM_Free(IN void *pMemBuf) ++{ ++#ifdef MEM_CHECK ++ struct memInfo *pMem = (void *)((u32)pMemBuf - sizeof(struct memInfo)); ++#endif ++ ++ DBC_Require(pMemBuf != NULL); ++ ++ GT_1trace(MEM_debugMask, GT_ENTER, "MEM_Free: pMemBufs 0x%x\n", ++ pMemBuf); ++ ++ if (pMemBuf) { ++#ifndef MEM_CHECK ++ kfree(pMemBuf); ++#else ++ if (pMem) { ++ if (pMem->dwSignature == memInfoSign) { ++ spin_lock(&mMan.lock); ++ MLST_RemoveElem(&mMan.lst, ++ (struct LST_ELEM *) pMem); ++ spin_unlock(&mMan.lock); ++ pMem->dwSignature = 0; ++ kfree(pMem); ++ } else { ++ GT_1trace(MEM_debugMask, GT_7CLASS, ++ "Invalid allocation or " ++ "Buffer underflow at %x\n", ++ (u32) pMem + sizeof(struct memInfo)); ++ } ++ } ++#endif ++ } ++} ++ ++/* ++ * ======== MEM_FreePhysMem ======== ++ * Purpose: ++ * Free the given block of physically contiguous memory. ++ */ ++void MEM_FreePhysMem(void *pVirtualAddress, u32 pPhysicalAddress, ++ u32 cBytes) ++{ ++ DBC_Require(cRefs > 0); ++ DBC_Require(pVirtualAddress != NULL); ++ ++ GT_1trace(MEM_debugMask, GT_ENTER, "MEM_FreePhysMem: pVirtualAddress " ++ "0x%x\n", pVirtualAddress); ++ ++ if (!extPhysMemPoolEnabled) ++ dma_free_coherent(NULL, cBytes, pVirtualAddress, ++ pPhysicalAddress); ++} ++ ++/* ++ * ======== MEM_Init ======== ++ * Purpose: ++ * Initialize MEM module private state. ++ */ ++bool MEM_Init(void) ++{ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++ GT_create(&MEM_debugMask, "MM"); /* MM for MeM module */ ++ ++#ifdef MEM_CHECK ++ mMan.lst.head.next = &mMan.lst.head; ++ mMan.lst.head.prev = &mMan.lst.head; ++ mMan.lst.head.self = NULL; ++ spin_lock_init(&mMan.lock); ++#endif ++ ++ } ++ ++ cRefs++; ++ ++ GT_1trace(MEM_debugMask, GT_5CLASS, "MEM_Init: cRefs 0x%x\n", cRefs); ++ ++ DBC_Ensure(cRefs > 0); ++ ++ return true; ++} +diff --git a/drivers/dsp/bridge/services/ntfy.c b/drivers/dsp/bridge/services/ntfy.c +new file mode 100644 +index 0000000..2eff3eb +--- /dev/null ++++ b/drivers/dsp/bridge/services/ntfy.c +@@ -0,0 +1,329 @@ ++/* ++ * ntfy.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== ntfyce.c ======== ++ * Purpose: ++ * Manage lists of notification events. ++ * ++ * Public Functions: ++ * NTFY_Create ++ * NTFY_Delete ++ * NTFY_Exit ++ * NTFY_Init ++ * NTFY_Notify ++ * NTFY_Register ++ * ++ *! Revision History: ++ *! ================= ++ *! 06-Feb-2003 kc Removed DSP_POSTMESSAGE related code. ++ *! 05-Nov-2001 kc Updated DSP_HNOTIFICATION structure. ++ *! 10-May-2001 jeh Removed SERVICES module init/exit from NTFY_Init/Exit. ++ *! NTFY_Register() returns DSP_ENOTIMPL for all but ++ *! DSP_SIGNALEVENT. ++ *! 12-Oct-2000 jeh Use MEM_IsValidHandle(). ++ *! 07-Sep-2000 jeh Created. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ----------------------------------- Defines, Data Structures, Typedefs */ ++#define NTFY_SIGNATURE 0x5946544e /* "YFTN" */ ++ ++/* ++ * ======== NTFY_OBJECT ======== ++ */ ++struct NTFY_OBJECT { ++ u32 dwSignature; /* For object validation */ ++ struct LST_LIST *notifyList; /* List of NOTIFICATION objects */ ++ struct SYNC_CSOBJECT *hSync; /* For critical sections */ ++}; ++ ++/* ++ * ======== NOTIFICATION ======== ++ * This object will be created when a client registers for events. ++ */ ++struct NOTIFICATION { ++ struct LST_ELEM listElem; ++ u32 uEventMask; /* Events to be notified about */ ++ u32 uNotifyType; /* Type of notification to be sent */ ++ ++ /* ++ * We keep a copy of the event name to check if the event has ++ * already been registered. (SYNC also keeps a copy of the name). ++ */ ++ char *pstrName; /* Name of event */ ++ HANDLE hEvent; /* Handle for notification */ ++ struct SYNC_OBJECT *hSync; ++}; ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask NTFY_debugMask = { NULL, NULL }; /* GT trace variable */ ++#endif ++ ++/* ----------------------------------- Function Prototypes */ ++static void DeleteNotify(struct NOTIFICATION *pNotify); ++ ++/* ++ * ======== NTFY_Create ======== ++ * Purpose: ++ * Create an empty list of notifications. ++ */ ++DSP_STATUS NTFY_Create(struct NTFY_OBJECT **phNtfy) ++{ ++ struct NTFY_OBJECT *pNtfy; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(phNtfy != NULL); ++ ++ *phNtfy = NULL; ++ MEM_AllocObject(pNtfy, struct NTFY_OBJECT, NTFY_SIGNATURE); ++ ++ if (pNtfy) { ++ ++ status = SYNC_InitializeDPCCS(&pNtfy->hSync); ++ if (DSP_SUCCEEDED(status)) { ++ pNtfy->notifyList = LST_Create(); ++ if (pNtfy->notifyList == NULL) { ++ (void) SYNC_DeleteCS(pNtfy->hSync); ++ MEM_FreeObject(pNtfy); ++ status = DSP_EMEMORY; ++ } else { ++ *phNtfy = pNtfy; ++ } ++ } ++ } else { ++ status = DSP_EMEMORY; ++ } ++ ++ DBC_Ensure((DSP_FAILED(status) && *phNtfy == NULL) || ++ (DSP_SUCCEEDED(status) && MEM_IsValidHandle((*phNtfy), ++ NTFY_SIGNATURE))); ++ ++ return status; ++} ++ ++/* ++ * ======== NTFY_Delete ======== ++ * Purpose: ++ * Free resources allocated in NTFY_Create. ++ */ ++void NTFY_Delete(struct NTFY_OBJECT *hNtfy) ++{ ++ struct NOTIFICATION *pNotify; ++ ++ DBC_Require(MEM_IsValidHandle(hNtfy, NTFY_SIGNATURE)); ++ ++ /* Remove any elements remaining in list */ ++ if (hNtfy->notifyList) { ++ while ((pNotify = (struct NOTIFICATION *)LST_GetHead(hNtfy-> ++ notifyList))) { ++ DeleteNotify(pNotify); ++ } ++ DBC_Assert(LST_IsEmpty(hNtfy->notifyList)); ++ LST_Delete(hNtfy->notifyList); ++ } ++ if (hNtfy->hSync) ++ (void)SYNC_DeleteCS(hNtfy->hSync); ++ ++ MEM_FreeObject(hNtfy); ++} ++ ++/* ++ * ======== NTFY_Exit ======== ++ * Purpose: ++ * Discontinue usage of NTFY module. ++ */ ++void NTFY_Exit(void) ++{ ++ GT_0trace(NTFY_debugMask, GT_5CLASS, "Entered NTFY_Exit\n"); ++} ++ ++/* ++ * ======== NTFY_Init ======== ++ * Purpose: ++ * Initialize the NTFY module. ++ */ ++bool NTFY_Init(void) ++{ ++ GT_create(&NTFY_debugMask, "NY"); /* "NY" for NtfY */ ++ ++ GT_0trace(NTFY_debugMask, GT_5CLASS, "NTFY_Init()\n"); ++ ++ return true; ++} ++ ++/* ++ * ======== NTFY_Notify ======== ++ * Purpose: ++ * Execute notify function (signal event) for every ++ * element in the notification list that is to be notified about the ++ * event specified in uEventMask. ++ */ ++void NTFY_Notify(struct NTFY_OBJECT *hNtfy, u32 uEventMask) ++{ ++ struct NOTIFICATION *pNotify; ++ ++ DBC_Require(MEM_IsValidHandle(hNtfy, NTFY_SIGNATURE)); ++ ++ /* ++ * Go through notifyList and notify all clients registered for ++ * uEventMask events. ++ */ ++ ++ (void) SYNC_EnterCS(hNtfy->hSync); ++ ++ pNotify = (struct NOTIFICATION *)LST_First(hNtfy->notifyList); ++ while (pNotify != NULL) { ++ if (pNotify->uEventMask & uEventMask) { ++ /* Notify */ ++ if (pNotify->uNotifyType == DSP_SIGNALEVENT) ++ (void)SYNC_SetEvent(pNotify->hSync); ++ ++ } ++ pNotify = (struct NOTIFICATION *)LST_Next(hNtfy->notifyList, ++ (struct LST_ELEM *)pNotify); ++ } ++ ++ (void) SYNC_LeaveCS(hNtfy->hSync); ++} ++ ++/* ++ * ======== NTFY_Register ======== ++ * Purpose: ++ * Add a notification element to the list. If the notification is already ++ * registered, and uEventMask != 0, the notification will get posted for ++ * events specified in the new event mask. If the notification is already ++ * registered and uEventMask == 0, the notification will be unregistered. ++ */ ++DSP_STATUS NTFY_Register(struct NTFY_OBJECT *hNtfy, ++ struct DSP_NOTIFICATION *hNotification, ++ u32 uEventMask, u32 uNotifyType) ++{ ++ struct NOTIFICATION *pNotify; ++ struct SYNC_ATTRS syncAttrs; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(MEM_IsValidHandle(hNtfy, NTFY_SIGNATURE)); ++ ++ if (hNotification == NULL) ++ status = DSP_EHANDLE; ++ ++ /* Return DSP_ENOTIMPL if uNotifyType is not supported */ ++ if (DSP_SUCCEEDED(status)) { ++ if (!IsValidNotifyMask(uNotifyType)) ++ status = DSP_ENOTIMPL; ++ ++ } ++ ++ if (DSP_FAILED(status)) ++ return status; ++ ++ (void)SYNC_EnterCS(hNtfy->hSync); ++ ++ pNotify = (struct NOTIFICATION *)LST_First(hNtfy->notifyList); ++ while (pNotify != NULL) { ++ /* If there is more than one notification type, each ++ * type may require its own handler code. */ ++ ++ if (hNotification->handle == pNotify->hSync) { ++ /* found */ ++ break; ++ } ++ pNotify = (struct NOTIFICATION *)LST_Next(hNtfy->notifyList, ++ (struct LST_ELEM *)pNotify); ++ } ++ if (pNotify == NULL) { ++ /* Not registered */ ++ if (uEventMask == 0) { ++ status = DSP_EVALUE; ++ } else { ++ /* Allocate NOTIFICATION object, add to list */ ++ pNotify = MEM_Calloc(sizeof(struct NOTIFICATION), ++ MEM_PAGED); ++ if (pNotify == NULL) ++ status = DSP_EMEMORY; ++ ++ } ++ if (DSP_SUCCEEDED(status)) { ++ LST_InitElem((struct LST_ELEM *) pNotify); ++ /* If there is more than one notification type, each ++ * type may require its own handler code. */ ++ status = SYNC_OpenEvent(&pNotify->hSync, &syncAttrs); ++ hNotification->handle = pNotify->hSync; ++ ++ if (DSP_SUCCEEDED(status)) { ++ pNotify->uEventMask = uEventMask; ++ pNotify->uNotifyType = uNotifyType; ++ LST_PutTail(hNtfy->notifyList, ++ (struct LST_ELEM *)pNotify); ++ } else { ++ DeleteNotify(pNotify); ++ } ++ } ++ } else { ++ /* Found in list */ ++ if (uEventMask == 0) { ++ /* Remove from list and free */ ++ LST_RemoveElem(hNtfy->notifyList, ++ (struct LST_ELEM *)pNotify); ++ DeleteNotify(pNotify); ++ } else { ++ /* Update notification mask (type shouldn't change) */ ++ pNotify->uEventMask = uEventMask; ++ } ++ } ++ (void)SYNC_LeaveCS(hNtfy->hSync); ++ return status; ++} ++ ++/* ++ * ======== DeleteNotify ======== ++ * Purpose: ++ * Free the notification object. ++ */ ++static void DeleteNotify(struct NOTIFICATION *pNotify) ++{ ++ if (pNotify->hSync) ++ (void) SYNC_CloseEvent(pNotify->hSync); ++ ++ if (pNotify->pstrName) ++ MEM_Free(pNotify->pstrName); ++ ++ MEM_Free(pNotify); ++} ++ +diff --git a/drivers/dsp/bridge/services/reg.c b/drivers/dsp/bridge/services/reg.c +new file mode 100644 +index 0000000..0d85f41 +--- /dev/null ++++ b/drivers/dsp/bridge/services/reg.c +@@ -0,0 +1,196 @@ ++/* ++ * reg.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== regce.c ======== ++ * Purpose: ++ * Provide registry functions. ++ * ++ * Public Functions: ++ * REG_DeleteValue ++ * REG_EnumValue ++ * REG_Exit ++ * REG_GetValue ++ * REG_Init ++ * REG_SetValue ++ * ++ *! Revision History: ++ *! ================ ++ * ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++ ++/* ----------------------------------- Others */ ++#include ++ ++/* ----------------------------------- This */ ++#include ++#include ++ ++#if GT_TRACE ++struct GT_Mask REG_debugMask = { NULL, NULL }; /* GT trace var. */ ++#endif ++ ++/* ++ * ======== REG_DeleteValue ======== ++ * Deletes a registry entry value. NOTE: A registry entry value is not the ++ * same as * a registry key. ++ */ ++DSP_STATUS REG_DeleteValue(OPTIONAL IN HANDLE *phKey, IN CONST char *pstrSubkey, ++ IN CONST char *pstrValue) ++{ ++ DSP_STATUS status; ++ DBC_Require(pstrSubkey && pstrValue); ++ DBC_Require(phKey == NULL); ++ DBC_Require(strlen(pstrSubkey) < REG_MAXREGPATHLENGTH); ++ DBC_Require(strlen(pstrValue) < REG_MAXREGPATHLENGTH); ++ ++ GT_0trace(REG_debugMask, GT_ENTER, "REG_DeleteValue: entered\n"); ++ ++ /* Note that we don't use phKey */ ++ if (regsupDeleteValue(pstrSubkey, pstrValue) == DSP_SOK) ++ status = DSP_SOK; ++ else ++ status = DSP_EFAIL; ++ ++ return status; ++} ++ ++/* ++ * ======== REG_EnumValue ======== ++ * Enumerates a registry key and retrieve values stored under the key. ++ * We will assume the input pdwValueSize is smaller than ++ * REG_MAXREGPATHLENGTH for implementation purposes. ++ */ ++DSP_STATUS REG_EnumValue(IN HANDLE *phKey, IN u32 dwIndex, ++ IN CONST char *pstrKey, IN OUT char *pstrValue, ++ IN OUT u32 *pdwValueSize, IN OUT char *pstrData, ++ IN OUT u32 *pdwDataSize) ++{ ++ DSP_STATUS status; ++ ++ DBC_Require(pstrKey && pstrValue && pdwValueSize && pstrData && ++ pdwDataSize); ++ DBC_Require(*pdwValueSize <= REG_MAXREGPATHLENGTH); ++ DBC_Require(phKey == NULL); ++ DBC_Require(strlen(pstrKey) < REG_MAXREGPATHLENGTH); ++ ++ GT_0trace(REG_debugMask, GT_ENTER, "REG_EnumValue: entered\n"); ++ ++ status = regsupEnumValue(dwIndex, pstrKey, pstrValue, pdwValueSize, ++ pstrData, pdwDataSize); ++ ++ return status; ++} ++ ++/* ++ * ======== REG_Exit ======== ++ * Discontinue usage of the REG module. ++ */ ++void REG_Exit(void) ++{ ++ GT_0trace(REG_debugMask, GT_5CLASS, "REG_Exit\n"); ++ ++ regsupExit(); ++} ++ ++/* ++ * ======== REG_GetValue ======== ++ * Retrieve a value from the registry. ++ */ ++DSP_STATUS REG_GetValue(OPTIONAL IN HANDLE *phKey, IN CONST char *pstrSubkey, ++ IN CONST char *pstrValue, OUT u8 *pbData, ++ IN OUT u32 *pdwDataSize) ++{ ++ DSP_STATUS status; ++ ++ DBC_Require(pstrSubkey && pstrValue && pbData); ++ DBC_Require(phKey == NULL); ++ DBC_Require(strlen(pstrSubkey) < REG_MAXREGPATHLENGTH); ++ DBC_Require(strlen(pstrValue) < REG_MAXREGPATHLENGTH); ++ ++ GT_0trace(REG_debugMask, GT_ENTER, "REG_GetValue: entered\n"); ++ ++ /* We need to use regsup calls... */ ++ /* ...for now we don't need the key handle or */ ++ /* the subkey, all we need is the value to lookup. */ ++ if (regsupGetValue((char *)pstrValue, pbData, pdwDataSize) == DSP_SOK) ++ status = DSP_SOK; ++ else ++ status = DSP_EFAIL; ++ ++ return status; ++} ++ ++/* ++ * ======== REG_Init ======== ++ * Initialize the REG module's private state. ++ */ ++bool REG_Init(void) ++{ ++ bool fInit; ++ ++ GT_create(®_debugMask, "RG"); /* RG for ReG */ ++ ++ fInit = regsupInit(); ++ ++ GT_0trace(REG_debugMask, GT_5CLASS, "REG_Init\n"); ++ ++ return fInit; ++} ++ ++/* ++ * ======== REG_SetValue ======== ++ * Set a value in the registry. ++ */ ++DSP_STATUS REG_SetValue(OPTIONAL IN HANDLE *phKey, IN CONST char *pstrSubkey, ++ IN CONST char *pstrValue, IN CONST u32 dwType, ++ IN u8 *pbData, IN u32 dwDataSize) ++{ ++ DSP_STATUS status; ++ ++ DBC_Require(pstrValue && pbData); ++ DBC_Require(phKey == NULL); ++ DBC_Require(dwDataSize > 0); ++ DBC_Require(strlen(pstrValue) < REG_MAXREGPATHLENGTH); ++ ++ /* We need to use regsup calls... */ ++ /* ...for now we don't need the key handle or */ ++ /* the subkey, all we need is the value to lookup. */ ++ if (regsupSetValue((char *)pstrValue, pbData, dwDataSize) == DSP_SOK) ++ status = DSP_SOK; ++ else ++ status = DSP_EFAIL; ++ ++ return status; ++} ++ +diff --git a/drivers/dsp/bridge/services/regsup.c b/drivers/dsp/bridge/services/regsup.c +new file mode 100644 +index 0000000..5251b68 +--- /dev/null ++++ b/drivers/dsp/bridge/services/regsup.c +@@ -0,0 +1,368 @@ ++/* ++ * regsup.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== regsup.c ======== ++ * Purpose: ++ * Provide registry support functions. ++ * ++ *! Revision History: ++ *! ================ ++ *! 28-May-2002 map: Integrated PSI's dspimage update mechanism ++ *! 11-May-2002 gp: Turned PERF "on". ++ *! 21-May-2002 map: Fixed bug in SetValue - if resizing datasize, set ++ *! new size too ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++struct RegValueStruct { ++ char name[BRIDGE_MAX_NAME_SIZE]; /* Name of a given value entry */ ++ u32 dataSize; /* Size of the data */ ++ void *pData; /* Pointer to the actual data */ ++}; ++ ++struct RegKeyStruct { ++ /*The current number of value entries this key has*/ ++ u32 numValueEntries; ++ /* Array of value entries */ ++ struct RegValueStruct values[BRIDGE_MAX_NUM_REG_ENTRIES]; ++}; ++ ++ ++/* Pointer to the registry support key */ ++static struct RegKeyStruct *pRegKey; ++ ++#if GT_TRACE ++extern struct GT_Mask REG_debugMask; /* GT trace var. */ ++/* ++ * ======== printS ======== ++ * Purpose: ++ * Displays printable characters in pBuf, if any. ++ */ ++static inline void printS(void *pBuf) ++{ ++ int pos = 0; ++ if (*(REG_debugMask).flags & (GT_2CLASS)) { ++ while (*(u8 *)((pBuf)+pos) >= ' ' && ++ *(u8 *)((pBuf)+pos) <= '~') { ++ GT_1trace(REG_debugMask, GT_2CLASS, "%c", ++ *(u8 *)((pBuf) + pos++)); ++ } ++ ++ GT_0trace(REG_debugMask, GT_2CLASS, "\n"); ++ } ++} ++#else ++#define printS(pBuf) ++#endif ++ ++/* ++ * ======== regsupInit ======== ++ * Purpose: ++ * Initialize the Registry Support module's private state. ++ */ ++bool regsupInit(void) ++{ ++ if (pRegKey != NULL) ++ return true; ++ ++ /* Need to allocate and setup our registry. */ ++ pRegKey = MEM_Calloc(sizeof(struct RegKeyStruct), MEM_NONPAGED); ++ if (pRegKey == NULL) ++ return false; ++ ++ return true; ++} ++ ++/* ++ * ======== regsupExit ======== ++ * Purpose: ++ * Release all registry support allocations. ++ */ ++void regsupExit(void) ++{ ++ u32 i; ++ ++ /* Make sure data has actually been allocated. */ ++ if (pRegKey == NULL) { ++ /* Nothing initialized.return! */ ++ return; ++ } ++ ++ GT_1trace(REG_debugMask, GT_2CLASS, "pRegKey->numValueEntries %d\n", ++ pRegKey->numValueEntries); ++ ++ /* Now go through each entry and free all resources. */ ++ for (i = 0; ((i < BRIDGE_MAX_NUM_REG_ENTRIES) && ++ (i < pRegKey->numValueEntries)); i++) { ++ if (pRegKey->values[i].name[0] != '\0') { ++ /* We have a valid entry.free it up! */ ++ if (pRegKey->values[i].pData != NULL) { ++ GT_3trace(REG_debugMask, GT_2CLASS, ++ "E %d\t %s DATA %x ", i, ++ pRegKey->values[i].name, ++ *(u32 *)pRegKey->values[i].pData); ++ printS((u8 *)(pRegKey->values[i].pData)); ++ MEM_Free(pRegKey->values[i].pData); ++ } ++ pRegKey->values[i].pData = NULL; ++ pRegKey->values[i].dataSize = 0; ++ pRegKey->values[i].name[0] = '\0'; ++ } ++ } ++ ++ /* Now that all of the resources are freed up, free the main one! */ ++ MEM_Free(pRegKey); ++ ++ /* Don't forget to NULL out the global entry! */ ++ pRegKey = NULL; ++} ++ ++/* ++ * ======== regsupGetValue ======== ++ * Purpose: ++ * Get the value of the entry having the given name. ++ */ ++DSP_STATUS regsupGetValue(char *valName, void *pBuf, u32 *dataSize) ++{ ++ DSP_STATUS retVal = DSP_EFAIL; ++ u32 i; ++ ++ /* Need to search through the entries looking for the right one. */ ++ for (i = 0; i < pRegKey->numValueEntries; i++) { ++ /* See if the name matches. */ ++ if (strncmp(pRegKey->values[i].name, valName, ++ BRIDGE_MAX_NAME_SIZE) == 0) { ++ ++ /* We have a match! Copy out the data. */ ++ memcpy(pBuf, pRegKey->values[i].pData, ++ pRegKey->values[i].dataSize); ++ ++ /* Get the size for the caller. */ ++ *dataSize = pRegKey->values[i].dataSize; ++ ++ /* Set our status to good and exit. */ ++ retVal = DSP_SOK; ++ break; ++ } ++ } ++ ++ if (DSP_SUCCEEDED(retVal)) { ++ GT_2trace(REG_debugMask, GT_2CLASS, "G %s DATA %x ", valName, ++ *(u32 *)pBuf); ++ printS((u8 *)pBuf); ++ } else { ++ GT_1trace(REG_debugMask, GT_3CLASS, "G %s FAILED\n", valName); ++ } ++ ++ return retVal; ++} ++ ++/* ++ * ======== regsupSetValue ======== ++ * Purpose: ++ * Sets the value of the entry having the given name. ++ */ ++DSP_STATUS regsupSetValue(char *valName, void *pBuf, u32 dataSize) ++{ ++ DSP_STATUS retVal = DSP_EFAIL; ++ u32 i; ++ ++ GT_2trace(REG_debugMask, GT_2CLASS, "S %s DATA %x ", valName, ++ *(u32 *)pBuf); ++ printS((u8 *)pBuf); ++ ++ /* Need to search through the entries looking for the right one. */ ++ for (i = 0; i < pRegKey->numValueEntries; i++) { ++ /* See if the name matches. */ ++ if (strncmp(pRegKey->values[i].name, valName, ++ BRIDGE_MAX_NAME_SIZE) == 0) { ++ /* Make sure the new data size is the same. */ ++ if (dataSize != pRegKey->values[i].dataSize) { ++ /* The caller needs a different data size! */ ++ MEM_Free(pRegKey->values[i].pData); ++ pRegKey->values[i].pData = MEM_Alloc(dataSize, ++ MEM_NONPAGED); ++ if (pRegKey->values[i].pData == NULL) ++ break; ++ ++ } ++ ++ /* We have a match! Copy out the data. */ ++ memcpy(pRegKey->values[i].pData, pBuf, dataSize); ++ ++ /* Reset datasize - overwrite if new or same */ ++ pRegKey->values[i].dataSize = dataSize; ++ ++ /* Set our status to good and exit. */ ++ retVal = DSP_SOK; ++ break; ++ } ++ } ++ ++ /* See if we found a match or if this is a new entry */ ++ if (i == pRegKey->numValueEntries) { ++ /* No match, need to make a new entry */ ++ /* First check to see if we can make any more entries. */ ++ if (pRegKey->numValueEntries < BRIDGE_MAX_NUM_REG_ENTRIES) { ++ strncpy(pRegKey->values[pRegKey->numValueEntries].name, ++ valName, BRIDGE_MAX_NAME_SIZE); ++ pRegKey->values[pRegKey->numValueEntries].pData = ++ MEM_Alloc(dataSize, MEM_NONPAGED); ++ if (pRegKey->values[pRegKey->numValueEntries].pData != ++ NULL) { ++ memcpy(pRegKey-> ++ values[pRegKey->numValueEntries].pData, ++ pBuf, dataSize); ++ pRegKey-> ++ values[pRegKey->numValueEntries].dataSize = ++ dataSize; ++ pRegKey->numValueEntries++; ++ retVal = DSP_SOK; ++ } ++ } else { ++ GT_0trace(REG_debugMask, GT_7CLASS, ++ "MAX NUM REG ENTRIES REACHED\n"); ++ } ++ } ++ ++ return retVal; ++} ++ ++/* ++ * ======== regsupEnumValue ======== ++ * Purpose: ++ * Returns registry "values" and their "data" under a (sub)key. ++ */ ++DSP_STATUS regsupEnumValue(IN u32 dwIndex, IN CONST char *pstrKey, ++ IN OUT char *pstrValue, IN OUT u32 *pdwValueSize, ++ IN OUT char *pstrData, IN OUT u32 *pdwDataSize) ++{ ++ DSP_STATUS retVal = REG_E_INVALIDSUBKEY; ++ u32 i; ++ u32 dwKeyLen; ++ u32 count = 0; ++ ++ DBC_Require(pstrKey); ++ dwKeyLen = strlen(pstrKey); ++ ++ /* Need to search through the entries looking for the right one. */ ++ for (i = 0; i < pRegKey->numValueEntries; i++) { ++ /* See if the name matches. */ ++ if ((strncmp(pRegKey->values[i].name, pstrKey, ++ dwKeyLen) == 0) && count++ == dwIndex) { ++ /* We have a match! Copy out the data. */ ++ memcpy(pstrData, pRegKey->values[i].pData, ++ pRegKey->values[i].dataSize); ++ /* Get the size for the caller. */ ++ *pdwDataSize = pRegKey->values[i].dataSize; ++ *pdwValueSize = strlen(&(pRegKey-> ++ values[i].name[dwKeyLen])); ++ strncpy(pstrValue, ++ &(pRegKey->values[i].name[dwKeyLen]), ++ *pdwValueSize + 1); ++ GT_3trace(REG_debugMask, GT_2CLASS, ++ "E Key %s, Value %s, Data %x ", ++ pstrKey, pstrValue, *(u32 *)pstrData); ++ printS((u8 *)pstrData); ++ /* Set our status to good and exit. */ ++ retVal = DSP_SOK; ++ break; ++ } ++ } ++ ++ if (count && DSP_FAILED(retVal)) ++ retVal = REG_E_NOMOREITEMS; ++ ++ return retVal; ++} ++ ++/* ++ * ======== regsupDeleteValue ======== ++ */ ++DSP_STATUS regsupDeleteValue(IN CONST char *pstrSubkey, ++ IN CONST char *pstrValue) ++{ ++ DSP_STATUS retVal = DSP_EFAIL; ++ u32 i; ++ ++ for (i = 0; ((i < BRIDGE_MAX_NUM_REG_ENTRIES) && ++ (i < pRegKey->numValueEntries)); i++) { ++ /* See if the name matches... */ ++ if (strncmp(pRegKey->values[i].name, pstrValue, ++ BRIDGE_MAX_NAME_SIZE) == 0) { ++ /* We have a match! Delete this key. To delete a ++ * key, we free all resources associated with this ++ * key and, if we're not already the last entry in ++ * the array, we copy that entry into this deleted ++ * key. ++ */ ++ MEM_Free(pRegKey->values[i].pData); ++ if ((pRegKey->numValueEntries - 1) == i) { ++ /* we're deleting the last one */ ++ pRegKey->values[i].name[0] = '\0'; ++ pRegKey->values[i].dataSize = 0; ++ pRegKey->values[i].pData = NULL; ++ } else { ++ /* move the last one here */ ++ strncpy(pRegKey->values[i].name, pRegKey-> ++ values[pRegKey->numValueEntries - 1].name, ++ BRIDGE_MAX_NAME_SIZE); ++ pRegKey->values[i].dataSize = ++ pRegKey-> ++ values[pRegKey->numValueEntries-1].dataSize; ++ pRegKey->values[i].pData = ++ pRegKey-> ++ values[pRegKey->numValueEntries-1].pData; ++ /* don't have to do this, but for ++ * the paranoid... */ ++ pRegKey-> ++ values[pRegKey->numValueEntries-1].name[0] = ++ '\0'; ++ } ++ ++ /* another one bites the dust. */ ++ pRegKey->numValueEntries--; ++ ++ /* Set our status to good and exit... */ ++ retVal = DSP_SOK; ++ break; ++ } ++ } ++ return retVal; ++ ++} ++ +diff --git a/drivers/dsp/bridge/services/regsup.h b/drivers/dsp/bridge/services/regsup.h +new file mode 100644 +index 0000000..011be52 +--- /dev/null ++++ b/drivers/dsp/bridge/services/regsup.h +@@ -0,0 +1,58 @@ ++/* ++ * regsup.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== regsup.h ======== ++ * ++ *! Revision History ++ *! ================ ++ */ ++ ++#ifndef _REGSUP_H_ ++#define _REGSUP_H_ ++ ++#define BRIDGE_MAX_NAME_SIZE MAXREGPATHLENGTH ++#define BRIDGE_MAX_NUM_REG_ENTRIES 52 ++ ++/* Init function. MUST be called BEFORE any calls are */ ++/* made into this psuedo-registry!!! Returns TRUE/FALSE for SUCCESS/ERROR */ ++extern bool regsupInit(void); ++ ++/* Release all registry support allocations. */ ++extern void regsupExit(void); ++ ++/* ++ * ======== regsupDeleteValue ======== ++ */ ++extern DSP_STATUS regsupDeleteValue(IN CONST char *pstrSubkey, ++ IN CONST char *pstrValue); ++/* Get the value of the entry having the given name. Returns DSP_SOK */ ++/* if an entry was found and the value retrieved. Returns DSP_EFAIL ++ * otherwise.*/ ++extern DSP_STATUS regsupGetValue(char *valName, void *pBuf, u32 *dataSize); ++ ++/* Sets the value of the entry having the given name. Returns DSP_SOK */ ++/* if an entry was found and the value set. Returns DSP_EFAIL otherwise. */ ++extern DSP_STATUS regsupSetValue(char *valName, void *pBuf, u32 dataSize); ++ ++/* Returns registry "values" and their "data" under a (sub)key. */ ++extern DSP_STATUS regsupEnumValue(IN u32 dwIndex, IN CONST char *pstrKey, ++ IN OUT char *pstrValue, IN OUT u32 *pdwValueSize, ++ IN OUT char *pstrData, IN OUT u32 *pdwDataSize); ++ ++#endif ++ +diff --git a/drivers/dsp/bridge/services/services.c b/drivers/dsp/bridge/services/services.c +new file mode 100644 +index 0000000..346007e +--- /dev/null ++++ b/drivers/dsp/bridge/services/services.c +@@ -0,0 +1,193 @@ ++/* ++ * services.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== services.c ======== ++ * Purpose: ++ * Provide SERVICES loading. ++ * ++ * Public Functions: ++ * SERVICES_Exit ++ * SERVICES_Init ++ * ++ * ++ *! Revision History ++ *! ================ ++ *! 20-Nov-2000 rr: NTFY_Init/Exit added. ++ *! 06-Jul-2000 rr: PROC prefix changed to PRCS to accomodate RM. ++ *! 01-Feb-2000 kc: Created. ++ */ ++ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask SERVICES_debugMask = { NULL, NULL }; /* GT trace var. */ ++#endif ++ ++static u32 cRefs; /* SERVICES module reference count */ ++ ++/* ++ * ======== SERVICES_Exit ======== ++ * Purpose: ++ * Discontinue usage of module; free resources when reference count ++ * reaches 0. ++ */ ++void SERVICES_Exit(void) ++{ ++ DBC_Require(cRefs > 0); ++ ++ GT_1trace(SERVICES_debugMask, GT_5CLASS, "SERVICES_Exit: cRefs 0x%x\n", ++ cRefs); ++ ++ cRefs--; ++ if (cRefs == 0) { ++ /* Uninitialize all SERVICES modules here */ ++ NTFY_Exit(); ++ UTIL_Exit(); ++ SYNC_Exit(); ++ CLK_Exit(); ++ REG_Exit(); ++ LST_Exit(); ++ KFILE_Exit(); ++ DPC_Exit(); ++ DBG_Exit(); ++ CSL_Exit(); ++ CFG_Exit(); ++ MEM_Exit(); ++ ++ GT_exit(); ++ } ++ ++ DBC_Ensure(cRefs >= 0); ++} ++ ++/* ++ * ======== SERVICES_Init ======== ++ * Purpose: ++ * Initializes SERVICES modules. ++ */ ++bool SERVICES_Init(void) ++{ ++ bool fInit = true; ++ bool fCFG, fCSL, fDBG, fDPC, fKFILE, fLST, fMEM; ++ bool fREG, fSYNC, fCLK, fUTIL, fNTFY; ++ ++ DBC_Require(cRefs >= 0); ++ ++ if (cRefs == 0) { ++ ++ GT_init(); ++ GT_create(&SERVICES_debugMask, "OS"); /* OS for OSal */ ++ ++ GT_0trace(SERVICES_debugMask, GT_ENTER, ++ "SERVICES_Init: entered\n"); ++ ++ /* Perform required initialization of SERVICES modules. */ ++ fMEM = MEM_Init(); ++ fREG = REG_Init(); ++ fCFG = CFG_Init(); ++ fCSL = CSL_Init(); ++ fDBG = DBG_Init(); ++ fDPC = DPC_Init(); ++ fKFILE = KFILE_Init(); ++ fLST = LST_Init(); ++ /* fREG = REG_Init(); */ ++ fSYNC = SYNC_Init(); ++ fCLK = CLK_Init(); ++ fUTIL = UTIL_Init(); ++ fNTFY = NTFY_Init(); ++ ++ fInit = fCFG && fCSL && fDBG && fDPC && fKFILE && ++ fLST && fMEM && fREG && fSYNC && fCLK && fUTIL; ++ ++ if (!fInit) { ++ if (fNTFY) ++ NTFY_Exit(); ++ ++ if (fUTIL) ++ UTIL_Exit(); ++ ++ if (fSYNC) ++ SYNC_Exit(); ++ ++ if (fCLK) ++ CLK_Exit(); ++ ++ if (fREG) ++ REG_Exit(); ++ ++ if (fLST) ++ LST_Exit(); ++ ++ if (fKFILE) ++ KFILE_Exit(); ++ ++ if (fDPC) ++ DPC_Exit(); ++ ++ if (fDBG) ++ DBG_Exit(); ++ ++ if (fCSL) ++ CSL_Exit(); ++ ++ if (fCFG) ++ CFG_Exit(); ++ ++ if (fMEM) ++ MEM_Exit(); ++ ++ } ++ } ++ ++ if (fInit) ++ cRefs++; ++ ++ GT_1trace(SERVICES_debugMask, GT_5CLASS, "SERVICES_Init: cRefs 0x%x\n", ++ cRefs); ++ ++ DBC_Ensure((fInit && (cRefs > 0)) || (!fInit && (cRefs >= 0))); ++ ++ return fInit; ++} ++ +diff --git a/drivers/dsp/bridge/services/sync.c b/drivers/dsp/bridge/services/sync.c +new file mode 100644 +index 0000000..7ab9347 +--- /dev/null ++++ b/drivers/dsp/bridge/services/sync.c +@@ -0,0 +1,602 @@ ++/* ++ * sync.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== sync.c ======== ++ * Purpose: ++ * Synchronization services. ++ * ++ * Public Functions: ++ * SYNC_CloseEvent ++ * SYNC_DeleteCS ++ * SYNC_EnterCS ++ * SYNC_Exit ++ * SYNC_Init ++ * SYNC_InitializeCS ++ * SYNC_LeaveCS ++ * SYNC_OpenEvent ++ * SYNC_ResetEvent ++ * SYNC_SetEvent ++ * SYNC_WaitOnEvent ++ * SYNC_WaitOnMultipleEvents ++ * ++ *! Revision History: ++ *! ================ ++ *! 05-Nov-2001 kc: Minor cosmetic changes. ++ *! 05-Oct-2000 jeh Added SYNC_WaitOnMultipleEvents(). ++ *! 10-Aug-2000 rr: SYNC_PostMessage added. ++ *! 10-Jul-2000 jeh Modified SYNC_OpenEvent() to handle NULL attrs. ++ *! 03-Feb-2000 rr: Module init/exit is handled by SERVICES Init/Exit. ++ *! GT Changes. ++ *! 01-Dec-1999 ag: Added optional named event creation in SYNC_OpenEvent(). ++ *! 22-Nov-1999 kc: Added changes from code review. ++ *! 22-Sep-1999 kc: Modified from sync95.c. ++ *! 05-Aug-1996 gp: Created. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include ++ ++/* ----------------------------------- Defines, Data Structures, Typedefs */ ++#define SIGNATURE 0x434e5953 /* "SYNC" (in reverse) */ ++ ++enum wait_state { ++ wo_waiting, ++ wo_signalled ++} ; ++ ++enum sync_state { ++ so_reset, ++ so_signalled ++} ; ++ ++struct WAIT_OBJECT { ++ enum wait_state state; ++ struct SYNC_OBJECT *signalling_event; ++ struct semaphore sem; ++}; ++ ++/* Generic SYNC object: */ ++struct SYNC_OBJECT { ++ u32 dwSignature; /* Used for object validation. */ ++ enum sync_state state; ++ spinlock_t sync_lock; ++ struct WAIT_OBJECT *pWaitObj; ++}; ++ ++struct SYNC_DPCCSOBJECT { ++ u32 dwSignature; /* used for object validation */ ++ spinlock_t sync_dpccs_lock; ++ s32 count; ++} ; ++ ++/* ----------------------------------- Globals */ ++#if GT_TRACE ++static struct GT_Mask SYNC_debugMask = { NULL, NULL }; /* GT trace variable */ ++#endif ++ ++static int test_and_set(volatile void *ptr, int val) ++{ ++ int ret = val; ++ asm volatile (" swp %0, %0, [%1]" : "+r" (ret) : "r"(ptr) : "memory"); ++ return ret; ++} ++ ++static void timeout_callback(unsigned long hWaitObj); ++ ++/* ++ * ======== SYNC_CloseEvent ======== ++ * Purpose: ++ * Close an existing SYNC event object. ++ */ ++DSP_STATUS SYNC_CloseEvent(struct SYNC_OBJECT *hEvent) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct SYNC_OBJECT *pEvent = (struct SYNC_OBJECT *)hEvent; ++ ++ DBC_Require(pEvent != NULL && pEvent->pWaitObj == NULL); ++ ++ GT_1trace(SYNC_debugMask, GT_ENTER, "SYNC_CloseEvent: hEvent 0x%x\n", ++ hEvent); ++ ++ if (MEM_IsValidHandle(hEvent, SIGNATURE)) { ++ if (pEvent->pWaitObj) { ++ status = DSP_EFAIL; ++ GT_0trace(SYNC_debugMask, GT_6CLASS, ++ "SYNC_CloseEvent: Wait object not NULL\n"); ++ } ++ MEM_FreeObject(pEvent); ++ ++ } else { ++ status = DSP_EHANDLE; ++ GT_1trace(SYNC_debugMask, GT_6CLASS, ++ "SYNC_CloseEvent: invalid " ++ "hEvent handle 0x%x\n", hEvent); ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== SYNC_Exit ======== ++ * Purpose: ++ * Cleanup SYNC module. ++ */ ++void SYNC_Exit(void) ++{ ++ GT_0trace(SYNC_debugMask, GT_5CLASS, "SYNC_Exit\n"); ++} ++ ++/* ++ * ======== SYNC_Init ======== ++ * Purpose: ++ * Initialize SYNC module. ++ */ ++bool SYNC_Init(void) ++{ ++ GT_create(&SYNC_debugMask, "SY"); /* SY for SYnc */ ++ ++ GT_0trace(SYNC_debugMask, GT_5CLASS, "SYNC_Init\n"); ++ ++ return true; ++} ++ ++/* ++ * ======== SYNC_OpenEvent ======== ++ * Purpose: ++ * Open a new synchronization event object. ++ */ ++DSP_STATUS SYNC_OpenEvent(OUT struct SYNC_OBJECT **phEvent, ++ IN OPTIONAL struct SYNC_ATTRS *pAttrs) ++{ ++ struct SYNC_OBJECT *pEvent = NULL; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(phEvent != NULL); ++ ++ GT_2trace(SYNC_debugMask, GT_ENTER, ++ "SYNC_OpenEvent: phEvent 0x%x, pAttrs " ++ "0x%x\n", phEvent, pAttrs); ++ ++ /* Allocate memory for sync object */ ++ MEM_AllocObject(pEvent, struct SYNC_OBJECT, SIGNATURE); ++ if (pEvent != NULL) { ++ pEvent->state = so_reset; ++ pEvent->pWaitObj = NULL; ++ spin_lock_init(&pEvent->sync_lock); ++ } else { ++ status = DSP_EMEMORY; ++ GT_0trace(SYNC_debugMask, GT_6CLASS, ++ "SYNC_OpenEvent: MEM_AllocObject failed\n"); ++ } ++ ++ *phEvent = pEvent; ++ ++ return status; ++} ++ ++/* ++ * ======== SYNC_ResetEvent ======== ++ * Purpose: ++ * Reset an event to non-signalled. ++ */ ++DSP_STATUS SYNC_ResetEvent(struct SYNC_OBJECT *hEvent) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct SYNC_OBJECT *pEvent = (struct SYNC_OBJECT *)hEvent; ++ ++ GT_1trace(SYNC_debugMask, GT_ENTER, "SYNC_ResetEvent: hEvent 0x%x\n", ++ hEvent); ++ ++ if (MEM_IsValidHandle(hEvent, SIGNATURE)) { ++ pEvent->state = so_reset; ++ status = DSP_SOK; ++ } else { ++ status = DSP_EHANDLE; ++ GT_1trace(SYNC_debugMask, GT_6CLASS, ++ "SYNC_ResetEvent: invalid hEvent " ++ "handle 0x%x\n", hEvent); ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== SYNC_SetEvent ======== ++ * Purpose: ++ * Set an event to signaled and unblock one waiting thread. ++ * ++ * This function is called from ISR, DPC and user context. Hence interrupts ++ * are disabled to ensure atomicity. ++ */ ++ ++DSP_STATUS SYNC_SetEvent(struct SYNC_OBJECT *hEvent) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct SYNC_OBJECT *pEvent = (struct SYNC_OBJECT *)hEvent; ++ unsigned long flags; ++ ++ GT_1trace(SYNC_debugMask, GT_6CLASS, "SYNC_SetEvent: hEvent 0x%x\n", ++ hEvent); ++ ++ if (MEM_IsValidHandle(hEvent, SIGNATURE)) { ++ spin_lock_irqsave(&hEvent->sync_lock, flags); ++ GT_1trace(SYNC_debugMask, GT_6CLASS, ++ "SYNC_SetEvent: pEvent->pWaitObj " ++ "= 0x%x \n", pEvent->pWaitObj); ++ if (pEvent->pWaitObj) ++ GT_1trace(SYNC_debugMask, GT_6CLASS, "SYNC_SetEvent: " ++ "pEvent->pWaitObj->state = 0x%x \n", ++ pEvent->pWaitObj->state); ++ if (pEvent->pWaitObj != NULL && ++ test_and_set(&pEvent->pWaitObj->state, ++ wo_signalled) == wo_waiting) { ++ ++ pEvent->state = so_reset; ++ pEvent->pWaitObj->signalling_event = pEvent; ++ up(&pEvent->pWaitObj->sem); ++ GT_1trace(SYNC_debugMask, GT_6CLASS, ++ "SYNC_SetEvent: Unlock " ++ "Semaphore for hEvent 0x%x\n", hEvent); ++ } else { ++ pEvent->state = so_signalled; ++ } ++ spin_unlock_irqrestore(&hEvent->sync_lock, flags); ++ } else { ++ status = DSP_EHANDLE; ++ GT_1trace(SYNC_debugMask, GT_6CLASS, ++ "SYNC_SetEvent: invalid hEvent " ++ "handle 0x%x\n", hEvent); ++ } ++ return status; ++} ++ ++/* ++ * ======== SYNC_WaitOnEvent ======== ++ * Purpose: ++ * Wait for an event to be signalled, up to the specified timeout. ++ * Note: dwTimeOut must be 0xffffffff to signal infinite wait. ++ */ ++DSP_STATUS SYNC_WaitOnEvent(struct SYNC_OBJECT *hEvent, u32 dwTimeout) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct SYNC_OBJECT *pEvent = (struct SYNC_OBJECT *)hEvent; ++ u32 temp; ++ ++ GT_2trace(SYNC_debugMask, GT_6CLASS, "SYNC_WaitOnEvent: hEvent 0x%x\n, " ++ "dwTimeOut 0x%x", hEvent, dwTimeout); ++ if (MEM_IsValidHandle(hEvent, SIGNATURE)) { ++ status = SYNC_WaitOnMultipleEvents(&pEvent, 1, dwTimeout, ++ &temp); ++ } else { ++ status = DSP_EHANDLE; ++ GT_1trace(SYNC_debugMask, GT_6CLASS, ++ "SYNC_WaitOnEvent: invalid hEvent" ++ "handle 0x%x\n", hEvent); ++ } ++ return status; ++} ++ ++/* ++ * ======== SYNC_WaitOnMultipleEvents ======== ++ * Purpose: ++ * Wait for any of an array of events to be signalled, up to the ++ * specified timeout. ++ */ ++DSP_STATUS SYNC_WaitOnMultipleEvents(struct SYNC_OBJECT **hSyncEvents, ++ u32 uCount, u32 dwTimeout, ++ OUT u32 *puIndex) ++{ ++ u32 i; ++ DSP_STATUS status = DSP_SOK; ++ u32 curr; ++ struct WAIT_OBJECT *Wp; ++ ++ DBC_Require(uCount > 0); ++ DBC_Require(hSyncEvents != NULL); ++ DBC_Require(puIndex != NULL); ++ ++ for (i = 0; i < uCount; i++) ++ DBC_Require(MEM_IsValidHandle(hSyncEvents[i], SIGNATURE)); ++ ++ GT_4trace(SYNC_debugMask, GT_6CLASS, ++ "SYNC_WaitOnMultipleEvents: hSyncEvents:" ++ "0x%x\tuCount: 0x%x" "\tdwTimeout: 0x%x\tpuIndex: 0x%x\n", ++ hSyncEvents, uCount, dwTimeout, puIndex); ++ ++ Wp = MEM_Calloc(sizeof(struct WAIT_OBJECT), MEM_NONPAGED); ++ if (Wp == NULL) ++ return DSP_EMEMORY; ++ ++ Wp->state = wo_waiting; ++ Wp->signalling_event = NULL; ++ init_MUTEX_LOCKED(&(Wp->sem)); ++ ++ for (curr = 0; curr < uCount; curr++) { ++ hSyncEvents[curr]->pWaitObj = Wp; ++ if (hSyncEvents[curr]->state == so_signalled) { ++ GT_0trace(SYNC_debugMask, GT_6CLASS, ++ "Detected signaled Event !!!\n"); ++ if (test_and_set(&(Wp->state), wo_signalled) == ++ wo_waiting) { ++ GT_0trace(SYNC_debugMask, GT_6CLASS, ++ "Setting Signal Event!!!\n"); ++ hSyncEvents[curr]->state = so_reset; ++ Wp->signalling_event = hSyncEvents[curr]; ++ } ++ curr++; /* Will try optimizing later */ ++ break; ++ } ++ } ++ ++ curr--; /* Will try optimizing later */ ++ if (Wp->state != wo_signalled && dwTimeout > 0) { ++ struct timer_list timeout; ++ if (dwTimeout != SYNC_INFINITE) { ++ init_timer(&timeout); ++ timeout.function = timeout_callback; ++ timeout.data = (unsigned long)Wp; ++ timeout.expires = jiffies + dwTimeout * HZ / 1000; ++ add_timer(&timeout); ++ } ++ if (down_interruptible(&(Wp->sem))) { ++ GT_0trace(SYNC_debugMask, GT_7CLASS, "SYNC: " ++ "WaitOnMultipleEvents Interrupted by signal\n"); ++ status = DSP_EFAIL; ++ } ++ if (dwTimeout != SYNC_INFINITE) { ++ if (in_interrupt()) { ++ if (!del_timer(&timeout)) { ++ GT_0trace(SYNC_debugMask, GT_7CLASS, ++ "SYNC: Timer expired\n"); ++ } ++ } else { ++ if (!del_timer_sync(&timeout)) { ++ GT_0trace(SYNC_debugMask, GT_7CLASS, ++ "SYNC: Timer expired\n"); ++ } ++ } ++ } ++ } ++ for (i = 0; i <= curr; i++) { ++ if (MEM_IsValidHandle(hSyncEvents[i], SIGNATURE)) { ++ /* Memory corruption here if hSyncEvents[i] is ++ * freed before following statememt. */ ++ hSyncEvents[i]->pWaitObj = NULL; ++ } ++ if (hSyncEvents[i] == Wp->signalling_event) ++ *puIndex = i; ++ ++ } ++ if (Wp->signalling_event == NULL && DSP_SUCCEEDED(status)) { ++ GT_0trace(SYNC_debugMask, GT_7CLASS, ++ "SYNC:Signaling Event NULL!!!(:-\n"); ++ status = DSP_ETIMEOUT; ++ } ++ if (Wp) ++ MEM_Free(Wp); ++ return status; ++} ++ ++static void timeout_callback(unsigned long hWaitObj) ++{ ++ struct WAIT_OBJECT *pWaitObj = (struct WAIT_OBJECT *)hWaitObj; ++ if (test_and_set(&pWaitObj->state, wo_signalled) == wo_waiting) ++ up(&pWaitObj->sem); ++ ++} ++ ++/* ++ * ======== SYNC_DeleteCS ======== ++ */ ++DSP_STATUS SYNC_DeleteCS(struct SYNC_CSOBJECT *hCSObj) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct SYNC_CSOBJECT *pCSObj = (struct SYNC_CSOBJECT *)hCSObj; ++ ++ GT_0trace(SYNC_debugMask, GT_ENTER, "SYNC_DeleteCS\n"); ++ ++ if (MEM_IsValidHandle(hCSObj, SIGNATURECS)) { ++ if (down_trylock(&pCSObj->sem) != 0) { ++ GT_1trace(SYNC_debugMask, GT_7CLASS, ++ "CS in use (locked) while " ++ "deleting! pCSObj=0x%X", pCSObj); ++ DBC_Assert(0); ++ } ++ MEM_FreeObject(hCSObj); ++ } else if (MEM_IsValidHandle(hCSObj, SIGNATUREDPCCS)) { ++ struct SYNC_DPCCSOBJECT *pDPCCSObj = ++ (struct SYNC_DPCCSOBJECT *)hCSObj; ++ if (pDPCCSObj->count != 1) { ++ GT_1trace(SYNC_debugMask, GT_7CLASS, ++ "DPC CS in use (locked) while " ++ "deleting! pCSObj=0x%X", pCSObj); ++ DBC_Assert(0); ++ } ++ MEM_FreeObject(pDPCCSObj); ++ } else { ++ status = DSP_EHANDLE; ++ GT_1trace(SYNC_debugMask, GT_6CLASS, ++ "SYNC_DeleteCS: invalid hCSObj " ++ "handle 0x%x\n", hCSObj); ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== SYNC_EnterCS ======== ++ */ ++DSP_STATUS SYNC_EnterCS(struct SYNC_CSOBJECT *hCSObj) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct SYNC_CSOBJECT *pCSObj = (struct SYNC_CSOBJECT *)hCSObj; ++ ++ GT_1trace(SYNC_debugMask, GT_ENTER, "SYNC_EnterCS: hCSObj %p\n", ++ hCSObj); ++ if (MEM_IsValidHandle(hCSObj, SIGNATURECS)) { ++ if (in_interrupt()) { ++ status = DSP_EFAIL; ++ GT_0trace(SYNC_debugMask, GT_7CLASS, ++ "SYNC_EnterCS called from " ++ "ISR/DPC or with ISR/DPC disabled!"); ++ DBC_Assert(0); ++ } else if (down_interruptible(&pCSObj->sem)) { ++ GT_1trace(SYNC_debugMask, GT_7CLASS, ++ "CS interrupted by signal! " ++ "pCSObj=0x%X", pCSObj); ++ status = DSP_EFAIL; ++ } ++ } else if (MEM_IsValidHandle(hCSObj, SIGNATUREDPCCS)) { ++ struct SYNC_DPCCSOBJECT *pDPCCSObj = ++ (struct SYNC_DPCCSOBJECT *)hCSObj; ++ GT_0trace(SYNC_debugMask, GT_ENTER, "SYNC_EnterCS DPC\n"); ++ spin_lock_bh(&pDPCCSObj->sync_dpccs_lock); ++ pDPCCSObj->count--; ++ if (pDPCCSObj->count != 0) { ++ /* FATAL ERROR : Failed to acquire DPC CS */ ++ GT_2trace(SYNC_debugMask, GT_7CLASS, ++ "SYNC_EnterCS DPCCS %x locked," ++ "count %d", pDPCCSObj, pDPCCSObj->count); ++ spin_unlock_bh(&pDPCCSObj->sync_dpccs_lock); ++ DBC_Assert(0); ++ } ++ } else { ++ status = DSP_EHANDLE; ++ GT_1trace(SYNC_debugMask, GT_6CLASS, ++ "SYNC_EnterCS: invalid hCSObj " ++ "handle 0x%x\n", hCSObj); ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== SYNC_InitializeCS ======== ++ */ ++DSP_STATUS SYNC_InitializeCS(OUT struct SYNC_CSOBJECT **phCSObj) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct SYNC_CSOBJECT *pCSObj = NULL; ++ ++ GT_0trace(SYNC_debugMask, GT_ENTER, "SYNC_InitializeCS\n"); ++ ++ /* Allocate memory for sync CS object */ ++ MEM_AllocObject(pCSObj, struct SYNC_CSOBJECT, SIGNATURECS); ++ if (pCSObj != NULL) { ++ init_MUTEX(&pCSObj->sem); ++ } else { ++ status = DSP_EMEMORY; ++ GT_0trace(SYNC_debugMask, GT_6CLASS, ++ "SYNC_InitializeCS: MEM_AllocObject" ++ "failed\n"); ++ } ++ /* return CS object */ ++ *phCSObj = pCSObj; ++ DBC_Assert(DSP_FAILED(status) || (pCSObj)); ++ return status; ++} ++ ++DSP_STATUS SYNC_InitializeDPCCS(OUT struct SYNC_CSOBJECT **phCSObj) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct SYNC_DPCCSOBJECT *pCSObj = NULL; ++ ++ DBC_Require(phCSObj); ++ ++ GT_0trace(SYNC_debugMask, GT_ENTER, "SYNC_InitializeDPCCS\n"); ++ ++ if (phCSObj) { ++ /* Allocate memory for sync CS object */ ++ MEM_AllocObject(pCSObj, struct SYNC_DPCCSOBJECT, ++ SIGNATUREDPCCS); ++ if (pCSObj != NULL) { ++ pCSObj->count = 1; ++ spin_lock_init(&pCSObj->sync_dpccs_lock); ++ } else { ++ status = DSP_EMEMORY; ++ GT_0trace(SYNC_debugMask, GT_6CLASS, ++ "SYNC_InitializeDPCCS: " ++ "MEM_AllocObject failed\n"); ++ } ++ ++ /* return CS object */ ++ *phCSObj = (struct SYNC_CSOBJECT *)pCSObj; ++ } else { ++ status = DSP_EPOINTER; ++ } ++ ++ GT_1trace(SYNC_debugMask, GT_ENTER, "SYNC_InitializeDPCCS " ++ "pCSObj %p\n", pCSObj); ++ DBC_Assert(DSP_FAILED(status) || (pCSObj)); ++ ++ return status; ++} ++ ++/* ++ * ======== SYNC_LeaveCS ======== ++ */ ++DSP_STATUS SYNC_LeaveCS(struct SYNC_CSOBJECT *hCSObj) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct SYNC_CSOBJECT *pCSObj = (struct SYNC_CSOBJECT *)hCSObj; ++ ++ GT_1trace(SYNC_debugMask, GT_ENTER, "SYNC_LeaveCS: hCSObj %p\n", ++ hCSObj); ++ ++ if (MEM_IsValidHandle(hCSObj, SIGNATURECS)) { ++ up(&pCSObj->sem); ++ } else if (MEM_IsValidHandle(hCSObj, SIGNATUREDPCCS)) { ++ struct SYNC_DPCCSOBJECT *pDPCCSObj = ++ (struct SYNC_DPCCSOBJECT *)hCSObj; ++ pDPCCSObj->count++; ++ if (pDPCCSObj->count != 1) { ++ /* FATAL ERROR : Invalid DPC CS count */ ++ GT_2trace(SYNC_debugMask, GT_7CLASS, ++ "SYNC_LeaveCS DPCCS %x, " ++ "Invalid count %d", pDPCCSObj, ++ pDPCCSObj->count); ++ spin_unlock_bh(&pDPCCSObj->sync_dpccs_lock); ++ DBC_Assert(0); ++ spin_lock_bh(&pDPCCSObj->sync_dpccs_lock); ++ } ++ spin_unlock_bh(&pDPCCSObj->sync_dpccs_lock); ++ GT_0trace(SYNC_debugMask, GT_ENTER, "SYNC_LeaveCS DPC\n"); ++ } else { ++ status = DSP_EHANDLE; ++ GT_1trace(SYNC_debugMask, GT_6CLASS, ++ "SYNC_LeaveCS: invalid hCSObj " ++ "handle 0x%x\n", hCSObj); ++ } ++ ++ return status; ++} +diff --git a/drivers/dsp/bridge/wmd/_cmm.h b/drivers/dsp/bridge/wmd/_cmm.h +new file mode 100644 +index 0000000..801b000 +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/_cmm.h +@@ -0,0 +1,59 @@ ++/* ++ * _cmm.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== _cmm.h ======== ++ * Description: ++ * Private header file defining CMM manager objects and defines needed ++ * by IO manager to register shared memory regions when DSP base image ++ * is loaded(WMD_IO_OnLoaded). ++ * ++ * Public Functions: ++ * None. ++ * ++ * Notes: ++ * ++ *! Revision History: ++ *! ================ ++ *! 24-Aug-2001 ag Created. ++ */ ++ ++#ifndef _CMM_ ++#define _CMM_ ++ ++/* ++ * These target side symbols define the beginning and ending addresses ++ * of the section of shared memory used for shared memory manager CMM. ++ * They are defined in the *cfg.cmd file by cdb code. ++ */ ++#define SHM0_SHARED_BASE_SYM "_SHM0_BEG" ++#define SHM0_SHARED_END_SYM "_SHM0_END" ++#define SHM0_SHARED_RESERVED_BASE_SYM "_SHM0_RSVDSTRT" ++ ++/* ++ * Shared Memory Region #0(SHMSEG0) is used in the following way: ++ * ++ * |(_SHM0_BEG) | (_SHM0_RSVDSTRT) | (_SHM0_END) ++ * V V V ++ * ------------------------------------------------------------ ++ * | DSP-side allocations | GPP-side allocations | ++ * ------------------------------------------------------------ ++ * ++ * ++ */ ++ ++#endif /* _CMM_ */ +diff --git a/drivers/dsp/bridge/wmd/_deh.h b/drivers/dsp/bridge/wmd/_deh.h +new file mode 100644 +index 0000000..df281ad +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/_deh.h +@@ -0,0 +1,46 @@ ++/* ++ * _deh.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== _deh.h ======== ++ * Description: ++ * Private header for DEH module. ++ * ++ *! Revision History: ++ *! ================ ++ *! 21-Sep-2001 kc: created. ++ */ ++ ++#ifndef _DEH_ ++#define _DEH_ ++ ++#include ++#include ++#include ++ ++#define SIGNATURE 0x5f484544 /* "DEH_" backwards */ ++ ++/* DEH Manager: only one created per board: */ ++struct DEH_MGR { ++ u32 dwSignature; /* Used for object validation. */ ++ struct WMD_DEV_CONTEXT *hWmdContext; /* WMD device context. */ ++ struct NTFY_OBJECT *hNtfy; /* NTFY object */ ++ struct DPC_OBJECT *hMmuFaultDpc; /* DPC object handle. */ ++ struct DSP_ERRORINFO errInfo; /* DSP exception info. */ ++} ; ++ ++#endif /* _DEH_ */ +diff --git a/drivers/dsp/bridge/wmd/_msg_sm.h b/drivers/dsp/bridge/wmd/_msg_sm.h +new file mode 100644 +index 0000000..fa5e9ee +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/_msg_sm.h +@@ -0,0 +1,158 @@ ++/* ++ * _msg_sm.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== _msg_sm.h ======== ++ * Description: ++ * Private header file defining MSG manager objects and defines needed ++ * by IO manager. ++ * ++ * Public Functions: ++ * None. ++ * ++ * Notes: ++ * ++ *! Revision History: ++ *! ================ ++ *! 09-May-2001 jeh Code Review cleanup. ++ *! 08-Nov-2000 jeh Created. ++ */ ++ ++#ifndef _MSG_SM_ ++#define _MSG_SM_ ++ ++#include ++#include ++ ++/* ++ * These target side symbols define the beginning and ending addresses ++ * of the section of shared memory used for messages. They are ++ * defined in the *cfg.cmd file by cdb code. ++ */ ++#define MSG_SHARED_BUFFER_BASE_SYM "_MSG_BEG" ++#define MSG_SHARED_BUFFER_LIMIT_SYM "_MSG_END" ++ ++#ifndef _CHNL_WORDSIZE ++#define _CHNL_WORDSIZE 4 /* default _CHNL_WORDSIZE is 2 bytes/word */ ++#endif ++ ++/* ++ * ======== MSG ======== ++ * There is a control structure for messages to the DSP, and a control ++ * structure for messages from the DSP. The shared memory region for ++ * transferring messages is partitioned as follows: ++ * ++ * ---------------------------------------------------------- ++ * |Control | Messages from DSP | Control | Messages to DSP | ++ * ---------------------------------------------------------- ++ * ++ * MSG control structure for messages to the DSP is used in the following ++ * way: ++ * ++ * bufEmpty - This flag is set to FALSE by the GPP after it has output ++ * messages for the DSP. The DSP host driver sets it to ++ * TRUE after it has copied the messages. ++ * postSWI - Set to 1 by the GPP after it has written the messages, ++ * set the size, and set bufEmpty to FALSE. ++ * The DSP Host driver uses SWI_andn of the postSWI field ++ * when a host interrupt occurs. The host driver clears ++ * this after posting the SWI. ++ * size - Number of messages to be read by the DSP. ++ * ++ * For messages from the DSP: ++ * bufEmpty - This flag is set to FALSE by the DSP after it has output ++ * messages for the GPP. The DPC on the GPP sets it to ++ * TRUE after it has copied the messages. ++ * postSWI - Set to 1 the DPC on the GPP after copying the messages. ++ * size - Number of messages to be read by the GPP. ++ */ ++struct MSG { ++ u32 bufEmpty; /* to/from DSP buffer is empty */ ++ u32 postSWI; /* Set to "1" to post MSG SWI */ ++ u32 size; /* Number of messages to/from the DSP */ ++ u32 resvd; ++} ; ++ ++/* ++ * ======== MSG_MGR ======== ++ * The MSG_MGR maintains a list of all MSG_QUEUEs. Each NODE object can ++ * have MSG_QUEUE to hold all messages that come up from the corresponding ++ * node on the DSP. The MSG_MGR also has a shared queue of messages ++ * ready to go to the DSP. ++ */ ++struct MSG_MGR { ++ /* The first two fields must match those in msgobj.h */ ++ u32 dwSignature; ++ struct WMD_DRV_INTERFACE *pIntfFxns; /* Function interface to WMD. */ ++ ++ struct IO_MGR *hIOMgr; /* IO manager */ ++ struct LST_LIST *queueList; /* List of MSG_QUEUEs */ ++ struct SYNC_CSOBJECT *hSyncCS; /* For critical sections */ ++ /* Signalled when MsgFrame is available */ ++ struct SYNC_OBJECT *hSyncEvent; ++ struct LST_LIST *msgFreeList; /* Free MsgFrames ready to be filled */ ++ struct LST_LIST *msgUsedList; /* MsgFrames ready to go to DSP */ ++ u32 uMsgsPending; /* # of queued messages to go to DSP */ ++ u32 uMaxMsgs; /* Max # of msgs that fit in buffer */ ++ MSG_ONEXIT onExit; /* called when RMS_EXIT is received */ ++} ; ++ ++/* ++ * ======== MSG_QUEUE ======== ++ * Each NODE has a MSG_QUEUE for receiving messages from the ++ * corresponding node on the DSP. The MSG_QUEUE object maintains a list ++ * of messages that have been sent to the host, but not yet read (MSG_Get), ++ * and a list of free frames that can be filled when new messages arrive ++ * from the DSP. ++ * The MSG_QUEUE's hSynEvent gets posted when a message is ready. ++ */ ++struct MSG_QUEUE { ++ struct LST_ELEM listElem; ++ u32 dwSignature; ++ struct MSG_MGR *hMsgMgr; ++ u32 uMaxMsgs; /* Node message depth */ ++ u32 dwId; /* Node environment pointer */ ++ struct LST_LIST *msgFreeList; /* Free MsgFrames ready to be filled */ ++ /* Filled MsgFramess waiting to be read */ ++ struct LST_LIST *msgUsedList; ++ HANDLE hArg; /* Handle passed to mgr onExit callback */ ++ struct SYNC_OBJECT *hSyncEvent; /* Signalled when message is ready */ ++ struct SYNC_OBJECT *hSyncDone; /* For synchronizing cleanup */ ++ struct SYNC_OBJECT *hSyncDoneAck; /* For synchronizing cleanup */ ++ struct NTFY_OBJECT *hNtfy; /* For notification of message ready */ ++ bool fDone; /* TRUE <==> deleting the object */ ++ u32 refCount; /* Number of pending MSG_get/put calls */ ++}; ++ ++/* ++ * ======== MSG_DSPMSG ======== ++ */ ++struct MSG_DSPMSG { ++ struct DSP_MSG msg; ++ u32 dwId; /* Identifies the node the message goes to */ ++} ; ++ ++/* ++ * ======== MSG_FRAME ======== ++ */ ++struct MSG_FRAME { ++ struct LST_ELEM listElem; ++ struct MSG_DSPMSG msgData; ++} ; ++ ++#endif /* _MSG_SM_ */ ++ +diff --git a/drivers/dsp/bridge/wmd/_tiomap.h b/drivers/dsp/bridge/wmd/_tiomap.h +new file mode 100644 +index 0000000..815f695 +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/_tiomap.h +@@ -0,0 +1,384 @@ ++/* ++ * _tiomap.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== _tiomap.h ======== ++ * Description: ++ * Definitions and types private to this WMD. ++ * ++ */ ++ ++#ifndef _TIOMAP_ ++#define _TIOMAP_ ++ ++#include ++#include ++#include ++#include /* for WMDIOCTL_EXTPROC defn */ ++#include ++#include ++ ++struct MAP_L4PERIPHERAL { ++ u32 physAddr; ++ u32 dspVirtAddr; ++} ; ++ ++#define ARM_MAILBOX_START 0xfffcf000 ++#define ARM_MAILBOX_LENGTH 0x800 ++ ++/* New Registers in OMAP3.1 */ ++ ++#define TESTBLOCK_ID_START 0xfffed400 ++#define TESTBLOCK_ID_LENGTH 0xff ++ ++/* ID Returned by OMAP1510 */ ++#define TBC_ID_VALUE 0xB47002F ++ ++#define SPACE_LENGTH 0x2000 ++#define API_CLKM_DPLL_DMA 0xfffec000 ++#define ARM_INTERRUPT_OFFSET 0xb00 ++ ++#define BIOS_24XX ++ ++#define L4_PERIPHERAL_NULL 0x0 ++#define DSPVA_PERIPHERAL_NULL 0x0 ++ ++#define MAX_LOCK_TLB_ENTRIES 15 ++ ++#define L4_PERIPHERAL_PRM 0x48306000 /*PRM L4 Peripheral */ ++#define DSPVA_PERIPHERAL_PRM 0x1181e000 ++#define L4_PERIPHERAL_SCM 0x48002000 /*SCM L4 Peripheral */ ++#define DSPVA_PERIPHERAL_SCM 0x1181f000 ++#define L4_PERIPHERAL_MMU 0x5D000000 /*MMU L4 Peripheral */ ++#define DSPVA_PERIPHERAL_MMU 0x11820000 ++#define L4_PERIPHERAL_CM 0x48004000 /* Core L4, Clock Management */ ++#define DSPVA_PERIPHERAL_CM 0x1181c000 ++#define L4_PERIPHERAL_PER 0x48005000 /* PER */ ++#define DSPVA_PERIPHERAL_PER 0x1181d000 ++ ++#define L4_PERIPHERAL_GPIO1 0x48310000 ++#define DSPVA_PERIPHERAL_GPIO1 0x11809000 ++#define L4_PERIPHERAL_GPIO2 0x49050000 ++#define DSPVA_PERIPHERAL_GPIO2 0x1180a000 ++#define L4_PERIPHERAL_GPIO3 0x49052000 ++#define DSPVA_PERIPHERAL_GPIO3 0x1180b000 ++#define L4_PERIPHERAL_GPIO4 0x49054000 ++#define DSPVA_PERIPHERAL_GPIO4 0x1180c000 ++#define L4_PERIPHERAL_GPIO5 0x49056000 ++#define DSPVA_PERIPHERAL_GPIO5 0x1180d000 ++ ++#define L4_PERIPHERAL_IVA2WDT 0x49030000 ++#define DSPVA_PERIPHERAL_IVA2WDT 0x1180e000 ++ ++#define L4_PERIPHERAL_DISPLAY 0x48050000 ++#define DSPVA_PERIPHERAL_DISPLAY 0x1180f000 ++ ++#define L4_PERIPHERAL_SSI 0x48058000 ++#define DSPVA_PERIPHERAL_SSI 0x11804000 ++#define L4_PERIPHERAL_GDD 0x48059000 ++#define DSPVA_PERIPHERAL_GDD 0x11805000 ++#define L4_PERIPHERAL_SS1 0x4805a000 ++#define DSPVA_PERIPHERAL_SS1 0x11806000 ++#define L4_PERIPHERAL_SS2 0x4805b000 ++#define DSPVA_PERIPHERAL_SS2 0x11807000 ++ ++#define L4_PERIPHERAL_CAMERA 0x480BC000 ++#define DSPVA_PERIPHERAL_CAMERA 0x11819000 ++ ++#define L4_PERIPHERAL_SDMA 0x48056000 ++#define DSPVA_PERIPHERAL_SDMA 0x11810000 /*0x1181d000 conflicts with PER */ ++ ++#define L4_PERIPHERAL_UART1 0x4806a000 ++#define DSPVA_PERIPHERAL_UART1 0x11811000 ++#define L4_PERIPHERAL_UART2 0x4806c000 ++#define DSPVA_PERIPHERAL_UART2 0x11812000 ++#define L4_PERIPHERAL_UART3 0x49020000 ++#define DSPVA_PERIPHERAL_UART3 0x11813000 ++ ++#define L4_PERIPHERAL_MCBSP1 0x48074000 ++#define DSPVA_PERIPHERAL_MCBSP1 0x11814000 ++#define L4_PERIPHERAL_MCBSP2 0x49022000 ++#define DSPVA_PERIPHERAL_MCBSP2 0x11815000 ++#define L4_PERIPHERAL_MCBSP3 0x49024000 ++#define DSPVA_PERIPHERAL_MCBSP3 0x11816000 ++#define L4_PERIPHERAL_MCBSP4 0x49026000 ++#define DSPVA_PERIPHERAL_MCBSP4 0x11817000 ++#define L4_PERIPHERAL_MCBSP5 0x48096000 ++#define DSPVA_PERIPHERAL_MCBSP5 0x11818000 ++ ++#define L4_PERIPHERAL_GPTIMER5 0x49038000 ++#define DSPVA_PERIPHERAL_GPTIMER5 0x11800000 ++#define L4_PERIPHERAL_GPTIMER6 0x4903a000 ++#define DSPVA_PERIPHERAL_GPTIMER6 0x11801000 ++#define L4_PERIPHERAL_GPTIMER7 0x4903c000 ++#define DSPVA_PERIPHERAL_GPTIMER7 0x11802000 ++#define L4_PERIPHERAL_GPTIMER8 0x4903e000 ++#define DSPVA_PERIPHERAL_GPTIMER8 0x11803000 ++ ++#define L4_PERIPHERAL_SPI1 0x48098000 ++#define DSPVA_PERIPHERAL_SPI1 0x1181a000 ++#define L4_PERIPHERAL_SPI2 0x4809a000 ++#define DSPVA_PERIPHERAL_SPI2 0x1181b000 ++ ++#define L4_PERIPHERAL_MBOX 0x48094000 ++#define DSPVA_PERIPHERAL_MBOX 0x11808000 ++ ++#define PM_GRPSEL_BASE 0x48307000 ++#define DSPVA_GRPSEL_BASE 0x11821000 ++ ++#define L4_PERIPHERAL_SIDETONE_MCBSP2 0x49028000 ++#define DSPVA_PERIPHERAL_SIDETONE_MCBSP2 0x11824000 ++#define L4_PERIPHERAL_SIDETONE_MCBSP3 0x4902a000 ++#define DSPVA_PERIPHERAL_SIDETONE_MCBSP3 0x11825000 ++ ++/* define a static array with L4 mappings */ ++static const struct MAP_L4PERIPHERAL L4PeripheralTable[] = { ++ {L4_PERIPHERAL_MBOX, DSPVA_PERIPHERAL_MBOX}, ++ {L4_PERIPHERAL_SCM, DSPVA_PERIPHERAL_SCM}, ++ {L4_PERIPHERAL_MMU, DSPVA_PERIPHERAL_MMU}, ++ {L4_PERIPHERAL_GPTIMER5, DSPVA_PERIPHERAL_GPTIMER5}, ++ {L4_PERIPHERAL_GPTIMER6, DSPVA_PERIPHERAL_GPTIMER6}, ++ {L4_PERIPHERAL_GPTIMER7, DSPVA_PERIPHERAL_GPTIMER7}, ++ {L4_PERIPHERAL_GPTIMER8, DSPVA_PERIPHERAL_GPTIMER8}, ++ {L4_PERIPHERAL_GPIO1, DSPVA_PERIPHERAL_GPIO1}, ++ {L4_PERIPHERAL_GPIO2, DSPVA_PERIPHERAL_GPIO2}, ++ {L4_PERIPHERAL_GPIO3, DSPVA_PERIPHERAL_GPIO3}, ++ {L4_PERIPHERAL_GPIO4, DSPVA_PERIPHERAL_GPIO4}, ++ {L4_PERIPHERAL_GPIO5, DSPVA_PERIPHERAL_GPIO5}, ++ {L4_PERIPHERAL_IVA2WDT, DSPVA_PERIPHERAL_IVA2WDT}, ++ {L4_PERIPHERAL_DISPLAY, DSPVA_PERIPHERAL_DISPLAY}, ++ {L4_PERIPHERAL_SSI, DSPVA_PERIPHERAL_SSI}, ++ {L4_PERIPHERAL_GDD, DSPVA_PERIPHERAL_GDD}, ++ {L4_PERIPHERAL_SS1, DSPVA_PERIPHERAL_SS1}, ++ {L4_PERIPHERAL_SS2, DSPVA_PERIPHERAL_SS2}, ++ {L4_PERIPHERAL_UART1, DSPVA_PERIPHERAL_UART1}, ++ {L4_PERIPHERAL_UART2, DSPVA_PERIPHERAL_UART2}, ++ {L4_PERIPHERAL_UART3, DSPVA_PERIPHERAL_UART3}, ++ {L4_PERIPHERAL_MCBSP1, DSPVA_PERIPHERAL_MCBSP1}, ++ {L4_PERIPHERAL_MCBSP2, DSPVA_PERIPHERAL_MCBSP2}, ++ {L4_PERIPHERAL_MCBSP3, DSPVA_PERIPHERAL_MCBSP3}, ++ {L4_PERIPHERAL_MCBSP4, DSPVA_PERIPHERAL_MCBSP4}, ++ {L4_PERIPHERAL_MCBSP5, DSPVA_PERIPHERAL_MCBSP5}, ++ {L4_PERIPHERAL_CAMERA, DSPVA_PERIPHERAL_CAMERA}, ++ {L4_PERIPHERAL_SPI1, DSPVA_PERIPHERAL_SPI1}, ++ {L4_PERIPHERAL_SPI2, DSPVA_PERIPHERAL_SPI2}, ++ {L4_PERIPHERAL_PRM, DSPVA_PERIPHERAL_PRM}, ++ {L4_PERIPHERAL_CM, DSPVA_PERIPHERAL_CM}, ++ {L4_PERIPHERAL_PER, DSPVA_PERIPHERAL_PER}, ++ {PM_GRPSEL_BASE, DSPVA_GRPSEL_BASE}, ++ {L4_PERIPHERAL_SIDETONE_MCBSP2, DSPVA_PERIPHERAL_SIDETONE_MCBSP2}, ++ {L4_PERIPHERAL_SIDETONE_MCBSP3, DSPVA_PERIPHERAL_SIDETONE_MCBSP3}, ++ {L4_PERIPHERAL_NULL, DSPVA_PERIPHERAL_NULL} ++}; ++ ++/* ++ * 15 10 0 ++ * --------------------------------- ++ * |0|0|1|0|0|0|c|c|c|i|i|i|i|i|i|i| ++ * --------------------------------- ++ * | (class) | (module specific) | ++ * ++ * where c -> Externel Clock Command: Clk & Autoidle Disable/Enable ++ * i -> External Clock ID Timers 5,6,7,8, McBSP1,2 and WDT3 ++ */ ++ ++/* MBX_PM_CLK_IDMASK: DSP External clock id mask. */ ++#define MBX_PM_CLK_IDMASK 0x7F ++ ++/* MBX_PM_CLK_CMDSHIFT: DSP External clock command shift. */ ++#define MBX_PM_CLK_CMDSHIFT 7 ++ ++/* MBX_PM_CLK_CMDMASK: DSP External clock command mask. */ ++#define MBX_PM_CLK_CMDMASK 7 ++ ++/* MBX_PM_MAX_RESOURCES: CORE 1 Clock resources. */ ++#define MBX_CORE1_RESOURCES 7 ++ ++/* MBX_PM_MAX_RESOURCES: CORE 2 Clock Resources. */ ++#define MBX_CORE2_RESOURCES 1 ++ ++/* MBX_PM_MAX_RESOURCES: TOTAL Clock Reosurces. */ ++#define MBX_PM_MAX_RESOURCES 11 ++ ++/* Power Management Commands */ ++enum BPWR_ExtClockCmd { ++ BPWR_DisableClock = 0, ++ BPWR_EnableClock, ++ BPWR_DisableAutoIdle, ++ BPWR_EnableAutoIdle ++} ; ++ ++/* OMAP242x specific resources */ ++enum BPWR_ExtClockId { ++ BPWR_GPTimer5 = 0x10, ++ BPWR_GPTimer6, ++ BPWR_GPTimer7, ++ BPWR_GPTimer8, ++ BPWR_WDTimer3, ++ BPWR_MCBSP1, ++ BPWR_MCBSP2, ++ BPWR_MCBSP3, ++ BPWR_MCBSP4, ++ BPWR_MCBSP5, ++ BPWR_SSI = 0x20 ++} ; ++ ++static const u32 BPWR_CLKID[] = { ++ (u32) BPWR_GPTimer5, ++ (u32) BPWR_GPTimer6, ++ (u32) BPWR_GPTimer7, ++ (u32) BPWR_GPTimer8, ++ (u32) BPWR_WDTimer3, ++ (u32) BPWR_MCBSP1, ++ (u32) BPWR_MCBSP2, ++ (u32) BPWR_MCBSP3, ++ (u32) BPWR_MCBSP4, ++ (u32) BPWR_MCBSP5, ++ (u32) BPWR_SSI ++}; ++ ++struct BPWR_Clk_t { ++ u32 clkId; ++ enum SERVICES_ClkId funClk; ++ enum SERVICES_ClkId intClk; ++} ; ++ ++static const struct BPWR_Clk_t BPWR_Clks[] = { ++ {(u32) BPWR_GPTimer5, SERVICESCLK_gpt5_fck, SERVICESCLK_gpt5_ick}, ++ {(u32) BPWR_GPTimer6, SERVICESCLK_gpt6_fck, SERVICESCLK_gpt6_ick}, ++ {(u32) BPWR_GPTimer7, SERVICESCLK_gpt7_fck, SERVICESCLK_gpt7_ick}, ++ {(u32) BPWR_GPTimer8, SERVICESCLK_gpt8_fck, SERVICESCLK_gpt8_ick}, ++ {(u32) BPWR_WDTimer3, SERVICESCLK_wdt3_fck, SERVICESCLK_wdt3_ick}, ++ {(u32) BPWR_MCBSP1, SERVICESCLK_mcbsp1_fck, SERVICESCLK_mcbsp1_ick}, ++ {(u32) BPWR_MCBSP2, SERVICESCLK_mcbsp2_fck, SERVICESCLK_mcbsp2_ick}, ++ {(u32) BPWR_MCBSP3, SERVICESCLK_mcbsp3_fck, SERVICESCLK_mcbsp3_ick}, ++ {(u32) BPWR_MCBSP4, SERVICESCLK_mcbsp4_fck, SERVICESCLK_mcbsp4_ick}, ++ {(u32) BPWR_MCBSP5, SERVICESCLK_mcbsp5_fck, SERVICESCLK_mcbsp5_ick}, ++ {(u32) BPWR_SSI, SERVICESCLK_ssi_fck, SERVICESCLK_ssi_ick} ++}; ++ ++/* Interrupt Register Offsets */ ++#define INTH_IT_REG_OFFSET 0x00 /* Interrupt register offset */ ++#define INTH_MASK_IT_REG_OFFSET 0x04 /* Mask Interrupt reg offset */ ++ ++#define DSP_MAILBOX1_INT 10 ++ ++/* ++ * INTH_InterruptKind_t ++ * Identify the kind of interrupt: either FIQ/IRQ ++ */ ++enum INTH_InterruptKind_t { ++ INTH_IRQ = 0, ++ INTH_FIQ = 1 ++} ; ++ ++enum INTH_SensitiveEdge_t { ++ FALLING_EDGE_SENSITIVE = 0, ++ LOW_LEVEL_SENSITIVE = 1 ++} ; ++ ++/* ++ * Bit definition of Interrupt Level Registers ++ */ ++ ++/* Mail Box defines */ ++#define MB_ARM2DSP1_REG_OFFSET 0x00 ++ ++#define MB_ARM2DSP1B_REG_OFFSET 0x04 ++ ++#define MB_DSP2ARM1B_REG_OFFSET 0x0C ++ ++#define MB_ARM2DSP1_FLAG_REG_OFFSET 0x18 ++ ++#define MB_ARM2DSP_FLAG 0x0001 ++ ++#define MBOX_ARM2DSP HW_MBOX_ID_0 ++#define MBOX_DSP2ARM HW_MBOX_ID_1 ++#define MBOX_ARM HW_MBOX_U0_ARM ++#define MBOX_DSP HW_MBOX_U1_DSP1 ++ ++#define ENABLE true ++#define DISABLE false ++ ++#define HIGH_LEVEL true ++#define LOW_LEVEL false ++ ++/* Macro's */ ++#define REG16(A) (*(REG_UWORD16 *)(A)) ++ ++#define ClearBit(reg, mask) (reg &= ~mask) ++#define SetBit(reg, mask) (reg |= mask) ++ ++#define SetGroupBits16(reg, position, width, value) \ ++ do {\ ++ reg &= ~((0xFFFF >> (16 - (width))) << (position)) ; \ ++ reg |= ((value & (0xFFFF >> (16 - (width)))) << (position)); \ ++ } while (0); ++ ++#define ClearBitIndex(reg, index) (reg &= ~(1 << (index))) ++ ++/* This mini driver's device context: */ ++struct WMD_DEV_CONTEXT { ++ struct DEV_OBJECT *hDevObject; /* Handle to WCD device object. */ ++ u32 dwDspBaseAddr; /* Arm's API to DSP virtual base addr */ ++ /* ++ * DSP External memory prog address as seen virtually by the OS on ++ * the host side. ++ */ ++ u32 dwDspExtBaseAddr; /* See the comment above */ ++ u32 dwAPIRegBase; /* API memory mapped registers */ ++ u32 dwDSPMmuBase; /* DSP MMU Mapped registers */ ++ u32 dwMailBoxBase; /* Mail box mapped registers */ ++ u32 dwAPIClkBase; /* CLK Registers */ ++ u32 dwDSPClkM2Base; /* DSP Clock Module m2 */ ++ u32 dwPublicRhea; /* Pub Rhea */ ++ u32 dwIntAddr; /* MB INTR reg */ ++ u32 dwTCEndianism; /* TC Endianism register */ ++ u32 dwTestBase; /* DSP MMU Mapped registers */ ++ u32 dwSelfLoop; /* Pointer to the selfloop */ ++ u32 dwDSPStartAdd; /* API Boot vector */ ++ u32 dwInternalSize; /* Internal memory size */ ++ ++ /* ++ * Processor specific info is set when prog loaded and read from DCD. ++ * [See WMD_BRD_Ctrl()] PROC info contains DSP-MMU TLB entries. ++ */ ++ /* DMMU TLB entries */ ++ struct WMDIOCTL_EXTPROC aTLBEntry[WMDIOCTL_NUMOFMMUTLB]; ++ u32 dwBrdState; /* Last known board state. */ ++ u32 ulIntMask; /* int mask */ ++ u16 ioBase; /* Board I/O base */ ++ u32 numTLBEntries; /* DSP MMU TLB entry counter */ ++ u32 fixedTLBEntries; /* Fixed DSPMMU TLB entry count */ ++ ++ /* TC Settings */ ++ bool tcWordSwapOn; /* Traffic Controller Word Swap */ ++ struct PgTableAttrs *pPtAttrs; ++ u32 uDspPerClks; ++} ; ++ ++ /* ++ * ======== WMD_TLB_DspVAToMpuPA ======== ++ * Given a DSP virtual address, traverse the page table and return ++ * a corresponding MPU physical address and size. ++ */ ++extern DSP_STATUS WMD_TLB_DspVAToMpuPA(struct WMD_DEV_CONTEXT *pDevContext, ++ IN u32 ulVirtAddr, ++ OUT u32 *ulPhysAddr, ++ OUT u32 *sizeTlb); ++ ++#endif /* _TIOMAP_ */ ++ +diff --git a/drivers/dsp/bridge/wmd/_tiomap_mmu.h b/drivers/dsp/bridge/wmd/_tiomap_mmu.h +new file mode 100644 +index 0000000..6b21047 +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/_tiomap_mmu.h +@@ -0,0 +1,53 @@ ++/* ++ * _tiomap_mmu.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== _tiomap_mmu.h ======== ++ * Description: ++ * Definitions and types for the DSP MMU modules ++ * ++ *! Revision History ++ *! ================ ++ *! 19-Apr-2004 sb: Renamed HW types. Removed dspMmuTlbEntry ++ *! 05-Jan-2004 vp: Moved the file to a platform specific folder from common. ++ *! 21-Mar-2003 sb: Added macro definition TIHEL_LARGEPAGESIZE ++ *! 08-Oct-2002 rr: Created. ++ */ ++ ++#ifndef _TIOMAP_MMU_ ++#define _TIOMAP_MMU_ ++ ++#include "_tiomap.h" ++ ++/* ++ * ======== configureDspMmu ======== ++ * ++ * Make DSP MMu page table entries. ++ * Note: Not utilizing Coarse / Fine page tables. ++ * SECTION = 1MB, LARGE_PAGE = 64KB, SMALL_PAGE = 4KB, TINY_PAGE = 1KB. ++ * DSP Byte address 0x40_0000 is word addr 0x20_0000. ++ */ ++extern void configureDspMmu(struct WMD_DEV_CONTEXT *pDevContext, ++ u32 dataBasePhys, ++ u32 dspBaseVirt, ++ u32 sizeInBytes, ++ s32 nEntryStart, ++ enum HW_Endianism_t endianism, ++ enum HW_ElementSize_t elemSize, ++ enum HW_MMUMixedSize_t mixedSize); ++ ++#endif /* _TIOMAP_MMU_ */ +diff --git a/drivers/dsp/bridge/wmd/_tiomap_pwr.h b/drivers/dsp/bridge/wmd/_tiomap_pwr.h +new file mode 100644 +index 0000000..7ebd7b5 +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/_tiomap_pwr.h +@@ -0,0 +1,102 @@ ++/* ++ * _tiomap_pwr.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== _tiomap_pwr.h ======== ++ * Description: ++ * Definitions and types for the DSP wake/sleep routines. ++ * ++ *! Revision History ++ *! ================ ++ *! 08-Oct-2002 rr: Created. ++ */ ++ ++#ifndef _TIOMAP_PWR_ ++#define _TIOMAP_PWR_ ++ ++/* ++ * ======== WakeDSP ========= ++ * Wakes up the DSP from DeepSleep ++ */ ++extern DSP_STATUS WakeDSP(struct WMD_DEV_CONTEXT *pDevContext, IN void *pArgs); ++ ++/* ++ * ======== SleepDSP ========= ++ * Places the DSP in DeepSleep. ++ */ ++extern DSP_STATUS SleepDSP(struct WMD_DEV_CONTEXT *pDevContext, ++ IN u32 dwCmd, IN void *pArgs); ++/* ++ * ========InterruptDSP======== ++ * Sends an interrupt to DSP unconditionally. ++ */ ++extern void InterruptDSP(struct WMD_DEV_CONTEXT *pDevContext, IN u16 wMbVal); ++ ++/* ++ * ======== WakeDSP ========= ++ * Wakes up the DSP from DeepSleep ++ */ ++extern DSP_STATUS DSPPeripheralClkCtrl(struct WMD_DEV_CONTEXT *pDevContext, ++ IN void *pArgs); ++/* ++ * ======== handle_hibernation_fromDSP ======== ++ * Handle Hibernation requested from DSP ++ */ ++DSP_STATUS handle_hibernation_fromDSP(struct WMD_DEV_CONTEXT *pDevContext); ++/* ++ * ======== PostScale_DSP ======== ++ * Handle Post Scale notification to DSP ++ */ ++DSP_STATUS PostScale_DSP(struct WMD_DEV_CONTEXT *pDevContext, IN void *pArgs); ++/* ++ * ======== PreScale_DSP ======== ++ * Handle Pre Scale notification to DSP ++ */ ++DSP_STATUS PreScale_DSP(struct WMD_DEV_CONTEXT *pDevContext, IN void *pArgs); ++/* ++ * ======== handle_constraints_set ======== ++ * Handle constraints request from DSP ++ */ ++DSP_STATUS handle_constraints_set(struct WMD_DEV_CONTEXT *pDevContext, ++ IN void *pArgs); ++/* ++ * ======== DSP_PeripheralClocks_Disable ======== ++ * This function disables all the peripheral clocks that ++ * were enabled by DSP. Call this function only when ++ * DSP is entering Hibernation or when DSP is in ++ * Error state ++ */ ++DSP_STATUS DSP_PeripheralClocks_Disable(struct WMD_DEV_CONTEXT *pDevContext, ++ IN void *pArgs); ++ ++/* ++ * ======== DSP_PeripheralClocks_Enable ======== ++ * This function enables all the peripheral clocks that ++ * were requested by DSP. ++ */ ++DSP_STATUS DSP_PeripheralClocks_Enable(struct WMD_DEV_CONTEXT *pDevContext, ++ IN void *pArgs); ++ ++/* ++ * ======== DSPClkWakeupEventCtrl ======== ++ * This function sets the group selction bits for while ++ * enabling/disabling. ++ */ ++void DSPClkWakeupEventCtrl(u32 ClkId, bool enable); ++ ++#endif /* _TIOMAP_PWR_ */ ++ +diff --git a/drivers/dsp/bridge/wmd/_tiomap_util.h b/drivers/dsp/bridge/wmd/_tiomap_util.h +new file mode 100644 +index 0000000..47e1e5d +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/_tiomap_util.h +@@ -0,0 +1,46 @@ ++/* ++ * _tiomap_util.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== _tiomap_util.h ======== ++ * Description: ++ * Definitions and types for the utility routines. ++ * ++ *! Revision History ++ *! ================ ++ *! 08-Oct-2002 rr: Created. ++ */ ++ ++#ifndef _TIOMAP_UTIL_ ++#define _TIOMAP_UTIL_ ++ ++/* Time out Values in uSeconds*/ ++#define TIHELEN_ACKTIMEOUT 10000 ++ ++/* Time delay for HOM->SAM transition. */ ++#define WAIT_SAM 1000000 /* in usec (1000 millisec) */ ++ ++/* ++ * ======== WaitForStart ======== ++ * Wait for the singal from DSP that it has started, or time out. ++ * The argument dwSyncAddr is set to 1 before releasing the DSP. ++ * If the DSP starts running, it will clear this location. ++ */ ++extern bool WaitForStart(struct WMD_DEV_CONTEXT *pDevContext, u32 dwSyncAddr); ++ ++#endif /* _TIOMAP_UTIL_ */ ++ +diff --git a/drivers/dsp/bridge/wmd/chnl_sm.c b/drivers/dsp/bridge/wmd/chnl_sm.c +new file mode 100644 +index 0000000..e8ffb2f +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/chnl_sm.c +@@ -0,0 +1,1100 @@ ++/* ++ * chnl_sm.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== chnl_sm.c ======== ++ * Description: ++ * Implements upper edge functions for WMD channel module. ++ * ++ * Public Functions: ++ * WMD_CHNL_AddIOReq ++ * WMD_CHNL_CancelIO ++ * WMD_CHNL_Close ++ * WMD_CHNL_Create ++ * WMD_CHNL_Destroy ++ * WMD_CHNL_FlushIO ++ * WMD_CHNL_GetInfo ++ * WMD_CHNL_GetIOC ++ * WMD_CHNL_GetMgrInfo ++ * WMD_CHNL_Idle ++ * WMD_CHNL_Open ++ * ++ * Notes: ++ * The lower edge functions must be implemented by the WMD writer, and ++ * are declared in chnl_sm.h. ++ * ++ * Care is taken in this code to prevent simulataneous access to channel ++ * queues from ++ * 1. Threads. ++ * 2. IO_DPC(), scheduled from the IO_ISR() as an event. ++ * ++ * This is done primarily by: ++ * - Semaphores. ++ * - state flags in the channel object; and ++ * - ensuring the IO_Dispatch() routine, which is called from both ++ * CHNL_AddIOReq() and the DPC(if implemented), is not re-entered. ++ * ++ * Channel Invariant: ++ * There is an important invariant condition which must be maintained per ++ * channel outside of WMD_CHNL_GetIOC() and IO_Dispatch(), violation of ++ * which may cause timeouts and/or failure offunction SYNC_WaitOnEvent. ++ * This invariant condition is: ++ * ++ * LST_Empty(pChnl->pIOCompletions) ==> pChnl->hSyncEvent is reset ++ * and ++ * !LST_Empty(pChnl->pIOCompletions) ==> pChnl->hSyncEvent is set. ++ * ++ *! Revision History: ++ *! ================ ++ *! 10-Feb-2004 sb: Consolidated the MAILBOX_IRQ macro at the top of the file. ++ *! 05-Jan-2004 vp: Updated for 2.6 kernel on 24xx platform. ++ *! 23-Apr-2003 sb: Fixed mailbox deadlock ++ *! 24-Feb-2003 vp: Code Review Updates. ++ *! 18-Oct-2002 vp: Ported to Linux platform ++ *! 29-Aug-2002 rr Changed the SYNC error code return to DSP error code return ++ * in WMD_CHNL_GetIOC. ++ *! 22-Jan-2002 ag Zero-copy support added. ++ *! CMM_CallocBuf() used for SM allocations. ++ *! 04-Feb-2001 ag DSP-DMA support added. ++ *! 22-Nov-2000 kc: Updated usage of PERF_RegisterStat. ++ *! 06-Nov-2000 jeh Move ISR_Install, DPC_Create from CHNL_Create to IO_Create. ++ *! 13-Oct-2000 jeh Added dwArg parameter to WMD_CHNL_AddIOReq(), added ++ *! WMD_CHNL_Idle and WMD_CHNL_RegisterNotify for DSPStream. ++ *! Remove #ifdef DEBUG from around channel cIOCs field. ++ *! 21-Sep-2000 rr: PreOMAP chnl class library acts like a IO class library. ++ *! 25-Sep-2000 ag: MEM_[Unmap]LinearAddress added for #ifdef CHNL_PREOMAP. ++ *! 07-Sep-2000 rr: Added new channel class for PreOMAP. ++ *! 11-Jul-2000 jeh Allow NULL user event in WMD_CHNL_Open(). ++ *! 06-Jul-2000 rr: Changed prefix PROC to PRCS for process module calls. ++ *! 20-Jan-2000 ag: Incorporated code review comments. ++ *! 05-Jan-2000 ag: Text format cleanup. ++ *! 07-Dec-1999 ag: Now setting ChnlMgr fSharedIRQ flag before ISR_Install(). ++ *! 01-Dec-1999 ag: WMD_CHNL_Open() now accepts named sync event. ++ *! 14-Nov-1999 ag: DPC_Schedule() uncommented. ++ *! 28-Oct-1999 ag: CHNL Attrs userEvent not supported. ++ *! SM addrs taken from COFF(IO) or host resource(SM). ++ *! 25-May-1999 jg: CHNL_IOCLASS boards now get their shared memory buffer ++ *! address and length from symbols defined in the currently ++ *! loaded COFF file. See _chn_sm.h. ++ *! 18-Jun-1997 gp: Moved waiting back to ring 0 to improve performance. ++ *! 22-Jan-1998 gp: Update User's pIOC struct in GetIOC at lower IRQL (NT). ++ *! 16-Jan-1998 gp: Commented out PERF stuff, since it is not all there in NT. ++ *! 13-Jan-1998 gp: Protect IOCTLs from IO_DPC by raising IRQL to DIRQL (NT). ++ *! 22-Oct-1997 gp: Call SYNC_OpenEvent in CHNL_Open, for NT support. ++ *! 18-Jun-1997 gp: Moved waiting back to ring 0 to improve performance. ++ *! 16-Jun-1997 gp: Added call into lower edge CHNL function to allow override ++ *! of the SHM window length reported by Windows CM. ++ *! 05-Jun-1997 gp: Removed unnecessary critical sections. ++ *! 18-Mar-1997 gp: Ensured CHNL_FlushIO on input leaves channel in READY state. ++ *! 06-Jan-1997 gp: ifdefed to support the IO variant of SHM channel class lib. ++ *! 21-Jan-1997 gp: CHNL_Close: set pChnl = NULL for DBC_Ensure(). ++ *! 14-Jan-1997 gp: Updated based on code review feedback. ++ *! 03-Jan-1997 gp: Added CHNL_E_WAITTIMEOUT error return code to CHNL_FlushIO() ++ *! 23-Oct-1996 gp: Tag channel with ring 0 process handle. ++ *! 13-Sep-1996 gp: Added performance statistics for channel. ++ *! 09-Sep-1996 gp: Added WMD_CHNL_GetMgrInfo(). ++ *! 04-Sep-1996 gp: Removed shared memory control struct offset: made zero. ++ *! 01-Aug-1996 gp: Implemented basic channel manager and channel create/delete. ++ *! 17-Jul-1996 gp: Started pseudo coding. ++ *! 11-Jul-1996 gp: Stubbed out. ++ */ ++ ++/* ----------------------------------- OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Mini-Driver */ ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++ ++/* ----------------------------------- Others */ ++#include ++ ++/* ----------------------------------- Define for This */ ++#define USERMODE_ADDR PAGE_OFFSET ++ ++#define MAILBOX_IRQ INT_MAIL_MPU_IRQ ++ ++/* ----------------------------------- Function Prototypes */ ++static struct LST_LIST *CreateChirpList(u32 uChirps); ++ ++static void FreeChirpList(struct LST_LIST *pList); ++ ++static struct CHNL_IRP *MakeNewChirp(void); ++ ++static DSP_STATUS SearchFreeChannel(struct CHNL_MGR *pChnlMgr, ++ OUT u32 *pdwChnl); ++ ++/* ++ * ======== WMD_CHNL_AddIOReq ======== ++ * Enqueue an I/O request for data transfer on a channel to the DSP. ++ * The direction (mode) is specified in the channel object. Note the DSP ++ * address is specified for channels opened in direct I/O mode. ++ */ ++DSP_STATUS WMD_CHNL_AddIOReq(struct CHNL_OBJECT *hChnl, void *pHostBuf, ++ u32 cBytes, u32 cBufSize, ++ OPTIONAL u32 dwDspAddr, u32 dwArg) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct CHNL_OBJECT *pChnl = (struct CHNL_OBJECT *)hChnl; ++ struct CHNL_IRP *pChirp = NULL; ++ u32 dwState; ++ bool fIsEOS; ++ struct CHNL_MGR *pChnlMgr = pChnl->pChnlMgr; ++ u8 *pHostSysBuf = NULL; ++ bool fSchedDPC = false; ++ u16 wMbVal = 0; ++ ++ DBG_Trace(DBG_ENTER, ++ "> WMD_CHNL_AddIOReq pChnl %p CHNL_IsOutput %x uChnlType " ++ "%x Id %d\n", pChnl, CHNL_IsOutput(pChnl->uMode), ++ pChnl->uChnlType, pChnl->uId); ++ ++ fIsEOS = (cBytes == 0) ? true : false; ++ ++ if (pChnl->uChnlType == CHNL_PCPY && pChnl->uId > 1 && pHostBuf) { ++ if (!(pHostBuf < (void *)USERMODE_ADDR)) { ++ pHostSysBuf = pHostBuf; ++ goto func_cont; ++ } ++ /* if addr in user mode, then copy to kernel space */ ++ pHostSysBuf = MEM_Alloc(cBufSize, MEM_NONPAGED); ++ if (pHostSysBuf == NULL) { ++ status = DSP_EMEMORY; ++ DBG_Trace(DBG_LEVEL7, ++ "No memory to allocate kernel buffer\n"); ++ goto func_cont; ++ } ++ if (CHNL_IsOutput(pChnl->uMode)) { ++ status = copy_from_user(pHostSysBuf, pHostBuf, ++ cBufSize); ++ if (status) { ++ DBG_Trace(DBG_LEVEL7, ++ "Error copying user buffer to " ++ "kernel, %d bytes remaining.\n", ++ status); ++ MEM_Free(pHostSysBuf); ++ pHostSysBuf = NULL; ++ status = DSP_EPOINTER; ++ } ++ } ++ } ++func_cont: ++ /* Validate args: */ ++ if (pHostBuf == NULL) { ++ status = DSP_EPOINTER; ++ } else if (!MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ } else if (fIsEOS && CHNL_IsInput(pChnl->uMode)) { ++ status = CHNL_E_NOEOS; ++ } else { ++ /* Check the channel state: only queue chirp if channel state ++ * allows */ ++ dwState = pChnl->dwState; ++ if (dwState != CHNL_STATEREADY) { ++ if (dwState & CHNL_STATECANCEL) { ++ status = CHNL_E_CANCELLED; ++ } else if ((dwState & CHNL_STATEEOS) ++ && CHNL_IsOutput(pChnl->uMode)) { ++ status = CHNL_E_EOS; ++ } else { ++ /* No other possible states left: */ ++ DBC_Assert(0); ++ } ++ } ++ } ++ /* Mailbox IRQ is disabled to avoid race condition with DMA/ZCPY ++ * channels. DPCCS is held to avoid race conditions with PCPY channels. ++ * If DPC is scheduled in process context (IO_Schedule) and any ++ * non-mailbox interrupt occurs, that DPC will run and break CS. Hence ++ * we disable ALL DPCs. We will try to disable ONLY IO DPC later. */ ++ SYNC_EnterCS(pChnlMgr->hCSObj); ++ disable_irq(MAILBOX_IRQ); ++ if (pChnl->uChnlType == CHNL_PCPY) { ++ /* This is a processor-copy channel. */ ++ if (DSP_SUCCEEDED(status) && CHNL_IsOutput(pChnl->uMode)) { ++ /* Check buffer size on output channels for fit. */ ++ if (cBytes > IO_BufSize(pChnl->pChnlMgr->hIOMgr)) ++ status = CHNL_E_BUFSIZE; ++ ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Get a free chirp: */ ++ pChirp = (struct CHNL_IRP *)LST_GetHead(pChnl->pFreeList); ++ if (pChirp == NULL) ++ status = CHNL_E_NOIORPS; ++ ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Enqueue the chirp on the chnl's IORequest queue: */ ++ pChirp->pHostUserBuf = pChirp->pHostSysBuf = pHostBuf; ++ if (pChnl->uChnlType == CHNL_PCPY && pChnl->uId > 1) ++ pChirp->pHostSysBuf = pHostSysBuf; ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Note: for dma chans dwDspAddr contains dsp address ++ * of SM buffer.*/ ++ DBC_Assert(pChnlMgr->uWordSize != 0); ++ /* DSP address */ ++ pChirp->uDspAddr = dwDspAddr / pChnlMgr->uWordSize; ++ pChirp->cBytes = cBytes; ++ pChirp->cBufSize = cBufSize; ++ /* Only valid for output channel */ ++ pChirp->dwArg = dwArg; ++ pChirp->status = (fIsEOS ? CHNL_IOCSTATEOS : ++ CHNL_IOCSTATCOMPLETE); ++ LST_PutTail(pChnl->pIORequests, (struct LST_ELEM *) ++ pChirp); ++ pChnl->cIOReqs++; ++ DBC_Assert(pChnl->cIOReqs <= pChnl->cChirps); ++ /* If end of stream, update the channel state to prevent ++ * more IOR's: */ ++ if (fIsEOS) ++ pChnl->dwState |= CHNL_STATEEOS; ++ ++ { ++ /* Legacy DSM Processor-Copy */ ++ DBC_Assert(pChnl->uChnlType == CHNL_PCPY); ++ /* Request IO from the DSP */ ++ IO_RequestChnl(pChnlMgr->hIOMgr, pChnl, ++ (CHNL_IsInput(pChnl->uMode) ? ++ IO_INPUT : IO_OUTPUT), &wMbVal); ++ fSchedDPC = true; ++ } ++ } ++ } ++ enable_irq(MAILBOX_IRQ); ++ SYNC_LeaveCS(pChnlMgr->hCSObj); ++ if (wMbVal != 0) ++ IO_IntrDSP2(pChnlMgr->hIOMgr, wMbVal); ++ ++ if (fSchedDPC == true) { ++ /* Schedule a DPC, to do the actual data transfer: */ ++ IO_Schedule(pChnlMgr->hIOMgr); ++ } ++ DBG_Trace(DBG_ENTER, "< WMD_CHNL_AddIOReq pChnl %p\n", pChnl); ++ return status; ++} ++ ++/* ++ * ======== WMD_CHNL_CancelIO ======== ++ * Return all I/O requests to the client which have not yet been ++ * transferred. The channel's I/O completion object is ++ * signalled, and all the I/O requests are queued as IOC's, with the ++ * status field set to CHNL_IOCSTATCANCEL. ++ * This call is typically used in abort situations, and is a prelude to ++ * CHNL_Close(); ++ */ ++DSP_STATUS WMD_CHNL_CancelIO(struct CHNL_OBJECT *hChnl) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct CHNL_OBJECT *pChnl = (struct CHNL_OBJECT *)hChnl; ++ u32 iChnl = -1; ++ CHNL_MODE uMode; ++ struct CHNL_IRP *pChirp; ++ struct CHNL_MGR *pChnlMgr = NULL; ++ ++ /* Check args: */ ++ if (MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)) { ++ iChnl = pChnl->uId; ++ uMode = pChnl->uMode; ++ pChnlMgr = pChnl->pChnlMgr; ++ } else { ++ status = DSP_EHANDLE; ++ } ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ /* Mark this channel as cancelled, to prevent further IORequests or ++ * IORequests or dispatching. */ ++ SYNC_EnterCS(pChnlMgr->hCSObj); ++ pChnl->dwState |= CHNL_STATECANCEL; ++ if (LST_IsEmpty(pChnl->pIORequests)) ++ goto func_cont; ++ ++ if (pChnl->uChnlType == CHNL_PCPY) { ++ /* Indicate we have no more buffers available for transfer: */ ++ if (CHNL_IsInput(pChnl->uMode)) { ++ IO_CancelChnl(pChnlMgr->hIOMgr, iChnl); ++ } else { ++ /* Record that we no longer have output buffers ++ * available: */ ++ pChnlMgr->dwOutputMask &= ~(1 << iChnl); ++ } ++ } ++ /* Move all IOR's to IOC queue: */ ++ while (!LST_IsEmpty(pChnl->pIORequests)) { ++ pChirp = (struct CHNL_IRP *)LST_GetHead(pChnl->pIORequests); ++ if (pChirp) { ++ pChirp->cBytes = 0; ++ pChirp->status |= CHNL_IOCSTATCANCEL; ++ LST_PutTail(pChnl->pIOCompletions, ++ (struct LST_ELEM *)pChirp); ++ pChnl->cIOCs++; ++ pChnl->cIOReqs--; ++ DBC_Assert(pChnl->cIOReqs >= 0); ++ } ++ } ++func_cont: ++ SYNC_LeaveCS(pChnlMgr->hCSObj); ++func_end: ++ return status; ++} ++ ++/* ++ * ======== WMD_CHNL_Close ======== ++ * Purpose: ++ * Ensures all pending I/O on this channel is cancelled, discards all ++ * queued I/O completion notifications, then frees the resources allocated ++ * for this channel, and makes the corresponding logical channel id ++ * available for subsequent use. ++ */ ++DSP_STATUS WMD_CHNL_Close(struct CHNL_OBJECT *hChnl) ++{ ++ DSP_STATUS status; ++ struct CHNL_OBJECT *pChnl = (struct CHNL_OBJECT *)hChnl; ++ ++ /* Check args: */ ++ if (!MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ goto func_cont; ++ } ++ { ++ /* Cancel IO: this ensures no further IO requests or ++ * notifications.*/ ++ status = WMD_CHNL_CancelIO(hChnl); ++ } ++func_cont: ++ if (DSP_SUCCEEDED(status)) { ++ /* Assert I/O on this channel is now cancelled: Protects ++ * from IO_DPC. */ ++ DBC_Assert((pChnl->dwState & CHNL_STATECANCEL)); ++ /* Invalidate channel object: Protects from ++ * CHNL_GetIOCompletion(). */ ++ pChnl->dwSignature = 0x0000; ++ /* Free the slot in the channel manager: */ ++ pChnl->pChnlMgr->apChannel[pChnl->uId] = NULL; ++ pChnl->pChnlMgr->cOpenChannels -= 1; ++ if (pChnl->hNtfy) { ++ NTFY_Delete(pChnl->hNtfy); ++ pChnl->hNtfy = NULL; ++ } ++ /* Reset channel event: (NOTE: hUserEvent freed in user ++ * context.). */ ++ if (pChnl->hSyncEvent) { ++ SYNC_ResetEvent(pChnl->hSyncEvent); ++ SYNC_CloseEvent(pChnl->hSyncEvent); ++ pChnl->hSyncEvent = NULL; ++ } ++ /* Free I/O request and I/O completion queues: */ ++ if (pChnl->pIOCompletions) { ++ FreeChirpList(pChnl->pIOCompletions); ++ pChnl->pIOCompletions = NULL; ++ pChnl->cIOCs = 0; ++ } ++ if (pChnl->pIORequests) { ++ FreeChirpList(pChnl->pIORequests); ++ pChnl->pIORequests = NULL; ++ pChnl->cIOReqs = 0; ++ } ++ if (pChnl->pFreeList) { ++ FreeChirpList(pChnl->pFreeList); ++ pChnl->pFreeList = NULL; ++ } ++ /* Release channel object. */ ++ MEM_FreeObject(pChnl); ++ pChnl = NULL; ++ } ++ DBC_Ensure(DSP_FAILED(status) || ++ !MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)); ++ return status; ++} ++ ++/* ++ * ======== WMD_CHNL_Create ======== ++ * Create a channel manager object, responsible for opening new channels ++ * and closing old ones for a given board. ++ */ ++DSP_STATUS WMD_CHNL_Create(OUT struct CHNL_MGR **phChnlMgr, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct CHNL_MGRATTRS *pMgrAttrs) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct CHNL_MGR *pChnlMgr = NULL; ++ s32 cChannels; ++#ifdef DEBUG ++ struct CHNL_MGR *hChnlMgr; ++#endif ++ /* Check DBC requirements: */ ++ DBC_Require(phChnlMgr != NULL); ++ DBC_Require(pMgrAttrs != NULL); ++ DBC_Require(pMgrAttrs->cChannels > 0); ++ DBC_Require(pMgrAttrs->cChannels <= CHNL_MAXCHANNELS); ++ DBC_Require(pMgrAttrs->uWordSize != 0); ++#ifdef DEBUG ++ /* This for the purposes of DBC_Require: */ ++ status = DEV_GetChnlMgr(hDevObject, &hChnlMgr); ++ DBC_Require(status != DSP_EHANDLE); ++ DBC_Require(hChnlMgr == NULL); ++#endif ++ if (DSP_SUCCEEDED(status)) { ++ /* Allocate channel manager object: */ ++ MEM_AllocObject(pChnlMgr, struct CHNL_MGR, CHNL_MGRSIGNATURE); ++ if (pChnlMgr) { ++ /* The cChannels attr must equal the # of supported ++ * chnls for each transport(# chnls for PCPY = DDMA = ++ * ZCPY): i.e. pMgrAttrs->cChannels = CHNL_MAXCHANNELS = ++ * DDMA_MAXDDMACHNLS = DDMA_MAXZCPYCHNLS. */ ++ DBC_Assert(pMgrAttrs->cChannels == CHNL_MAXCHANNELS); ++ cChannels = (CHNL_MAXCHANNELS + (CHNL_MAXCHANNELS * ++ CHNL_PCPY)); ++ /* Create array of channels: */ ++ pChnlMgr->apChannel = MEM_Calloc( ++ sizeof(struct CHNL_OBJECT *) * ++ cChannels, MEM_NONPAGED); ++ if (pChnlMgr->apChannel) { ++ /* Initialize CHNL_MGR object: */ ++ /* Shared memory driver. */ ++ pChnlMgr->dwType = CHNL_TYPESM; ++ pChnlMgr->uWordSize = pMgrAttrs->uWordSize; ++ /* total # chnls supported */ ++ pChnlMgr->cChannels = cChannels; ++ pChnlMgr->cOpenChannels = 0; ++ pChnlMgr->dwOutputMask = 0; ++ pChnlMgr->dwLastOutput = 0; ++ pChnlMgr->hDevObject = hDevObject; ++ if (DSP_SUCCEEDED(status)) { ++ status = SYNC_InitializeDPCCS ++ (&pChnlMgr->hCSObj); ++ } ++ } else { ++ status = DSP_EMEMORY; ++ } ++ } else { ++ status = DSP_EMEMORY; ++ } ++ } ++ if (DSP_FAILED(status)) { ++ WMD_CHNL_Destroy(pChnlMgr); ++ *phChnlMgr = NULL; ++ } else { ++ /* Return channel manager object to caller... */ ++ *phChnlMgr = pChnlMgr; ++ } ++ return status; ++} ++ ++/* ++ * ======== WMD_CHNL_Destroy ======== ++ * Purpose: ++ * Close all open channels, and destroy the channel manager. ++ */ ++DSP_STATUS WMD_CHNL_Destroy(struct CHNL_MGR *hChnlMgr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct CHNL_MGR *pChnlMgr = hChnlMgr; ++ u32 iChnl; ++ ++ if (MEM_IsValidHandle(hChnlMgr, CHNL_MGRSIGNATURE)) { ++ /* Close all open channels: */ ++ for (iChnl = 0; iChnl < pChnlMgr->cChannels; iChnl++) { ++ if (DSP_SUCCEEDED ++ (WMD_CHNL_Close(pChnlMgr->apChannel[iChnl]))) { ++ DBC_Assert(pChnlMgr->apChannel[iChnl] == NULL); ++ } ++ } ++ /* release critical section */ ++ if (pChnlMgr->hCSObj) ++ SYNC_DeleteCS(pChnlMgr->hCSObj); ++ ++ /* Free channel manager object: */ ++ if (pChnlMgr->apChannel) ++ MEM_Free(pChnlMgr->apChannel); ++ ++ /* Set hChnlMgr to NULL in device object. */ ++ DEV_SetChnlMgr(pChnlMgr->hDevObject, NULL); ++ /* Free this Chnl Mgr object: */ ++ MEM_FreeObject(hChnlMgr); ++ } else { ++ status = DSP_EHANDLE; ++ } ++ return status; ++} ++ ++/* ++ * ======== WMD_CHNL_FlushIO ======== ++ * purpose: ++ * Flushes all the outstanding data requests on a channel. ++ */ ++DSP_STATUS WMD_CHNL_FlushIO(struct CHNL_OBJECT *hChnl, u32 dwTimeOut) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct CHNL_OBJECT *pChnl = (struct CHNL_OBJECT *)hChnl; ++ CHNL_MODE uMode = -1; ++ struct CHNL_MGR *pChnlMgr; ++ struct CHNL_IOC chnlIOC; ++ /* Check args: */ ++ if (MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)) { ++ if ((dwTimeOut == CHNL_IOCNOWAIT) ++ && CHNL_IsOutput(pChnl->uMode)) { ++ status = DSP_EINVALIDARG; ++ } else { ++ uMode = pChnl->uMode; ++ pChnlMgr = pChnl->pChnlMgr; ++ } ++ } else { ++ status = DSP_EHANDLE; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Note: Currently, if another thread continues to add IO ++ * requests to this channel, this function will continue to ++ * flush all such queued IO requests. */ ++ if (CHNL_IsOutput(uMode) && (pChnl->uChnlType == CHNL_PCPY)) { ++ /* Wait for IO completions, up to the specified ++ * timeout: */ ++ while (!LST_IsEmpty(pChnl->pIORequests) && ++ DSP_SUCCEEDED(status)) { ++ status = WMD_CHNL_GetIOC(hChnl, dwTimeOut, ++ &chnlIOC); ++ if (DSP_FAILED(status)) ++ continue; ++ ++ if (chnlIOC.status & CHNL_IOCSTATTIMEOUT) ++ status = CHNL_E_WAITTIMEOUT; ++ ++ } ++ } else { ++ status = WMD_CHNL_CancelIO(hChnl); ++ /* Now, leave the channel in the ready state: */ ++ pChnl->dwState &= ~CHNL_STATECANCEL; ++ } ++ } ++ DBC_Ensure(DSP_FAILED(status) || LST_IsEmpty(pChnl->pIORequests)); ++ return status; ++} ++ ++/* ++ * ======== WMD_CHNL_GetInfo ======== ++ * Purpose: ++ * Retrieve information related to a channel. ++ */ ++DSP_STATUS WMD_CHNL_GetInfo(struct CHNL_OBJECT *hChnl, ++ OUT struct CHNL_INFO *pInfo) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct CHNL_OBJECT *pChnl = (struct CHNL_OBJECT *)hChnl; ++ if (pInfo != NULL) { ++ if (MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)) { ++ /* Return the requested information: */ ++ pInfo->hChnlMgr = pChnl->pChnlMgr; ++ pInfo->hEvent = pChnl->hUserEvent; ++ pInfo->dwID = pChnl->uId; ++ pInfo->dwMode = pChnl->uMode; ++ pInfo->cPosition = pChnl->cBytesMoved; ++ pInfo->hProcess = pChnl->hProcess; ++ pInfo->hSyncEvent = pChnl->hSyncEvent; ++ pInfo->cIOCs = pChnl->cIOCs; ++ pInfo->cIOReqs = pChnl->cIOReqs; ++ pInfo->dwState = pChnl->dwState; ++ } else { ++ status = DSP_EHANDLE; ++ } ++ } else { ++ status = DSP_EPOINTER; ++ } ++ return status; ++} ++ ++/* ++ * ======== WMD_CHNL_GetIOC ======== ++ * Optionally wait for I/O completion on a channel. Dequeue an I/O ++ * completion record, which contains information about the completed ++ * I/O request. ++ * Note: Ensures Channel Invariant (see notes above). ++ */ ++DSP_STATUS WMD_CHNL_GetIOC(struct CHNL_OBJECT *hChnl, u32 dwTimeOut, ++ OUT struct CHNL_IOC *pIOC) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct CHNL_OBJECT *pChnl = (struct CHNL_OBJECT *)hChnl; ++ struct CHNL_IRP *pChirp; ++ DSP_STATUS statSync; ++ bool fDequeueIOC = true; ++ struct CHNL_IOC ioc = { NULL, 0, 0, 0, 0 }; ++ u8 *pHostSysBuf = NULL; ++ ++ DBG_Trace(DBG_ENTER, "> WMD_CHNL_GetIOC pChnl %p CHNL_IsOutput %x " ++ "uChnlType %x\n", pChnl, CHNL_IsOutput(pChnl->uMode), ++ pChnl->uChnlType); ++ /* Check args: */ ++ if (pIOC == NULL) { ++ status = DSP_EPOINTER; ++ } else if (!MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)) { ++ status = DSP_EHANDLE; ++ } else if (dwTimeOut == CHNL_IOCNOWAIT) { ++ if (LST_IsEmpty(pChnl->pIOCompletions)) ++ status = CHNL_E_NOIOC; ++ ++ } ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ ioc.status = CHNL_IOCSTATCOMPLETE; ++ if (dwTimeOut != CHNL_IOCNOWAIT && LST_IsEmpty(pChnl->pIOCompletions)) { ++ if (dwTimeOut == CHNL_IOCINFINITE) ++ dwTimeOut = SYNC_INFINITE; ++ ++ statSync = SYNC_WaitOnEvent(pChnl->hSyncEvent, dwTimeOut); ++ if (statSync == DSP_ETIMEOUT) { ++ /* No response from DSP */ ++ ioc.status |= CHNL_IOCSTATTIMEOUT; ++ fDequeueIOC = false; ++ } else if (statSync == DSP_EFAIL) { ++ /* This can occur when the user mode thread is ++ * aborted (^C), or when _VWIN32_WaitSingleObject() ++ * fails due to unkown causes. */ ++ /* Even though Wait failed, there may be something in ++ * the Q: */ ++ if (LST_IsEmpty(pChnl->pIOCompletions)) { ++ ioc.status |= CHNL_IOCSTATCANCEL; ++ fDequeueIOC = false; ++ } ++ } ++ } ++ /* See comment in AddIOReq */ ++ SYNC_EnterCS(pChnl->pChnlMgr->hCSObj); ++ disable_irq(MAILBOX_IRQ); ++ if (fDequeueIOC) { ++ /* Dequeue IOC and set pIOC; */ ++ DBC_Assert(!LST_IsEmpty(pChnl->pIOCompletions)); ++ pChirp = (struct CHNL_IRP *)LST_GetHead(pChnl->pIOCompletions); ++ /* Update pIOC from channel state and chirp: */ ++ if (pChirp) { ++ pChnl->cIOCs--; ++ /* If this is a zero-copy channel, then set IOC's pBuf ++ * to the DSP's address. This DSP address will get ++ * translated to user's virtual addr later. */ ++ { ++ pHostSysBuf = pChirp->pHostSysBuf; ++ ioc.pBuf = pChirp->pHostUserBuf; ++ } ++ ioc.cBytes = pChirp->cBytes; ++ ioc.cBufSize = pChirp->cBufSize; ++ ioc.dwArg = pChirp->dwArg; ++ ioc.status |= pChirp->status; ++ /* Place the used chirp on the free list: */ ++ LST_PutTail(pChnl->pFreeList, (struct LST_ELEM *) ++ pChirp); ++ } else { ++ ioc.pBuf = NULL; ++ ioc.cBytes = 0; ++ } ++ } else { ++ ioc.pBuf = NULL; ++ ioc.cBytes = 0; ++ ioc.dwArg = 0; ++ ioc.cBufSize = 0; ++ } ++ /* Ensure invariant: If any IOC's are queued for this channel... */ ++ if (!LST_IsEmpty(pChnl->pIOCompletions)) { ++ /* Since DSPStream_Reclaim() does not take a timeout ++ * parameter, we pass the stream's timeout value to ++ * WMD_CHNL_GetIOC. We cannot determine whether or not ++ * we have waited in User mode. Since the stream's timeout ++ * value may be non-zero, we still have to set the event. ++ * Therefore, this optimization is taken out. ++ * ++ * if (dwTimeOut == CHNL_IOCNOWAIT) { ++ * ... ensure event is set.. ++ * SYNC_SetEvent(pChnl->hSyncEvent); ++ * } */ ++ SYNC_SetEvent(pChnl->hSyncEvent); ++ } else { ++ /* else, if list is empty, ensure event is reset. */ ++ SYNC_ResetEvent(pChnl->hSyncEvent); ++ } ++ enable_irq(MAILBOX_IRQ); ++ SYNC_LeaveCS(pChnl->pChnlMgr->hCSObj); ++ if (fDequeueIOC && (pChnl->uChnlType == CHNL_PCPY && pChnl->uId > 1)) { ++ if (!(ioc.pBuf < (void *) USERMODE_ADDR)) ++ goto func_cont; ++ ++ /* If the addr is in user mode, then copy it */ ++ if (!pHostSysBuf || !ioc.pBuf) { ++ status = DSP_EPOINTER; ++ DBG_Trace(DBG_LEVEL7, ++ "System buffer NULL in IO completion.\n"); ++ goto func_cont; ++ } ++ if (!CHNL_IsInput(pChnl->uMode)) ++ goto func_cont1; ++ ++ /*pHostUserBuf */ ++ status = copy_to_user(ioc.pBuf, pHostSysBuf, ioc.cBytes); ++#ifndef RES_CLEANUP_DISABLE ++ if (status) { ++ if (current->flags & PF_EXITING) { ++ DBG_Trace(DBG_LEVEL7, ++ "\n2current->flags == PF_EXITING, " ++ " current->flags;0x%x\n", ++ current->flags); ++ status = 0; ++ } else { ++ DBG_Trace(DBG_LEVEL7, ++ "\n2current->flags != PF_EXITING, " ++ " current->flags;0x%x\n", ++ current->flags); ++ } ++ } ++#endif ++ if (status) { ++ DBG_Trace(DBG_LEVEL7, ++ "Error copying kernel buffer to user, %d" ++ " bytes remaining. in_interupt %d\n", ++ status, in_interrupt()); ++ status = DSP_EPOINTER; ++ } ++func_cont1: ++ MEM_Free(pHostSysBuf); ++ } ++func_cont: ++ /* Update User's IOC block: */ ++ *pIOC = ioc; ++func_end: ++ DBG_Trace(DBG_ENTER, "< WMD_CHNL_GetIOC pChnl %p\n", pChnl); ++ return status; ++} ++ ++/* ++ * ======== WMD_CHNL_GetMgrInfo ======== ++ * Retrieve information related to the channel manager. ++ */ ++DSP_STATUS WMD_CHNL_GetMgrInfo(struct CHNL_MGR *hChnlMgr, u32 uChnlID, ++ OUT struct CHNL_MGRINFO *pMgrInfo) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct CHNL_MGR *pChnlMgr = (struct CHNL_MGR *)hChnlMgr; ++ ++ if (pMgrInfo != NULL) { ++ if (uChnlID <= CHNL_MAXCHANNELS) { ++ if (MEM_IsValidHandle(hChnlMgr, CHNL_MGRSIGNATURE)) { ++ /* Return the requested information: */ ++ pMgrInfo->hChnl = pChnlMgr->apChannel[uChnlID]; ++ pMgrInfo->cOpenChannels = pChnlMgr-> ++ cOpenChannels; ++ pMgrInfo->dwType = pChnlMgr->dwType; ++ /* total # of chnls */ ++ pMgrInfo->cChannels = pChnlMgr->cChannels; ++ } else { ++ status = DSP_EHANDLE; ++ } ++ } else { ++ status = CHNL_E_BADCHANID; ++ } ++ } else { ++ status = DSP_EPOINTER; ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== WMD_CHNL_Idle ======== ++ * Idles a particular channel. ++ */ ++DSP_STATUS WMD_CHNL_Idle(struct CHNL_OBJECT *hChnl, u32 dwTimeOut, ++ bool fFlush) ++{ ++ CHNL_MODE uMode; ++ struct CHNL_MGR *pChnlMgr; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(MEM_IsValidHandle(hChnl, CHNL_SIGNATURE)); ++ ++ uMode = hChnl->uMode; ++ pChnlMgr = hChnl->pChnlMgr; ++ ++ if (CHNL_IsOutput(uMode) && !fFlush) { ++ /* Wait for IO completions, up to the specified timeout: */ ++ status = WMD_CHNL_FlushIO(hChnl, dwTimeOut); ++ } else { ++ status = WMD_CHNL_CancelIO(hChnl); ++ ++ /* Reset the byte count and put channel back in ready state. */ ++ hChnl->cBytesMoved = 0; ++ hChnl->dwState &= ~CHNL_STATECANCEL; ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== WMD_CHNL_Open ======== ++ * Open a new half-duplex channel to the DSP board. ++ */ ++DSP_STATUS WMD_CHNL_Open(OUT struct CHNL_OBJECT **phChnl, ++ struct CHNL_MGR *hChnlMgr, CHNL_MODE uMode, ++ u32 uChnlId, CONST IN struct CHNL_ATTRS *pAttrs) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct CHNL_MGR *pChnlMgr = hChnlMgr; ++ struct CHNL_OBJECT *pChnl = NULL; ++ struct SYNC_ATTRS *pSyncAttrs = NULL; ++ struct SYNC_OBJECT *hSyncEvent = NULL; ++ /* Ensure DBC requirements: */ ++ DBC_Require(phChnl != NULL); ++ DBC_Require(pAttrs != NULL); ++ *phChnl = NULL; ++ /* Validate Args: */ ++ if (pAttrs->uIOReqs == 0) { ++ status = DSP_EINVALIDARG; ++ } else { ++ if (!MEM_IsValidHandle(hChnlMgr, CHNL_MGRSIGNATURE)) { ++ status = DSP_EHANDLE; ++ } else { ++ if (uChnlId != CHNL_PICKFREE) { ++ if (uChnlId >= pChnlMgr->cChannels) { ++ status = CHNL_E_BADCHANID; ++ } else if (pChnlMgr->apChannel[uChnlId] != ++ NULL) { ++ status = CHNL_E_CHANBUSY; ++ } ++ } else { ++ /* Check for free channel */ ++ status = SearchFreeChannel(pChnlMgr, &uChnlId); ++ } ++ } ++ } ++ if (DSP_FAILED(status)) ++ goto func_end; ++ ++ DBC_Assert(uChnlId < pChnlMgr->cChannels); ++ /* Create channel object: */ ++ MEM_AllocObject(pChnl, struct CHNL_OBJECT, 0x0000); ++ if (!pChnl) { ++ status = DSP_EMEMORY; ++ goto func_cont; ++ } ++ /* Protect queues from IO_DPC: */ ++ pChnl->dwState = CHNL_STATECANCEL; ++ /* Allocate initial IOR and IOC queues: */ ++ pChnl->pFreeList = CreateChirpList(pAttrs->uIOReqs); ++ pChnl->pIORequests = CreateChirpList(0); ++ pChnl->pIOCompletions = CreateChirpList(0); ++ pChnl->cChirps = pAttrs->uIOReqs; ++ pChnl->cIOCs = 0; ++ pChnl->cIOReqs = 0; ++ status = SYNC_OpenEvent(&hSyncEvent, pSyncAttrs); ++ if (DSP_SUCCEEDED(status)) { ++ status = NTFY_Create(&pChnl->hNtfy); ++ if (DSP_FAILED(status)) { ++ /* The only failure that could have occurred */ ++ status = DSP_EMEMORY; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ if (pChnl->pIOCompletions && pChnl->pIORequests && ++ pChnl->pFreeList) { ++ /* Initialize CHNL object fields: */ ++ pChnl->pChnlMgr = pChnlMgr; ++ pChnl->uId = uChnlId; ++ pChnl->uMode = uMode; ++ pChnl->hUserEvent = hSyncEvent; /* for Linux */ ++ pChnl->hSyncEvent = hSyncEvent; ++ /* get the process handle */ ++ pChnl->hProcess = current->pid; ++ pChnl->pCBArg = 0; ++ pChnl->cBytesMoved = 0; ++ /* Default to proc-copy */ ++ pChnl->uChnlType = CHNL_PCPY; ++ } else { ++ status = DSP_EMEMORY; ++ } ++ } else { ++ status = DSP_EINVALIDARG; ++ } ++ if (DSP_FAILED(status)) { ++ /* Free memory */ ++ if (pChnl->pIOCompletions) { ++ FreeChirpList(pChnl->pIOCompletions); ++ pChnl->pIOCompletions = NULL; ++ pChnl->cIOCs = 0; ++ } ++ if (pChnl->pIORequests) { ++ FreeChirpList(pChnl->pIORequests); ++ pChnl->pIORequests = NULL; ++ } ++ if (pChnl->pFreeList) { ++ FreeChirpList(pChnl->pFreeList); ++ pChnl->pFreeList = NULL; ++ } ++ if (hSyncEvent) { ++ SYNC_CloseEvent(hSyncEvent); ++ hSyncEvent = NULL; ++ } ++ if (pChnl->hNtfy) { ++ NTFY_Delete(pChnl->hNtfy); ++ pChnl->hNtfy = NULL; ++ } ++ MEM_FreeObject(pChnl); ++ } ++func_cont: ++ if (DSP_SUCCEEDED(status)) { ++ /* Insert channel object in channel manager: */ ++ pChnlMgr->apChannel[pChnl->uId] = pChnl; ++ SYNC_EnterCS(pChnlMgr->hCSObj); ++ pChnlMgr->cOpenChannels++; ++ SYNC_LeaveCS(pChnlMgr->hCSObj); ++ /* Return result... */ ++ pChnl->dwSignature = CHNL_SIGNATURE; ++ pChnl->dwState = CHNL_STATEREADY; ++ *phChnl = pChnl; ++ } ++func_end: ++ DBC_Ensure((DSP_SUCCEEDED(status) && ++ MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)) || ++ (*phChnl == NULL)); ++ return status; ++} ++ ++/* ++ * ======== WMD_CHNL_RegisterNotify ======== ++ * Registers for events on a particular channel. ++ */ ++DSP_STATUS WMD_CHNL_RegisterNotify(struct CHNL_OBJECT *hChnl, u32 uEventMask, ++ u32 uNotifyType, ++ struct DSP_NOTIFICATION *hNotification) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Assert(!(uEventMask & ~(DSP_STREAMDONE | DSP_STREAMIOCOMPLETION))); ++ ++ status = NTFY_Register(hChnl->hNtfy, hNotification, uEventMask, ++ uNotifyType); ++ ++ return status; ++} ++ ++/* ++ * ======== CreateChirpList ======== ++ * Purpose: ++ * Initialize a queue of channel I/O Request/Completion packets. ++ * Parameters: ++ * uChirps: Number of Chirps to allocate. ++ * Returns: ++ * Pointer to queue of IRPs, or NULL. ++ * Requires: ++ * Ensures: ++ */ ++static struct LST_LIST *CreateChirpList(u32 uChirps) ++{ ++ struct LST_LIST *pChirpList; ++ struct CHNL_IRP *pChirp; ++ u32 i; ++ ++ pChirpList = LST_Create(); ++ ++ if (pChirpList) { ++ /* Make N chirps and place on queue. */ ++ for (i = 0; (i < uChirps) && ((pChirp = MakeNewChirp()) != ++ NULL); i++) { ++ LST_PutTail(pChirpList, (struct LST_ELEM *)pChirp); ++ } ++ ++ /* If we couldn't allocate all chirps, free those allocated: */ ++ if (i != uChirps) { ++ FreeChirpList(pChirpList); ++ pChirpList = NULL; ++ } ++ } ++ ++ return pChirpList; ++} ++ ++/* ++ * ======== FreeChirpList ======== ++ * Purpose: ++ * Free the queue of Chirps. ++ */ ++static void FreeChirpList(struct LST_LIST *pChirpList) ++{ ++ DBC_Require(pChirpList != NULL); ++ ++ while (!LST_IsEmpty(pChirpList)) ++ MEM_Free(LST_GetHead(pChirpList)); ++ ++ LST_Delete(pChirpList); ++} ++ ++/* ++ * ======== MakeNewChirp ======== ++ * Allocate the memory for a new channel IRP. ++ */ ++static struct CHNL_IRP *MakeNewChirp(void) ++{ ++ struct CHNL_IRP *pChirp; ++ ++ pChirp = (struct CHNL_IRP *)MEM_Calloc( ++ sizeof(struct CHNL_IRP), MEM_NONPAGED); ++ if (pChirp != NULL) { ++ /* LST_InitElem only resets the list's member values. */ ++ LST_InitElem(&pChirp->link); ++ } ++ ++ return pChirp; ++} ++ ++/* ++ * ======== SearchFreeChannel ======== ++ * Search for a free channel slot in the array of channel pointers. ++ */ ++static DSP_STATUS SearchFreeChannel(struct CHNL_MGR *pChnlMgr, ++ OUT u32 *pdwChnl) ++{ ++ DSP_STATUS status = CHNL_E_OUTOFSTREAMS; ++ u32 i; ++ ++ DBC_Require(MEM_IsValidHandle(pChnlMgr, CHNL_MGRSIGNATURE)); ++ ++ for (i = 0; i < pChnlMgr->cChannels; i++) { ++ if (pChnlMgr->apChannel[i] == NULL) { ++ status = DSP_SOK; ++ *pdwChnl = i; ++ break; ++ } ++ } ++ ++ return status; ++} +diff --git a/drivers/dsp/bridge/wmd/io_sm.c b/drivers/dsp/bridge/wmd/io_sm.c +new file mode 100644 +index 0000000..325f1f1 +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/io_sm.c +@@ -0,0 +1,2011 @@ ++/* ++ * io_sm.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== io_sm.c ======== ++ * Description: ++ * IO dispatcher for a shared memory channel driver. ++ * ++ * Public Functions: ++ * WMD_IO_Create ++ * WMD_IO_Destroy ++ * WMD_IO_OnLoaded ++ * IO_AndSetValue ++ * IO_BufSize ++ * IO_CancelChnl ++ * IO_DPC ++ * IO_ISR ++ * IO_IVAISR ++ * IO_OrSetValue ++ * IO_ReadValue ++ * IO_ReadValueLong ++ * IO_RequestChnl ++ * IO_Schedule ++ * IO_WriteValue ++ * IO_WriteValueLong ++ * ++ * Channel Invariant: ++ * There is an important invariant condition which must be maintained per ++ * channel outside of WMD_CHNL_GetIOC() and IO_Dispatch(), violation of ++ * which may cause timeouts and/or failure of the WIN32_WaitSingleObject ++ * function (SYNC_WaitOnEvent). ++ * ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ------------------------------------ Hardware Abstraction Layer */ ++#include ++#include ++ ++/* ----------------------------------- Mini Driver */ ++#include ++#include ++#include ++#include <_tiomap.h> ++#include ++#include <_tiomap_pwr.h> ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Others */ ++#include ++#include ++#include ++#include "_cmm.h" ++ ++/* ----------------------------------- This */ ++#include ++#include "_msg_sm.h" ++#include ++ ++/* ----------------------------------- Defines, Data Structures, Typedefs */ ++#define OUTPUTNOTREADY 0xffff ++#define NOTENABLED 0xffff /* channel(s) not enabled */ ++ ++#define EXTEND "_EXT_END" ++ ++#define SwapWord(x) (x) ++#define ulPageAlignSize 0x10000 /* Page Align Size */ ++ ++#define MAX_PM_REQS 32 ++ ++/* IO Manager: only one created per board: */ ++struct IO_MGR { ++ /* These four fields must be the first fields in a IO_MGR_ struct: */ ++ u32 dwSignature; /* Used for object validation */ ++ struct WMD_DEV_CONTEXT *hWmdContext; /* WMD device context */ ++ struct WMD_DRV_INTERFACE *pIntfFxns; /* Function interface to WMD */ ++ struct DEV_OBJECT *hDevObject; /* Device this board represents */ ++ ++ /* These fields initialized in WMD_IO_Create(): */ ++ struct CHNL_MGR *hChnlMgr; ++ struct SHM *pSharedMem; /* Shared Memory control */ ++ u8 *pInput; /* Address of input channel */ ++ u8 *pOutput; /* Address of output channel */ ++ struct MSG_MGR *hMsgMgr; /* Message manager */ ++ struct MSG *pMsgInputCtrl; /* Msg control for from DSP messages */ ++ struct MSG *pMsgOutputCtrl; /* Msg control for to DSP messages */ ++ u8 *pMsgInput; /* Address of input messages */ ++ u8 *pMsgOutput; /* Address of output messages */ ++ u32 uSMBufSize; /* Size of a shared memory I/O channel */ ++ bool fSharedIRQ; /* Is this IRQ shared? */ ++ struct DPC_OBJECT *hDPC; /* DPC object handle */ ++ struct SYNC_CSOBJECT *hCSObj; /* Critical section object handle */ ++ u32 uWordSize; /* Size in bytes of DSP word */ ++ u16 wIntrVal; /* interrupt value */ ++ /* private extnd proc info; mmu setup */ ++ struct MGR_PROCESSOREXTINFO extProcInfo; ++ struct CMM_OBJECT *hCmmMgr; /* Shared Mem Mngr */ ++ struct work_struct io_workq; /*workqueue */ ++ u32 dQuePowerMbxVal[MAX_PM_REQS]; ++ u32 iQuePowerHead; ++ u32 iQuePowerTail; ++#ifndef DSP_TRACEBUF_DISABLED ++ u32 ulTraceBufferBegin; /* Trace message start address */ ++ u32 ulTraceBufferEnd; /* Trace message end address */ ++ u32 ulTraceBufferCurrent; /* Trace message current address */ ++ u32 ulGPPReadPointer; /* GPP Read pointer to Trace buffer */ ++ u8 *pMsg; ++ u32 ulGppVa; ++ u32 ulDspVa; ++#endif ++} ; ++ ++/* ----------------------------------- Function Prototypes */ ++static void IO_DispatchChnl(IN struct IO_MGR *pIOMgr, ++ IN OUT struct CHNL_OBJECT *pChnl, u32 iMode); ++static void IO_DispatchMsg(IN struct IO_MGR *pIOMgr, struct MSG_MGR *hMsgMgr); ++static void IO_DispatchPM(struct work_struct *work); ++static void NotifyChnlComplete(struct CHNL_OBJECT *pChnl, ++ struct CHNL_IRP *pChirp); ++static void InputChnl(struct IO_MGR *pIOMgr, struct CHNL_OBJECT *pChnl, ++ u32 iMode); ++static void OutputChnl(struct IO_MGR *pIOMgr, struct CHNL_OBJECT *pChnl, ++ u32 iMode); ++static void InputMsg(struct IO_MGR *pIOMgr, struct MSG_MGR *hMsgMgr); ++static void OutputMsg(struct IO_MGR *pIOMgr, struct MSG_MGR *hMsgMgr); ++static u32 FindReadyOutput(struct CHNL_MGR *pChnlMgr, ++ struct CHNL_OBJECT *pChnl, u32 dwMask); ++static u32 ReadData(struct WMD_DEV_CONTEXT *hDevContext, void *pDest, ++ void *pSrc, u32 uSize); ++static u32 WriteData(struct WMD_DEV_CONTEXT *hDevContext, void *pDest, ++ void *pSrc, u32 uSize); ++static struct workqueue_struct *bridge_workqueue; ++#ifndef DSP_TRACEBUF_DISABLED ++void PrintDSPDebugTrace(struct IO_MGR *hIOMgr); ++#endif ++ ++/* Bus Addr (cached kernel)*/ ++static DSP_STATUS registerSHMSegs(struct IO_MGR *hIOMgr, ++ struct COD_MANAGER *hCodMan, ++ u32 dwGPPBasePA); ++ ++#ifdef CONFIG_BRIDGE_DVFS ++/* The maximum number of OPPs that are supported */ ++extern s32 dsp_max_opps; ++/* The Vdd1 opp table information */ ++extern u32 vdd1_dsp_freq[6][4] ; ++#endif ++ ++#if GT_TRACE ++static struct GT_Mask dsp_trace_mask = { NULL, NULL }; /* GT trace variable */ ++#endif ++ ++/* ++ * ======== WMD_IO_Create ======== ++ * Create an IO manager object. ++ */ ++DSP_STATUS WMD_IO_Create(OUT struct IO_MGR **phIOMgr, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct IO_ATTRS *pMgrAttrs) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct IO_MGR *pIOMgr = NULL; ++ struct SHM *pSharedMem = NULL; ++ struct WMD_DEV_CONTEXT *hWmdContext = NULL; ++ struct CFG_HOSTRES hostRes; ++ struct CFG_DEVNODE *hDevNode; ++ struct CHNL_MGR *hChnlMgr; ++ static int ref_count; ++ u32 devType; ++ /* Check DBC requirements: */ ++ DBC_Require(phIOMgr != NULL); ++ DBC_Require(pMgrAttrs != NULL); ++ DBC_Require(pMgrAttrs->uWordSize != 0); ++ /* This for the purposes of DBC_Require: */ ++ status = DEV_GetChnlMgr(hDevObject, &hChnlMgr); ++ DBC_Require(status != DSP_EHANDLE); ++ DBC_Require(hChnlMgr != NULL); ++ DBC_Require(hChnlMgr->hIOMgr == NULL); ++ /* Message manager will be created when a file is loaded, since ++ * size of message buffer in shared memory is configurable in ++ * the base image. */ ++ DEV_GetWMDContext(hDevObject, &hWmdContext); ++ DBC_Assert(hWmdContext); ++ DEV_GetDevType(hDevObject, &devType); ++ /* DSP shared memory area will get set properly when ++ * a program is loaded. They are unknown until a COFF file is ++ * loaded. I chose the value -1 because it was less likely to be ++ * a valid address than 0. */ ++ pSharedMem = (struct SHM *) -1; ++ if (DSP_FAILED(status)) ++ goto func_cont; ++ ++ /* ++ * Create a Single Threaded Work Queue ++ */ ++ ++ if (ref_count == 0) ++ bridge_workqueue = create_workqueue("bridge_work-queue"); ++ ++ if (bridge_workqueue <= 0) ++ DBG_Trace(DBG_LEVEL1, "Workque Create" ++ " failed 0x%d \n", bridge_workqueue); ++ ++ ++ /* Allocate IO manager object: */ ++ MEM_AllocObject(pIOMgr, struct IO_MGR, IO_MGRSIGNATURE); ++ if (pIOMgr == NULL) { ++ status = DSP_EMEMORY; ++ goto func_cont; ++ } ++ /*Intializing Work Element*/ ++ if (ref_count == 0) { ++ INIT_WORK(&pIOMgr->io_workq, (void *)IO_DispatchPM); ++ ref_count = 1; ++ } else ++ PREPARE_WORK(&pIOMgr->io_workq, (void *)IO_DispatchPM); ++ ++ /* Initialize CHNL_MGR object: */ ++#ifndef DSP_TRACEBUF_DISABLED ++ pIOMgr->pMsg = NULL; ++#endif ++ pIOMgr->hChnlMgr = hChnlMgr; ++ pIOMgr->uWordSize = pMgrAttrs->uWordSize; ++ pIOMgr->pSharedMem = pSharedMem; ++ if (DSP_SUCCEEDED(status)) ++ status = SYNC_InitializeCS(&pIOMgr->hCSObj); ++ ++ if (devType == DSP_UNIT) { ++ /* Create a DPC object: */ ++ status = DPC_Create(&pIOMgr->hDPC, IO_DPC, (void *)pIOMgr); ++ if (DSP_SUCCEEDED(status)) ++ status = DEV_GetDevNode(hDevObject, &hDevNode); ++ ++ pIOMgr->iQuePowerHead = 0; ++ pIOMgr->iQuePowerTail = 0; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = CFG_GetHostResources((struct CFG_DEVNODE *) ++ DRV_GetFirstDevExtension() , &hostRes); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ pIOMgr->hWmdContext = hWmdContext; ++ pIOMgr->fSharedIRQ = pMgrAttrs->fShared; ++ IO_DisableInterrupt(hWmdContext); ++ if (devType == DSP_UNIT) { ++ /* Plug the channel ISR:. */ ++ if ((request_irq(INT_MAIL_MPU_IRQ, IO_ISR, 0, ++ "DspBridge\tmailbox", (void *)pIOMgr)) == 0) ++ status = DSP_SOK; ++ else ++ status = DSP_EFAIL; ++ } ++ if (DSP_SUCCEEDED(status)) ++ DBG_Trace(DBG_LEVEL1, "ISR_IRQ Object 0x%x \n", ++ pIOMgr); ++ else ++ status = CHNL_E_ISR; ++ } else ++ status = CHNL_E_ISR; ++func_cont: ++ if (DSP_FAILED(status)) { ++ /* Cleanup: */ ++ WMD_IO_Destroy(pIOMgr); ++ *phIOMgr = NULL; ++ } else { ++ /* Return IO manager object to caller... */ ++ hChnlMgr->hIOMgr = pIOMgr; ++ *phIOMgr = pIOMgr; ++ } ++ return status; ++} ++ ++/* ++ * ======== WMD_IO_Destroy ======== ++ * Purpose: ++ * Disable interrupts, destroy the IO manager. ++ */ ++DSP_STATUS WMD_IO_Destroy(struct IO_MGR *hIOMgr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DEV_CONTEXT *hWmdContext; ++ if (MEM_IsValidHandle(hIOMgr, IO_MGRSIGNATURE)) { ++ /* Unplug IRQ: */ ++ /* Disable interrupts from the board: */ ++ if (DSP_SUCCEEDED(DEV_GetWMDContext(hIOMgr->hDevObject, ++ &hWmdContext))) ++ DBC_Assert(hWmdContext); ++ (void)CHNLSM_DisableInterrupt(hWmdContext); ++ destroy_workqueue(bridge_workqueue); ++ /* Linux function to uninstall ISR */ ++ free_irq(INT_MAIL_MPU_IRQ, (void *)hIOMgr); ++ (void)DPC_Destroy(hIOMgr->hDPC); ++#ifndef DSP_TRACEBUF_DISABLED ++ if (hIOMgr->pMsg) ++ MEM_Free(hIOMgr->pMsg); ++#endif ++ SYNC_DeleteCS(hIOMgr->hCSObj); /* Leak Fix. */ ++ /* Free this IO manager object: */ ++ MEM_FreeObject(hIOMgr); ++ } else ++ status = DSP_EHANDLE; ++ ++ return status; ++} ++ ++/* ++ * ======== WMD_IO_OnLoaded ======== ++ * Purpose: ++ * Called when a new program is loaded to get shared memory buffer ++ * parameters from COFF file. ulSharedBufferBase and ulSharedBufferLimit ++ * are in DSP address units. ++ */ ++DSP_STATUS WMD_IO_OnLoaded(struct IO_MGR *hIOMgr) ++{ ++ struct COD_MANAGER *hCodMan; ++ struct CHNL_MGR *hChnlMgr; ++ struct MSG_MGR *hMsgMgr; ++ u32 ulShmBase; ++ u32 ulShmBaseOffset; ++ u32 ulShmLimit; ++ u32 ulShmLength = -1; ++ u32 ulMemLength = -1; ++ u32 ulMsgBase; ++ u32 ulMsgLimit; ++ u32 ulMsgLength = -1; ++ u32 ulExtEnd; ++ u32 ulGppPa = 0; ++ u32 ulGppVa = 0; ++ u32 ulDspVa = 0; ++ u32 ulSegSize = 0; ++ u32 ulPadSize = 0; ++ u32 i; ++ DSP_STATUS status = DSP_SOK; ++ u32 uNumProcs = 0; ++ s32 ndx = 0; ++ /* DSP MMU setup table */ ++ struct WMDIOCTL_EXTPROC aEProc[WMDIOCTL_NUMOFMMUTLB]; ++ struct CFG_HOSTRES hostRes; ++ u32 mapAttrs; ++ u32 ulShm0End; ++ u32 ulDynExtBase; ++ u32 ulSeg1Size = 0; ++ u32 paCurr = 0; ++ u32 vaCurr = 0; ++ u32 gppVaCurr = 0; ++ u32 numBytes = 0; ++ u32 allBits = 0; ++ u32 pgSize[] = { HW_PAGE_SIZE_16MB, HW_PAGE_SIZE_1MB, ++ HW_PAGE_SIZE_64KB, HW_PAGE_SIZE_4KB }; ++ ++ status = DEV_GetCodMgr(hIOMgr->hDevObject, &hCodMan); ++ DBC_Assert(DSP_SUCCEEDED(status)); ++ hChnlMgr = hIOMgr->hChnlMgr; ++ /* The message manager is destroyed when the board is stopped. */ ++ DEV_GetMsgMgr(hIOMgr->hDevObject, &hIOMgr->hMsgMgr); ++ hMsgMgr = hIOMgr->hMsgMgr; ++ DBC_Assert(MEM_IsValidHandle(hChnlMgr, CHNL_MGRSIGNATURE)); ++ DBC_Assert(MEM_IsValidHandle(hMsgMgr, MSGMGR_SIGNATURE)); ++ if (hIOMgr->pSharedMem) ++ hIOMgr->pSharedMem = NULL; ++ ++ /* Get start and length of channel part of shared memory */ ++ status = COD_GetSymValue(hCodMan, CHNL_SHARED_BUFFER_BASE_SYM, ++ &ulShmBase); ++ if (DSP_FAILED(status)) { ++ status = CHNL_E_NOMEMMAP; ++ goto func_cont1; ++ } ++ status = COD_GetSymValue(hCodMan, CHNL_SHARED_BUFFER_LIMIT_SYM, ++ &ulShmLimit); ++ if (DSP_FAILED(status)) { ++ status = CHNL_E_NOMEMMAP; ++ goto func_cont1; ++ } ++ if (ulShmLimit <= ulShmBase) { ++ status = CHNL_E_INVALIDMEMBASE; ++ } else { ++ /* get total length in bytes */ ++ ulShmLength = (ulShmLimit - ulShmBase + 1) * hIOMgr->uWordSize; ++ /* Calculate size of a PROCCOPY shared memory region */ ++ DBG_Trace(DBG_LEVEL7, ++ "**(proc)PROCCOPY SHMMEM SIZE: 0x%x bytes\n", ++ (ulShmLength - sizeof(struct SHM))); ++ } ++func_cont1: ++ if (DSP_SUCCEEDED(status)) { ++ /* Get start and length of message part of shared memory */ ++ status = COD_GetSymValue(hCodMan, MSG_SHARED_BUFFER_BASE_SYM, ++ &ulMsgBase); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = COD_GetSymValue(hCodMan, MSG_SHARED_BUFFER_LIMIT_SYM, ++ &ulMsgLimit); ++ if (DSP_SUCCEEDED(status)) { ++ if (ulMsgLimit <= ulMsgBase) { ++ status = CHNL_E_INVALIDMEMBASE; ++ } else { ++ /* Length (bytes) of messaging part of shared ++ * memory */ ++ ulMsgLength = (ulMsgLimit - ulMsgBase + 1) * ++ hIOMgr->uWordSize; ++ /* Total length (bytes) of shared memory: ++ * chnl + msg */ ++ ulMemLength = ulShmLength + ulMsgLength; ++ } ++ } else { ++ status = CHNL_E_NOMEMMAP; ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++#ifndef DSP_TRACEBUF_DISABLED ++ status = COD_GetSymValue(hCodMan, DSP_TRACESEC_END, &ulShm0End); ++ DBG_Trace(DBG_LEVEL7, "_BRIDGE_TRACE_END value = %x \n", ++ ulShm0End); ++#else ++ status = COD_GetSymValue(hCodMan, SHM0_SHARED_END_SYM, ++ &ulShm0End); ++ DBG_Trace(DBG_LEVEL7, "_SHM0_END = %x \n", ulShm0End); ++#endif ++ if (DSP_FAILED(status)) ++ status = CHNL_E_NOMEMMAP; ++ ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = COD_GetSymValue(hCodMan, DYNEXTBASE, &ulDynExtBase); ++ if (DSP_FAILED(status)) ++ status = CHNL_E_NOMEMMAP; ++ ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = COD_GetSymValue(hCodMan, EXTEND, &ulExtEnd); ++ if (DSP_FAILED(status)) ++ status = CHNL_E_NOMEMMAP; ++ ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Get memory reserved in host resources */ ++ (void)MGR_EnumProcessorInfo(0, ++ (struct DSP_PROCESSORINFO *)&hIOMgr->extProcInfo, ++ sizeof(struct MGR_PROCESSOREXTINFO), &uNumProcs); ++ CFG_GetHostResources(( ++ struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), ++ &hostRes); ++ /* The first MMU TLB entry(TLB_0) in DCD is ShmBase. */ ++ ndx = 0; ++ ulGppPa = hostRes.dwMemPhys[1]; ++ ulGppVa = hostRes.dwMemBase[1]; ++ /* THIS IS THE VIRTUAL UNCACHED IOREMAPPED ADDRESS !!! */ ++ /* Why can't we directly take the DSPVA from the symbols? */ ++ ulDspVa = hIOMgr->extProcInfo.tyTlb[0].ulDspVirt; ++ ulSegSize = (ulShm0End - ulDspVa) * hIOMgr->uWordSize; ++ ulSeg1Size = (ulExtEnd - ulDynExtBase) * hIOMgr->uWordSize; ++ ulSeg1Size = (ulSeg1Size + 0xFFF) & (~0xFFFUL); /* 4K align*/ ++ ulSegSize = (ulSegSize + 0xFFFF) & (~0xFFFFUL); /* 64K align*/ ++ ulPadSize = ulPageAlignSize - ((ulGppPa + ulSeg1Size) % ++ ulPageAlignSize); ++ if (ulPadSize == ulPageAlignSize) ++ ulPadSize = 0x0; ++ ++ DBG_Trace(DBG_LEVEL7, "ulGppPa %x, ulGppVa %x, ulDspVa %x, " ++ "ulShm0End %x, ulDynExtBase %x, ulExtEnd %x, " ++ "ulSegSize %x ulSeg1Size %x \n", ulGppPa, ulGppVa, ++ ulDspVa, ulShm0End, ulDynExtBase, ulExtEnd, ulSegSize, ++ ulSeg1Size); ++ ++ if ((ulSegSize + ulSeg1Size + ulPadSize) > ++ hostRes.dwMemLength[1]) { ++ DBG_Trace(DBG_LEVEL7, "ulGppPa %x, ulGppVa %x, ulDspVa " ++ "%x, ulShm0End %x, ulDynExtBase %x, ulExtEnd " ++ "%x, ulSegSize %x, ulSeg1Size %x \n", ulGppPa, ++ ulGppVa, ulDspVa, ulShm0End, ulDynExtBase, ++ ulExtEnd, ulSegSize, ulSeg1Size); ++ DBG_Trace(DBG_LEVEL7, "Insufficient SHM Reserved 0x%x. " ++ "Required 0x%x\n", hostRes.dwMemLength[1], ++ ulSegSize + ulSeg1Size + ulPadSize); ++ status = DSP_EMEMORY; ++ } ++ } ++ if (DSP_FAILED(status)) ++ goto func_cont; ++ ++ paCurr = ulGppPa; ++ vaCurr = ulDynExtBase * hIOMgr->uWordSize; ++ gppVaCurr = ulGppVa; ++ numBytes = ulSeg1Size; ++ ++ /* ++ * Try to fit into TLB entries. If not possible, push them to page ++ * tables. It is quite possible that if sections are not on ++ * bigger page boundary, we may end up making several small pages. ++ * So, push them onto page tables, if that is the case. ++ */ ++ mapAttrs = 0x00000000; ++ mapAttrs = DSP_MAPLITTLEENDIAN; ++ mapAttrs |= DSP_MAPPHYSICALADDR; ++ mapAttrs |= DSP_MAPELEMSIZE32; ++ mapAttrs |= DSP_MAPDONOTLOCK; ++ ++ while (numBytes && DSP_SUCCEEDED(status)) { ++ /* To find the max. page size with which both PA & VA are ++ * aligned */ ++ allBits = paCurr | vaCurr; ++ DBG_Trace(DBG_LEVEL1, "allBits %x, paCurr %x, vaCurr %x, " ++ "numBytes %x\n", allBits, paCurr, vaCurr, numBytes); ++ for (i = 0; i < 4; i++) { ++ if ((numBytes >= pgSize[i]) && ((allBits & ++ (pgSize[i] - 1)) == 0)) { ++ status = hIOMgr->pIntfFxns->pfnBrdMemMap ++ (hIOMgr->hWmdContext, paCurr, vaCurr, ++ pgSize[i], mapAttrs); ++ DBC_Assert(DSP_SUCCEEDED(status)); ++ paCurr += pgSize[i]; ++ vaCurr += pgSize[i]; ++ gppVaCurr += pgSize[i]; ++ numBytes -= pgSize[i]; ++ /* Don't try smaller sizes. Hopefully we have ++ * reached an address aligned to a bigger page ++ * size*/ ++ break; ++ } ++ } ++ } ++ paCurr += ulPadSize; ++ vaCurr += ulPadSize; ++ gppVaCurr += ulPadSize; ++ ++ /* configure the TLB entries for the next cacheable segment */ ++ numBytes = ulSegSize; ++ vaCurr = ulDspVa * hIOMgr->uWordSize; ++ allBits = 0x0; ++ while (numBytes && DSP_SUCCEEDED(status)) { ++ /* To find the max. page size with which both PA & VA are ++ * aligned*/ ++ allBits = paCurr | vaCurr; ++ DBG_Trace(DBG_LEVEL1, "allBits for Seg1 %x, paCurr %x, " ++ "vaCurr %x, numBytes %x\n", allBits, paCurr, vaCurr, ++ numBytes); ++ for (i = 0; i < 4; i++) { ++ if (!(numBytes >= pgSize[i]) || ++ !((allBits & (pgSize[i]-1)) == 0)) ++ continue; ++ if (ndx < MAX_LOCK_TLB_ENTRIES) { ++ /* This is the physical address written to ++ * DSP MMU */ ++ aEProc[ndx].ulGppPa = paCurr; ++ /* THIS IS THE VIRTUAL UNCACHED IOREMAPPED ++ * ADDRESS!!! */ ++ aEProc[ndx].ulGppVa = gppVaCurr; ++ aEProc[ndx].ulDspVa = vaCurr / hIOMgr-> ++ uWordSize; ++ aEProc[ndx].ulSize = pgSize[i]; ++ aEProc[ndx].endianism = HW_LITTLE_ENDIAN; ++ aEProc[ndx].elemSize = HW_ELEM_SIZE_16BIT; ++ aEProc[ndx].mixedMode = HW_MMU_CPUES; ++ DBG_Trace(DBG_LEVEL1, "SHM MMU TLB entry PA %lx" ++ " VA %lx DSP_VA %lx Size %lx\n", ++ aEProc[ndx].ulGppPa, ++ aEProc[ndx].ulGppVa, ++ aEProc[ndx].ulDspVa * ++ hIOMgr->uWordSize, pgSize[i]); ++ ndx++; ++ } else { ++ status = hIOMgr->pIntfFxns->pfnBrdMemMap( ++ hIOMgr->hWmdContext, paCurr, vaCurr, pgSize[i], ++ mapAttrs); ++ DBG_Trace(DBG_LEVEL1, "SHM MMU PTE entry PA %lx" ++ " VA %lx DSP_VA %lx Size %lx\n", ++ aEProc[ndx].ulGppPa, ++ aEProc[ndx].ulGppVa, ++ aEProc[ndx].ulDspVa * ++ hIOMgr->uWordSize, pgSize[i]); ++ DBC_Assert(DSP_SUCCEEDED(status)); ++ } ++ paCurr += pgSize[i]; ++ vaCurr += pgSize[i]; ++ gppVaCurr += pgSize[i]; ++ numBytes -= pgSize[i]; ++ /* Don't try smaller sizes. Hopefully we have reached ++ an address aligned to a bigger page size*/ ++ break; ++ } ++ } ++ ++ /* Copy remaining entries from CDB. All entries are 1 MB and should not ++ * conflict with SHM entries on MPU or DSP side */ ++ for (i = 3; i < 7 && ndx < WMDIOCTL_NUMOFMMUTLB && ++ DSP_SUCCEEDED(status); i++) { ++ if (hIOMgr->extProcInfo.tyTlb[i].ulGppPhys == 0) ++ continue; ++ ++ if ((hIOMgr->extProcInfo.tyTlb[i].ulGppPhys > ulGppPa - 0x100000 ++ && hIOMgr->extProcInfo.tyTlb[i].ulGppPhys <= ++ ulGppPa + ulSegSize) ++ || (hIOMgr->extProcInfo.tyTlb[i].ulDspVirt > ulDspVa - ++ 0x100000 / hIOMgr->uWordSize && hIOMgr-> ++ extProcInfo.tyTlb[i].ulDspVirt ++ <= ulDspVa + ulSegSize / hIOMgr->uWordSize)) { ++ DBG_Trace(DBG_LEVEL7, "CDB MMU entry %d conflicts with " ++ "SHM.\n\tCDB: GppPa %x, DspVa %x.\n\tSHM: " ++ "GppPa %x, DspVa %x, Bytes %x.\n", i, ++ hIOMgr->extProcInfo.tyTlb[i].ulGppPhys, ++ hIOMgr->extProcInfo.tyTlb[i].ulDspVirt, ++ ulGppPa, ulDspVa, ulSegSize); ++ status = DSP_EFAIL; ++ } else { ++ if (ndx < MAX_LOCK_TLB_ENTRIES) { ++ aEProc[ndx].ulDspVa = hIOMgr->extProcInfo. ++ tyTlb[i].ulDspVirt; ++ aEProc[ndx].ulGppPa = hIOMgr->extProcInfo. ++ tyTlb[i].ulGppPhys; ++ aEProc[ndx].ulGppVa = 0; ++ /* Can't convert, so set to zero*/ ++ aEProc[ndx].ulSize = 0x100000; /* 1 MB*/ ++ DBG_Trace(DBG_LEVEL1, "SHM MMU entry PA %x " ++ "DSP_VA 0x%x\n", aEProc[ndx].ulGppPa, ++ aEProc[ndx].ulDspVa); ++ ndx++; ++ } else { ++ status = hIOMgr->pIntfFxns->pfnBrdMemMap ++ (hIOMgr->hWmdContext, ++ hIOMgr->extProcInfo.tyTlb[i].ulGppPhys, ++ hIOMgr->extProcInfo.tyTlb[i].ulDspVirt, ++ 0x100000, mapAttrs); ++ } ++ } ++ } ++ if (i < 7 && DSP_SUCCEEDED(status)) { ++ /* All CDB entries could not be made*/ ++ status = DSP_EFAIL; ++ } ++func_cont: ++ mapAttrs = 0x00000000; ++ mapAttrs = DSP_MAPLITTLEENDIAN; ++ mapAttrs |= DSP_MAPPHYSICALADDR; ++ mapAttrs |= DSP_MAPELEMSIZE32; ++ mapAttrs |= DSP_MAPDONOTLOCK; ++ ++ /* Map the L4 peripherals */ ++ i = 0; ++ while (L4PeripheralTable[i].physAddr && DSP_SUCCEEDED(status)) { ++ status = hIOMgr->pIntfFxns->pfnBrdMemMap ++ (hIOMgr->hWmdContext, L4PeripheralTable[i].physAddr, ++ L4PeripheralTable[i].dspVirtAddr, HW_PAGE_SIZE_4KB, ++ mapAttrs); ++ if (DSP_FAILED(status)) ++ break; ++ i++; ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ for (i = ndx; i < WMDIOCTL_NUMOFMMUTLB; i++) { ++ aEProc[i].ulDspVa = 0; ++ aEProc[i].ulGppPa = 0; ++ aEProc[i].ulGppVa = 0; ++ aEProc[i].ulSize = 0; ++ } ++ /* Set the SHM physical address entry (grayed out in CDB file) ++ * to the virtual uncached ioremapped address of SHM reserved ++ * on MPU */ ++ hIOMgr->extProcInfo.tyTlb[0].ulGppPhys = (ulGppVa + ulSeg1Size + ++ ulPadSize); ++ DBG_Trace(DBG_LEVEL1, "*********extProcInfo *********%x \n", ++ hIOMgr->extProcInfo.tyTlb[0].ulGppPhys); ++ /* Need SHM Phys addr. IO supports only one DSP for now: ++ * uNumProcs=1 */ ++ if ((hIOMgr->extProcInfo.tyTlb[0].ulGppPhys == 0) || ++ (uNumProcs != 1)) { ++ status = CHNL_E_NOMEMMAP; ++ DBC_Assert(false); ++ } else { ++ DBC_Assert(aEProc[0].ulDspVa <= ulShmBase); ++ /* ulShmBase may not be at ulDspVa address */ ++ ulShmBaseOffset = (ulShmBase - aEProc[0].ulDspVa) * ++ hIOMgr->uWordSize; ++ /* WMD_BRD_Ctrl() will set dev context dsp-mmu info. In ++ * _BRD_Start() the MMU will be re-programed with MMU ++ * DSPVa-GPPPa pair info while DSP is in a known ++ * (reset) state. */ ++ DBC_Assert(hIOMgr->pIntfFxns != NULL); ++ DBC_Assert(hIOMgr->hWmdContext != NULL); ++ status = hIOMgr->pIntfFxns->pfnDevCntrl(hIOMgr-> ++ hWmdContext, WMDIOCTL_SETMMUCONFIG, aEProc); ++ ulShmBase = hIOMgr->extProcInfo.tyTlb[0].ulGppPhys; ++ DBG_Trace(DBG_LEVEL1, "extProcInfo.tyTlb[0].ulGppPhys " ++ "%x \n ", hIOMgr->extProcInfo.tyTlb[0]. ++ ulGppPhys); ++ ulShmBase += ulShmBaseOffset; ++ ulShmBase = (u32)MEM_LinearAddress((void *)ulShmBase, ++ ulMemLength); ++ DBC_Assert(ulShmBase != 0); ++ if (DSP_SUCCEEDED(status)) { ++ status = registerSHMSegs(hIOMgr, hCodMan, ++ aEProc[0].ulGppPa); ++ /* Register SM */ ++ } ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ hIOMgr->pSharedMem = (struct SHM *)ulShmBase; ++ hIOMgr->pInput = (u8 *)hIOMgr->pSharedMem + ++ sizeof(struct SHM); ++ hIOMgr->pOutput = hIOMgr->pInput + (ulShmLength - ++ sizeof(struct SHM))/2; ++ hIOMgr->uSMBufSize = hIOMgr->pOutput - hIOMgr->pInput; ++ DBG_Trace(DBG_LEVEL3, ++ "hIOMgr: pInput %p pOutput %p ulShmLength %x\n", ++ hIOMgr->pInput, hIOMgr->pOutput, ulShmLength); ++ DBG_Trace(DBG_LEVEL3, ++ "pSharedMem %p uSMBufSize %x sizeof(SHM) %x\n", ++ hIOMgr->pSharedMem, hIOMgr->uSMBufSize, ++ sizeof(struct SHM)); ++ /* Set up Shared memory addresses for messaging. */ ++ hIOMgr->pMsgInputCtrl = (struct MSG *)((u8 *) ++ hIOMgr->pSharedMem + ++ ulShmLength); ++ hIOMgr->pMsgInput = (u8 *)hIOMgr->pMsgInputCtrl + ++ sizeof(struct MSG); ++ hIOMgr->pMsgOutputCtrl = (struct MSG *)((u8 *)hIOMgr-> ++ pMsgInputCtrl + ulMsgLength / 2); ++ hIOMgr->pMsgOutput = (u8 *)hIOMgr->pMsgOutputCtrl + ++ sizeof(struct MSG); ++ hMsgMgr->uMaxMsgs = ((u8 *)hIOMgr->pMsgOutputCtrl - ++ hIOMgr->pMsgInput) / ++ sizeof(struct MSG_DSPMSG); ++ DBG_Trace(DBG_LEVEL7, "IO MGR SHM details : pSharedMem 0x%x, " ++ "pInput 0x%x, pOutput 0x%x, pMsgInputCtrl 0x%x, " ++ "pMsgInput 0x%x, pMsgOutputCtrl 0x%x, pMsgOutput " ++ "0x%x \n", (u8 *)hIOMgr->pSharedMem, ++ (u8 *)hIOMgr->pInput, (u8 *)hIOMgr->pOutput, ++ (u8 *)hIOMgr->pMsgInputCtrl, ++ (u8 *)hIOMgr->pMsgInput, ++ (u8 *)hIOMgr->pMsgOutputCtrl, ++ (u8 *)hIOMgr->pMsgOutput); ++ DBG_Trace(DBG_LEVEL7, "** (proc) MAX MSGS IN SHARED MEMORY: " ++ "0x%x\n", hMsgMgr->uMaxMsgs); ++ memset((void *) hIOMgr->pSharedMem, 0, sizeof(struct SHM)); ++ } ++#ifndef DSP_TRACEBUF_DISABLED ++ if (DSP_SUCCEEDED(status)) { ++ /* Get the start address of trace buffer */ ++ if (DSP_SUCCEEDED(status)) { ++ status = COD_GetSymValue(hCodMan, SYS_PUTCBEG, ++ &hIOMgr->ulTraceBufferBegin); ++ if (DSP_FAILED(status)) ++ status = CHNL_E_NOMEMMAP; ++ ++ } ++ hIOMgr->ulGPPReadPointer = hIOMgr->ulTraceBufferBegin = ++ (ulGppVa + ulSeg1Size + ulPadSize) + ++ (hIOMgr->ulTraceBufferBegin - ulDspVa); ++ /* Get the end address of trace buffer */ ++ if (DSP_SUCCEEDED(status)) { ++ status = COD_GetSymValue(hCodMan, SYS_PUTCEND, ++ &hIOMgr->ulTraceBufferEnd); ++ if (DSP_FAILED(status)) ++ status = CHNL_E_NOMEMMAP; ++ ++ } ++ hIOMgr->ulTraceBufferEnd = (ulGppVa + ulSeg1Size + ulPadSize) + ++ (hIOMgr->ulTraceBufferEnd - ulDspVa); ++ /* Get the current address of DSP write pointer */ ++ if (DSP_SUCCEEDED(status)) { ++ status = COD_GetSymValue(hCodMan, ++ BRIDGE_SYS_PUTC_current, ++ &hIOMgr->ulTraceBufferCurrent); ++ if (DSP_FAILED(status)) ++ status = CHNL_E_NOMEMMAP; ++ ++ } ++ hIOMgr->ulTraceBufferCurrent = (ulGppVa + ulSeg1Size + ++ ulPadSize) + (hIOMgr-> ++ ulTraceBufferCurrent - ulDspVa); ++ /* Calculate the size of trace buffer */ ++ if (hIOMgr->pMsg) ++ MEM_Free(hIOMgr->pMsg); ++ hIOMgr->pMsg = MEM_Alloc(((hIOMgr->ulTraceBufferEnd - ++ hIOMgr->ulTraceBufferBegin) * ++ hIOMgr->uWordSize) + 2, MEM_NONPAGED); ++ if (!hIOMgr->pMsg) ++ status = DSP_EMEMORY; ++ ++ DBG_Trace(DBG_LEVEL1, "** hIOMgr->pMsg: 0x%x\n", hIOMgr->pMsg); ++ hIOMgr->ulDspVa = ulDspVa; ++ hIOMgr->ulGppVa = (ulGppVa + ulSeg1Size + ulPadSize); ++ } ++#endif ++ IO_EnableInterrupt(hIOMgr->hWmdContext); ++ return status; ++} ++ ++/* ++ * ======== IO_BufSize ======== ++ * Size of shared memory I/O channel. ++ */ ++u32 IO_BufSize(struct IO_MGR *hIOMgr) ++{ ++ DBC_Require(MEM_IsValidHandle(hIOMgr, IO_MGRSIGNATURE)); ++ ++ return hIOMgr->uSMBufSize; ++} ++ ++/* ++ * ======== IO_CancelChnl ======== ++ * Cancel IO on a given PCPY channel. ++ */ ++void IO_CancelChnl(struct IO_MGR *hIOMgr, u32 ulChnl) ++{ ++ struct IO_MGR *pIOMgr = (struct IO_MGR *)hIOMgr; ++ struct SHM *sm; ++ ++ DBC_Require(MEM_IsValidHandle(hIOMgr, IO_MGRSIGNATURE)); ++ sm = hIOMgr->pSharedMem; ++ ++ /* Inform DSP that we have no more buffers on this channel: */ ++ IO_AndValue(pIOMgr->hWmdContext, struct SHM, sm, hostFreeMask, ++ (~(1 << ulChnl))); ++ ++ CHNLSM_InterruptDSP2(pIOMgr->hWmdContext, MBX_PCPY_CLASS); ++} ++ ++/* ++ * ======== IO_DispatchChnl ======== ++ * Proc-copy chanl dispatch. ++ */ ++static void IO_DispatchChnl(IN struct IO_MGR *pIOMgr, ++ IN OUT struct CHNL_OBJECT *pChnl, u32 iMode) ++{ ++ DBC_Require(MEM_IsValidHandle(pIOMgr, IO_MGRSIGNATURE)); ++ ++ DBG_Trace(DBG_LEVEL3, "Entering IO_DispatchChnl \n"); ++ ++ /* See if there is any data available for transfer: */ ++ DBC_Assert(iMode == IO_SERVICE); ++ ++ /* Any channel will do for this mode: */ ++ InputChnl(pIOMgr, pChnl, iMode); ++ OutputChnl(pIOMgr, pChnl, iMode); ++} ++ ++/* ++ * ======== IO_DispatchMsg ======== ++ * Performs I/O dispatch on message queues. ++ */ ++static void IO_DispatchMsg(IN struct IO_MGR *pIOMgr, struct MSG_MGR *hMsgMgr) ++{ ++ DBC_Require(MEM_IsValidHandle(pIOMgr, IO_MGRSIGNATURE)); ++ ++ DBG_Trace(DBG_LEVEL3, "Entering IO_DispatchMsg \n"); ++ ++ /* We are performing both input and output processing. */ ++ InputMsg(pIOMgr, hMsgMgr); ++ OutputMsg(pIOMgr, hMsgMgr); ++} ++ ++/* ++ * ======== IO_DispatchPM ======== ++ * Performs I/O dispatch on PM related messages from DSP ++ */ ++static void IO_DispatchPM(struct work_struct *work) ++{ ++ struct IO_MGR *pIOMgr = ++ container_of(work, struct IO_MGR, io_workq); ++ DSP_STATUS status; ++ u32 pArg[2]; ++ ++ /*DBC_Require(MEM_IsValidHandle(pIOMgr, IO_MGRSIGNATURE));*/ ++ ++ DBG_Trace(DBG_LEVEL7, "IO_DispatchPM: Entering IO_DispatchPM : \n"); ++ ++ /* Perform Power message processing here */ ++ while (pIOMgr->iQuePowerHead != pIOMgr->iQuePowerTail) { ++ pArg[0] = *(u32 *)&(pIOMgr->dQuePowerMbxVal[pIOMgr-> ++ iQuePowerTail]); ++ DBG_Trace(DBG_LEVEL7, "IO_DispatchPM - pArg[0] - 0x%x: \n", ++ pArg[0]); ++ /* Send the command to the WMD clk/pwr manager to handle */ ++ if (pArg[0] == MBX_PM_HIBERNATE_EN) { ++ DBG_Trace(DBG_LEVEL7, "IO_DispatchPM : Hibernate " ++ "command\n"); ++ status = pIOMgr->pIntfFxns->pfnDevCntrl(pIOMgr-> ++ hWmdContext, WMDIOCTL_PWR_HIBERNATE, pArg); ++ if (DSP_FAILED(status)) { ++ DBG_Trace(DBG_LEVEL7, "IO_DispatchPM : " ++ "Hibernation command failed\n"); ++ } ++ } else if (pArg[0] == MBX_PM_OPP_REQ) { ++ pArg[1] = pIOMgr->pSharedMem->oppRequest.rqstOppPt; ++ DBG_Trace(DBG_LEVEL7, "IO_DispatchPM : Value of OPP " ++ "value =0x%x \n", pArg[1]); ++ status = pIOMgr->pIntfFxns->pfnDevCntrl(pIOMgr-> ++ hWmdContext, WMDIOCTL_CONSTRAINT_REQUEST, ++ pArg); ++ if (DSP_FAILED(status)) { ++ DBG_Trace(DBG_LEVEL7, "IO_DispatchPM : Failed " ++ "to set constraint = 0x%x \n", ++ pArg[1]); ++ } ++ ++ } else { ++ DBG_Trace(DBG_LEVEL7, "IO_DispatchPM - clock control - " ++ "value of msg = 0x%x: \n", pArg[0]); ++ status = pIOMgr->pIntfFxns->pfnDevCntrl(pIOMgr-> ++ hWmdContext, WMDIOCTL_CLK_CTRL, pArg); ++ if (DSP_FAILED(status)) { ++ DBG_Trace(DBG_LEVEL7, "IO_DispatchPM : Failed " ++ "to control the DSP clk = 0x%x \n", ++ *pArg); ++ } ++ } ++ /* increment the tail count here */ ++ pIOMgr->iQuePowerTail++; ++ if (pIOMgr->iQuePowerTail >= MAX_PM_REQS) ++ pIOMgr->iQuePowerTail = 0; ++ ++ } ++ ++} ++ ++/* ++ * ======== IO_DPC ======== ++ * Deferred procedure call for shared memory channel driver ISR. Carries ++ * out the dispatch of I/O as a non-preemptible event.It can only be ++ * pre-empted by an ISR. ++ */ ++void IO_DPC(IN OUT void *pRefData) ++{ ++ struct IO_MGR *pIOMgr = (struct IO_MGR *)pRefData; ++ struct CHNL_MGR *pChnlMgr; ++ struct MSG_MGR *pMsgMgr; ++ struct DEH_MGR *hDehMgr; ++ ++ DBC_Require(MEM_IsValidHandle(pIOMgr, IO_MGRSIGNATURE)); ++ pChnlMgr = pIOMgr->hChnlMgr; ++ DEV_GetMsgMgr(pIOMgr->hDevObject, &pMsgMgr); ++ DEV_GetDehMgr(pIOMgr->hDevObject, &hDehMgr); ++ DBC_Require(MEM_IsValidHandle(pChnlMgr, CHNL_MGRSIGNATURE)); ++ DBG_Trace(DBG_LEVEL7, "Entering IO_DPC(0x%x)\n", pRefData); ++ /* Check value of interrupt register to ensure it is a valid error */ ++ if ((pIOMgr->wIntrVal > DEH_BASE) && (pIOMgr->wIntrVal < DEH_LIMIT)) { ++ /* notify DSP/BIOS exception */ ++ if (hDehMgr) ++ WMD_DEH_Notify(hDehMgr, DSP_SYSERROR, pIOMgr->wIntrVal); ++ ++ } ++ IO_DispatchChnl(pIOMgr, NULL, IO_SERVICE); ++#ifdef CHNL_MESSAGES ++ if (pMsgMgr) { ++ DBC_Require(MEM_IsValidHandle(pMsgMgr, MSGMGR_SIGNATURE)); ++ IO_DispatchMsg(pIOMgr, pMsgMgr); ++ } ++#endif ++#ifndef DSP_TRACEBUF_DISABLED ++ if (pIOMgr->wIntrVal & MBX_DBG_CLASS) { ++ /* notify DSP Trace message */ ++ if (pIOMgr->wIntrVal & MBX_DBG_SYSPRINTF) ++ PrintDSPDebugTrace(pIOMgr); ++ } ++#endif ++ ++#ifndef DSP_TRACEBUF_DISABLED ++ PrintDSPDebugTrace(pIOMgr); ++#endif ++} ++ ++ ++/* ++ * ======== IO_ISR ======== ++ * Main interrupt handler for the shared memory IO manager. ++ * Calls the WMD's CHNL_ISR to determine if this interrupt is ours, then ++ * schedules a DPC to dispatch I/O. ++ */ ++irqreturn_t IO_ISR(int irq, IN void *pRefData) ++{ ++ struct IO_MGR *hIOMgr = (struct IO_MGR *)pRefData; ++ bool fSchedDPC; ++ DBC_Require(irq == INT_MAIL_MPU_IRQ); ++ DBC_Require(MEM_IsValidHandle(hIOMgr, IO_MGRSIGNATURE)); ++ DBG_Trace(DBG_LEVEL3, "Entering IO_ISR(0x%x)\n", pRefData); ++ ++ /* Call WMD's CHNLSM_ISR() to see if interrupt is ours, and process. */ ++ if (IO_CALLISR(hIOMgr->hWmdContext, &fSchedDPC, &hIOMgr->wIntrVal)) { ++ { ++ DBG_Trace(DBG_LEVEL3, "IO_ISR %x\n", hIOMgr->wIntrVal); ++ if (hIOMgr->wIntrVal & MBX_PM_CLASS) { ++ hIOMgr->dQuePowerMbxVal[hIOMgr->iQuePowerHead] = ++ hIOMgr->wIntrVal; ++ hIOMgr->iQuePowerHead++; ++ if (hIOMgr->iQuePowerHead >= MAX_PM_REQS) ++ hIOMgr->iQuePowerHead = 0; ++ ++ queue_work(bridge_workqueue, &hIOMgr->io_workq); ++ } ++ if (hIOMgr->wIntrVal == MBX_DEH_RESET) { ++ DBG_Trace(DBG_LEVEL6, "*** DSP RESET ***\n"); ++ hIOMgr->wIntrVal = 0; ++ } else if (fSchedDPC) { ++ /* PROC-COPY defer i/o */ ++ DPC_Schedule(hIOMgr->hDPC); ++ } ++ } ++ } else ++ /* Ensure that, if WMD didn't claim it, the IRQ is shared. */ ++ DBC_Ensure(hIOMgr->fSharedIRQ); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * ======== IO_RequestChnl ======== ++ * Purpose: ++ * Request chanenel I/O from the DSP. Sets flags in shared memory, then ++ * interrupts the DSP. ++ */ ++void IO_RequestChnl(struct IO_MGR *pIOMgr, struct CHNL_OBJECT *pChnl, ++ u32 iMode, OUT u16 *pwMbVal) ++{ ++ struct CHNL_MGR *pChnlMgr; ++ struct SHM *sm; ++ DBC_Require(pChnl != NULL); ++ DBC_Require(pwMbVal != NULL); ++ pChnlMgr = pIOMgr->hChnlMgr; ++ sm = pIOMgr->pSharedMem; ++ if (iMode == IO_INPUT) { ++ /* Assertion fires if CHNL_AddIOReq() called on a stream ++ * which was cancelled, or attached to a dead board: */ ++ DBC_Assert((pChnl->dwState == CHNL_STATEREADY) || ++ (pChnl->dwState == CHNL_STATEEOS)); ++ /* Indicate to the DSP we have a buffer available for input: */ ++ IO_OrValue(pIOMgr->hWmdContext, struct SHM, sm, hostFreeMask, ++ (1 << pChnl->uId)); ++ *pwMbVal = MBX_PCPY_CLASS; ++ } else if (iMode == IO_OUTPUT) { ++ /* This assertion fails if CHNL_AddIOReq() was called on a ++ * stream which was cancelled, or attached to a dead board: */ ++ DBC_Assert((pChnl->dwState & ~CHNL_STATEEOS) == ++ CHNL_STATEREADY); ++ /* Record the fact that we have a buffer available for ++ * output: */ ++ pChnlMgr->dwOutputMask |= (1 << pChnl->uId); ++ } else { ++ DBC_Assert(iMode); /* Shouldn't get here. */ ++ } ++} ++ ++/* ++ * ======== IO_Schedule ======== ++ * Schedule DPC for IO. ++ */ ++void IO_Schedule(struct IO_MGR *pIOMgr) ++{ ++ DBC_Require(MEM_IsValidHandle(pIOMgr, IO_MGRSIGNATURE)); ++ ++ DPC_Schedule(pIOMgr->hDPC); ++} ++ ++/* ++ * ======== FindReadyOutput ======== ++ * Search for a host output channel which is ready to send. If this is ++ * called as a result of servicing the DPC, then implement a round ++ * robin search; otherwise, this was called by a client thread (via ++ * IO_Dispatch()), so just start searching from the current channel id. ++ */ ++static u32 FindReadyOutput(struct CHNL_MGR *pChnlMgr, ++ struct CHNL_OBJECT *pChnl, u32 dwMask) ++{ ++ u32 uRetval = OUTPUTNOTREADY; ++ u32 id, startId; ++ u32 shift; ++ ++ id = (pChnl != NULL ? pChnl->uId : (pChnlMgr->dwLastOutput + 1)); ++ id = ((id == CHNL_MAXCHANNELS) ? 0 : id); ++ DBC_Assert(id < CHNL_MAXCHANNELS); ++ if (dwMask) { ++ shift = (1 << id); ++ startId = id; ++ do { ++ if (dwMask & shift) { ++ uRetval = id; ++ if (pChnl == NULL) ++ pChnlMgr->dwLastOutput = id; ++ ++ break; ++ } ++ id = id + 1; ++ id = ((id == CHNL_MAXCHANNELS) ? 0 : id); ++ shift = (1 << id); ++ } while (id != startId); ++ } ++ DBC_Ensure((uRetval == OUTPUTNOTREADY) || (uRetval < CHNL_MAXCHANNELS)); ++ return uRetval; ++} ++ ++/* ++ * ======== InputChnl ======== ++ * Dispatch a buffer on an input channel. ++ */ ++static void InputChnl(struct IO_MGR *pIOMgr, struct CHNL_OBJECT *pChnl, ++ u32 iMode) ++{ ++ struct CHNL_MGR *pChnlMgr; ++ struct SHM *sm; ++ u32 chnlId; ++ u32 uBytes; ++ struct CHNL_IRP *pChirp = NULL; ++ u32 dwArg; ++ bool fClearChnl = false; ++ bool fNotifyClient = false; ++ ++ sm = pIOMgr->pSharedMem; ++ pChnlMgr = pIOMgr->hChnlMgr; ++ ++ DBG_Trace(DBG_LEVEL3, "> InputChnl\n"); ++ ++ /* Attempt to perform input.... */ ++ if (!IO_GetValue(pIOMgr->hWmdContext, struct SHM, sm, inputFull)) ++ goto func_end; ++ ++ uBytes = IO_GetValue(pIOMgr->hWmdContext, struct SHM, sm, inputSize) * ++ pChnlMgr->uWordSize; ++ chnlId = IO_GetValue(pIOMgr->hWmdContext, struct SHM, sm, inputId); ++ dwArg = IO_GetLong(pIOMgr->hWmdContext, struct SHM, sm, arg); ++ if (!(chnlId >= 0) || !(chnlId < CHNL_MAXCHANNELS)) { ++ /* Shouldn't be here: would indicate corrupted SHM. */ ++ DBC_Assert(chnlId); ++ goto func_end; ++ } ++ pChnl = pChnlMgr->apChannel[chnlId]; ++ if ((pChnl != NULL) && CHNL_IsInput(pChnl->uMode)) { ++ if ((pChnl->dwState & ~CHNL_STATEEOS) == CHNL_STATEREADY) { ++ if (!pChnl->pIORequests) ++ goto func_end; ++ /* Get the I/O request, and attempt a transfer: */ ++ pChirp = (struct CHNL_IRP *)LST_GetHead(pChnl-> ++ pIORequests); ++ if (pChirp) { ++ pChnl->cIOReqs--; ++ DBC_Assert(pChnl->cIOReqs >= 0); ++ /* Ensure we don't overflow the client's ++ * buffer: */ ++ uBytes = min(uBytes, pChirp->cBytes); ++ /* Transfer buffer from DSP side: */ ++ uBytes = ReadData(pIOMgr->hWmdContext, ++ pChirp->pHostSysBuf, ++ pIOMgr->pInput, uBytes); ++ pChnl->cBytesMoved += uBytes; ++ pChirp->cBytes = uBytes; ++ pChirp->dwArg = dwArg; ++ pChirp->status = CHNL_IOCSTATCOMPLETE; ++ DBG_Trace(DBG_LEVEL7, "Input Chnl:status= 0x%x " ++ "\n", *((RMS_WORD *)(pChirp-> ++ pHostSysBuf))); ++ if (uBytes == 0) { ++ /* This assertion fails if the DSP ++ * sends EOS more than once on this ++ * channel: */ ++ DBC_Assert(!(pChnl->dwState & ++ CHNL_STATEEOS)); ++ /* Zero bytes indicates EOS. Update ++ * IOC status for this chirp, and also ++ * the channel state: */ ++ pChirp->status |= CHNL_IOCSTATEOS; ++ pChnl->dwState |= CHNL_STATEEOS; ++ /* Notify that end of stream has ++ * occurred */ ++ NTFY_Notify(pChnl->hNtfy, ++ DSP_STREAMDONE); ++ DBG_Trace(DBG_LEVEL7, "Input Chnl NTFY " ++ "chnl = 0x%x\n", pChnl); ++ } ++ /* Tell DSP if no more I/O buffers available: */ ++ if (!pChnl->pIORequests) ++ goto func_end; ++ if (LST_IsEmpty(pChnl->pIORequests)) { ++ IO_AndValue(pIOMgr->hWmdContext, ++ struct SHM, sm, hostFreeMask, ++ ~(1 << pChnl->uId)); ++ } ++ fClearChnl = true; ++ fNotifyClient = true; ++ } else { ++ /* Input full for this channel, but we have no ++ * buffers available. The channel must be ++ * "idling". Clear out the physical input ++ * channel. */ ++ fClearChnl = true; ++ } ++ } else { ++ /* Input channel cancelled: clear input channel. */ ++ fClearChnl = true; ++ } ++ } else { ++ /* DPC fired after host closed channel: clear input channel. */ ++ fClearChnl = true; ++ } ++ if (fClearChnl) { ++ /* Indicate to the DSP we have read the input: */ ++ IO_SetValue(pIOMgr->hWmdContext, struct SHM, sm, inputFull, 0); ++ CHNLSM_InterruptDSP2(pIOMgr->hWmdContext, MBX_PCPY_CLASS); ++ } ++ if (fNotifyClient) { ++ /* Notify client with IO completion record: */ ++ NotifyChnlComplete(pChnl, pChirp); ++ } ++func_end: ++ DBG_Trace(DBG_LEVEL3, "< InputChnl\n"); ++} ++ ++/* ++ * ======== InputMsg ======== ++ * Copies messages from shared memory to the message queues. ++ */ ++static void InputMsg(struct IO_MGR *pIOMgr, struct MSG_MGR *hMsgMgr) ++{ ++ u32 uMsgs; ++ u32 i; ++ u8 *pMsgInput; ++ struct MSG_QUEUE *hMsgQueue; ++ struct MSG_FRAME *pMsg; ++ struct MSG_DSPMSG msg; ++ struct MSG *pCtrl; ++ u32 fInputEmpty; ++ u32 addr; ++ ++ pCtrl = pIOMgr->pMsgInputCtrl; ++ /* Get the number of input messages to be read. */ ++ fInputEmpty = IO_GetValue(pIOMgr->hWmdContext, struct MSG, pCtrl, ++ bufEmpty); ++ uMsgs = IO_GetValue(pIOMgr->hWmdContext, struct MSG, pCtrl, size); ++ if (fInputEmpty || uMsgs >= hMsgMgr->uMaxMsgs) ++ return; ++ ++ pMsgInput = pIOMgr->pMsgInput; ++ for (i = 0; i < uMsgs; i++) { ++ /* Read the next message */ ++ addr = (u32)&(((struct MSG_DSPMSG *)pMsgInput)->msg.dwCmd); ++ msg.msg.dwCmd = ReadExt32BitDspData(pIOMgr->hWmdContext, addr); ++ addr = (u32)&(((struct MSG_DSPMSG *)pMsgInput)->msg.dwArg1); ++ msg.msg.dwArg1 = ReadExt32BitDspData(pIOMgr->hWmdContext, addr); ++ addr = (u32)&(((struct MSG_DSPMSG *)pMsgInput)->msg.dwArg2); ++ msg.msg.dwArg2 = ReadExt32BitDspData(pIOMgr->hWmdContext, addr); ++ addr = (u32)&(((struct MSG_DSPMSG *)pMsgInput)->dwId); ++ msg.dwId = ReadExt32BitDspData(pIOMgr->hWmdContext, addr); ++ pMsgInput += sizeof(struct MSG_DSPMSG); ++ if (!hMsgMgr->queueList) ++ goto func_end; ++ ++ /* Determine which queue to put the message in */ ++ hMsgQueue = (struct MSG_QUEUE *)LST_First(hMsgMgr->queueList); ++ DBG_Trace(DBG_LEVEL7, "InputMsg RECVD: dwCmd=0x%x dwArg1=0x%x " ++ "dwArg2=0x%x dwId=0x%x \n", msg.msg.dwCmd, ++ msg.msg.dwArg1, msg.msg.dwArg2, msg.dwId); ++ /* Interrupt may occur before shared memory and message ++ * input locations have been set up. If all nodes were ++ * cleaned up, hMsgMgr->uMaxMsgs should be 0. */ ++ if (hMsgQueue && uMsgs > hMsgMgr->uMaxMsgs) ++ goto func_end; ++ ++ while (hMsgQueue != NULL) { ++ if (msg.dwId == hMsgQueue->dwId) { ++ /* Found it */ ++ if (msg.msg.dwCmd == RMS_EXITACK) { ++ /* The exit message does not get ++ * queued */ ++ /* Call the node exit notification */ ++ /* Node handle */ /* status */ ++ (*hMsgMgr->onExit)((HANDLE)hMsgQueue-> ++ hArg, msg.msg.dwArg1); ++ } else { ++ /* Not an exit acknowledgement, queue ++ * the message */ ++ if (!hMsgQueue->msgFreeList) ++ goto func_end; ++ pMsg = (struct MSG_FRAME *)LST_GetHead ++ (hMsgQueue->msgFreeList); ++ if (hMsgQueue->msgUsedList && pMsg) { ++ pMsg->msgData = msg; ++ LST_PutTail(hMsgQueue-> ++ msgUsedList, ++ (struct LST_ELEM *)pMsg); ++ NTFY_Notify(hMsgQueue->hNtfy, ++ DSP_NODEMESSAGEREADY); ++ SYNC_SetEvent(hMsgQueue-> ++ hSyncEvent); ++ } else { ++ /* No free frame to copy the ++ * message into */ ++ DBG_Trace(DBG_LEVEL7, "NO FREE " ++ "MSG FRAMES, DISCARDING" ++ " MESSAGE\n"); ++ } ++ } ++ break; ++ } ++ ++ if (!hMsgMgr->queueList || !hMsgQueue) ++ goto func_end; ++ hMsgQueue = (struct MSG_QUEUE *)LST_Next(hMsgMgr-> ++ queueList, (struct LST_ELEM *)hMsgQueue); ++ } ++ } ++ /* Set the post SWI flag */ ++ if (uMsgs > 0) { ++ /* Tell the DSP we've read the messages */ ++ IO_SetValue(pIOMgr->hWmdContext, struct MSG, pCtrl, bufEmpty, ++ true); ++ IO_SetValue(pIOMgr->hWmdContext, struct MSG, pCtrl, postSWI, ++ true); ++ CHNLSM_InterruptDSP2(pIOMgr->hWmdContext, MBX_PCPY_CLASS); ++ } ++func_end: ++ return; ++ ++} ++ ++/* ++ * ======== NotifyChnlComplete ======== ++ * Purpose: ++ * Signal the channel event, notifying the client that I/O has completed. ++ */ ++static void NotifyChnlComplete(struct CHNL_OBJECT *pChnl, ++ struct CHNL_IRP *pChirp) ++{ ++ bool fSignalEvent; ++ ++ DBC_Require(MEM_IsValidHandle(pChnl, CHNL_SIGNATURE)); ++ DBC_Require(pChnl->hSyncEvent != NULL); ++ /* Note: we signal the channel event only if the queue of IO ++ * completions is empty. If it is not empty, the event is sure to be ++ * signalled by the only IO completion list consumer: ++ * WMD_CHNL_GetIOC(). */ ++ fSignalEvent = LST_IsEmpty(pChnl->pIOCompletions); ++ /* Enqueue the IO completion info for the client: */ ++ LST_PutTail(pChnl->pIOCompletions, (struct LST_ELEM *) pChirp); ++ pChnl->cIOCs++; ++ DBC_Assert(pChnl->cIOCs <= pChnl->cChirps); ++ /* Signal the channel event (if not already set) that IO is complete: */ ++ if (fSignalEvent) ++ SYNC_SetEvent(pChnl->hSyncEvent); ++ ++ /* Notify that IO is complete */ ++ NTFY_Notify(pChnl->hNtfy, DSP_STREAMIOCOMPLETION); ++} ++ ++/* ++ * ======== OutputChnl ======== ++ * Purpose: ++ * Dispatch a buffer on an output channel. ++ */ ++static void OutputChnl(struct IO_MGR *pIOMgr, struct CHNL_OBJECT *pChnl, ++ u32 iMode) ++{ ++ struct CHNL_MGR *pChnlMgr; ++ struct SHM *sm; ++ u32 chnlId; ++ struct CHNL_IRP *pChirp; ++ u32 dwDspFMask; ++ ++ pChnlMgr = pIOMgr->hChnlMgr; ++ sm = pIOMgr->pSharedMem; ++ DBG_Trace(DBG_LEVEL3, "> OutputChnl\n"); ++ /* Attempt to perform output: */ ++ if (IO_GetValue(pIOMgr->hWmdContext, struct SHM, sm, outputFull)) ++ goto func_end; ++ ++ if (pChnl && !((pChnl->dwState & ~CHNL_STATEEOS) == CHNL_STATEREADY)) ++ goto func_end; ++ ++ /* Look to see if both a PC and DSP output channel are ready: */ ++ dwDspFMask = IO_GetValue(pIOMgr->hWmdContext, struct SHM, sm, ++ dspFreeMask); ++ chnlId = FindReadyOutput(pChnlMgr, pChnl, (pChnlMgr->dwOutputMask & ++ dwDspFMask)); ++ if (chnlId == OUTPUTNOTREADY) ++ goto func_end; ++ ++ pChnl = pChnlMgr->apChannel[chnlId]; ++ if (!pChnl || !pChnl->pIORequests) { ++ /* Shouldn't get here: */ ++ goto func_end; ++ } ++ /* Get the I/O request, and attempt a transfer: */ ++ pChirp = (struct CHNL_IRP *)LST_GetHead(pChnl->pIORequests); ++ if (!pChirp) ++ goto func_end; ++ ++ pChnl->cIOReqs--; ++ if (pChnl->cIOReqs < 0 || !pChnl->pIORequests) ++ goto func_end; ++ ++ /* Record fact that no more I/O buffers available: */ ++ if (LST_IsEmpty(pChnl->pIORequests)) ++ pChnlMgr->dwOutputMask &= ~(1 << chnlId); ++ ++ /* Transfer buffer to DSP side: */ ++ pChirp->cBytes = WriteData(pIOMgr->hWmdContext, pIOMgr->pOutput, ++ pChirp->pHostSysBuf, min(pIOMgr->uSMBufSize, pChirp-> ++ cBytes)); ++ pChnl->cBytesMoved += pChirp->cBytes; ++ /* Write all 32 bits of arg */ ++ IO_SetLong(pIOMgr->hWmdContext, struct SHM, sm, arg, pChirp->dwArg); ++#if _CHNL_WORDSIZE == 2 ++ IO_SetValue(pIOMgr->hWmdContext, struct SHM, sm, outputId, ++ (u16)chnlId); ++ IO_SetValue(pIOMgr->hWmdContext, struct SHM, sm, outputSize, ++ (u16)(pChirp->cBytes + (pChnlMgr->uWordSize-1)) / ++ (u16)pChnlMgr->uWordSize); ++#else ++ IO_SetValue(pIOMgr->hWmdContext, struct SHM, sm, outputId, chnlId); ++ IO_SetValue(pIOMgr->hWmdContext, struct SHM, sm, outputSize, ++ (pChirp->cBytes + (pChnlMgr->uWordSize - 1)) / pChnlMgr-> ++ uWordSize); ++#endif ++ IO_SetValue(pIOMgr->hWmdContext, struct SHM, sm, outputFull, 1); ++ /* Indicate to the DSP we have written the output: */ ++ CHNLSM_InterruptDSP2(pIOMgr->hWmdContext, MBX_PCPY_CLASS); ++ /* Notify client with IO completion record (keep EOS) */ ++ pChirp->status &= CHNL_IOCSTATEOS; ++ NotifyChnlComplete(pChnl, pChirp); ++ /* Notify if stream is done. */ ++ if (pChirp->status & CHNL_IOCSTATEOS) ++ NTFY_Notify(pChnl->hNtfy, DSP_STREAMDONE); ++ ++func_end: ++ DBG_Trace(DBG_LEVEL3, "< OutputChnl\n"); ++} ++/* ++ * ======== OutputMsg ======== ++ * Copies messages from the message queues to the shared memory. ++ */ ++static void OutputMsg(struct IO_MGR *pIOMgr, struct MSG_MGR *hMsgMgr) ++{ ++ u32 uMsgs = 0; ++ u32 i; ++ u8 *pMsgOutput; ++ struct MSG_FRAME *pMsg; ++ struct MSG *pCtrl; ++ u32 fOutputEmpty; ++ u32 val; ++ u32 addr; ++ ++ pCtrl = pIOMgr->pMsgOutputCtrl; ++ ++ /* Check if output has been cleared */ ++ fOutputEmpty = IO_GetValue(pIOMgr->hWmdContext, struct MSG, pCtrl, ++ bufEmpty); ++ if (fOutputEmpty) { ++ uMsgs = (hMsgMgr->uMsgsPending > hMsgMgr->uMaxMsgs) ? ++ hMsgMgr->uMaxMsgs : hMsgMgr->uMsgsPending; ++ pMsgOutput = pIOMgr->pMsgOutput; ++ /* Copy uMsgs messages into shared memory */ ++ for (i = 0; i < uMsgs; i++) { ++ if (!hMsgMgr->msgUsedList) { ++ DBG_Trace(DBG_LEVEL3, "msgUsedList is NULL\n"); ++ pMsg = NULL; ++ goto func_end; ++ } else ++ pMsg = (struct MSG_FRAME *)LST_GetHead( ++ hMsgMgr->msgUsedList); ++ if (pMsg != NULL) { ++ val = (pMsg->msgData).dwId; ++ addr = (u32)&(((struct MSG_DSPMSG *) ++ pMsgOutput)->dwId); ++ WriteExt32BitDspData(pIOMgr->hWmdContext, addr, ++ val); ++ val = (pMsg->msgData).msg.dwCmd; ++ addr = (u32)&((((struct MSG_DSPMSG *) ++ pMsgOutput)->msg).dwCmd); ++ WriteExt32BitDspData(pIOMgr->hWmdContext, addr, ++ val); ++ val = (pMsg->msgData).msg.dwArg1; ++ addr = ++ (u32)&((((struct MSG_DSPMSG *) ++ pMsgOutput)->msg).dwArg1); ++ WriteExt32BitDspData(pIOMgr->hWmdContext, addr, ++ val); ++ val = (pMsg->msgData).msg.dwArg2; ++ addr = ++ (u32)&((((struct MSG_DSPMSG *) ++ pMsgOutput)->msg).dwArg2); ++ WriteExt32BitDspData(pIOMgr->hWmdContext, addr, ++ val); ++ pMsgOutput += sizeof(struct MSG_DSPMSG); ++ if (!hMsgMgr->msgFreeList) ++ goto func_end; ++ LST_PutTail(hMsgMgr->msgFreeList, ++ (struct LST_ELEM *) pMsg); ++ SYNC_SetEvent(hMsgMgr->hSyncEvent); ++ } else { ++ DBG_Trace(DBG_LEVEL3, "pMsg is NULL\n"); ++ } ++ } ++ ++ if (uMsgs > 0) { ++ hMsgMgr->uMsgsPending -= uMsgs; ++#if _CHNL_WORDSIZE == 2 ++ IO_SetValue(pIOMgr->hWmdContext, struct MSG, pCtrl, ++ size, (u16)uMsgs); ++#else ++ IO_SetValue(pIOMgr->hWmdContext, struct MSG, pCtrl, ++ size, uMsgs); ++#endif ++ IO_SetValue(pIOMgr->hWmdContext, struct MSG, pCtrl, ++ bufEmpty, false); ++ /* Set the post SWI flag */ ++ IO_SetValue(pIOMgr->hWmdContext, struct MSG, pCtrl, ++ postSWI, true); ++ /* Tell the DSP we have written the output. */ ++ CHNLSM_InterruptDSP2(pIOMgr->hWmdContext, MBX_PCPY_CLASS); ++ } ++ } ++func_end: ++ return; ++ ++} ++ ++/* ++ * ======== registerSHMSegs ======== ++ * purpose: ++ * Registers GPP SM segment with CMM. ++ */ ++static DSP_STATUS registerSHMSegs(struct IO_MGR *hIOMgr, ++ struct COD_MANAGER *hCodMan, ++ u32 dwGPPBasePA) ++{ ++ DSP_STATUS status = DSP_SOK; ++ u32 ulShm0_Base = 0; ++ u32 ulShm0_End = 0; ++ u32 ulShm0_RsrvdStart = 0; ++ u32 ulRsrvdSize = 0; ++ u32 ulGppPhys; ++ u32 ulDspVirt; ++ u32 ulShmSegId0 = 0; ++ u32 dwOffset, dwGPPBaseVA, ulDSPSize; ++ ++ /* Read address and size info for first SM region.*/ ++ /* Get start of 1st SM Heap region */ ++ status = COD_GetSymValue(hCodMan, SHM0_SHARED_BASE_SYM, &ulShm0_Base); ++ DBC_Assert(ulShm0_Base != 0); ++ /* Get end of 1st SM Heap region */ ++ if (DSP_SUCCEEDED(status)) { ++ /* Get start and length of message part of shared memory */ ++ status = COD_GetSymValue(hCodMan, SHM0_SHARED_END_SYM, ++ &ulShm0_End); ++ DBC_Assert(ulShm0_End != 0); ++ } ++ /* start of Gpp reserved region */ ++ if (DSP_SUCCEEDED(status)) { ++ /* Get start and length of message part of shared memory */ ++ status = COD_GetSymValue(hCodMan, SHM0_SHARED_RESERVED_BASE_SYM, ++ &ulShm0_RsrvdStart); ++ DBG_Trace(DBG_LEVEL1, "***ulShm0_RsrvdStart 0x%x \n", ++ ulShm0_RsrvdStart); ++ DBC_Assert(ulShm0_RsrvdStart != 0); ++ } ++ /* Register with CMM */ ++ if (DSP_SUCCEEDED(status)) { ++ status = DEV_GetCmmMgr(hIOMgr->hDevObject, &hIOMgr->hCmmMgr); ++ if (DSP_SUCCEEDED(status)) { ++ status = CMM_UnRegisterGPPSMSeg(hIOMgr->hCmmMgr, ++ CMM_ALLSEGMENTS); ++ if (DSP_FAILED(status)) { ++ DBG_Trace(DBG_LEVEL7, "ERROR - Unable to " ++ "Un-Register SM segments \n"); ++ } ++ } else { ++ DBG_Trace(DBG_LEVEL7, "ERROR - Unable to get CMM " ++ "Handle \n"); ++ } ++ } ++ /* Register new SM region(s) */ ++ if (DSP_SUCCEEDED(status) && (ulShm0_End - ulShm0_Base) > 0) { ++ /* calc size (bytes) of SM the GPP can alloc from */ ++ ulRsrvdSize = (ulShm0_End - ulShm0_RsrvdStart + 1) * hIOMgr-> ++ uWordSize; ++ DBC_Assert(ulRsrvdSize > 0); ++ /* calc size of SM DSP can alloc from */ ++ ulDSPSize = (ulShm0_RsrvdStart - ulShm0_Base) * hIOMgr-> ++ uWordSize; ++ DBC_Assert(ulDSPSize > 0); ++ /* First TLB entry reserved for Bridge SM use.*/ ++ ulGppPhys = hIOMgr->extProcInfo.tyTlb[0].ulGppPhys; ++ /* get size in bytes */ ++ ulDspVirt = hIOMgr->extProcInfo.tyTlb[0].ulDspVirt * hIOMgr-> ++ uWordSize; ++ /* Calc byte offset used to convert GPP phys <-> DSP byte ++ * address.*/ ++ if (dwGPPBasePA > ulDspVirt) ++ dwOffset = dwGPPBasePA - ulDspVirt; ++ else ++ dwOffset = ulDspVirt - dwGPPBasePA; ++ ++ DBC_Assert(ulShm0_RsrvdStart * hIOMgr->uWordSize >= ulDspVirt); ++ /* calc Gpp phys base of SM region */ ++ /* Linux - this is actually uncached kernel virtual address*/ ++ dwGPPBaseVA = ulGppPhys + ulShm0_RsrvdStart * hIOMgr->uWordSize ++ - ulDspVirt; ++ /* calc Gpp phys base of SM region */ ++ /* Linux - this is the physical address*/ ++ dwGPPBasePA = dwGPPBasePA + ulShm0_RsrvdStart * hIOMgr-> ++ uWordSize - ulDspVirt; ++ /* Register SM Segment 0.*/ ++ status = CMM_RegisterGPPSMSeg(hIOMgr->hCmmMgr, dwGPPBasePA, ++ ulRsrvdSize, dwOffset, (dwGPPBasePA > ulDspVirt) ? ++ CMM_ADDTODSPPA : CMM_SUBFROMDSPPA, ++ (u32)(ulShm0_Base * hIOMgr->uWordSize), ++ ulDSPSize, &ulShmSegId0, dwGPPBaseVA); ++ if (DSP_FAILED(status)) { ++ DBG_Trace(DBG_LEVEL7, "ERROR - Failed to register SM " ++ "Seg 0 \n"); ++ } ++ /* first SM region is segId = 1 */ ++ DBC_Assert(ulShmSegId0 == 1); ++ } ++ return status; ++} ++ ++/* ++ * ======== ReadData ======== ++ * Copies buffers from the shared memory to the host buffer. ++ */ ++static u32 ReadData(struct WMD_DEV_CONTEXT *hDevContext, void *pDest, ++ void *pSrc, u32 uSize) ++{ ++ memcpy(pDest, pSrc, uSize); ++ return uSize; ++} ++ ++/* ++ * ======== WriteData ======== ++ * Copies buffers from the host side buffer to the shared memory. ++ */ ++static u32 WriteData(struct WMD_DEV_CONTEXT *hDevContext, void *pDest, ++ void *pSrc, u32 uSize) ++{ ++ memcpy(pDest, pSrc, uSize); ++ return uSize; ++} ++ ++/* ZCPY IO routines. */ ++void IO_IntrDSP2(IN struct IO_MGR *pIOMgr, IN u16 wMbVal) ++{ ++ CHNLSM_InterruptDSP2(pIOMgr->hWmdContext, wMbVal); ++} ++ ++/* ++ * ======== IO_SHMcontrol ======== ++ * Sets the requested SHM setting. ++ */ ++DSP_STATUS IO_SHMsetting(IN struct IO_MGR *hIOMgr, IN enum SHM_DESCTYPE desc, ++ IN void *pArgs) ++{ ++#ifdef CONFIG_BRIDGE_DVFS ++ u32 i; ++ struct dspbridge_platform_data *pdata = ++ omap_dspbridge_dev->dev.platform_data; ++ ++ switch (desc) { ++ case SHM_CURROPP: ++ /* Update the shared memory with requested OPP information */ ++ if (pArgs != NULL) ++ hIOMgr->pSharedMem->oppTableStruct.currOppPt = ++ *(u32 *)pArgs; ++ else ++ return DSP_EFAIL; ++ break; ++ case SHM_OPPINFO: ++ /* Update the shared memory with the voltage, frequency, ++ min and max frequency values for an OPP */ ++ for (i = 0; i <= dsp_max_opps; i++) { ++ hIOMgr->pSharedMem->oppTableStruct.oppPoint[i].voltage = ++ vdd1_dsp_freq[i][0]; ++ DBG_Trace(DBG_LEVEL5, "OPP shared memory -voltage: " ++ "%d\n", hIOMgr->pSharedMem->oppTableStruct. ++ oppPoint[i].voltage); ++ hIOMgr->pSharedMem->oppTableStruct.oppPoint[i]. ++ frequency = vdd1_dsp_freq[i][1]; ++ DBG_Trace(DBG_LEVEL5, "OPP shared memory -frequency: " ++ "%d\n", hIOMgr->pSharedMem->oppTableStruct. ++ oppPoint[i].frequency); ++ hIOMgr->pSharedMem->oppTableStruct.oppPoint[i].minFreq = ++ vdd1_dsp_freq[i][2]; ++ DBG_Trace(DBG_LEVEL5, "OPP shared memory -min value: " ++ "%d\n", hIOMgr->pSharedMem->oppTableStruct. ++ oppPoint[i].minFreq); ++ hIOMgr->pSharedMem->oppTableStruct.oppPoint[i].maxFreq = ++ vdd1_dsp_freq[i][3]; ++ DBG_Trace(DBG_LEVEL5, "OPP shared memory -max value: " ++ "%d\n", hIOMgr->pSharedMem->oppTableStruct. ++ oppPoint[i].maxFreq); ++ } ++ hIOMgr->pSharedMem->oppTableStruct.numOppPts = dsp_max_opps; ++ DBG_Trace(DBG_LEVEL5, "OPP shared memory - max OPP number: " ++ "%d\n", hIOMgr->pSharedMem->oppTableStruct.numOppPts); ++ /* Update the current OPP number */ ++ if (pdata->dsp_get_opp) ++ i = (*pdata->dsp_get_opp)(); ++ hIOMgr->pSharedMem->oppTableStruct.currOppPt = i; ++ DBG_Trace(DBG_LEVEL7, "OPP value programmed to shared memory: " ++ "%d\n", i); ++ break; ++ case SHM_GETOPP: ++ /* Get the OPP that DSP has requested */ ++ *(u32 *)pArgs = hIOMgr->pSharedMem->oppRequest.rqstOppPt; ++ break; ++ default: ++ break; ++ ++ queue_work(bridge_workqueue, ++ &(hIOMgr->io_workq)); ++ } ++#endif ++ return DSP_SOK; ++} ++ ++/* ++ * ======== WMD_IO_GetProcLoad ======== ++ * Gets the Processor's Load information ++ */ ++DSP_STATUS WMD_IO_GetProcLoad(IN struct IO_MGR *hIOMgr, ++ OUT struct DSP_PROCLOADSTAT *pProcStat) ++{ ++ pProcStat->uCurrLoad = hIOMgr->pSharedMem->loadMonInfo.currDspLoad; ++ pProcStat->uPredictedLoad = hIOMgr->pSharedMem->loadMonInfo.predDspLoad; ++ pProcStat->uCurrDspFreq = hIOMgr->pSharedMem->loadMonInfo.currDspFreq; ++ pProcStat->uPredictedFreq = hIOMgr->pSharedMem->loadMonInfo.predDspFreq; ++ ++ DBG_Trace(DBG_LEVEL4, "Curr Load =%d, Pred Load = %d, Curr Freq = %d, " ++ "Pred Freq = %d\n", pProcStat->uCurrLoad, ++ pProcStat->uPredictedLoad, pProcStat->uCurrDspFreq, ++ pProcStat->uPredictedFreq); ++ return DSP_SOK; ++} ++ ++#ifndef DSP_TRACEBUF_DISABLED ++void PrintDSPDebugTrace(struct IO_MGR *hIOMgr) ++{ ++ u32 ulNewMessageLength = 0, ulGPPCurPointer; ++ ++ GT_0trace(dsp_trace_mask, GT_ENTER, "Entering PrintDSPDebugTrace\n"); ++ ++ while (true) { ++ /* Get the DSP current pointer */ ++ ulGPPCurPointer = *(u32 *) (hIOMgr->ulTraceBufferCurrent); ++ ulGPPCurPointer = hIOMgr->ulGppVa + (ulGPPCurPointer - ++ hIOMgr->ulDspVa); ++ ++ /* No new debug messages available yet */ ++ if (ulGPPCurPointer == hIOMgr->ulGPPReadPointer) ++ break; ++ ++ /* Continuous data */ ++ else if (ulGPPCurPointer > hIOMgr->ulGPPReadPointer) { ++ ulNewMessageLength = ulGPPCurPointer - hIOMgr-> ++ ulGPPReadPointer; ++ ++ memcpy(hIOMgr->pMsg, (char *)hIOMgr->ulGPPReadPointer, ++ ulNewMessageLength); ++ hIOMgr->pMsg[ulNewMessageLength] = '\0'; ++ /* Advance the GPP trace pointer to DSP current ++ * pointer */ ++ hIOMgr->ulGPPReadPointer += ulNewMessageLength; ++ /* Print the trace messages */ ++ GT_0trace(dsp_trace_mask, GT_1CLASS, hIOMgr->pMsg); ++ } ++ /* Handle trace buffer wraparound */ ++ else if (ulGPPCurPointer < hIOMgr->ulGPPReadPointer) { ++ memcpy(hIOMgr->pMsg, (char *)hIOMgr->ulGPPReadPointer, ++ hIOMgr->ulTraceBufferEnd - ++ hIOMgr->ulGPPReadPointer); ++ ulNewMessageLength = ulGPPCurPointer - ++ hIOMgr->ulTraceBufferBegin; ++ memcpy(&hIOMgr->pMsg[hIOMgr->ulTraceBufferEnd - ++ hIOMgr->ulGPPReadPointer], ++ (char *)hIOMgr->ulTraceBufferBegin, ++ ulNewMessageLength); ++ hIOMgr->pMsg[hIOMgr->ulTraceBufferEnd - ++ hIOMgr->ulGPPReadPointer + ++ ulNewMessageLength] = '\0'; ++ /* Advance the GPP trace pointer to DSP current ++ * pointer */ ++ hIOMgr->ulGPPReadPointer = hIOMgr->ulTraceBufferBegin + ++ ulNewMessageLength; ++ /* Print the trace messages */ ++ GT_0trace(dsp_trace_mask, GT_1CLASS, hIOMgr->pMsg); ++ } ++ } ++} ++#endif ++ ++/* ++ * ======== PackTraceBuffer ======== ++ * Removes extra nulls from the trace buffer returned from the DSP. ++ * Works even on buffers that already are packed (null removed); but has ++ * one bug in that case -- loses the last character (replaces with '\0'). ++ * Continues through conversion for full set of nBytes input characters. ++ * Parameters: ++ * lpBuf: Pointer to input/output buffer ++ * nBytes: Number of characters in the buffer ++ * ulNumWords: Number of DSP words in the buffer. Indicates potential ++ * number of extra carriage returns to generate. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Unable to allocate memory. ++ * Requires: ++ * lpBuf must be a fully allocated writable block of at least nBytes. ++ * There are no more than ulNumWords extra characters needed (the number of ++ * linefeeds minus the number of NULLS in the input buffer). ++ */ ++#if (defined(DEBUG) || defined(DDSP_DEBUG_PRODUCT)) && GT_TRACE ++static DSP_STATUS PackTraceBuffer(char *lpBuf, u32 nBytes, u32 ulNumWords) ++{ ++ DSP_STATUS status = DSP_SOK; ++ char *lpTmpBuf; ++ char *lpBufStart; ++ char *lpTmpStart; ++ u32 nCnt; ++ char thisChar; ++ ++ /* tmp workspace, 1 KB longer than input buf */ ++ lpTmpBuf = MEM_Calloc((nBytes + ulNumWords), MEM_PAGED); ++ if (lpTmpBuf == NULL) { ++ DBG_Trace(DBG_LEVEL7, "PackTrace buffer:OutofMemory \n"); ++ status = DSP_EMEMORY; ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ lpBufStart = lpBuf; ++ lpTmpStart = lpTmpBuf; ++ for (nCnt = nBytes; nCnt > 0; nCnt--) { ++ thisChar = *lpBuf++; ++ switch (thisChar) { ++ case '\0': /* Skip null bytes */ ++ break; ++ case '\n': /* Convert \n to \r\n */ ++ /* NOTE: do not reverse order; Some OS */ ++ /* editors control doesn't understand "\n\r" */ ++ *lpTmpBuf++ = '\r'; ++ *lpTmpBuf++ = '\n'; ++ break; ++ default: /* Copy in the actual ascii byte */ ++ *lpTmpBuf++ = thisChar; ++ break; ++ } ++ } ++ *lpTmpBuf = '\0'; /* Make sure tmp buf is null terminated */ ++ /* Cut output down to input buf size */ ++ strncpy(lpBufStart, lpTmpStart, nBytes); ++ /*Make sure output is null terminated */ ++ lpBufStart[nBytes - 1] = '\0'; ++ MEM_Free(lpTmpStart); ++ } ++ ++ return status; ++} ++#endif /* (defined(DEBUG) || defined(DDSP_DEBUG_PRODUCT)) && GT_TRACE */ ++ ++/* ++ * ======== PrintDspTraceBuffer ======== ++ * Prints the trace buffer returned from the DSP (if DBG_Trace is enabled). ++ * Parameters: ++ * hDehMgr: Handle to DEH manager object ++ * number of extra carriage returns to generate. ++ * Returns: ++ * DSP_SOK: Success. ++ * DSP_EMEMORY: Unable to allocate memory. ++ * Requires: ++ * hDehMgr muse be valid. Checked in WMD_DEH_Notify. ++ */ ++DSP_STATUS PrintDspTraceBuffer(struct WMD_DEV_CONTEXT *hWmdContext) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++#if (defined(DEBUG) || defined(DDSP_DEBUG_PRODUCT)) && GT_TRACE ++ struct COD_MANAGER *hCodMgr; ++ u32 ulTraceEnd; ++ u32 ulTraceBegin; ++ u32 ulNumBytes = 0; ++ u32 ulNumWords = 0; ++ u32 ulWordSize = 2; ++ CONST u32 uMaxSize = 512; ++ char *pszBuf; ++ u16 *lpszBuf; ++ ++ struct WMD_DEV_CONTEXT *pWmdContext = (struct WMD_DEV_CONTEXT *) ++ hWmdContext; ++ struct WMD_DRV_INTERFACE *pIntfFxns; ++ struct DEV_OBJECT *pDevObject = (struct DEV_OBJECT *) ++ pWmdContext->hDevObject; ++ ++ status = DEV_GetCodMgr(pDevObject, &hCodMgr); ++ if (DSP_FAILED(status)) ++ GT_0trace(dsp_trace_mask, GT_2CLASS, ++ "PrintDspTraceBuffer: Failed on DEV_GetCodMgr.\n"); ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Look for SYS_PUTCBEG/SYS_PUTCEND: */ ++ status = COD_GetSymValue(hCodMgr, COD_TRACEBEG, &ulTraceBegin); ++ GT_1trace(dsp_trace_mask, GT_2CLASS, ++ "PrintDspTraceBuffer: ulTraceBegin Value 0x%x\n", ++ ulTraceBegin); ++ if (DSP_FAILED(status)) ++ GT_0trace(dsp_trace_mask, GT_2CLASS, ++ "PrintDspTraceBuffer: Failed on " ++ "COD_GetSymValue.\n"); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ status = COD_GetSymValue(hCodMgr, COD_TRACEEND, &ulTraceEnd); ++ GT_1trace(dsp_trace_mask, GT_2CLASS, ++ "PrintDspTraceBuffer: ulTraceEnd Value 0x%x\n", ++ ulTraceEnd); ++ if (DSP_FAILED(status)) ++ GT_0trace(dsp_trace_mask, GT_2CLASS, ++ "PrintDspTraceBuffer: Failed on " ++ "COD_GetSymValue.\n"); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ ulNumBytes = (ulTraceEnd - ulTraceBegin) * ulWordSize; ++ /* If the chip type is 55 then the addresses will be ++ * byte addresses; convert them to word addresses. */ ++ if (ulNumBytes > uMaxSize) ++ ulNumBytes = uMaxSize; ++ ++ /* make sure the data we request fits evenly */ ++ ulNumBytes = (ulNumBytes / ulWordSize) * ulWordSize; ++ GT_1trace(dsp_trace_mask, GT_2CLASS, "PrintDspTraceBuffer: " ++ "ulNumBytes 0x%x\n", ulNumBytes); ++ ulNumWords = ulNumBytes * ulWordSize; ++ GT_1trace(dsp_trace_mask, GT_2CLASS, "PrintDspTraceBuffer: " ++ "ulNumWords 0x%x\n", ulNumWords); ++ status = DEV_GetIntfFxns(pDevObject, &pIntfFxns); ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ pszBuf = MEM_Calloc(uMaxSize, MEM_NONPAGED); ++ lpszBuf = MEM_Calloc(ulNumBytes * 2, MEM_NONPAGED); ++ if (pszBuf != NULL) { ++ /* Read bytes from the DSP trace buffer... */ ++ status = (*pIntfFxns->pfnBrdRead)(hWmdContext, ++ (u8 *)pszBuf, (u32)ulTraceBegin, ++ ulNumBytes, 0); ++ if (DSP_FAILED(status)) ++ GT_0trace(dsp_trace_mask, GT_2CLASS, ++ "PrintDspTraceBuffer: " ++ "Failed to Read Trace Buffer.\n"); ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Pack and do newline conversion */ ++ GT_0trace(dsp_trace_mask, GT_2CLASS, ++ "PrintDspTraceBuffer: " ++ "before pack and unpack.\n"); ++ PackTraceBuffer(pszBuf, ulNumBytes, ulNumWords); ++ GT_1trace(dsp_trace_mask, GT_1CLASS, ++ "DSP Trace Buffer:\n%s\n", pszBuf); ++ } ++ MEM_Free(pszBuf); ++ MEM_Free(lpszBuf); ++ } else { ++ GT_0trace(dsp_trace_mask, GT_2CLASS, ++ "PrintDspTraceBuffer: Failed to " ++ "allocate trace buffer.\n"); ++ status = DSP_EMEMORY; ++ } ++ } ++#endif ++ return status; ++} ++ ++void IO_SM_init(void) ++{ ++ ++ GT_create(&dsp_trace_mask, "DT"); /* DSP Trace Mask */ ++ ++} +diff --git a/drivers/dsp/bridge/wmd/mmu_fault.c b/drivers/dsp/bridge/wmd/mmu_fault.c +new file mode 100644 +index 0000000..5585cdb +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/mmu_fault.c +@@ -0,0 +1,172 @@ ++/* ++ * mmu_fault.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== mmu_fault.c ======== ++ * Description: ++ * Implements DSP MMU fault handling functions. ++ * ++ *! Revision History: ++ *! ================ ++ *! 26-Dec-2004 hn: Support for IVA MMU exception. ++ *! 06-Mar-2003 sb: Print MMU fault address. Cosmetic changes. ++ *! 16-Feb-2003 vp: Fixed warning in MMU_FaultIsr ++ *! 05-Jan-2004 vp: Updated support for 24xx silicon ++ *! 19-Feb-2003 vp: Code review updates. ++ *! - Cosmetic changes. ++ *! 18-Oct-2002 sb: Ported to Linux platform. ++ *! 10-Sep-2001 kc: created. ++ */ ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Link Driver */ ++#include ++ ++/* ------------------------------------ Hardware Abstraction Layer */ ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include "_deh.h" ++#include ++#include "_tiomap_mmu.h" ++#include "_tiomap.h" ++#include "mmu_fault.h" ++ ++static u32 dmmuEventMask; ++u32 faultAddr; ++ ++static bool MMU_CheckIfFault(struct WMD_DEV_CONTEXT *pDevContext); ++ ++/* ++ * ======== MMU_FaultDpc ======== ++ * Deferred procedure call to handle DSP MMU fault. ++ */ ++void MMU_FaultDpc(IN void *pRefData) ++{ ++ struct DEH_MGR *hDehMgr = (struct DEH_MGR *)pRefData; ++ struct DEH_MGR *pDehMgr = (struct DEH_MGR *)hDehMgr; ++ ++ DBG_Trace(DBG_LEVEL1, "MMU_FaultDpc Enter: 0x%x\n", pRefData); ++ ++ if (pDehMgr) ++ WMD_DEH_Notify(hDehMgr, DSP_MMUFAULT, 0L); ++ ++ DBG_Trace(DBG_LEVEL1, "MMU_FaultDpc Exit: 0x%x\n", pRefData); ++} ++ ++/* ++ * ======== MMU_FaultIsr ======== ++ * ISR to be triggered by a DSP MMU fault interrupt. ++ */ ++irqreturn_t MMU_FaultIsr(int irq, IN void *pRefData) ++{ ++ struct DEH_MGR *pDehMgr = (struct DEH_MGR *)pRefData; ++ struct WMD_DEV_CONTEXT *pDevContext; ++ struct CFG_HOSTRES resources; ++ DSP_STATUS status = DSP_SOK; ++ ++ ++ DBG_Trace(DBG_LEVEL1, "Entering DEH_DspMmuIsr: 0x%x\n", pRefData); ++ DBC_Require(irq == INT_DSP_MMU_IRQ); ++ DBC_Require(MEM_IsValidHandle(pDehMgr, SIGNATURE)); ++ ++ if (MEM_IsValidHandle(pDehMgr, SIGNATURE)) { ++ ++ pDevContext = (struct WMD_DEV_CONTEXT *)pDehMgr->hWmdContext; ++ status = CFG_GetHostResources( ++ (struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), ++ &resources); ++ if (DSP_FAILED(status)) ++ DBG_Trace(DBG_LEVEL7, ++ "**Failed to get Host Resources " ++ "in MMU ISR **\n"); ++ if (MMU_CheckIfFault(pDevContext)) { ++ printk(KERN_INFO "***** DSPMMU FAULT ***** IRQStatus " ++ "0x%x\n", dmmuEventMask); ++ printk(KERN_INFO "***** DSPMMU FAULT ***** faultAddr " ++ "0x%x\n", faultAddr); ++ /* Disable the MMU events, else once we clear it will ++ * start to raise INTs again */ ++ /* ++ * Schedule a DPC directly. In the future, it may be ++ * necessary to check if DSP MMU fault is intended for ++ * Bridge. ++ */ ++ DPC_Schedule(pDehMgr->hMmuFaultDpc); ++ /* Reset errInfo structure before use. */ ++ pDehMgr->errInfo.dwErrMask = DSP_MMUFAULT; ++ pDehMgr->errInfo.dwVal1 = faultAddr >> 16; ++ pDehMgr->errInfo.dwVal2 = faultAddr & 0xFFFF; ++ pDehMgr->errInfo.dwVal3 = 0L; ++ /* Disable the MMU events, else once we clear it will ++ * start to raise INTs again */ ++ HW_MMU_EventDisable(resources.dwDmmuBase, ++ HW_MMU_TRANSLATION_FAULT); ++ } else { ++ DBG_Trace(DBG_LEVEL7, ++ "***** MMU FAULT ***** faultcode 0x%x\n", ++ dmmuEventMask); ++ HW_MMU_EventDisable(resources.dwDmmuBase, ++ HW_MMU_ALL_INTERRUPTS); ++ } ++ } ++ return IRQ_HANDLED; ++} ++ ++ ++/* ++ * ======== MMU_CheckIfFault ======== ++ * Check to see if MMU Fault is valid TLB miss from DSP ++ * Note: This function is called from an ISR ++ */ ++static bool MMU_CheckIfFault(struct WMD_DEV_CONTEXT *pDevContext) ++{ ++ ++ ++ bool retVal = false; ++ DSP_STATUS status = DSP_SOK; ++ HW_STATUS hwStatus; ++ struct CFG_HOSTRES resources; ++ status = CFG_GetHostResources( ++ (struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); ++ if (DSP_FAILED(status)) ++ DBG_Trace(DBG_LEVEL7, "**Failed to get Host Resources in " ++ "MMU_CheckIfFault **\n"); ++ ++ hwStatus = HW_MMU_EventStatus(resources.dwDmmuBase, &dmmuEventMask); ++ if (dmmuEventMask == HW_MMU_TRANSLATION_FAULT) { ++ HW_MMU_FaultAddrRead(resources.dwDmmuBase, &faultAddr); ++ DBG_Trace(DBG_LEVEL1, "WMD_DEH_Notify: DSP_MMUFAULT, fault " ++ "address = 0x%x\n", faultAddr); ++ retVal = true; ++ } ++ return retVal; ++} +diff --git a/drivers/dsp/bridge/wmd/mmu_fault.h b/drivers/dsp/bridge/wmd/mmu_fault.h +new file mode 100644 +index 0000000..be59333 +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/mmu_fault.h +@@ -0,0 +1,45 @@ ++/* ++ * mmu_fault.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== mmu_fault.h ======== ++ * Description: ++ * Defines DSP MMU fault handling functions. ++ * ++ *! Revision History: ++ *! ================ ++ *! 26-Dec-2004 hn: IVA MMU handlers. ++ *! 10-Sep-2001 kc: created. ++ */ ++ ++#ifndef MMU_FAULT_ ++#define MMU_FAULT_ ++ ++/* ++ * ======== MMU_FaultDpc ======== ++ * Deferred procedure call to handle DSP MMU fault. ++ */ ++ void MMU_FaultDpc(IN void *pRefData); ++ ++/* ++ * ======== MMU_FaultIsr ======== ++ * ISR to be triggered by a DSP MMU fault interrupt. ++ */ ++irqreturn_t MMU_FaultIsr(int irq, IN void *pRefData); ++ ++#endif /* MMU_FAULT_ */ ++ +diff --git a/drivers/dsp/bridge/wmd/msg_sm.c b/drivers/dsp/bridge/wmd/msg_sm.c +new file mode 100644 +index 0000000..b9b2bec +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/msg_sm.c +@@ -0,0 +1,643 @@ ++/* ++ * msg_sm.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== msg_sm.c ======== ++ * Description: ++ * Implements upper edge functions for WMD message module. ++ * ++ * Public Functions: ++ * WMD_MSG_Create ++ * WMD_MSG_CreateQueue ++ * WMD_MSG_Delete ++ * WMD_MSG_DeleteQueue ++ * WMD_MSG_Get ++ * WMD_MSG_Put ++ * WMD_MSG_RegisterNotify ++ * WMD_MSG_SetQueueId ++ * ++ *! Revision History: ++ *! ================= ++ *! 24-Jul-2002 jeh Release critical section in WMD_MSG_Put() before ++ *! scheduling DPC. ++ *! 09-May-2001 jeh Free MSG queue NTFY object, remove unnecessary set/ ++ *! reset of events. ++ *! 10-Jan-2001 jeh Set/Reset message manager and message queue events ++ *! correctly. ++ *! 04-Dec-2000 jeh Bug fixes. ++ *! 12-Sep-2000 jeh Created. ++ */ ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++ ++/* ----------------------------------- Others */ ++#include ++ ++/* ----------------------------------- This */ ++#include <_msg_sm.h> ++#include ++ ++/* ----------------------------------- Defines, Data Structures, Typedefs */ ++#define MSGQ_SIGNATURE 0x5147534d /* "QGSM" */ ++ ++/* ----------------------------------- Function Prototypes */ ++static DSP_STATUS AddNewMsg(struct LST_LIST *msgList); ++static void DeleteMsgMgr(struct MSG_MGR *hMsgMgr); ++static void DeleteMsgQueue(struct MSG_QUEUE *hMsgQueue, u32 uNumToDSP); ++static void FreeMsgList(struct LST_LIST *msgList); ++ ++/* ++ * ======== WMD_MSG_Create ======== ++ * Create an object to manage message queues. Only one of these objects ++ * can exist per device object. ++ */ ++DSP_STATUS WMD_MSG_Create(OUT struct MSG_MGR **phMsgMgr, ++ struct DEV_OBJECT *hDevObject, MSG_ONEXIT msgCallback) ++{ ++ struct MSG_MGR *pMsgMgr; ++ struct IO_MGR *hIOMgr; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(phMsgMgr != NULL); ++ DBC_Require(msgCallback != NULL); ++ DBC_Require(hDevObject != NULL); ++ DEV_GetIOMgr(hDevObject, &hIOMgr); ++ DBC_Assert(hIOMgr != NULL); ++ *phMsgMgr = NULL; ++ /* Allocate MSG manager object */ ++ MEM_AllocObject(pMsgMgr, struct MSG_MGR, MSGMGR_SIGNATURE); ++ ++ if (pMsgMgr) { ++ pMsgMgr->onExit = msgCallback; ++ pMsgMgr->hIOMgr = hIOMgr; ++ /* List of MSG_QUEUEs */ ++ pMsgMgr->queueList = LST_Create(); ++ /* Queues of message frames for messages to the DSP. Message ++ * frames will only be added to the free queue when a ++ * MSG_QUEUE object is created. */ ++ pMsgMgr->msgFreeList = LST_Create(); ++ pMsgMgr->msgUsedList = LST_Create(); ++ if (pMsgMgr->queueList == NULL || ++ pMsgMgr->msgFreeList == NULL || ++ pMsgMgr->msgUsedList == NULL) ++ status = DSP_EMEMORY; ++ if (DSP_SUCCEEDED(status)) ++ status = SYNC_InitializeDPCCS(&pMsgMgr->hSyncCS); ++ ++ /* Create an event to be used by WMD_MSG_Put() in waiting ++ * for an available free frame from the message manager. */ ++ if (DSP_SUCCEEDED(status)) ++ status = SYNC_OpenEvent(&pMsgMgr->hSyncEvent, NULL); ++ ++ if (DSP_SUCCEEDED(status)) ++ *phMsgMgr = pMsgMgr; ++ else ++ DeleteMsgMgr(pMsgMgr); ++ ++ } else { ++ status = DSP_EMEMORY; ++ } ++ return status; ++} ++ ++/* ++ * ======== WMD_MSG_CreateQueue ======== ++ * Create a MSG_QUEUE for sending/receiving messages to/from a node ++ * on the DSP. ++ */ ++DSP_STATUS WMD_MSG_CreateQueue(struct MSG_MGR *hMsgMgr, ++ OUT struct MSG_QUEUE **phMsgQueue, ++ u32 dwId, u32 uMaxMsgs, HANDLE hArg) ++{ ++ u32 i; ++ u32 uNumAllocated = 0; ++ struct MSG_QUEUE *pMsgQ; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(MEM_IsValidHandle(hMsgMgr, MSGMGR_SIGNATURE)); ++ DBC_Require(phMsgQueue != NULL); ++ ++ *phMsgQueue = NULL; ++ /* Allocate MSG_QUEUE object */ ++ MEM_AllocObject(pMsgQ, struct MSG_QUEUE, MSGQ_SIGNATURE); ++ if (!pMsgQ) { ++ status = DSP_EMEMORY; ++ goto func_end; ++ } ++ LST_InitElem((struct LST_ELEM *) pMsgQ); ++ pMsgQ->uMaxMsgs = uMaxMsgs; ++ pMsgQ->hMsgMgr = hMsgMgr; ++ pMsgQ->hArg = hArg; /* Node handle */ ++ pMsgQ->dwId = dwId; /* Node env (not valid yet) */ ++ /* Queues of Message frames for messages from the DSP */ ++ pMsgQ->msgFreeList = LST_Create(); ++ pMsgQ->msgUsedList = LST_Create(); ++ if (pMsgQ->msgFreeList == NULL || pMsgQ->msgUsedList == NULL) ++ status = DSP_EMEMORY; ++ ++ /* Create event that will be signalled when a message from ++ * the DSP is available. */ ++ if (DSP_SUCCEEDED(status)) ++ status = SYNC_OpenEvent(&pMsgQ->hSyncEvent, NULL); ++ ++ /* Create a notification list for message ready notification. */ ++ if (DSP_SUCCEEDED(status)) ++ status = NTFY_Create(&pMsgQ->hNtfy); ++ ++ /* Create events that will be used to synchronize cleanup ++ * when the object is deleted. hSyncDone will be set to ++ * unblock threads in MSG_Put() or MSG_Get(). hSyncDoneAck ++ * will be set by the unblocked thread to signal that it ++ * is unblocked and will no longer reference the object. */ ++ if (DSP_SUCCEEDED(status)) ++ status = SYNC_OpenEvent(&pMsgQ->hSyncDone, NULL); ++ ++ if (DSP_SUCCEEDED(status)) ++ status = SYNC_OpenEvent(&pMsgQ->hSyncDoneAck, NULL); ++ ++ if (DSP_SUCCEEDED(status)) { ++ if (!hMsgMgr->msgFreeList) { ++ status = DSP_EHANDLE; ++ goto func_end; ++ } ++ /* Enter critical section */ ++ (void)SYNC_EnterCS(hMsgMgr->hSyncCS); ++ /* Initialize message frames and put in appropriate queues */ ++ for (i = 0; i < uMaxMsgs && DSP_SUCCEEDED(status); i++) { ++ status = AddNewMsg(hMsgMgr->msgFreeList); ++ if (DSP_SUCCEEDED(status)) { ++ uNumAllocated++; ++ status = AddNewMsg(pMsgQ->msgFreeList); ++ } ++ } ++ if (DSP_FAILED(status)) { ++ /* Stay inside CS to prevent others from taking any ++ * of the newly allocated message frames. */ ++ DeleteMsgQueue(pMsgQ, uNumAllocated); ++ } else { ++ LST_PutTail(hMsgMgr->queueList, ++ (struct LST_ELEM *)pMsgQ); ++ *phMsgQueue = pMsgQ; ++ /* Signal that free frames are now available */ ++ if (!LST_IsEmpty(hMsgMgr->msgFreeList)) ++ SYNC_SetEvent(hMsgMgr->hSyncEvent); ++ ++ } ++ /* Exit critical section */ ++ (void)SYNC_LeaveCS(hMsgMgr->hSyncCS); ++ } else { ++ DeleteMsgQueue(pMsgQ, 0); ++ } ++func_end: ++ return status; ++} ++ ++/* ++ * ======== WMD_MSG_Delete ======== ++ * Delete a MSG manager allocated in WMD_MSG_Create(). ++ */ ++void WMD_MSG_Delete(struct MSG_MGR *hMsgMgr) ++{ ++ DBC_Require(MEM_IsValidHandle(hMsgMgr, MSGMGR_SIGNATURE)); ++ ++ DeleteMsgMgr(hMsgMgr); ++} ++ ++/* ++ * ======== WMD_MSG_DeleteQueue ======== ++ * Delete a MSG queue allocated in WMD_MSG_CreateQueue. ++ */ ++void WMD_MSG_DeleteQueue(struct MSG_QUEUE *hMsgQueue) ++{ ++ struct MSG_MGR *hMsgMgr = hMsgQueue->hMsgMgr; ++ u32 refCount; ++ ++ DBC_Require(MEM_IsValidHandle(hMsgQueue, MSGQ_SIGNATURE)); ++ hMsgQueue->fDone = true; ++ /* Unblock all threads blocked in MSG_Get() or MSG_Put(). */ ++ refCount = hMsgQueue->refCount; ++ while (refCount) { ++ /* Unblock thread */ ++ SYNC_SetEvent(hMsgQueue->hSyncDone); ++ /* Wait for acknowledgement */ ++ SYNC_WaitOnEvent(hMsgQueue->hSyncDoneAck, SYNC_INFINITE); ++ refCount = hMsgQueue->refCount; ++ } ++ /* Remove message queue from hMsgMgr->queueList */ ++ (void)SYNC_EnterCS(hMsgMgr->hSyncCS); ++ LST_RemoveElem(hMsgMgr->queueList, (struct LST_ELEM *)hMsgQueue); ++ /* Free the message queue object */ ++ DeleteMsgQueue(hMsgQueue, hMsgQueue->uMaxMsgs); ++ if (!hMsgMgr->msgFreeList) ++ goto func_cont; ++ if (LST_IsEmpty(hMsgMgr->msgFreeList)) ++ SYNC_ResetEvent(hMsgMgr->hSyncEvent); ++func_cont: ++ (void)SYNC_LeaveCS(hMsgMgr->hSyncCS); ++} ++ ++/* ++ * ======== WMD_MSG_Get ======== ++ * Get a message from a MSG queue. ++ */ ++DSP_STATUS WMD_MSG_Get(struct MSG_QUEUE *hMsgQueue, ++ struct DSP_MSG *pMsg, u32 uTimeout) ++{ ++ struct MSG_FRAME *pMsgFrame; ++ struct MSG_MGR *hMsgMgr; ++ bool fGotMsg = false; ++ struct SYNC_OBJECT *hSyncs[2]; ++ u32 uIndex; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(MEM_IsValidHandle(hMsgQueue, MSGQ_SIGNATURE)); ++ DBC_Require(pMsg != NULL); ++ ++ hMsgMgr = hMsgQueue->hMsgMgr; ++ if (!hMsgQueue->msgUsedList) { ++ status = DSP_EHANDLE; ++ goto func_end; ++ } ++ ++ /* Enter critical section */ ++ (void)SYNC_EnterCS(hMsgMgr->hSyncCS); ++ /* If a message is already there, get it */ ++ if (!LST_IsEmpty(hMsgQueue->msgUsedList)) { ++ pMsgFrame = (struct MSG_FRAME *)LST_GetHead(hMsgQueue-> ++ msgUsedList); ++ if (pMsgFrame != NULL) { ++ *pMsg = pMsgFrame->msgData.msg; ++ LST_PutTail(hMsgQueue->msgFreeList, ++ (struct LST_ELEM *)pMsgFrame); ++ if (LST_IsEmpty(hMsgQueue->msgUsedList)) ++ SYNC_ResetEvent(hMsgQueue->hSyncEvent); ++ ++ fGotMsg = true; ++ } ++ } else { ++ if (hMsgQueue->fDone) ++ status = DSP_EFAIL; ++ else ++ hMsgQueue->refCount++; ++ ++ } ++ /* Exit critical section */ ++ (void)SYNC_LeaveCS(hMsgMgr->hSyncCS); ++ if (DSP_SUCCEEDED(status) && !fGotMsg) { ++ /* Wait til message is available, timeout, or done. We don't ++ * have to schedule the DPC, since the DSP will send messages ++ * when they are available. */ ++ hSyncs[0] = hMsgQueue->hSyncEvent; ++ hSyncs[1] = hMsgQueue->hSyncDone; ++ status = SYNC_WaitOnMultipleEvents(hSyncs, 2, uTimeout, ++ &uIndex); ++ /* Enter critical section */ ++ (void)SYNC_EnterCS(hMsgMgr->hSyncCS); ++ if (hMsgQueue->fDone) { ++ hMsgQueue->refCount--; ++ /* Exit critical section */ ++ (void)SYNC_LeaveCS(hMsgMgr->hSyncCS); ++ /* Signal that we're not going to access hMsgQueue ++ * anymore, so it can be deleted. */ ++ (void)SYNC_SetEvent(hMsgQueue->hSyncDoneAck); ++ status = DSP_EFAIL; ++ } else { ++ if (DSP_SUCCEEDED(status)) { ++ DBC_Assert(!LST_IsEmpty(hMsgQueue-> ++ msgUsedList)); ++ /* Get msg from used list */ ++ pMsgFrame = (struct MSG_FRAME *) ++ LST_GetHead(hMsgQueue->msgUsedList); ++ /* Copy message into pMsg and put frame on the ++ * free list */ ++ if (pMsgFrame != NULL) { ++ *pMsg = pMsgFrame->msgData.msg; ++ LST_PutTail(hMsgQueue->msgFreeList, ++ (struct LST_ELEM *)pMsgFrame); ++ } ++ } ++ hMsgQueue->refCount--; ++ /* Reset the event if there are still queued messages */ ++ if (!LST_IsEmpty(hMsgQueue->msgUsedList)) ++ SYNC_SetEvent(hMsgQueue->hSyncEvent); ++ ++ /* Exit critical section */ ++ (void)SYNC_LeaveCS(hMsgMgr->hSyncCS); ++ } ++ } ++func_end: ++ return status; ++} ++ ++/* ++ * ======== WMD_MSG_Put ======== ++ * Put a message onto a MSG queue. ++ */ ++DSP_STATUS WMD_MSG_Put(struct MSG_QUEUE *hMsgQueue, ++ IN CONST struct DSP_MSG *pMsg, u32 uTimeout) ++{ ++ struct MSG_FRAME *pMsgFrame; ++ struct MSG_MGR *hMsgMgr; ++ bool fPutMsg = false; ++ struct SYNC_OBJECT *hSyncs[2]; ++ u32 uIndex; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(MEM_IsValidHandle(hMsgQueue, MSGQ_SIGNATURE)); ++ DBC_Require(pMsg != NULL); ++ ++ hMsgMgr = hMsgQueue->hMsgMgr; ++ ++ if (!hMsgMgr->msgFreeList) { ++ status = DSP_EHANDLE; ++ goto func_end; ++ } ++ ++ ++ (void) SYNC_EnterCS(hMsgMgr->hSyncCS); ++ ++ /* If a message frame is available, use it */ ++ if (!LST_IsEmpty(hMsgMgr->msgFreeList)) { ++ pMsgFrame = (struct MSG_FRAME *)LST_GetHead(hMsgMgr-> ++ msgFreeList); ++ if (pMsgFrame != NULL) { ++ pMsgFrame->msgData.msg = *pMsg; ++ pMsgFrame->msgData.dwId = hMsgQueue->dwId; ++ LST_PutTail(hMsgMgr->msgUsedList, (struct LST_ELEM *) ++ pMsgFrame); ++ hMsgMgr->uMsgsPending++; ++ fPutMsg = true; ++ } ++ if (LST_IsEmpty(hMsgMgr->msgFreeList)) ++ SYNC_ResetEvent(hMsgMgr->hSyncEvent); ++ ++ /* Release critical section before scheduling DPC */ ++ (void)SYNC_LeaveCS(hMsgMgr->hSyncCS); ++ /* Schedule a DPC, to do the actual data transfer: */ ++ IO_Schedule(hMsgMgr->hIOMgr); ++ } else { ++ if (hMsgQueue->fDone) ++ status = DSP_EFAIL; ++ else ++ hMsgQueue->refCount++; ++ ++ (void)SYNC_LeaveCS(hMsgMgr->hSyncCS); ++ } ++ if (DSP_SUCCEEDED(status) && !fPutMsg) { ++ /* Wait til a free message frame is available, timeout, ++ * or done */ ++ hSyncs[0] = hMsgMgr->hSyncEvent; ++ hSyncs[1] = hMsgQueue->hSyncDone; ++ status = SYNC_WaitOnMultipleEvents(hSyncs, 2, uTimeout, ++ &uIndex); ++ /* Enter critical section */ ++ (void)SYNC_EnterCS(hMsgMgr->hSyncCS); ++ if (hMsgQueue->fDone) { ++ hMsgQueue->refCount--; ++ /* Exit critical section */ ++ (void)SYNC_LeaveCS(hMsgMgr->hSyncCS); ++ /* Signal that we're not going to access hMsgQueue ++ * anymore, so it can be deleted. */ ++ (void)SYNC_SetEvent(hMsgQueue->hSyncDoneAck); ++ status = DSP_EFAIL; ++ } else { ++ if (DSP_SUCCEEDED(status)) { ++ if (LST_IsEmpty(hMsgMgr->msgFreeList)) { ++ status = DSP_EPOINTER; ++ goto func_cont; ++ } ++ /* Get msg from free list */ ++ pMsgFrame = (struct MSG_FRAME *) ++ LST_GetHead(hMsgMgr->msgFreeList); ++ /* Copy message into pMsg and put frame on the ++ * used list */ ++ if (pMsgFrame != NULL) { ++ pMsgFrame->msgData.msg = *pMsg; ++ pMsgFrame->msgData.dwId = ++ hMsgQueue->dwId; ++ LST_PutTail(hMsgMgr->msgUsedList, ++ (struct LST_ELEM *) ++ pMsgFrame); ++ hMsgMgr->uMsgsPending++; ++ /* Schedule a DPC, to do the actual ++ * data transfer: */ ++ IO_Schedule(hMsgMgr->hIOMgr); ++ } ++ } ++ hMsgQueue->refCount--; ++ /* Reset event if there are still frames available */ ++ if (!LST_IsEmpty(hMsgMgr->msgFreeList)) ++ SYNC_SetEvent(hMsgMgr->hSyncEvent); ++func_cont: ++ /* Exit critical section */ ++ (void) SYNC_LeaveCS(hMsgMgr->hSyncCS); ++ } ++ } ++func_end: ++ return status; ++} ++ ++/* ++ * ======== WMD_MSG_RegisterNotify ======== ++ */ ++DSP_STATUS WMD_MSG_RegisterNotify(struct MSG_QUEUE *hMsgQueue, u32 uEventMask, ++ u32 uNotifyType, ++ struct DSP_NOTIFICATION *hNotification) ++{ ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(MEM_IsValidHandle(hMsgQueue, MSGQ_SIGNATURE)); ++ DBC_Require(hNotification != NULL); ++ DBC_Require(uEventMask == DSP_NODEMESSAGEREADY || uEventMask == 0); ++ DBC_Require(uNotifyType == DSP_SIGNALEVENT); ++ ++ status = NTFY_Register(hMsgQueue->hNtfy, hNotification, uEventMask, ++ uNotifyType); ++ ++ if (status == DSP_EVALUE) { ++ /* Not registered. Ok, since we couldn't have known. Node ++ * notifications are split between node state change handled ++ * by NODE, and message ready handled by MSG. */ ++ status = DSP_SOK; ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== WMD_MSG_SetQueueId ======== ++ */ ++void WMD_MSG_SetQueueId(struct MSG_QUEUE *hMsgQueue, u32 dwId) ++{ ++ DBC_Require(MEM_IsValidHandle(hMsgQueue, MSGQ_SIGNATURE)); ++ /* DBC_Require(dwId != 0); */ ++ ++ /* ++ * A message queue must be created when a node is allocated, ++ * so that NODE_RegisterNotify() can be called before the node ++ * is created. Since we don't know the node environment until the ++ * node is created, we need this function to set hMsgQueue->dwId ++ * to the node environment, after the node is created. ++ */ ++ hMsgQueue->dwId = dwId; ++} ++ ++/* ++ * ======== AddNewMsg ======== ++ * Must be called in message manager critical section. ++ */ ++static DSP_STATUS AddNewMsg(struct LST_LIST *msgList) ++{ ++ struct MSG_FRAME *pMsg; ++ DSP_STATUS status = DSP_SOK; ++ ++ pMsg = (struct MSG_FRAME *)MEM_Calloc(sizeof(struct MSG_FRAME), ++ MEM_PAGED); ++ if (pMsg != NULL) { ++ LST_InitElem((struct LST_ELEM *) pMsg); ++ LST_PutTail(msgList, (struct LST_ELEM *) pMsg); ++ } else { ++ status = DSP_EMEMORY; ++ } ++ ++ return status; ++} ++ ++/* ++ * ======== DeleteMsgMgr ======== ++ */ ++static void DeleteMsgMgr(struct MSG_MGR *hMsgMgr) ++{ ++ DBC_Require(MEM_IsValidHandle(hMsgMgr, MSGMGR_SIGNATURE)); ++ ++ if (hMsgMgr->queueList) { ++ if (LST_IsEmpty(hMsgMgr->queueList)) { ++ LST_Delete(hMsgMgr->queueList); ++ hMsgMgr->queueList = NULL; ++ } ++ } ++ ++ if (hMsgMgr->msgFreeList) { ++ FreeMsgList(hMsgMgr->msgFreeList); ++ hMsgMgr->msgFreeList = NULL; ++ } ++ ++ if (hMsgMgr->msgUsedList) { ++ FreeMsgList(hMsgMgr->msgUsedList); ++ hMsgMgr->msgUsedList = NULL; ++ } ++ ++ if (hMsgMgr->hSyncEvent) ++ SYNC_CloseEvent(hMsgMgr->hSyncEvent); ++ ++ if (hMsgMgr->hSyncCS) ++ SYNC_DeleteCS(hMsgMgr->hSyncCS); ++ ++ MEM_FreeObject(hMsgMgr); ++} ++ ++/* ++ * ======== DeleteMsgQueue ======== ++ */ ++static void DeleteMsgQueue(struct MSG_QUEUE *hMsgQueue, u32 uNumToDSP) ++{ ++ struct MSG_MGR *hMsgMgr; ++ struct MSG_FRAME *pMsg; ++ u32 i; ++ ++ if (!MEM_IsValidHandle(hMsgQueue, MSGQ_SIGNATURE) ++ || !hMsgQueue->hMsgMgr || !hMsgQueue->hMsgMgr->msgFreeList) ++ goto func_end; ++ hMsgMgr = hMsgQueue->hMsgMgr; ++ ++ ++ /* Pull off uNumToDSP message frames from Msg manager and free */ ++ for (i = 0; i < uNumToDSP; i++) { ++ ++ if (!LST_IsEmpty(hMsgMgr->msgFreeList)) { ++ pMsg = (struct MSG_FRAME *)LST_GetHead(hMsgMgr-> ++ msgFreeList); ++ MEM_Free(pMsg); ++ } else { ++ /* Cannot free all of the message frames */ ++ break; ++ } ++ } ++ ++ if (hMsgQueue->msgFreeList) { ++ FreeMsgList(hMsgQueue->msgFreeList); ++ hMsgQueue->msgFreeList = NULL; ++ } ++ ++ if (hMsgQueue->msgUsedList) { ++ FreeMsgList(hMsgQueue->msgUsedList); ++ hMsgQueue->msgUsedList = NULL; ++ } ++ ++ ++ if (hMsgQueue->hNtfy) ++ NTFY_Delete(hMsgQueue->hNtfy); ++ ++ if (hMsgQueue->hSyncEvent) ++ SYNC_CloseEvent(hMsgQueue->hSyncEvent); ++ ++ if (hMsgQueue->hSyncDone) ++ SYNC_CloseEvent(hMsgQueue->hSyncDone); ++ ++ if (hMsgQueue->hSyncDoneAck) ++ SYNC_CloseEvent(hMsgQueue->hSyncDoneAck); ++ ++ MEM_FreeObject(hMsgQueue); ++func_end: ++ return; ++ ++} ++ ++/* ++ * ======== FreeMsgList ======== ++ */ ++static void FreeMsgList(struct LST_LIST *msgList) ++{ ++ struct MSG_FRAME *pMsg; ++ ++ if (!msgList) ++ goto func_end; ++ ++ while ((pMsg = (struct MSG_FRAME *)LST_GetHead(msgList)) != NULL) ++ MEM_Free(pMsg); ++ ++ DBC_Assert(LST_IsEmpty(msgList)); ++ ++ LST_Delete(msgList); ++func_end: ++ return; ++} ++ +diff --git a/drivers/dsp/bridge/wmd/tiomap3430.c b/drivers/dsp/bridge/wmd/tiomap3430.c +new file mode 100644 +index 0000000..2ab585d +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/tiomap3430.c +@@ -0,0 +1,2149 @@ ++/* ++ * tiomap.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== tiomap.c ======== ++ * Processor Manager Driver for TI OMAP3430 EVM. ++ * ++ * Public Function: ++ * WMD_DRV_Entry ++ * ++ *! Revision History: ++ *! ================ ++ * 26-March-2008 HK and AL: Added WMD_DEV_WalkTbl funciton. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ------------------------------------ Hardware Abstraction Layer */ ++#include ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Link Driver */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Local */ ++#include "_tiomap.h" ++#include "_tiomap_pwr.h" ++#include "_tiomap_mmu.h" ++#include "_tiomap_util.h" ++#include "tiomap_io.h" ++ ++ ++/* Offset in shared mem to write to in order to synchronize start with DSP */ ++#define SHMSYNCOFFSET 4 /* GPP byte offset */ ++ ++#define BUFFERSIZE 1024 ++ ++#define MMU_SECTION_ADDR_MASK 0xFFF00000 ++#define MMU_SSECTION_ADDR_MASK 0xFF000000 ++#define MMU_LARGE_PAGE_MASK 0xFFFF0000 ++#define MMU_SMALL_PAGE_MASK 0xFFFFF000 ++#define PAGES_II_LVL_TABLE 512 ++#define phys_to_page(phys) pfn_to_page((phys) >> PAGE_SHIFT) ++ ++#define MMU_GFLUSH 0x60 ++ ++/* Forward Declarations: */ ++static DSP_STATUS WMD_BRD_Monitor(struct WMD_DEV_CONTEXT *pDevContext); ++static DSP_STATUS WMD_BRD_Read(struct WMD_DEV_CONTEXT *pDevContext, ++ OUT u8 *pbHostBuf, ++ u32 dwDSPAddr, u32 ulNumBytes, u32 ulMemType); ++static DSP_STATUS WMD_BRD_Start(struct WMD_DEV_CONTEXT *pDevContext, ++ u32 dwDSPAddr); ++static DSP_STATUS WMD_BRD_Status(struct WMD_DEV_CONTEXT *pDevContext, ++ OUT BRD_STATUS *pdwState); ++static DSP_STATUS WMD_BRD_Stop(struct WMD_DEV_CONTEXT *pDevContext); ++static DSP_STATUS WMD_BRD_Write(struct WMD_DEV_CONTEXT *pDevContext, ++ IN u8 *pbHostBuf, ++ u32 dwDSPAddr, u32 ulNumBytes, u32 ulMemType); ++static DSP_STATUS WMD_BRD_SetState(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 ulBrdState); ++static DSP_STATUS WMD_BRD_MemCopy(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 ulDspDestAddr, u32 ulDspSrcAddr, ++ u32 ulNumBytes, u32 ulMemType); ++static DSP_STATUS WMD_BRD_MemWrite(struct WMD_DEV_CONTEXT *pDevContext, ++ IN u8 *pbHostBuf, u32 dwDSPAddr, ++ u32 ulNumBytes, u32 ulMemType); ++static DSP_STATUS WMD_BRD_MemMap(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 ulMpuAddr, u32 ulVirtAddr, u32 ulNumBytes, ++ u32 ulMapAttr); ++static DSP_STATUS WMD_BRD_MemUnMap(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 ulVirtAddr, u32 ulNumBytes); ++static DSP_STATUS WMD_DEV_Create(OUT struct WMD_DEV_CONTEXT **ppDevContext, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct CFG_HOSTRES *pConfig, ++ IN CONST struct CFG_DSPRES *pDspConfig); ++static DSP_STATUS WMD_DEV_Ctrl(struct WMD_DEV_CONTEXT *pDevContext, u32 dwCmd, ++ IN OUT void *pArgs); ++static DSP_STATUS WMD_DEV_Destroy(struct WMD_DEV_CONTEXT *pDevContext); ++static u32 user_va2pa(struct mm_struct *mm, u32 address); ++static DSP_STATUS PteUpdate(struct WMD_DEV_CONTEXT *hDevContext, u32 pa, ++ u32 va, u32 size, ++ struct HW_MMUMapAttrs_t *mapAttrs); ++static DSP_STATUS PteSet(struct PgTableAttrs *pt, u32 pa, u32 va, ++ u32 size, struct HW_MMUMapAttrs_t *attrs); ++static DSP_STATUS MemMapVmalloc(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 ulMpuAddr, u32 ulVirtAddr, ++ u32 ulNumBytes, struct HW_MMUMapAttrs_t *hwAttrs); ++static DSP_STATUS run_IdleBoot(u32 prcm_base, u32 cm_base, ++ u32 sysctrl_base); ++void GetHWRegs(u32 prcm_base, u32 cm_base); ++ ++/* ----------------------------------- Globals */ ++ ++/* Attributes of L2 page tables for DSP MMU */ ++struct PageInfo { ++ u32 numEntries; /* Number of valid PTEs in the L2 PT */ ++} ; ++ ++/* Attributes used to manage the DSP MMU page tables */ ++struct PgTableAttrs { ++ struct SYNC_CSOBJECT *hCSObj; /* Critical section object handle */ ++ ++ u32 L1BasePa; /* Physical address of the L1 PT */ ++ u32 L1BaseVa; /* Virtual address of the L1 PT */ ++ u32 L1size; /* Size of the L1 PT */ ++ u32 L1TblAllocPa; ++ /* Physical address of Allocated mem for L1 table. May not be aligned */ ++ u32 L1TblAllocVa; ++ /* Virtual address of Allocated mem for L1 table. May not be aligned */ ++ u32 L1TblAllocSz; ++ /* Size of consistent memory allocated for L1 table. ++ * May not be aligned */ ++ ++ u32 L2BasePa; /* Physical address of the L2 PT */ ++ u32 L2BaseVa; /* Virtual address of the L2 PT */ ++ u32 L2size; /* Size of the L2 PT */ ++ u32 L2TblAllocPa; ++ /* Physical address of Allocated mem for L2 table. May not be aligned */ ++ u32 L2TblAllocVa; ++ /* Virtual address of Allocated mem for L2 table. May not be aligned */ ++ u32 L2TblAllocSz; ++ /* Size of consistent memory allocated for L2 table. ++ * May not be aligned */ ++ ++ u32 L2NumPages; /* Number of allocated L2 PT */ ++ struct PageInfo *pgInfo; /* Array [L2NumPages] of L2 PT info structs */ ++} ; ++ ++/* ++ * If dsp_debug is true, do not branch to the DSP entry point and wait for DSP ++ * to boot ++ */ ++extern s32 dsp_debug; ++ ++ ++/* ++ * This mini driver's function interface table. ++ */ ++static struct WMD_DRV_INTERFACE drvInterfaceFxns = { ++ WCD_MAJOR_VERSION, /* WCD ver. for which this mini driver is built. */ ++ WCD_MINOR_VERSION, ++ WMD_DEV_Create, ++ WMD_DEV_Destroy, ++ WMD_DEV_Ctrl, ++ WMD_BRD_Monitor, ++ WMD_BRD_Start, ++ WMD_BRD_Stop, ++ WMD_BRD_Status, ++ WMD_BRD_Read, ++ WMD_BRD_Write, ++ WMD_BRD_SetState, ++ WMD_BRD_MemCopy, ++ WMD_BRD_MemWrite, ++ WMD_BRD_MemMap, ++ WMD_BRD_MemUnMap, ++ /* The following CHNL functions are provided by chnl_io.lib: */ ++ WMD_CHNL_Create, ++ WMD_CHNL_Destroy, ++ WMD_CHNL_Open, ++ WMD_CHNL_Close, ++ WMD_CHNL_AddIOReq, ++ WMD_CHNL_GetIOC, ++ WMD_CHNL_CancelIO, ++ WMD_CHNL_FlushIO, ++ WMD_CHNL_GetInfo, ++ WMD_CHNL_GetMgrInfo, ++ WMD_CHNL_Idle, ++ WMD_CHNL_RegisterNotify, ++ /* The following DEH functions are provided by tihelen_ue_deh.c */ ++ WMD_DEH_Create, ++ WMD_DEH_Destroy, ++ WMD_DEH_Notify, ++ WMD_DEH_RegisterNotify, ++ WMD_DEH_GetInfo, ++ /* The following IO functions are provided by chnl_io.lib: */ ++ WMD_IO_Create, ++ WMD_IO_Destroy, ++ WMD_IO_OnLoaded, ++ WMD_IO_GetProcLoad, ++ /* The following MSG functions are provided by chnl_io.lib: */ ++ WMD_MSG_Create, ++ WMD_MSG_CreateQueue, ++ WMD_MSG_Delete, ++ WMD_MSG_DeleteQueue, ++ WMD_MSG_Get, ++ WMD_MSG_Put, ++ WMD_MSG_RegisterNotify, ++ WMD_MSG_SetQueueId, ++}; ++ ++static inline void tlb_flush_all(const u32 base) ++{ ++ __raw_writeb(__raw_readb(base + MMU_GFLUSH) | 1, base + MMU_GFLUSH); ++} ++ ++static inline void flush_all(struct WMD_DEV_CONTEXT *pDevContext) ++{ ++ struct CFG_HOSTRES resources; ++ u32 temp = 0; ++ ++ CFG_GetHostResources((struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); ++ HW_PWRST_IVA2RegGet(resources.dwPrmBase, &temp); ++ ++ if ((temp & HW_PWR_STATE_ON) == HW_PWR_STATE_OFF || ++ (temp & HW_PWR_STATE_ON) == HW_PWR_STATE_RET) { ++ CLK_Enable(SERVICESCLK_iva2_ck); ++ WakeDSP(pDevContext, NULL); ++ tlb_flush_all(pDevContext->dwDSPMmuBase); ++ CLK_Disable(SERVICESCLK_iva2_ck); ++ } else ++ tlb_flush_all(pDevContext->dwDSPMmuBase); ++} ++ ++static void bad_page_dump(u32 pa, struct page *pg) ++{ ++ pr_emerg("DSPBRIDGE: MAP function: COUNT 0 FOR PA 0x%x\n", pa); ++ pr_emerg("Bad page state in process '%s'\n" ++ "page:%p flags:0x%0*lx mapping:%p mapcount:%d count:%d\n" ++ "Backtrace:\n", ++ current->comm, pg, (int)(2*sizeof(unsigned long)), ++ (unsigned long)pg->flags, pg->mapping, ++ page_mapcount(pg), page_count(pg)); ++ BUG(); ++} ++ ++/* ++ * ======== WMD_DRV_Entry ======== ++ * purpose: ++ * Mini Driver entry point. ++ */ ++void WMD_DRV_Entry(OUT struct WMD_DRV_INTERFACE **ppDrvInterface, ++ IN CONST char *pstrWMDFileName) ++{ ++ ++ DBC_Require(pstrWMDFileName != NULL); ++ DBG_Trace(DBG_ENTER, "In the WMD_DRV_Entry \n"); ++ ++ IO_SM_init(); /* Initialization of io_sm module */ ++ ++ if (strcmp(pstrWMDFileName, "UMA") == 0) ++ *ppDrvInterface = &drvInterfaceFxns; ++ else ++ DBG_Trace(DBG_LEVEL7, "WMD_DRV_Entry Unknown WMD file name"); ++ ++} ++ ++/* ++ * ======== WMD_BRD_Monitor ======== ++ * purpose: ++ * This WMD_BRD_Monitor puts DSP into a Loadable state. ++ * i.e Application can load and start the device. ++ * ++ * Preconditions: ++ * Device in 'OFF' state. ++ */ ++static DSP_STATUS WMD_BRD_Monitor(struct WMD_DEV_CONTEXT *hDevContext) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DEV_CONTEXT *pDevContext = hDevContext; ++ struct CFG_HOSTRES resources; ++ u32 temp; ++ enum HW_PwrState_t pwrState; ++ ++ DBG_Trace(DBG_ENTER, "Board in the monitor state \n"); ++ status = CFG_GetHostResources( ++ (struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); ++ if (DSP_FAILED(status)) ++ goto error_return; ++ ++ GetHWRegs(resources.dwPrmBase, resources.dwCmBase); ++ HW_PWRST_IVA2RegGet(resources.dwPrmBase, &temp); ++ if ((temp & 0x03) != 0x03 || (temp & 0x03) != 0x02) { ++ /* IVA2 is not in ON state */ ++ /* Read and set PM_PWSTCTRL_IVA2 to ON */ ++ HW_PWR_IVA2PowerStateSet(resources.dwPrmBase, ++ HW_PWR_DOMAIN_DSP, ++ HW_PWR_STATE_ON); ++ /* Set the SW supervised state transition */ ++ HW_PWR_CLKCTRL_IVA2RegSet(resources.dwCmBase, ++ HW_SW_SUP_WAKEUP); ++ /* Wait until the state has moved to ON */ ++ HW_PWR_IVA2StateGet(resources.dwPrmBase, HW_PWR_DOMAIN_DSP, ++ &pwrState); ++ /* Disable Automatic transition */ ++ HW_PWR_CLKCTRL_IVA2RegSet(resources.dwCmBase, HW_AUTOTRANS_DIS); ++ } ++ DBG_Trace(DBG_LEVEL6, "WMD_BRD_Monitor - Middle ****** \n"); ++ GetHWRegs(resources.dwPrmBase, resources.dwCmBase); ++ status = run_IdleBoot(resources.dwPrmBase, resources.dwCmBase, ++ resources.dwSysCtrlBase); ++ if (DSP_SUCCEEDED(status)) { ++ /* set the device state to IDLE */ ++ pDevContext->dwBrdState = BRD_IDLE; ++ ++ } ++error_return: ++ DBG_Trace(DBG_LEVEL6, "WMD_BRD_Monitor - End ****** \n"); ++ GetHWRegs(resources.dwPrmBase, resources.dwCmBase); ++ return status; ++} ++ ++/* ++ * ======== WMD_BRD_Read ======== ++ * purpose: ++ * Reads buffers for DSP memory. ++ */ ++static DSP_STATUS WMD_BRD_Read(struct WMD_DEV_CONTEXT *hDevContext, ++ OUT u8 *pbHostBuf, u32 dwDSPAddr, ++ u32 ulNumBytes, u32 ulMemType) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DEV_CONTEXT *pDevContext = hDevContext; ++ u32 offset; ++ u32 dspBaseAddr = hDevContext->dwDspBaseAddr; ++ ++ DBG_Trace(DBG_ENTER, "WMD_BRD_Read, pDevContext: 0x%x\n\t\tpbHostBuf:" ++ " 0x%x\n\t\tdwDSPAddr: 0x%x\n\t\tulNumBytes: 0x%x\n\t\t" ++ "ulMemType: 0x%x\n", pDevContext, pbHostBuf, ++ dwDSPAddr, ulNumBytes, ulMemType); ++ if (dwDSPAddr < pDevContext->dwDSPStartAdd) { ++ DBG_Trace(DBG_LEVEL7, ++ "WMD_BRD_Read: DSP address < start address \n "); ++ status = DSP_EFAIL; ++ return status; ++ } ++ /* change here to account for the 3 bands of the DSP internal memory */ ++ if ((dwDSPAddr - pDevContext->dwDSPStartAdd) < ++ pDevContext->dwInternalSize) { ++ offset = dwDSPAddr - pDevContext->dwDSPStartAdd; ++ } else { ++ DBG_Trace(DBG_LEVEL1, ++ "**** Reading From external memory **** \n "); ++ status = ReadExtDspData(pDevContext, pbHostBuf, dwDSPAddr, ++ ulNumBytes, ulMemType); ++ return status; ++ } ++ /* copy the data from DSP memory, */ ++ memcpy(pbHostBuf, (void *)(dspBaseAddr + offset), ulNumBytes); ++ return status; ++} ++ ++/* ++ * ======== WMD_BRD_SetState ======== ++ * purpose: ++ * This routine updates the Board status. ++ */ ++static DSP_STATUS WMD_BRD_SetState(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 ulBrdState) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DEV_CONTEXT *pDevContext = hDevContext; ++ ++ DBG_Trace(DBG_ENTER, "WMD_BRD_SetState: Board State: 0x%x \n", ++ ulBrdState); ++ pDevContext->dwBrdState = ulBrdState; ++ return status; ++} ++ ++/* ++ * ======== WMD_BRD_Start ======== ++ * purpose: ++ * Initializes DSP MMU and Starts DSP. ++ * ++ * Preconditions: ++ * a) DSP domain is 'ACTIVE'. ++ * b) DSP_RST1 is asserted. ++ * b) DSP_RST2 is released. ++ */ ++static DSP_STATUS WMD_BRD_Start(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 dwDSPAddr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DEV_CONTEXT *pDevContext = hDevContext; ++ u32 dwSyncAddr = 0; ++ u32 ulShmBase; /* Gpp Phys SM base addr(byte) */ ++ u32 ulShmBaseVirt; /* Dsp Virt SM base addr */ ++ u32 ulTLBBaseVirt; /* Base of MMU TLB entry */ ++ u32 ulShmOffsetVirt; /* offset of ulShmBaseVirt from ulTLBBaseVirt */ ++ s32 iEntryNdx; ++ s32 itmpEntryNdx = 0; /* DSP-MMU TLB entry base address */ ++ struct CFG_HOSTRES resources; ++ u32 temp; ++ u32 ulDspClkRate; ++ u32 ulDspClkAddr; ++ u32 ulBiosGpTimer; ++ u32 uClkCmd; ++ struct IO_MGR *hIOMgr; ++ u32 ulLoadMonitorTimer; ++ u32 extClkId = 0; ++ u32 tmpIndex; ++ u32 clkIdIndex = MBX_PM_MAX_RESOURCES; ++ ++ DBG_Trace(DBG_ENTER, "Entering WMD_BRD_Start:\n hDevContext: 0x%x\n\t " ++ "dwDSPAddr: 0x%x\n", hDevContext, dwDSPAddr); ++ ++ /* The device context contains all the mmu setup info from when the ++ * last dsp base image was loaded. The first entry is always ++ * SHMMEM base. */ ++ /* Get SHM_BEG - convert to byte address */ ++ (void) DEV_GetSymbol(pDevContext->hDevObject, SHMBASENAME, ++ &ulShmBaseVirt); ++ ulShmBaseVirt *= DSPWORDSIZE; ++ DBC_Assert(ulShmBaseVirt != 0); ++ /* DSP Virtual address */ ++ ulTLBBaseVirt = pDevContext->aTLBEntry[0].ulDspVa; ++ DBC_Assert(ulTLBBaseVirt <= ulShmBaseVirt); ++ ulShmOffsetVirt = ulShmBaseVirt - (ulTLBBaseVirt * DSPWORDSIZE); ++ /* Kernel logical address */ ++ ulShmBase = pDevContext->aTLBEntry[0].ulGppVa + ulShmOffsetVirt; ++ ++ DBC_Assert(ulShmBase != 0); ++ /* 2nd wd is used as sync field */ ++ dwSyncAddr = ulShmBase + SHMSYNCOFFSET; ++ /* Write a signature into the SHM base + offset; this will ++ * get cleared when the DSP program starts. */ ++ if ((ulShmBaseVirt == 0) || (ulShmBase == 0)) { ++ DBG_Trace(DBG_LEVEL6, "WMD_BRD_Start: Illegal SM base\n"); ++ status = DSP_EFAIL; ++ } else ++ *((volatile u32 *)dwSyncAddr) = 0xffffffff; ++ ++ if (DSP_SUCCEEDED(status)) { ++ status = CFG_GetHostResources( ++ (struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), ++ &resources); ++ /* Assert RST1 i.e only the RST only for DSP megacell */ ++ /* HW_RST_Reset(resources.dwPrcmBase, HW_RST1_IVA2);*/ ++ if (DSP_SUCCEEDED(status)) { ++ HW_RST_Reset(resources.dwPrmBase, HW_RST1_IVA2); ++ if (dsp_debug) { ++ /* Set the bootmode to self loop */ ++ DBG_Trace(DBG_LEVEL7, ++ "Set boot mode to self loop" ++ " for IVA2 Device\n"); ++ HW_DSPSS_BootModeSet(resources.dwSysCtrlBase, ++ HW_DSPSYSC_SELFLOOPBOOT, dwDSPAddr); ++ } else { ++ /* Set the bootmode to '0' - direct boot */ ++ DBG_Trace(DBG_LEVEL7, ++ "Set boot mode to direct" ++ " boot for IVA2 Device \n"); ++ HW_DSPSS_BootModeSet(resources.dwSysCtrlBase, ++ HW_DSPSYSC_DIRECTBOOT, dwDSPAddr); ++ } ++ } ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Reset and Unreset the RST2, so that BOOTADDR is copied to ++ * IVA2 SYSC register */ ++ HW_RST_Reset(resources.dwPrmBase, HW_RST2_IVA2); ++ udelay(100); ++ HW_RST_UnReset(resources.dwPrmBase, HW_RST2_IVA2); ++ udelay(100); ++ DBG_Trace(DBG_LEVEL6, "WMD_BRD_Start 0 ****** \n"); ++ GetHWRegs(resources.dwPrmBase, resources.dwCmBase); ++ /* Disbale the DSP MMU */ ++ HW_MMU_Disable(resources.dwDmmuBase); ++ /* Disable TWL */ ++ HW_MMU_TWLDisable(resources.dwDmmuBase); ++ ++ /* Only make TLB entry if both addresses are non-zero */ ++ for (iEntryNdx = 0; iEntryNdx < WMDIOCTL_NUMOFMMUTLB; ++ iEntryNdx++) { ++ if ((pDevContext->aTLBEntry[iEntryNdx].ulGppPa != 0) && ++ (pDevContext->aTLBEntry[iEntryNdx].ulDspVa != 0)) { ++ DBG_Trace(DBG_LEVEL4, "** (proc) MMU %d GppPa:" ++ " 0x%x DspVa 0x%x Size 0x%x\n", ++ itmpEntryNdx, ++ pDevContext->aTLBEntry[iEntryNdx].ulGppPa, ++ pDevContext->aTLBEntry[iEntryNdx].ulDspVa, ++ pDevContext->aTLBEntry[iEntryNdx].ulSize); ++ configureDspMmu(pDevContext, ++ pDevContext->aTLBEntry[iEntryNdx].ulGppPa, ++ pDevContext->aTLBEntry[iEntryNdx].ulDspVa * ++ DSPWORDSIZE, ++ pDevContext->aTLBEntry[iEntryNdx].ulSize, ++ itmpEntryNdx, ++ pDevContext->aTLBEntry[iEntryNdx].endianism, ++ pDevContext->aTLBEntry[iEntryNdx].elemSize, ++ pDevContext->aTLBEntry[iEntryNdx]. ++ mixedMode); ++ itmpEntryNdx++; ++ } ++ } /* end for */ ++ } ++ ++ /* Lock the above TLB entries and get the BIOS and load monitor timer ++ * information*/ ++ if (DSP_SUCCEEDED(status)) { ++ HW_MMU_NumLockedSet(resources.dwDmmuBase, itmpEntryNdx); ++ HW_MMU_VictimNumSet(resources.dwDmmuBase, itmpEntryNdx); ++ HW_MMU_TTBSet(resources.dwDmmuBase, ++ pDevContext->pPtAttrs->L1BasePa); ++ HW_MMU_TWLEnable(resources.dwDmmuBase); ++ /* Enable the SmartIdle and AutoIdle bit for MMU_SYSCONFIG */ ++ ++ temp = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwDmmuBase) + 0x10)); ++ temp = (temp & 0xFFFFFFEF) | 0x11; ++ *((REG_UWORD32 *) ((u32) (resources.dwDmmuBase) + 0x10)) = ++ (u32) temp; ++ ++ /* Let the DSP MMU run */ ++ HW_MMU_Enable(resources.dwDmmuBase); ++ ++ /* Enable the BIOS clock */ ++ (void)DEV_GetSymbol(pDevContext->hDevObject, ++ BRIDGEINIT_BIOSGPTIMER, ++ &ulBiosGpTimer); ++ DBG_Trace(DBG_LEVEL7, "BIOS GPTimer : 0x%x\n", ulBiosGpTimer); ++ (void)DEV_GetSymbol(pDevContext->hDevObject, ++ BRIDGEINIT_LOADMON_GPTIMER, ++ &ulLoadMonitorTimer); ++ DBG_Trace(DBG_LEVEL7, "Load Monitor Timer : 0x%x\n", ++ ulLoadMonitorTimer); ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ if (ulLoadMonitorTimer != 0xFFFF) { ++ uClkCmd = (BPWR_DisableClock << MBX_PM_CLK_CMDSHIFT) | ++ ulLoadMonitorTimer; ++ DBG_Trace(DBG_LEVEL7, ++ "encoded LoadMonitor cmd for Disable: 0x%x\n", ++ uClkCmd); ++ DSPPeripheralClkCtrl(pDevContext, &uClkCmd); ++ ++ extClkId = uClkCmd & MBX_PM_CLK_IDMASK; ++ for (tmpIndex = 0; tmpIndex < MBX_PM_MAX_RESOURCES; ++ tmpIndex++) { ++ if (extClkId == BPWR_CLKID[tmpIndex]) { ++ clkIdIndex = tmpIndex; ++ break; ++ } ++ } ++ ++ if (clkIdIndex < MBX_PM_MAX_RESOURCES) ++ status = CLK_Set_32KHz( ++ BPWR_Clks[clkIdIndex].funClk); ++ else ++ status = DSP_EFAIL; ++ ++ if (DSP_FAILED(status)) { ++ DBG_Trace(DBG_LEVEL7, " Error while setting" ++ "LM Timer to 32KHz\n"); ++ } ++ uClkCmd = (BPWR_EnableClock << MBX_PM_CLK_CMDSHIFT) | ++ ulLoadMonitorTimer; ++ DBG_Trace(DBG_LEVEL7, ++ "encoded LoadMonitor cmd for Enable : 0x%x\n", ++ uClkCmd); ++ DSPPeripheralClkCtrl(pDevContext, &uClkCmd); ++ ++ } else { ++ DBG_Trace(DBG_LEVEL7, ++ "Not able to get the symbol for Load " ++ "Monitor Timer\n"); ++ } ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ if (ulBiosGpTimer != 0xFFFF) { ++ uClkCmd = (BPWR_DisableClock << MBX_PM_CLK_CMDSHIFT) | ++ ulBiosGpTimer; ++ DBG_Trace(DBG_LEVEL7, "encoded BIOS GPTimer cmd for" ++ "Disable: 0x%x\n", uClkCmd); ++ DSPPeripheralClkCtrl(pDevContext, &uClkCmd); ++ ++ extClkId = uClkCmd & MBX_PM_CLK_IDMASK; ++ ++ for (tmpIndex = 0; tmpIndex < MBX_PM_MAX_RESOURCES; ++ tmpIndex++) { ++ if (extClkId == BPWR_CLKID[tmpIndex]) { ++ clkIdIndex = tmpIndex; ++ break; ++ } ++ } ++ ++ if (clkIdIndex < MBX_PM_MAX_RESOURCES) ++ status = CLK_Set_32KHz( ++ BPWR_Clks[clkIdIndex].funClk); ++ else ++ status = DSP_EFAIL; ++ ++ if (DSP_FAILED(status)) { ++ DBG_Trace(DBG_LEVEL7, ++ " Error while setting BIOS Timer to 32KHz\n"); ++ } ++ ++ uClkCmd = (BPWR_EnableClock << MBX_PM_CLK_CMDSHIFT) | ++ ulBiosGpTimer; ++ DBG_Trace(DBG_LEVEL7, "encoded BIOS GPTimer cmd :" ++ "0x%x\n", uClkCmd); ++ DSPPeripheralClkCtrl(pDevContext, &uClkCmd); ++ ++ } else { ++ DBG_Trace(DBG_LEVEL7, ++ "Not able to get the symbol for BIOS Timer\n"); ++ } ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Set the DSP clock rate */ ++ (void)DEV_GetSymbol(pDevContext->hDevObject, ++ "_BRIDGEINIT_DSP_FREQ", &ulDspClkAddr); ++ /*Set Autoidle Mode for IVA2 PLL */ ++ temp = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwCmBase) + 0x34)); ++ temp = (temp & 0xFFFFFFFE) | 0x1; ++ *((REG_UWORD32 *) ((u32) (resources.dwCmBase) + 0x34)) = ++ (u32) temp; ++ DBG_Trace(DBG_LEVEL5, "WMD_BRD_Start: _BRIDGE_DSP_FREQ Addr:" ++ "0x%x \n", ulDspClkAddr); ++ if ((unsigned int *)ulDspClkAddr != NULL) { ++ /* Get the clock rate */ ++ status = CLK_GetRate(SERVICESCLK_iva2_ck, ++ &ulDspClkRate); ++ DBG_Trace(DBG_LEVEL5, ++ "WMD_BRD_Start: DSP clock rate (KHZ): 0x%x \n", ++ ulDspClkRate); ++ (void)WMD_BRD_Write(pDevContext, (u8 *)&ulDspClkRate, ++ ulDspClkAddr, sizeof(u32), 0); ++ } ++/*PM_IVA2GRPSEL_PER = 0xC0;*/ ++ temp = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerPmBase) + 0xA8)); ++ temp = (temp & 0xFFFFFF30) | 0xC0; ++ *((REG_UWORD32 *) ((u32) (resources.dwPerPmBase) + 0xA8)) = ++ (u32) temp; ++ ++/*PM_MPUGRPSEL_PER &= 0xFFFFFF3F;*/ ++ temp = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerPmBase) + 0xA4)); ++ temp = (temp & 0xFFFFFF3F); ++ *((REG_UWORD32 *) ((u32) (resources.dwPerPmBase) + 0xA4)) = ++ (u32) temp; ++/*CM_SLEEPDEP_PER |= 0x04;*/ ++ temp = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerBase) + 0x44)); ++ temp = (temp & 0xFFFFFFFB) | 0x04; ++ *((REG_UWORD32 *) ((u32) (resources.dwPerBase) + 0x44)) = ++ (u32) temp; ++ ++/*CM_CLKSTCTRL_IVA2 = 0x00000003 -To Allow automatic transitions*/ ++ temp = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwCmBase) + 0x48)); ++ temp = (temp & 0xFFFFFFFC) | 0x03; ++ *((REG_UWORD32 *) ((u32) (resources.dwCmBase) + 0x48)) = ++ (u32) temp; ++ ++ ++ /* Enable Mailbox events and also drain any pending ++ * stale messages */ ++ (void)CHNLSM_EnableInterrupt(pDevContext); ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ HW_RSTCTRL_RegGet(resources.dwPrmBase, HW_RST1_IVA2, &temp); ++ DBG_Trace(DBG_LEVEL7, "BRD_Start: RM_RSTCTRL_DSP = 0x%x \n", ++ temp); ++ HW_RSTST_RegGet(resources.dwPrmBase, HW_RST1_IVA2, &temp); ++ DBG_Trace(DBG_LEVEL7, "BRD_Start0: RM_RSTST_DSP = 0x%x \n", ++ temp); ++ ++ /* Let DSP go */ ++ DBG_Trace(DBG_LEVEL7, "Unreset, WMD_BRD_Start\n"); ++ /* Enable DSP MMU Interrupts */ ++ HW_MMU_EventEnable(resources.dwDmmuBase, ++ HW_MMU_ALL_INTERRUPTS); ++ /* release the RST1, DSP starts executing now .. */ ++ HW_RST_UnReset(resources.dwPrmBase, HW_RST1_IVA2); ++ ++ HW_RSTST_RegGet(resources.dwPrmBase, HW_RST1_IVA2, &temp); ++ DBG_Trace(DBG_LEVEL7, "BRD_Start: RM_RSTST_DSP = 0x%x \n", ++ temp); ++ HW_RSTCTRL_RegGet(resources.dwPrmBase, HW_RST1_IVA2, &temp); ++ DBG_Trace(DBG_LEVEL5, "WMD_BRD_Start: CM_RSTCTRL_DSP: 0x%x \n", ++ temp); ++ DBG_Trace(DBG_LEVEL7, "Driver waiting for Sync @ 0x%x \n", ++ dwSyncAddr); ++ DBG_Trace(DBG_LEVEL7, "DSP c_int00 Address = 0x%x \n", ++ dwDSPAddr); ++ if (dsp_debug) ++ while (*((volatile u16 *)dwSyncAddr)) ++ ;; ++ } ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Wait for DSP to clear word in shared memory */ ++ /* Read the Location */ ++ if (!WaitForStart(pDevContext, dwSyncAddr)) { ++ status = WMD_E_TIMEOUT; ++ DBG_Trace(DBG_LEVEL7, ++ "WMD_BRD_Start Failed to Synchronize\n"); ++ } ++ status = DEV_GetIOMgr(pDevContext->hDevObject, &hIOMgr); ++ if (DSP_SUCCEEDED(status)) { ++ IO_SHMsetting(hIOMgr, SHM_OPPINFO, NULL); ++ DBG_Trace(DBG_LEVEL7, ++ "WMD_BRD_Start: OPP information initialzed\n"); ++ /* Write the synchronization bit to indicate the ++ * completion of OPP table update to DSP ++ */ ++ *((volatile u32 *)dwSyncAddr) = 0XCAFECAFE; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* update board state */ ++ pDevContext->dwBrdState = BRD_RUNNING; ++ /* (void)CHNLSM_EnableInterrupt(pDevContext);*/ ++ DBG_Trace(DBG_LEVEL7, "Device Started \n "); ++ } else { ++ pDevContext->dwBrdState = BRD_UNKNOWN; ++ DBG_Trace(DBG_LEVEL7, "Device not Started \n "); ++ } ++ } ++ return status; ++} ++ ++/* ++ * ======== WMD_BRD_Stop ======== ++ * purpose: ++ * Puts DSP in self loop. ++ * ++ * Preconditions : ++ * a) None ++ */ ++static DSP_STATUS WMD_BRD_Stop(struct WMD_DEV_CONTEXT *hDevContext) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DEV_CONTEXT *pDevContext = hDevContext; ++ struct CFG_HOSTRES resources; ++ struct PgTableAttrs *pPtAttrs; ++ u32 dspPwrState; ++ DSP_STATUS clk_status; ++ ++ DBG_Trace(DBG_ENTER, "Entering WMD_BRD_Stop:\nhDevContext: 0x%x\n", ++ hDevContext); ++ ++ /* Disable the mail box interrupts */ ++ (void)CHNLSM_DisableInterrupt(pDevContext); ++ ++ if (pDevContext->dwBrdState == BRD_STOPPED) ++ return status; ++ ++ /* as per TRM, it is advised to first drive the IVA2 to 'Standby' mode, ++ * before turning off the clocks.. This is to ensure that there are no ++ * pending L3 or other transactons from IVA2 */ ++ status = CFG_GetHostResources( ++ (struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), ++ &resources); ++ if (DSP_FAILED(status)) { ++ DBG_Trace(DBG_LEVEL7, ++ "WMD_BRD_Stop: Get Host resources failed \n"); ++ DBG_Trace(DBG_LEVEL1, "Device Stopp failed \n "); ++ return DSP_EFAIL; ++ } ++ ++ HW_PWRST_IVA2RegGet(resources.dwPrmBase, &dspPwrState); ++ if (dspPwrState != HW_PWR_STATE_OFF) { ++ ++ CHNLSM_InterruptDSP2(pDevContext, MBX_PM_DSPIDLE); ++ ++ mdelay(10); ++ ++ GetHWRegs(resources.dwPrmBase, resources.dwCmBase); ++ ++ run_IdleBoot(resources.dwPrmBase, resources.dwCmBase, ++ resources.dwSysCtrlBase); ++ ++ udelay(50); ++ ++ clk_status = CLK_Disable(SERVICESCLK_iva2_ck); ++ if (DSP_FAILED(clk_status)) { ++ DBG_Trace(DBG_LEVEL6, ++ "\n WMD_BRD_Stop: CLK_Disable failed " ++ "for iva2_fck\n"); ++ } ++ /* IVA2 is not in OFF state */ ++ /* Set PM_PWSTCTRL_IVA2 to OFF */ ++ HW_PWR_IVA2PowerStateSet(resources.dwPrmBase, ++ HW_PWR_DOMAIN_DSP, ++ HW_PWR_STATE_OFF); ++ /* Set the SW supervised state transition for Sleep */ ++ HW_PWR_CLKCTRL_IVA2RegSet(resources.dwCmBase, ++ HW_SW_SUP_SLEEP); ++ } else { ++ clk_status = CLK_Disable(SERVICESCLK_iva2_ck); ++ if (DSP_FAILED(clk_status)) { ++ DBG_Trace(DBG_LEVEL6, ++ "\n WMD_BRD_Stop: Else loop CLK_Disable failed" ++ " for iva2_fck\n"); ++ } ++ } ++ udelay(10); ++ /* Release the Ext Base virtual Address as the next DSP Program ++ * may have a different load address */ ++ if (pDevContext->dwDspExtBaseAddr) ++ pDevContext->dwDspExtBaseAddr = 0; ++ ++ pDevContext->dwBrdState = BRD_STOPPED; /* update board state */ ++ DBG_Trace(DBG_LEVEL1, "Device Stopped \n "); ++ /* This is a good place to clear the MMU page tables as well */ ++ if (pDevContext->pPtAttrs) { ++ pPtAttrs = pDevContext->pPtAttrs; ++ memset((u8 *) pPtAttrs->L1BaseVa, 0x00, pPtAttrs->L1size); ++ memset((u8 *) pPtAttrs->L2BaseVa, 0x00, pPtAttrs->L2size); ++ memset((u8 *) pPtAttrs->pgInfo, 0x00, ++ (pPtAttrs->L2NumPages * sizeof(struct PageInfo))); ++ } ++ ++ DBG_Trace(DBG_LEVEL6, "WMD_BRD_Stop - End ****** \n"); ++ ++ return status; ++} ++ ++ ++/* ++ * ======== WMD_BRD_Delete ======== ++ * purpose: ++ * Puts DSP in Low power mode ++ * ++ * Preconditions : ++ * a) None ++ */ ++static DSP_STATUS WMD_BRD_Delete(struct WMD_DEV_CONTEXT *hDevContext) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DEV_CONTEXT *pDevContext = hDevContext; ++ struct CFG_HOSTRES resources; ++ struct PgTableAttrs *pPtAttrs; ++ DSP_STATUS clk_status; ++ ++ DBG_Trace(DBG_ENTER, "Entering WMD_BRD_Delete:\nhDevContext: 0x%x\n", ++ hDevContext); ++ ++ /* Disable the mail box interrupts */ ++ (void) CHNLSM_DisableInterrupt(pDevContext); ++ ++ if (pDevContext->dwBrdState == BRD_STOPPED) ++ return status; ++ ++ /* as per TRM, it is advised to first drive ++ * the IVA2 to 'Standby' mode, before turning off the clocks.. This is ++ * to ensure that there are no pending L3 or other transactons from ++ * IVA2 */ ++ status = CFG_GetHostResources( ++ (struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); ++ if (DSP_FAILED(status)) { ++ DBG_Trace(DBG_LEVEL7, ++ "WMD_BRD_Stop: Get Host resources failed \n"); ++ DBG_Trace(DBG_LEVEL1, "Device Delete failed \n "); ++ return DSP_EFAIL; ++ } ++ status = SleepDSP(pDevContext, PWR_EMERGENCYDEEPSLEEP, NULL); ++ clk_status = CLK_Disable(SERVICESCLK_iva2_ck); ++ if (DSP_FAILED(clk_status)) { ++ DBG_Trace(DBG_LEVEL6, "\n WMD_BRD_Stop: CLK_Disable failed for" ++ " iva2_fck\n"); ++ } ++ /* Release the Ext Base virtual Address as the next DSP Program ++ * may have a different load address */ ++ if (pDevContext->dwDspExtBaseAddr) ++ pDevContext->dwDspExtBaseAddr = 0; ++ ++ pDevContext->dwBrdState = BRD_STOPPED; /* update board state */ ++ DBG_Trace(DBG_LEVEL1, "Device Stopped \n "); ++ /* This is a good place to clear the MMU page tables as well */ ++ if (pDevContext->pPtAttrs) { ++ pPtAttrs = pDevContext->pPtAttrs; ++ memset((u8 *)pPtAttrs->L1BaseVa, 0x00, pPtAttrs->L1size); ++ memset((u8 *)pPtAttrs->L2BaseVa, 0x00, pPtAttrs->L2size); ++ memset((u8 *)pPtAttrs->pgInfo, 0x00, ++ (pPtAttrs->L2NumPages * sizeof(struct PageInfo))); ++ } ++ DBG_Trace(DBG_LEVEL6, "WMD_BRD_Stop - End ****** \n"); ++ return status; ++} ++ ++ ++/* ++ * ======== WMD_BRD_Status ======== ++ * Returns the board status. ++ */ ++static DSP_STATUS WMD_BRD_Status(struct WMD_DEV_CONTEXT *hDevContext, ++ OUT BRD_STATUS *pdwState) ++{ ++ struct WMD_DEV_CONTEXT *pDevContext = hDevContext; ++ *pdwState = pDevContext->dwBrdState; ++ return DSP_SOK; ++} ++ ++/* ++ * ======== WMD_BRD_Write ======== ++ * Copies the buffers to DSP internal or external memory. ++ */ ++static DSP_STATUS WMD_BRD_Write(struct WMD_DEV_CONTEXT *hDevContext, ++ IN u8 *pbHostBuf, u32 dwDSPAddr, ++ u32 ulNumBytes, u32 ulMemType) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DEV_CONTEXT *pDevContext = hDevContext; ++ ++ DBG_Trace(DBG_ENTER, "WMD_BRD_Write, pDevContext: 0x%x\n\t\t " ++ "pbHostBuf: 0x%x\n\t\tdwDSPAddr: 0x%x\n\t\tulNumBytes: " ++ "0x%x\n \t\t ulMemtype: 0x%x\n", pDevContext, pbHostBuf, ++ dwDSPAddr, ulNumBytes, ulMemType); ++ if (dwDSPAddr < pDevContext->dwDSPStartAdd) { ++ DBG_Trace(DBG_LEVEL7, ++ "WMD_BRD_Write: DSP address < start address \n "); ++ status = DSP_EFAIL; ++ return status; ++ } ++ if ((dwDSPAddr - pDevContext->dwDSPStartAdd) < ++ pDevContext->dwInternalSize) { ++ status = WriteDspData(hDevContext, pbHostBuf, dwDSPAddr, ++ ulNumBytes, ulMemType); ++ } else { ++ status = WriteExtDspData(pDevContext, pbHostBuf, dwDSPAddr, ++ ulNumBytes, ulMemType, false); ++ } ++ ++ DBG_Trace(DBG_ENTER, "WMD_BRD_Write, memcopy : DspLogicAddr=0x%x \n", ++ pDevContext->dwDspBaseAddr); ++ return status; ++} ++ ++/* ++ * ======== WMD_DEV_Create ======== ++ * Creates a driver object. Puts DSP in self loop. ++ */ ++static DSP_STATUS WMD_DEV_Create(OUT struct WMD_DEV_CONTEXT **ppDevContext, ++ struct DEV_OBJECT *hDevObject, ++ IN CONST struct CFG_HOSTRES *pConfig, ++ IN CONST struct CFG_DSPRES *pDspConfig) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DEV_CONTEXT *pDevContext = NULL; ++ s32 iEntryNdx; ++ s32 tcWordSwap; ++ u32 tcWordSwapSize = sizeof(tcWordSwap); ++ struct CFG_HOSTRES resources; ++ struct PgTableAttrs *pPtAttrs; ++ u32 pg_tbl_pa; ++ u32 pg_tbl_va; ++ u32 align_size; ++ ++ DBG_Trace(DBG_ENTER, "WMD_DEV_Create, ppDevContext: 0x%x\n\t\t " ++ "hDevObject: 0x%x\n\t\tpConfig: 0x%x\n\t\tpDspConfig: 0x%x\n", ++ ppDevContext, hDevObject, pConfig, pDspConfig); ++ /* Allocate and initialize a data structure to contain the mini driver ++ * state, which becomes the context for later calls into this WMD. */ ++ pDevContext = MEM_Calloc(sizeof(struct WMD_DEV_CONTEXT), MEM_NONPAGED); ++ if (!pDevContext) { ++ DBG_Trace(DBG_ENTER, "Failed to allocate mem \n"); ++ status = DSP_EMEMORY; ++ goto func_end; ++ } ++ status = CFG_GetHostResources( ++ (struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); ++ if (DSP_FAILED(status)) { ++ DBG_Trace(DBG_ENTER, "Failed to get host resources \n"); ++ status = DSP_EMEMORY; ++ goto func_end; ++ } ++ ++ pDevContext->dwDSPStartAdd = (u32)OMAP_GEM_BASE; ++ pDevContext->dwSelfLoop = (u32)NULL; ++ pDevContext->uDspPerClks = 0; ++ pDevContext->dwInternalSize = OMAP_DSP_SIZE; ++ /* Clear dev context MMU table entries. ++ * These get set on WMD_BRD_IOCTL() call after program loaded. */ ++ for (iEntryNdx = 0; iEntryNdx < WMDIOCTL_NUMOFMMUTLB; iEntryNdx++) { ++ pDevContext->aTLBEntry[iEntryNdx].ulGppPa = ++ pDevContext->aTLBEntry[iEntryNdx].ulDspVa = 0; ++ } ++ pDevContext->numTLBEntries = 0; ++ pDevContext->dwDspBaseAddr = (u32)MEM_LinearAddress((void *) ++ (pConfig->dwMemBase[3]), pConfig->dwMemLength[3]); ++ if (!pDevContext->dwDspBaseAddr) { ++ status = DSP_EFAIL; ++ DBG_Trace(DBG_LEVEL7, ++ "WMD_DEV_Create: failed to Map the API memory\n"); ++ } ++ pPtAttrs = MEM_Calloc(sizeof(struct PgTableAttrs), MEM_NONPAGED); ++ if (pPtAttrs != NULL) { ++ /* Assuming that we use only DSP's memory map ++ * until 0x4000:0000 , we would need only 1024 ++ * L1 enties i.e L1 size = 4K */ ++ pPtAttrs->L1size = 0x1000; ++ align_size = pPtAttrs->L1size; ++ /* Align sizes are expected to be power of 2 */ ++ /* we like to get aligned on L1 table size */ ++ pg_tbl_va = (u32)MEM_AllocPhysMem(pPtAttrs->L1size, ++ align_size, &pg_tbl_pa); ++ /* Check if the PA is aligned for us */ ++ if ((pg_tbl_pa) & (align_size-1)) { ++ /* PA not aligned to page table size , ++ * try with more allocation and align */ ++ MEM_FreePhysMem((void *)pg_tbl_va, pg_tbl_pa, pPtAttrs->L1size); ++ /* we like to get aligned on L1 table size */ ++ pg_tbl_va = (u32) MEM_AllocPhysMem((pPtAttrs->L1size)*2, ++ align_size, &pg_tbl_pa); ++ /* We should be able to get aligned table now */ ++ pPtAttrs->L1TblAllocPa = pg_tbl_pa; ++ pPtAttrs->L1TblAllocVa = pg_tbl_va; ++ pPtAttrs->L1TblAllocSz = pPtAttrs->L1size * 2; ++ /* Align the PA to the next 'align' boundary */ ++ pPtAttrs->L1BasePa = ((pg_tbl_pa) + (align_size-1)) & ++ (~(align_size-1)); ++ pPtAttrs->L1BaseVa = pg_tbl_va + (pPtAttrs->L1BasePa - ++ pg_tbl_pa); ++ } else { ++ /* We got aligned PA, cool */ ++ pPtAttrs->L1TblAllocPa = pg_tbl_pa; ++ pPtAttrs->L1TblAllocVa = pg_tbl_va; ++ pPtAttrs->L1TblAllocSz = pPtAttrs->L1size; ++ pPtAttrs->L1BasePa = pg_tbl_pa; ++ pPtAttrs->L1BaseVa = pg_tbl_va; ++ } ++ if (pPtAttrs->L1BaseVa) ++ memset((u8 *)pPtAttrs->L1BaseVa, 0x00, pPtAttrs->L1size); ++ ++ /* number of L2 page tables = DMM pool used + SHMMEM +EXTMEM + ++ * L4 pages */ ++ pPtAttrs->L2NumPages = ((DMMPOOLSIZE >> 20) + 6); ++ pPtAttrs->L2size = HW_MMU_COARSE_PAGE_SIZE * ++ pPtAttrs->L2NumPages; ++ align_size = 4; /* Make it u32 aligned */ ++ /* we like to get aligned on L1 table size */ ++ pg_tbl_va = (u32)MEM_AllocPhysMem(pPtAttrs->L2size, ++ align_size, &pg_tbl_pa); ++ pPtAttrs->L2TblAllocPa = pg_tbl_pa; ++ pPtAttrs->L2TblAllocVa = pg_tbl_va; ++ pPtAttrs->L2TblAllocSz = pPtAttrs->L2size; ++ pPtAttrs->L2BasePa = pg_tbl_pa; ++ pPtAttrs->L2BaseVa = pg_tbl_va; ++ ++ if (pPtAttrs->L2BaseVa) ++ memset((u8 *)pPtAttrs->L2BaseVa, 0x00, pPtAttrs->L2size); ++ ++ pPtAttrs->pgInfo = MEM_Calloc(pPtAttrs->L2NumPages * ++ sizeof(struct PageInfo), MEM_NONPAGED); ++ DBG_Trace(DBG_LEVEL1, "L1 pa %x, va %x, size %x\n L2 pa %x, va " ++ "%x, size %x\n", pPtAttrs->L1BasePa, ++ pPtAttrs->L1BaseVa, pPtAttrs->L1size, ++ pPtAttrs->L2BasePa, pPtAttrs->L2BaseVa, ++ pPtAttrs->L2size); ++ DBG_Trace(DBG_LEVEL1, "pPtAttrs %x L2 NumPages %x pgInfo %x\n", ++ pPtAttrs, pPtAttrs->L2NumPages, pPtAttrs->pgInfo); ++ } ++ if ((pPtAttrs != NULL) && (pPtAttrs->L1BaseVa != 0) && ++ (pPtAttrs->L2BaseVa != 0) && (pPtAttrs->pgInfo != NULL)) ++ pDevContext->pPtAttrs = pPtAttrs; ++ else ++ status = DSP_EMEMORY; ++ ++ if (DSP_SUCCEEDED(status)) ++ status = SYNC_InitializeCS(&pPtAttrs->hCSObj); ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Set the Endianism Register */ /* Need to set this */ ++ /* Retrieve the TC u16 SWAP Option */ ++ status = REG_GetValue(NULL, CURRENTCONFIG, TCWORDSWAP, ++ (u8 *)&tcWordSwap, &tcWordSwapSize); ++ /* Save the value */ ++ pDevContext->tcWordSwapOn = tcWordSwap; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ /* Set the Clock Divisor for the DSP module */ ++ DBG_Trace(DBG_LEVEL7, "WMD_DEV_create:Reset mail box and " ++ "enable the clock \n"); ++ status = CLK_Enable(SERVICESCLK_mailbox_ick); ++ if (DSP_FAILED(status)) { ++ DBG_Trace(DBG_LEVEL7, ++ "WMD_DEV_create:Reset mail box and " ++ "enable the clock Fail\n"); ++ } ++ udelay(5); ++ /* 24xx-Linux MMU address is obtained from the host ++ * resources struct */ ++ pDevContext->dwDSPMmuBase = resources.dwDmmuBase; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ pDevContext->hDevObject = hDevObject; ++ pDevContext->ulIntMask = 0; ++ /* Store current board state. */ ++ pDevContext->dwBrdState = BRD_STOPPED; ++ /* Return this ptr to our device state to the WCD for storage:*/ ++ *ppDevContext = pDevContext; ++ DBG_Trace(DBG_ENTER, "Device Created \n"); ++ } else { ++ if (pPtAttrs != NULL) { ++ if (pPtAttrs->hCSObj) ++ SYNC_DeleteCS(pPtAttrs->hCSObj); ++ ++ if (pPtAttrs->pgInfo) ++ MEM_Free(pPtAttrs->pgInfo); ++ ++ if (pPtAttrs->L2TblAllocVa) { ++ MEM_FreePhysMem((void *)pPtAttrs->L2TblAllocVa, ++ pPtAttrs->L2TblAllocPa, ++ pPtAttrs->L2TblAllocSz); ++ } ++ if (pPtAttrs->L1TblAllocVa) { ++ MEM_FreePhysMem((void *)pPtAttrs->L1TblAllocVa, ++ pPtAttrs->L1TblAllocPa, ++ pPtAttrs->L1TblAllocSz); ++ } ++ } ++ if (pPtAttrs) ++ MEM_Free(pPtAttrs); ++ ++ if (pDevContext) ++ MEM_Free(pDevContext); ++ ++ DBG_Trace(DBG_LEVEL7, ++ "WMD_DEV_Create Error Device not created\n"); ++ } ++func_end: ++ return status; ++} ++ ++/* ++ * ======== WMD_DEV_Ctrl ======== ++ * Receives device specific commands. ++ */ ++static DSP_STATUS WMD_DEV_Ctrl(struct WMD_DEV_CONTEXT *pDevContext, u32 dwCmd, ++ IN OUT void *pArgs) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct WMDIOCTL_EXTPROC *paExtProc = (struct WMDIOCTL_EXTPROC *)pArgs; ++ s32 ndx; ++ ++ DBG_Trace(DBG_ENTER, "WMD_DEV_Ctrl, pDevContext: 0x%x\n\t\t dwCmd: " ++ "0x%x\n\t\tpArgs: 0x%x\n", pDevContext, dwCmd, pArgs); ++ switch (dwCmd) { ++ case WMDIOCTL_CHNLREAD: ++ break; ++ case WMDIOCTL_CHNLWRITE: ++ break; ++ case WMDIOCTL_SETMMUCONFIG: ++ /* store away dsp-mmu setup values for later use */ ++ for (ndx = 0; ndx < WMDIOCTL_NUMOFMMUTLB; ndx++, paExtProc++) ++ pDevContext->aTLBEntry[ndx] = *paExtProc; ++ ++ break; ++ case WMDIOCTL_DEEPSLEEP: ++ case WMDIOCTL_EMERGENCYSLEEP: ++ /* Currently only DSP Idle is supported Need to update for ++ * later releases */ ++ DBG_Trace(DBG_LEVEL5, "WMDIOCTL_DEEPSLEEP\n"); ++ status = SleepDSP(pDevContext, PWR_DEEPSLEEP, pArgs); ++ break; ++ case WMDIOCTL_WAKEUP: ++ DBG_Trace(DBG_LEVEL5, "WMDIOCTL_WAKEUP\n"); ++ status = WakeDSP(pDevContext, pArgs); ++ break; ++ case WMDIOCTL_CLK_CTRL: ++ DBG_Trace(DBG_LEVEL5, "WMDIOCTL_CLK_CTRL\n"); ++ status = DSP_SOK; ++ /* Looking For Baseport Fix for Clocks */ ++ status = DSPPeripheralClkCtrl(pDevContext, pArgs); ++ break; ++ case WMDIOCTL_PWR_HIBERNATE: ++ DBG_Trace(DBG_LEVEL5, "WMDIOCTL_PWR_HIBERNATE\n"); ++ status = handle_hibernation_fromDSP(pDevContext); ++ break; ++ case WMDIOCTL_PRESCALE_NOTIFY: ++ DBG_Trace(DBG_LEVEL5, "WMDIOCTL_PRESCALE_NOTIFY\n"); ++ status = PreScale_DSP(pDevContext, pArgs); ++ break; ++ case WMDIOCTL_POSTSCALE_NOTIFY: ++ DBG_Trace(DBG_LEVEL5, "WMDIOCTL_POSTSCALE_NOTIFY\n"); ++ status = PostScale_DSP(pDevContext, pArgs); ++ break; ++ case WMDIOCTL_CONSTRAINT_REQUEST: ++ DBG_Trace(DBG_LEVEL5, "WMDIOCTL_CONSTRAINT_REQUEST\n"); ++ status = handle_constraints_set(pDevContext, pArgs); ++ break; ++ default: ++ status = DSP_EFAIL; ++ DBG_Trace(DBG_LEVEL7, "Error in WMD_BRD_Ioctl \n"); ++ break; ++ } ++ return status; ++} ++ ++/* ++ * ======== WMD_DEV_Destroy ======== ++ * Destroys the driver object. ++ */ ++static DSP_STATUS WMD_DEV_Destroy(struct WMD_DEV_CONTEXT *hDevContext) ++{ ++ struct PgTableAttrs *pPtAttrs; ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DEV_CONTEXT *pDevContext = (struct WMD_DEV_CONTEXT *) ++ hDevContext; ++ DBG_Trace(DBG_ENTER, "Entering WMD_DEV_Destroy:n hDevContext ::0x%x\n", ++ hDevContext); ++ /* first put the device to stop state */ ++ WMD_BRD_Delete(pDevContext); ++ if (pDevContext && pDevContext->pPtAttrs) { ++ pPtAttrs = pDevContext->pPtAttrs; ++ if (pPtAttrs->hCSObj) ++ SYNC_DeleteCS(pPtAttrs->hCSObj); ++ ++ if (pPtAttrs->pgInfo) ++ MEM_Free(pPtAttrs->pgInfo); ++ ++ if (pPtAttrs->L2TblAllocVa) { ++ MEM_FreePhysMem((void *)pPtAttrs->L2TblAllocVa, ++ pPtAttrs->L2TblAllocPa, pPtAttrs-> ++ L2TblAllocSz); ++ } ++ if (pPtAttrs->L1TblAllocVa) { ++ MEM_FreePhysMem((void *)pPtAttrs->L1TblAllocVa, ++ pPtAttrs->L1TblAllocPa, pPtAttrs-> ++ L1TblAllocSz); ++ } ++ if (pPtAttrs) ++ MEM_Free(pPtAttrs); ++ ++ } ++ /* Free the driver's device context: */ ++ MEM_Free((void *) hDevContext); ++ return status; ++} ++ ++static DSP_STATUS WMD_BRD_MemCopy(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 ulDspDestAddr, u32 ulDspSrcAddr, ++ u32 ulNumBytes, u32 ulMemType) ++{ ++ DSP_STATUS status = DSP_SOK; ++ u32 srcAddr = ulDspSrcAddr; ++ u32 destAddr = ulDspDestAddr; ++ u32 copyBytes = 0; ++ u32 totalBytes = ulNumBytes; ++ u8 hostBuf[BUFFERSIZE]; ++ struct WMD_DEV_CONTEXT *pDevContext = hDevContext; ++ while ((totalBytes > 0) && DSP_SUCCEEDED(status)) { ++ copyBytes = totalBytes > BUFFERSIZE ? BUFFERSIZE : totalBytes; ++ /* Read from External memory */ ++ status = ReadExtDspData(hDevContext, hostBuf, srcAddr, ++ copyBytes, ulMemType); ++ if (DSP_SUCCEEDED(status)) { ++ if (destAddr < (pDevContext->dwDSPStartAdd + ++ pDevContext->dwInternalSize)) { ++ /* Write to Internal memory */ ++ status = WriteDspData(hDevContext, hostBuf, ++ destAddr, copyBytes, ulMemType); ++ } else { ++ /* Write to External memory */ ++ status = WriteExtDspData(hDevContext, hostBuf, ++ destAddr, copyBytes, ulMemType, false); ++ } ++ } ++ totalBytes -= copyBytes; ++ srcAddr += copyBytes; ++ destAddr += copyBytes; ++ } ++ return status; ++} ++ ++/* Mem Write does not halt the DSP to write unlike WMD_BRD_Write */ ++static DSP_STATUS WMD_BRD_MemWrite(struct WMD_DEV_CONTEXT *hDevContext, ++ IN u8 *pbHostBuf, u32 dwDSPAddr, ++ u32 ulNumBytes, u32 ulMemType) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DEV_CONTEXT *pDevContext = hDevContext; ++ u32 ulRemainBytes = 0; ++ u32 ulBytes = 0; ++ ulRemainBytes = ulNumBytes; ++ while (ulRemainBytes > 0 && DSP_SUCCEEDED(status)) { ++ ulBytes = ++ ulRemainBytes > BUFFERSIZE ? BUFFERSIZE : ulRemainBytes; ++ if (dwDSPAddr < (pDevContext->dwDSPStartAdd + ++ pDevContext->dwInternalSize)) { ++ status = WriteDspData(hDevContext, pbHostBuf, dwDSPAddr, ++ ulBytes, ulMemType); ++ } else { ++ status = WriteExtDspData(hDevContext, pbHostBuf, ++ dwDSPAddr, ulBytes, ulMemType, true); ++ } ++ ulRemainBytes -= ulBytes; ++ dwDSPAddr += ulBytes; ++ pbHostBuf = pbHostBuf + ulBytes; ++ } ++ return status; ++} ++ ++/* ++ * ======== WMD_BRD_MemMap ======== ++ * This function maps MPU buffer to the DSP address space. It performs ++ * linear to physical address translation if required. It translates each ++ * page since linear addresses can be physically non-contiguous ++ * All address & size arguments are assumed to be page aligned (in proc.c) ++ * ++ * TODO: Disable MMU while updating the page tables (but that'll stall DSP) ++ */ ++static DSP_STATUS WMD_BRD_MemMap(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 ulMpuAddr, u32 ulVirtAddr, ++ u32 ulNumBytes, u32 ulMapAttr) ++{ ++ u32 attrs; ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DEV_CONTEXT *pDevContext = hDevContext; ++ struct HW_MMUMapAttrs_t hwAttrs; ++ struct vm_area_struct *vma; ++ struct mm_struct *mm = current->mm; ++ u32 write = 0; ++ u32 numUsrPgs = 0; ++ struct page *mappedPage, *pg; ++ s32 pgNum; ++ u32 va = ulVirtAddr; ++ struct task_struct *curr_task = current; ++ u32 pgI = 0; ++ u32 mpuAddr, pa; ++ ++ DBG_Trace(DBG_ENTER, "> WMD_BRD_MemMap hDevContext %x, pa %x, va %x, " ++ "size %x, ulMapAttr %x\n", hDevContext, ulMpuAddr, ulVirtAddr, ++ ulNumBytes, ulMapAttr); ++ if (ulNumBytes == 0) ++ return DSP_EINVALIDARG; ++ ++ if (ulMapAttr != 0) { ++ attrs = ulMapAttr; ++ } else { ++ /* Assign default attributes */ ++ attrs = DSP_MAPVIRTUALADDR | DSP_MAPELEMSIZE16; ++ } ++ /* Take mapping properties */ ++ if (attrs & DSP_MAPBIGENDIAN) ++ hwAttrs.endianism = HW_BIG_ENDIAN; ++ else ++ hwAttrs.endianism = HW_LITTLE_ENDIAN; ++ ++ hwAttrs.mixedSize = (enum HW_MMUMixedSize_t) ++ ((attrs & DSP_MAPMIXEDELEMSIZE) >> 2); ++ /* Ignore elementSize if mixedSize is enabled */ ++ if (hwAttrs.mixedSize == 0) { ++ if (attrs & DSP_MAPELEMSIZE8) { ++ /* Size is 8 bit */ ++ hwAttrs.elementSize = HW_ELEM_SIZE_8BIT; ++ } else if (attrs & DSP_MAPELEMSIZE16) { ++ /* Size is 16 bit */ ++ hwAttrs.elementSize = HW_ELEM_SIZE_16BIT; ++ } else if (attrs & DSP_MAPELEMSIZE32) { ++ /* Size is 32 bit */ ++ hwAttrs.elementSize = HW_ELEM_SIZE_32BIT; ++ } else if (attrs & DSP_MAPELEMSIZE64) { ++ /* Size is 64 bit */ ++ hwAttrs.elementSize = HW_ELEM_SIZE_64BIT; ++ } else { ++ /* ++ * Mixedsize isn't enabled, so size can't be ++ * zero here ++ */ ++ DBG_Trace(DBG_LEVEL7, ++ "WMD_BRD_MemMap: MMU element size is zero\n"); ++ return DSP_EINVALIDARG; ++ } ++ } ++ if (attrs & DSP_MAPDONOTLOCK) ++ hwAttrs.donotlockmpupage = 1; ++ else ++ hwAttrs.donotlockmpupage = 0; ++ ++ if (attrs & DSP_MAPVMALLOCADDR) { ++ return MemMapVmalloc(hDevContext, ulMpuAddr, ulVirtAddr, ++ ulNumBytes, &hwAttrs); ++ } ++ /* ++ * Do OS-specific user-va to pa translation. ++ * Combine physically contiguous regions to reduce TLBs. ++ * Pass the translated pa to PteUpdate. ++ */ ++ if ((attrs & DSP_MAPPHYSICALADDR)) { ++ status = PteUpdate(pDevContext, ulMpuAddr, ulVirtAddr, ++ ulNumBytes, &hwAttrs); ++ goto func_cont; ++ } ++ ++ /* ++ * Important Note: ulMpuAddr is mapped from user application process ++ * to current process - it must lie completely within the current ++ * virtual memory address space in order to be of use to us here! ++ */ ++ down_read(&mm->mmap_sem); ++ vma = find_vma(mm, ulMpuAddr); ++ if (vma) ++ DBG_Trace(DBG_LEVEL6, "VMAfor UserBuf: ulMpuAddr=%x, " ++ "ulNumBytes=%x, vm_start=%x vm_end=%x vm_flags=%x \n", ++ ulMpuAddr, ulNumBytes, vma->vm_start, ++ vma->vm_end, vma->vm_flags); ++ ++ /* ++ * It is observed that under some circumstances, the user buffer is ++ * spread across several VMAs. So loop through and check if the entire ++ * user buffer is covered ++ */ ++ while ((vma) && (ulMpuAddr + ulNumBytes > vma->vm_end)) { ++ /* jump to the next VMA region */ ++ vma = find_vma(mm, vma->vm_end + 1); ++ DBG_Trace(DBG_LEVEL6, "VMAfor UserBuf ulMpuAddr=%x, " ++ "ulNumBytes=%x, vm_start=%x vm_end=%x vm_flags=%x\n", ++ ulMpuAddr, ulNumBytes, vma->vm_start, ++ vma->vm_end, vma->vm_flags); ++ } ++ if (!vma) { ++ DBG_Trace(DBG_LEVEL7, "Failed to get the VMA region for " ++ "MPU Buffer !!! \n"); ++ status = DSP_EINVALIDARG; ++ up_read(&mm->mmap_sem); ++ goto func_cont; ++ } ++ ++ if (vma->vm_flags & VM_IO) { ++ numUsrPgs = ulNumBytes / PG_SIZE_4K; ++ mpuAddr = ulMpuAddr; ++ DBG_Trace(DBG_LEVEL4, "WMD_BRD_MemMap:numOfActualTabEntries=%d," ++ "ulNumBytes= %d\n", numUsrPgs, ulNumBytes); ++ /* Get the physical addresses for user buffer */ ++ for (pgI = 0; pgI < numUsrPgs; pgI++) { ++ pa = user_va2pa(mm, mpuAddr); ++ if (!pa) { ++ status = DSP_EFAIL; ++ pr_err("DSPBRIDGE: VM_IO mapping physical" ++ "address is invalid\n"); ++ break; ++ } ++ if (pfn_valid(__phys_to_pfn(pa))) { ++ pg = phys_to_page(pa); ++ get_page(pg); ++ if (page_count(pg) < 1) { ++ pr_err("Bad page in VM_IO buffer\n"); ++ bad_page_dump(pa, pg); ++ } ++ } ++ status = PteSet(pDevContext->pPtAttrs, pa, ++ va, HW_PAGE_SIZE_4KB, &hwAttrs); ++ if (DSP_FAILED(status)) { ++ DBG_Trace(DBG_LEVEL7, ++ "WMD_BRD_MemMap: FAILED IN VM_IO" ++ "PTESET \n"); ++ break; ++ } ++ va += HW_PAGE_SIZE_4KB; ++ mpuAddr += HW_PAGE_SIZE_4KB; ++ pa += HW_PAGE_SIZE_4KB; ++ } ++ } else { ++ numUsrPgs = ulNumBytes / PG_SIZE_4K; ++ if (vma->vm_flags & (VM_WRITE | VM_MAYWRITE)) ++ write = 1; ++ ++ for (pgI = 0; pgI < numUsrPgs; pgI++) { ++ pgNum = get_user_pages(curr_task, mm, ulMpuAddr, 1, ++ write, 1, &mappedPage, NULL); ++ if (pgNum > 0) { ++ if (page_count(mappedPage) < 1) { ++ pr_err("Bad page count after doing" ++ "get_user_pages on" ++ "user buffer\n"); ++ bad_page_dump(page_to_phys(mappedPage), ++ mappedPage); ++ } ++ status = PteSet(pDevContext->pPtAttrs, ++ page_to_phys(mappedPage), va, ++ HW_PAGE_SIZE_4KB, &hwAttrs); ++ if (DSP_FAILED(status)) { ++ DBG_Trace(DBG_LEVEL7, ++ "WMD_BRD_MemMap: FAILED IN PTESET \n"); ++ break; ++ } ++ va += HW_PAGE_SIZE_4KB; ++ ulMpuAddr += HW_PAGE_SIZE_4KB; ++ } else { ++ pr_err("DSPBRIDGE: get_user_pages FAILED," ++ "MPU addr = 0x%x," ++ "vma->vm_flags = 0x%lx," ++ "get_user_pages Err" ++ "Value = %d, Buffer" ++ "size=0x%x\n", ulMpuAddr, ++ vma->vm_flags, pgNum, ++ ulNumBytes); ++ status = DSP_EFAIL; ++ break; ++ } ++ } ++ } ++ up_read(&mm->mmap_sem); ++func_cont: ++ /* Don't propogate Linux or HW status to upper layers */ ++ if (DSP_SUCCEEDED(status)) { ++ status = DSP_SOK; ++ } else { ++ DBG_Trace(DBG_LEVEL7, "< WMD_BRD_MemMap status %x\n", status); ++ /* ++ * Roll out the mapped pages incase it failed in middle of ++ * mapping ++ */ ++ if (pgI) { ++ WMD_BRD_MemUnMap(pDevContext, ulVirtAddr, ++ (pgI * PG_SIZE_4K)); ++ } ++ status = DSP_EFAIL; ++ } ++ /* ++ * In any case, flush the TLB ++ * This is called from here instead from PteUpdate to avoid unnecessary ++ * repetition while mapping non-contiguous physical regions of a virtual ++ * region ++ */ ++ flush_all(pDevContext); ++ DBG_Trace(DBG_ENTER, "< WMD_BRD_MemMap status %x\n", status); ++ return status; ++} ++ ++/* ++ * ======== WMD_BRD_MemUnMap ======== ++ * Invalidate the PTEs for the DSP VA block to be unmapped. ++ * ++ * PTEs of a mapped memory block are contiguous in any page table ++ * So, instead of looking up the PTE address for every 4K block, ++ * we clear consecutive PTEs until we unmap all the bytes ++ */ ++static DSP_STATUS WMD_BRD_MemUnMap(struct WMD_DEV_CONTEXT *hDevContext, ++ u32 ulVirtAddr, u32 ulNumBytes) ++{ ++ u32 L1BaseVa; ++ u32 L2BaseVa; ++ u32 L2BasePa; ++ u32 L2PageNum; ++ u32 pteVal; ++ u32 pteSize; ++ u32 pteCount; ++ u32 pteAddrL1; ++ u32 pteAddrL2 = 0; ++ u32 remBytes; ++ u32 remBytesL2; ++ u32 vaCurr; ++ struct page *pg = NULL; ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DEV_CONTEXT *pDevContext = hDevContext; ++ struct PgTableAttrs *pt = pDevContext->pPtAttrs; ++ u32 pacount = 0; ++ u32 *pPhysAddrPageTbl = NULL; ++ u32 temp; ++ u32 patemp = 0; ++ u32 pAddr; ++ u32 numof4KPages = 0; ++ ++ DBG_Trace(DBG_ENTER, "> WMD_BRD_MemUnMap hDevContext %x, va %x, " ++ "NumBytes %x\n", hDevContext, ulVirtAddr, ulNumBytes); ++ pPhysAddrPageTbl = DMM_GetPhysicalAddrTable(); ++ vaCurr = ulVirtAddr; ++ remBytes = ulNumBytes; ++ remBytesL2 = 0; ++ L1BaseVa = pt->L1BaseVa; ++ pteAddrL1 = HW_MMU_PteAddrL1(L1BaseVa, vaCurr); ++ DBG_Trace(DBG_ENTER, "WMD_BRD_MemUnMap L1BaseVa %x, pteAddrL1 %x " ++ "vaCurr %x remBytes %x\n", L1BaseVa, pteAddrL1, ++ vaCurr, remBytes); ++ while (remBytes && (DSP_SUCCEEDED(status))) { ++ u32 vaCurrOrig = vaCurr; ++ /* Find whether the L1 PTE points to a valid L2 PT */ ++ pteAddrL1 = HW_MMU_PteAddrL1(L1BaseVa, vaCurr); ++ pteVal = *(u32 *)pteAddrL1; ++ pteSize = HW_MMU_PteSizeL1(pteVal); ++ if (pteSize == HW_MMU_COARSE_PAGE_SIZE) { ++ /* ++ * Get the L2 PA from the L1 PTE, and find ++ * corresponding L2 VA ++ */ ++ L2BasePa = HW_MMU_PteCoarseL1(pteVal); ++ L2BaseVa = L2BasePa - pt->L2BasePa + pt->L2BaseVa; ++ L2PageNum = (L2BasePa - pt->L2BasePa) / ++ HW_MMU_COARSE_PAGE_SIZE; ++ /* ++ * Find the L2 PTE address from which we will start ++ * clearing, the number of PTEs to be cleared on this ++ * page, and the size of VA space that needs to be ++ * cleared on this L2 page ++ */ ++ pteAddrL2 = HW_MMU_PteAddrL2(L2BaseVa, vaCurr); ++ pteCount = pteAddrL2 & (HW_MMU_COARSE_PAGE_SIZE - 1); ++ pteCount = (HW_MMU_COARSE_PAGE_SIZE - pteCount) / ++ sizeof(u32); ++ if (remBytes < (pteCount * PG_SIZE_4K)) ++ pteCount = remBytes / PG_SIZE_4K; ++ ++ remBytesL2 = pteCount * PG_SIZE_4K; ++ DBG_Trace(DBG_LEVEL1, "WMD_BRD_MemUnMap L2BasePa %x, " ++ "L2BaseVa %x pteAddrL2 %x, remBytesL2 %x\n", ++ L2BasePa, L2BaseVa, pteAddrL2, remBytesL2); ++ /* ++ * Unmap the VA space on this L2 PT. A quicker way ++ * would be to clear pteCount entries starting from ++ * pteAddrL2. However, below code checks that we don't ++ * clear invalid entries or less than 64KB for a 64KB ++ * entry. Similar checking is done for L1 PTEs too ++ * below ++ */ ++ while (remBytesL2 && (DSP_SUCCEEDED(status))) { ++ pteVal = *(u32 *)pteAddrL2; ++ pteSize = HW_MMU_PteSizeL2(pteVal); ++ /* vaCurr aligned to pteSize? */ ++ if ((pteSize != 0) && (remBytesL2 >= pteSize) && ++ !(vaCurr & (pteSize - 1))) { ++ /* Collect Physical addresses from VA */ ++ pAddr = (pteVal & ~(pteSize - 1)); ++ if (pteSize == HW_PAGE_SIZE_64KB) ++ numof4KPages = 16; ++ else ++ numof4KPages = 1; ++ temp = 0; ++ while (temp++ < numof4KPages) { ++ pPhysAddrPageTbl[pacount++] = ++ pAddr; ++ pAddr += HW_PAGE_SIZE_4KB; ++ } ++ if (HW_MMU_PteClear(pteAddrL2, ++ vaCurr, pteSize) == RET_OK) { ++ status = DSP_SOK; ++ remBytesL2 -= pteSize; ++ vaCurr += pteSize; ++ pteAddrL2 += (pteSize >> 12) * ++ sizeof(u32); ++ } else { ++ status = DSP_EFAIL; ++ goto EXIT_LOOP; ++ } ++ } else { ++ status = DSP_EFAIL; ++ } ++ } ++ SYNC_EnterCS(pt->hCSObj); ++ if (remBytesL2 == 0) { ++ pt->pgInfo[L2PageNum].numEntries -= pteCount; ++ if (pt->pgInfo[L2PageNum].numEntries == 0) { ++ /* ++ * Clear the L1 PTE pointing to the ++ * L2 PT ++ */ ++ if (RET_OK == HW_MMU_PteClear(L1BaseVa, ++ vaCurrOrig, HW_MMU_COARSE_PAGE_SIZE)) ++ status = DSP_SOK; ++ else { ++ status = DSP_EFAIL; ++ SYNC_LeaveCS(pt->hCSObj); ++ goto EXIT_LOOP; ++ } ++ } ++ remBytes -= pteCount * PG_SIZE_4K; ++ } else { ++ status = DSP_EFAIL; ++ } ++ DBG_Trace(DBG_LEVEL1, "WMD_BRD_MemUnMap L2PageNum %x, " ++ "numEntries %x, pteCount %x, status: 0x%x\n", ++ L2PageNum, pt->pgInfo[L2PageNum].numEntries, ++ pteCount, status); ++ SYNC_LeaveCS(pt->hCSObj); ++ } else ++ /* vaCurr aligned to pteSize? */ ++ /* pteSize = 1 MB or 16 MB */ ++ if ((pteSize != 0) && (remBytes >= pteSize) && ++ !(vaCurr & (pteSize - 1))) { ++ if (pteSize == HW_PAGE_SIZE_1MB) ++ numof4KPages = 256; ++ else ++ numof4KPages = 4096; ++ temp = 0; ++ /* Collect Physical addresses from VA */ ++ pAddr = (pteVal & ~(pteSize - 1)); ++ while (temp++ < numof4KPages) { ++ pPhysAddrPageTbl[pacount++] = pAddr; ++ pAddr += HW_PAGE_SIZE_4KB; ++ } ++ if (HW_MMU_PteClear(L1BaseVa, vaCurr, pteSize) ++ == RET_OK) { ++ status = DSP_SOK; ++ remBytes -= pteSize; ++ vaCurr += pteSize; ++ } else { ++ status = DSP_EFAIL; ++ goto EXIT_LOOP; ++ } ++ } else { ++ status = DSP_EFAIL; ++ } ++ } ++ /* ++ * It is better to flush the TLB here, so that any stale old entries ++ * get flushed ++ */ ++EXIT_LOOP: ++ flush_all(pDevContext); ++ for (temp = 0; temp < pacount; temp++) { ++ patemp = pPhysAddrPageTbl[temp]; ++ if (pfn_valid(__phys_to_pfn(patemp))) { ++ pg = phys_to_page(patemp); ++ if (page_count(pg) < 1) { ++ pr_info("DSPBRIDGE:UNMAP function: COUNT 0" ++ "FOR PA 0x%x, size = 0x%x\n", ++ patemp, ulNumBytes); ++ bad_page_dump(patemp, pg); ++ } ++ SetPageDirty(pg); ++ page_cache_release(pg); ++ } ++ } ++ DBG_Trace(DBG_LEVEL1, "WMD_BRD_MemUnMap vaCurr %x, pteAddrL1 %x " ++ "pteAddrL2 %x\n", vaCurr, pteAddrL1, pteAddrL2); ++ DBG_Trace(DBG_ENTER, "< WMD_BRD_MemUnMap status %x remBytes %x, " ++ "remBytesL2 %x\n", status, remBytes, remBytesL2); ++ return status; ++} ++ ++/* ++ * ======== user_va2pa ======== ++ * Purpose: ++ * This function walks through the Linux page tables to convert a userland ++ * virtual address to physical address ++ */ ++static u32 user_va2pa(struct mm_struct *mm, u32 address) ++{ ++ pgd_t *pgd; ++ pmd_t *pmd; ++ pte_t *ptep, pte; ++ ++ pgd = pgd_offset(mm, address); ++ if (!(pgd_none(*pgd) || pgd_bad(*pgd))) { ++ pmd = pmd_offset(pgd, address); ++ if (!(pmd_none(*pmd) || pmd_bad(*pmd))) { ++ ptep = pte_offset_map(pmd, address); ++ if (ptep) { ++ pte = *ptep; ++ if (pte_present(pte)) ++ return pte & PAGE_MASK; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++ ++/* ++ * ======== PteUpdate ======== ++ * This function calculates the optimum page-aligned addresses and sizes ++ * Caller must pass page-aligned values ++ */ ++static DSP_STATUS PteUpdate(struct WMD_DEV_CONTEXT *hDevContext, u32 pa, ++ u32 va, u32 size, ++ struct HW_MMUMapAttrs_t *mapAttrs) ++{ ++ u32 i; ++ u32 allBits; ++ u32 paCurr = pa; ++ u32 vaCurr = va; ++ u32 numBytes = size; ++ struct WMD_DEV_CONTEXT *pDevContext = hDevContext; ++ DSP_STATUS status = DSP_SOK; ++ u32 pgSize[] = { HW_PAGE_SIZE_16MB, HW_PAGE_SIZE_1MB, ++ HW_PAGE_SIZE_64KB, HW_PAGE_SIZE_4KB }; ++ DBG_Trace(DBG_ENTER, "> PteUpdate hDevContext %x, pa %x, va %x, " ++ "size %x, mapAttrs %x\n", hDevContext, pa, va, size, mapAttrs); ++ while (numBytes && DSP_SUCCEEDED(status)) { ++ /* To find the max. page size with which both PA & VA are ++ * aligned */ ++ allBits = paCurr | vaCurr; ++ DBG_Trace(DBG_LEVEL1, "allBits %x, paCurr %x, vaCurr %x, " ++ "numBytes %x ", allBits, paCurr, vaCurr, numBytes); ++ for (i = 0; i < 4; i++) { ++ if ((numBytes >= pgSize[i]) && ((allBits & ++ (pgSize[i] - 1)) == 0)) { ++ DBG_Trace(DBG_LEVEL1, "pgSize %x\n", pgSize[i]); ++ status = PteSet(pDevContext->pPtAttrs, paCurr, ++ vaCurr, pgSize[i], mapAttrs); ++ paCurr += pgSize[i]; ++ vaCurr += pgSize[i]; ++ numBytes -= pgSize[i]; ++ /* Don't try smaller sizes. Hopefully we have ++ * reached an address aligned to a bigger page ++ * size */ ++ break; ++ } ++ } ++ } ++ DBG_Trace(DBG_ENTER, "< PteUpdate status %x numBytes %x\n", status, ++ numBytes); ++ return status; ++} ++ ++/* ++ * ======== PteSet ======== ++ * This function calculates PTE address (MPU virtual) to be updated ++ * It also manages the L2 page tables ++ */ ++static DSP_STATUS PteSet(struct PgTableAttrs *pt, u32 pa, u32 va, ++ u32 size, struct HW_MMUMapAttrs_t *attrs) ++{ ++ u32 i; ++ u32 pteVal; ++ u32 pteAddrL1; ++ u32 pteSize; ++ u32 pgTblVa; /* Base address of the PT that will be updated */ ++ u32 L1BaseVa; ++ /* Compiler warns that the next three variables might be used ++ * uninitialized in this function. Doesn't seem so. Working around, ++ * anyways. */ ++ u32 L2BaseVa = 0; ++ u32 L2BasePa = 0; ++ u32 L2PageNum = 0; ++ DSP_STATUS status = DSP_SOK; ++ DBG_Trace(DBG_ENTER, "> PteSet pPgTableAttrs %x, pa %x, va %x, " ++ "size %x, attrs %x\n", pt, pa, va, size, attrs); ++ L1BaseVa = pt->L1BaseVa; ++ pgTblVa = L1BaseVa; ++ if ((size == HW_PAGE_SIZE_64KB) || (size == HW_PAGE_SIZE_4KB)) { ++ /* Find whether the L1 PTE points to a valid L2 PT */ ++ pteAddrL1 = HW_MMU_PteAddrL1(L1BaseVa, va); ++ if (pteAddrL1 <= (pt->L1BaseVa + pt->L1size)) { ++ pteVal = *(u32 *)pteAddrL1; ++ pteSize = HW_MMU_PteSizeL1(pteVal); ++ } else { ++ return DSP_EFAIL; ++ } ++ SYNC_EnterCS(pt->hCSObj); ++ if (pteSize == HW_MMU_COARSE_PAGE_SIZE) { ++ /* Get the L2 PA from the L1 PTE, and find ++ * corresponding L2 VA */ ++ L2BasePa = HW_MMU_PteCoarseL1(pteVal); ++ L2BaseVa = L2BasePa - pt->L2BasePa + pt->L2BaseVa; ++ L2PageNum = (L2BasePa - pt->L2BasePa) / ++ HW_MMU_COARSE_PAGE_SIZE; ++ } else if (pteSize == 0) { ++ /* L1 PTE is invalid. Allocate a L2 PT and ++ * point the L1 PTE to it */ ++ /* Find a free L2 PT. */ ++ for (i = 0; (i < pt->L2NumPages) && ++ (pt->pgInfo[i].numEntries != 0); i++) ++ ;; ++ if (i < pt->L2NumPages) { ++ L2PageNum = i; ++ L2BasePa = pt->L2BasePa + (L2PageNum * ++ HW_MMU_COARSE_PAGE_SIZE); ++ L2BaseVa = pt->L2BaseVa + (L2PageNum * ++ HW_MMU_COARSE_PAGE_SIZE); ++ /* Endianness attributes are ignored for ++ * HW_MMU_COARSE_PAGE_SIZE */ ++ status = HW_MMU_PteSet(L1BaseVa, L2BasePa, va, ++ HW_MMU_COARSE_PAGE_SIZE, attrs); ++ } else { ++ status = DSP_EMEMORY; ++ } ++ } else { ++ /* Found valid L1 PTE of another size. ++ * Should not overwrite it. */ ++ status = DSP_EFAIL; ++ } ++ if (DSP_SUCCEEDED(status)) { ++ pgTblVa = L2BaseVa; ++ if (size == HW_PAGE_SIZE_64KB) ++ pt->pgInfo[L2PageNum].numEntries += 16; ++ else ++ pt->pgInfo[L2PageNum].numEntries++; ++ DBG_Trace(DBG_LEVEL1, "L2 BaseVa %x, BasePa %x, " ++ "PageNum %x numEntries %x\n", L2BaseVa, ++ L2BasePa, L2PageNum, ++ pt->pgInfo[L2PageNum].numEntries); ++ } ++ SYNC_LeaveCS(pt->hCSObj); ++ } ++ if (DSP_SUCCEEDED(status)) { ++ DBG_Trace(DBG_LEVEL1, "PTE pgTblVa %x, pa %x, va %x, size %x\n", ++ pgTblVa, pa, va, size); ++ DBG_Trace(DBG_LEVEL1, "PTE endianism %x, elementSize %x, " ++ "mixedSize %x\n", attrs->endianism, ++ attrs->elementSize, attrs->mixedSize); ++ status = HW_MMU_PteSet(pgTblVa, pa, va, size, attrs); ++ } ++ DBG_Trace(DBG_ENTER, "< PteSet status %x\n", status); ++ return status; ++} ++ ++/* Memory map kernel VA -- memory allocated with vmalloc */ ++static DSP_STATUS MemMapVmalloc(struct WMD_DEV_CONTEXT *pDevContext, ++ u32 ulMpuAddr, u32 ulVirtAddr, u32 ulNumBytes, ++ struct HW_MMUMapAttrs_t *hwAttrs) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct page *pPage[1]; ++ u32 i; ++ u32 paCurr; ++ u32 paNext; ++ u32 vaCurr; ++ u32 sizeCurr; ++ u32 numPages; ++ u32 pa; ++ u32 numOf4KPages; ++ u32 temp = 0; ++ ++ DBG_Trace(DBG_ENTER, "> MemMapVmalloc hDevContext %x, pa %x, va %x, " ++ "size %x\n", pDevContext, ulMpuAddr, ulVirtAddr, ulNumBytes); ++ ++ /* ++ * Do Kernel va to pa translation. ++ * Combine physically contiguous regions to reduce TLBs. ++ * Pass the translated pa to PteUpdate. ++ */ ++ numPages = ulNumBytes / PAGE_SIZE; /* PAGE_SIZE = OS page size */ ++ i = 0; ++ vaCurr = ulMpuAddr; ++ pPage[0] = vmalloc_to_page((void *)vaCurr); ++ paNext = page_to_phys(pPage[0]); ++ while (DSP_SUCCEEDED(status) && (i < numPages)) { ++ /* ++ * Reuse paNext from the previous iteraion to avoid ++ * an extra va2pa call ++ */ ++ paCurr = paNext; ++ sizeCurr = PAGE_SIZE; ++ /* ++ * If the next page is physically contiguous, ++ * map it with the current one by increasing ++ * the size of the region to be mapped ++ */ ++ while (++i < numPages) { ++ pPage[0] = vmalloc_to_page((void *)(vaCurr + sizeCurr)); ++ paNext = page_to_phys(pPage[0]); ++ DBG_Trace(DBG_LEVEL5, "Xlate Vmalloc VA=0x%x , " ++ "PA=0x%x \n", (vaCurr + sizeCurr), paNext); ++ if (paNext == (paCurr + sizeCurr)) ++ sizeCurr += PAGE_SIZE; ++ else ++ break; ++ ++ } ++ if (paNext == 0) { ++ status = DSP_EMEMORY; ++ break; ++ } ++ pa = paCurr; ++ numOf4KPages = sizeCurr / HW_PAGE_SIZE_4KB; ++ while (temp++ < numOf4KPages) { ++ get_page(phys_to_page(pa)); ++ pa += HW_PAGE_SIZE_4KB; ++ } ++ status = PteUpdate(pDevContext, paCurr, ulVirtAddr + ++ (vaCurr - ulMpuAddr), sizeCurr, hwAttrs); ++ vaCurr += sizeCurr; ++ } ++ /* Don't propogate Linux or HW status to upper layers */ ++ if (DSP_SUCCEEDED(status)) { ++ status = DSP_SOK; ++ DBG_Trace(DBG_LEVEL7, "< WMD_BRD_MemMap succeeded %x\n", ++ status); ++ } else { ++ DBG_Trace(DBG_LEVEL7, "< WMD_BRD_MemMap status %x\n", status); ++ status = DSP_EFAIL; ++ } ++ /* ++ * In any case, flush the TLB ++ * This is called from here instead from PteUpdate to avoid unnecessary ++ * repetition while mapping non-contiguous physical regions of a virtual ++ * region ++ */ ++ flush_all(pDevContext); ++ DBG_Trace(DBG_LEVEL7, "< WMD_BRD_MemMap at end status %x\n", status); ++ return status; ++} ++ ++static DSP_STATUS run_IdleBoot(u32 prm_base, u32 cm_base, ++ u32 sysctrl_base) ++{ ++ u32 temp; ++ DSP_STATUS status = DSP_SOK; ++ enum HW_PwrState_t pwrState; ++ ++ /* Read PM_PWSTST_IVA2 */ ++ HW_PWRST_IVA2RegGet(prm_base, &temp); ++ if ((temp & 0x03) != 0x03 || (temp & 0x03) != 0x02) { ++ /* IVA2 is not in ON state */ ++ /* Set PM_PWSTCTRL_IVA2 to ON */ ++ HW_PWR_IVA2PowerStateSet(prm_base, HW_PWR_DOMAIN_DSP, ++ HW_PWR_STATE_ON); ++ /* Set the SW supervised state transition */ ++ HW_PWR_CLKCTRL_IVA2RegSet(cm_base, HW_SW_SUP_WAKEUP); ++ /* Wait until the state has moved to ON */ ++ HW_PWR_IVA2StateGet(prm_base, HW_PWR_DOMAIN_DSP, &pwrState); ++ } ++ CLK_Disable(SERVICESCLK_iva2_ck); ++ udelay(10); ++ /* Assert IVA2-RST1 and IVA2-RST2 */ ++ *((REG_UWORD32 *)((u32)(prm_base) + 0x50)) = (u32)0x07; ++ udelay(30); ++ /* set the SYSC for Idle Boot */ ++ *((REG_UWORD32 *)((u32)(sysctrl_base) + 0x404)) = (u32)0x01; ++ temp = (u32) *((REG_UWORD32 *) ++ ((u32) (cm_base) + 0x34)); ++ temp = (temp & 0xFFFFFFFE) | 0x1; ++ *((REG_UWORD32 *) ((u32) (cm_base) + 0x34)) = ++ (u32) temp; ++ temp = (u32) *((REG_UWORD32 *) ++ ((u32) (cm_base) + 0x4)); ++ temp = (temp & 0xFFFFFC8) | 0x37; ++ *((REG_UWORD32 *) ((u32) (cm_base) + 0x4)) = ++ (u32) temp; ++ CLK_Enable(SERVICESCLK_iva2_ck); ++ udelay(20); ++ GetHWRegs(prm_base, cm_base); ++ /* Release Reset1 and Reset2 */ ++ *((REG_UWORD32 *)((u32)(prm_base) + 0x50)) = (u32)0x05; ++ udelay(20); ++ *((REG_UWORD32 *)((u32)(prm_base) + 0x50)) = (u32)0x04; ++ udelay(30); ++ return status; ++} ++ ++ ++void GetHWRegs(u32 prm_base, u32 cm_base) ++{ ++ u32 temp; ++ temp = (u32)*((REG_UWORD32 *)((u32)(cm_base) + 0x00)); ++ DBG_Trace(DBG_LEVEL6, "CM_FCLKEN_IVA2 = 0x%x \n", temp); ++ temp = (u32)*((REG_UWORD32 *)((u32)(cm_base) + 0x10)); ++ DBG_Trace(DBG_LEVEL6, "CM_ICLKEN1_IVA2 = 0x%x \n", temp); ++ temp = (u32)*((REG_UWORD32 *)((u32)(cm_base) + 0x20)); ++ DBG_Trace(DBG_LEVEL6, "CM_IDLEST_IVA2 = 0x%x \n", temp); ++ temp = (u32)*((REG_UWORD32 *)((u32)(cm_base) + 0x48)); ++ DBG_Trace(DBG_LEVEL6, "CM_CLKSTCTRL_IVA2 = 0x%x \n", temp); ++ temp = (u32)*((REG_UWORD32 *)((u32)(cm_base) + 0x4c)); ++ DBG_Trace(DBG_LEVEL6, "CM_CLKSTST_IVA2 = 0x%x \n", temp); ++ temp = (u32)*((REG_UWORD32 *)((u32)(prm_base) + 0x50)); ++ DBG_Trace(DBG_LEVEL6, "RM_RSTCTRL_IVA2 = 0x%x \n", temp); ++ temp = (u32)*((REG_UWORD32 *)((u32)(prm_base) + 0x58)); ++ DBG_Trace(DBG_LEVEL6, "RM_RSTST_IVA2 = 0x%x \n", temp); ++ temp = (u32)*((REG_UWORD32 *)((u32)(prm_base) + 0xE0)); ++ DBG_Trace(DBG_LEVEL6, "PM_PWSTCTRL_IVA2 = 0x%x \n", temp); ++ temp = (u32)*((REG_UWORD32 *)((u32)(prm_base) + 0xE4)); ++ DBG_Trace(DBG_LEVEL6, "PM_PWSTST_IVA2 = 0x%x \n", temp); ++ temp = (u32)*((REG_UWORD32 *)((u32)(cm_base) + 0xA10)); ++ DBG_Trace(DBG_LEVEL6, "CM_ICLKEN1_CORE = 0x%x \n", temp); ++} ++ ++/* ++ * ======== configureDspMmu ======== ++ * Make DSP MMU page table entries. ++ */ ++void configureDspMmu(struct WMD_DEV_CONTEXT *pDevContext, u32 dataBasePhys, ++ u32 dspBaseVirt, u32 sizeInBytes, s32 nEntryStart, ++ enum HW_Endianism_t endianism, ++ enum HW_ElementSize_t elemSize, ++ enum HW_MMUMixedSize_t mixedSize) ++{ ++ struct CFG_HOSTRES resources; ++ struct HW_MMUMapAttrs_t mapAttrs = { endianism, elemSize, mixedSize }; ++ DSP_STATUS status = DSP_SOK; ++ ++ DBC_Require(sizeInBytes > 0); ++ DBG_Trace(DBG_LEVEL1, ++ "configureDspMmu entry %x pa %x, va %x, bytes %x ", ++ nEntryStart, dataBasePhys, dspBaseVirt, sizeInBytes); ++ ++ DBG_Trace(DBG_LEVEL1, "endianism %x, elemSize %x, mixedSize %x\n", ++ endianism, elemSize, mixedSize); ++ status = CFG_GetHostResources( ++ (struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); ++ status = HW_MMU_TLBAdd(pDevContext->dwDSPMmuBase, dataBasePhys, ++ dspBaseVirt, sizeInBytes, nEntryStart, ++ &mapAttrs, HW_SET, HW_SET); ++} ++ ++/* ++ * ======== WaitForStart ======== ++ * Wait for the singal from DSP that it has started, or time out. ++ */ ++bool WaitForStart(struct WMD_DEV_CONTEXT *pDevContext, u32 dwSyncAddr) ++{ ++ u16 usCount = TIHELEN_ACKTIMEOUT; ++ ++ /* Wait for response from board */ ++ while (*((volatile u16 *)dwSyncAddr) && --usCount) ++ udelay(10); ++ ++ /* If timed out: return FALSE */ ++ if (!usCount) { ++ DBG_Trace(DBG_LEVEL7, "Timed out Waiting for DSP to Start\n"); ++ return FALSE; ++ } ++ return TRUE; ++} +diff --git a/drivers/dsp/bridge/wmd/tiomap3430_pwr.c b/drivers/dsp/bridge/wmd/tiomap3430_pwr.c +new file mode 100644 +index 0000000..cbf6925 +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/tiomap3430_pwr.c +@@ -0,0 +1,731 @@ ++/* ++ * tiomap_pwr.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2007-2008 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* ++ * ======== _tiomap_pwr.c ======== ++ * Description: ++ * Implementation of DSP wake/sleep routines. ++ * ++ *! Revision History ++ *! ================ ++ *! 01-Nov-2007 HK: Added Off mode(Hibernation) support and DVFS support ++ *! 05-Jan-2004 vp: Moved the file to platform specific folder and commented the ++ *! code. ++ *! 27-Mar-2003 vp: Added support for DSP boot idle mode. ++ *! 06-Dec-2002 cring: Added Palm support. ++ *! 08-Oct-2002 rr: Created. ++ */ ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++#include ++#include ++ ++/* ------------------------------------ Hardware Abstraction Layer */ ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* ----------------------------------- specific to this file */ ++#include "_tiomap.h" ++#include "_tiomap_pwr.h" ++#include "_tiomap_util.h" ++#include ++#include ++ ++#ifdef CONFIG_PM ++#include ++#endif ++extern struct MAILBOX_CONTEXT mboxsetting; ++static unsigned short enable_off_mode = 0; ++/* ++ * ======== handle_constraints_set ======== ++ * Sets new DSP constraint ++ */ ++DSP_STATUS handle_constraints_set(struct WMD_DEV_CONTEXT *pDevContext, ++ IN void *pArgs) ++{ ++#ifdef CONFIG_BRIDGE_DVFS ++ u32 *pConstraintVal; ++ DSP_STATUS status = DSP_SOK; ++ struct CFG_HOSTRES resources; ++ struct dspbridge_platform_data *pdata = ++ omap_dspbridge_dev->dev.platform_data; ++ status = CFG_GetHostResources( ++ (struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); ++ ++ pConstraintVal = (u32 *)(pArgs); ++ /* Read the target value requested by DSP */ ++ DBG_Trace(DBG_LEVEL7, "handle_constraints_set:" ++ "opp requested = 0x%x\n", (u32)*(pConstraintVal+1)); ++ status = HW_MBOX_saveSettings(resources.dwMboxBase); ++ ++ /* Set the new opp value */ ++ if (pdata->dsp_set_min_opp) ++ (*pdata->dsp_set_min_opp)((u32)*(pConstraintVal+1)); ++ return DSP_SOK; ++#endif /* #ifdef CONFIG_BRIDGE_DVFS */ ++ return DSP_SOK; ++} ++ ++/* ++ * ======== handle_hibernation_fromDSP ======== ++ * Handle Hibernation requested from DSP ++ */ ++DSP_STATUS handle_hibernation_fromDSP(struct WMD_DEV_CONTEXT *pDevContext) ++{ ++ DSP_STATUS status = DSP_SOK; ++#ifdef CONFIG_PM ++ u16 usCount = TIHELEN_ACKTIMEOUT; ++ struct CFG_HOSTRES resources; ++ enum HW_PwrState_t pwrState; ++#ifdef CONFIG_BRIDGE_DVFS ++ u32 opplevel; ++ struct IO_MGR *hIOMgr; ++ struct dspbridge_platform_data *pdata = ++ omap_dspbridge_dev->dev.platform_data; ++#endif ++ ++ status = CFG_GetHostResources( ++ (struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); ++ if (DSP_FAILED(status)) ++ return status; ++ ++ HW_PWR_IVA2StateGet(resources.dwPrmBase, HW_PWR_DOMAIN_DSP, ++ &pwrState); ++ /* Wait for DSP to move into Off state, how much time should ++ * we wait? */ ++ while ((pwrState != HW_PWR_STATE_OFF) && --usCount) { ++ udelay(500); ++ HW_PWR_IVA2StateGet(resources.dwPrmBase, HW_PWR_DOMAIN_DSP, ++ &pwrState); ++ } ++ if (usCount == 0) { ++ DBG_Trace(DBG_LEVEL7, "Timed out Waiting for DSP Off mode \n"); ++ status = WMD_E_TIMEOUT; ++ return status; ++ } else { ++ ++ /* Save mailbox settings */ ++ status = HW_MBOX_saveSettings(resources.dwMboxBase); ++ DBG_Trace(DBG_LEVEL6, "MailBoxSettings: SYSCONFIG = 0x%x\n", ++ mboxsetting.sysconfig); ++ DBG_Trace(DBG_LEVEL6, "MailBoxSettings: IRQENABLE0 = 0x%x\n", ++ mboxsetting.irqEnable0); ++ DBG_Trace(DBG_LEVEL6, "MailBoxSettings: IRQENABLE1 = 0x%x\n", ++ mboxsetting.irqEnable1); ++ /* Turn off DSP Peripheral clocks and DSP Load monitor timer */ ++ status = DSP_PeripheralClocks_Disable(pDevContext, NULL); ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Update the Bridger Driver state */ ++ pDevContext->dwBrdState = BRD_DSP_HIBERNATION; ++#ifdef CONFIG_BRIDGE_DVFS ++ status = DEV_GetIOMgr(pDevContext->hDevObject, &hIOMgr); ++ if (DSP_FAILED(status)) ++ return status; ++ IO_SHMsetting(hIOMgr, SHM_GETOPP, &opplevel); ++ /* Set the OPP to low level before moving to OFF mode */ ++ if (opplevel != VDD1_OPP1) { ++ DBG_Trace(DBG_LEVEL5, ++ "Tiomap_pwr.c - DSP requested" ++ " OPP = %d, MPU requesting low" ++ " OPP %d instead\n", opplevel, ++ VDD1_OPP1); ++ if (pdata->dsp_set_min_opp) ++ (*pdata->dsp_set_min_opp)(VDD1_OPP1); ++ status = DSP_SOK; ++ } ++#endif /* CONFIG_BRIDGE_DVFS */ ++ } else { ++ DBG_Trace(DBG_LEVEL7, ++ "handle_hibernation_fromDSP- FAILED\n"); ++ } ++ } ++#endif ++ return status; ++} ++ ++/* ++ * ======== SleepDSP ======== ++ * Put DSP in low power consuming state. ++ */ ++DSP_STATUS SleepDSP(struct WMD_DEV_CONTEXT *pDevContext, IN u32 dwCmd, ++ IN void *pArgs) ++{ ++ DSP_STATUS status = DSP_SOK; ++#ifdef CONFIG_PM ++ struct CFG_HOSTRES resources; ++ u16 usCount = TIHELEN_ACKTIMEOUT; ++ enum HW_PwrState_t pwrState; ++ enum HW_PwrState_t targetPwrState; ++ ++ status = CFG_GetHostResources( ++ (struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); ++ if (DSP_FAILED(status)) ++ return status; ++ DBG_Trace(DBG_LEVEL7, "SleepDSP- Enter function \n"); ++ ++ /* next, check if sleep code is valid... */ ++ if ((dwCmd != PWR_DEEPSLEEP) && (dwCmd != PWR_EMERGENCYDEEPSLEEP)) { ++ DBG_Trace(DBG_LEVEL7, "SleepDSP- Illegal sleep command\n"); ++ return DSP_EINVALIDARG; ++ } ++ switch (pDevContext->dwBrdState) { ++ case BRD_RUNNING: ++ status = HW_MBOX_saveSettings(resources.dwMboxBase); ++ if (enable_off_mode) { ++ CHNLSM_InterruptDSP2(pDevContext, ++ MBX_PM_DSPHIBERNATE); ++ DBG_Trace(DBG_LEVEL7, ++ "SleepDSP - Sent hibernate " ++ "command to DSP\n"); ++ targetPwrState = HW_PWR_STATE_OFF; ++ } else { ++ CHNLSM_InterruptDSP2(pDevContext, ++ MBX_PM_DSPRETENTION); ++ targetPwrState = HW_PWR_STATE_RET; ++ } ++ break; ++ case BRD_RETENTION: ++ status = HW_MBOX_saveSettings(resources.dwMboxBase); ++ if (enable_off_mode) { ++ CHNLSM_InterruptDSP2(pDevContext, ++ MBX_PM_DSPHIBERNATE); ++ targetPwrState = HW_PWR_STATE_OFF; ++ } else ++ return DSP_SOK; ++ break; ++ case BRD_HIBERNATION: ++ case BRD_DSP_HIBERNATION: ++ status = HW_MBOX_saveSettings(resources.dwMboxBase); ++ /* Already in Hibernation, so just return */ ++ DBG_Trace(DBG_LEVEL7, "SleepDSP- DSP already in " ++ "hibernation\n"); ++ return DSP_SOK; ++ case BRD_STOPPED: ++ DBG_Trace(DBG_LEVEL7, ++ "SleepDSP- Board in STOP state \n"); ++ return DSP_SALREADYASLEEP; ++ default: ++ DBG_Trace(DBG_LEVEL7, ++ "SleepDSP- Bridge in Illegal state\n"); ++ return DSP_EFAIL; ++ } ++ /* Get the PRCM DSP power domain status */ ++ HW_PWR_IVA2StateGet(resources.dwPrmBase, HW_PWR_DOMAIN_DSP, ++ &pwrState); ++ /* Wait for DSP to move into Standby state, how much time ++ * should we wait?*/ ++ while ((pwrState != targetPwrState) && --usCount) { ++ udelay(500); ++ HW_PWR_IVA2StateGet(resources.dwPrmBase, HW_PWR_DOMAIN_DSP, ++ &pwrState); ++ } ++ if (usCount == 0) { ++ DBG_Trace(DBG_LEVEL7, "SleepDSP: Timed out Waiting for DSP" ++ " STANDBY %x \n", pwrState); ++ return WMD_E_TIMEOUT; ++ } else { ++ DBG_Trace(DBG_LEVEL7, "SleepDSP: DSP STANDBY Pwr state %x \n", ++ pwrState); ++ /* Update the Bridger Driver state */ ++ if (enable_off_mode) ++ pDevContext->dwBrdState = BRD_HIBERNATION; ++ else ++ pDevContext->dwBrdState = BRD_RETENTION; ++ /* Turn off DSP Peripheral clocks */ ++ status = DSP_PeripheralClocks_Disable(pDevContext, NULL); ++ if (DSP_FAILED(status)) ++ DBG_Trace(DBG_LEVEL7, "SleepDSP- FAILED\n"); ++ } ++#endif ++ return status; ++} ++ ++ ++/* ++ * ======== WakeDSP ======== ++ * Wake up DSP from sleep. ++ */ ++DSP_STATUS WakeDSP(struct WMD_DEV_CONTEXT *pDevContext, IN void *pArgs) ++{ ++ DSP_STATUS status = DSP_SOK; ++#ifdef CONFIG_PM ++ struct CFG_HOSTRES resources; ++ enum HW_PwrState_t pwrState; ++ u32 temp; ++ ++ status = CFG_GetHostResources( ++ (struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); ++ if (DSP_FAILED(status)) ++ return status; ++ /* check the BRD/WMD state, if it is not 'SLEEP' then return failure */ ++ if (pDevContext->dwBrdState == BRD_RUNNING || ++ pDevContext->dwBrdState == BRD_STOPPED || ++ pDevContext->dwBrdState == BRD_DSP_HIBERNATION) { ++ /* The Device is in 'RET' or 'OFF' state and WMD state is not ++ * 'SLEEP', this means state inconsistency, so return */ ++ status = DSP_SOK; ++ return status; ++ } ++ /* Enable the DSP peripheral clocks and load monitor timer ++ * before waking the DSP */ ++ DBG_Trace(DBG_LEVEL6, "WakeDSP: enable DSP Peripheral Clks = 0x%x \n", ++ pDevContext->uDspPerClks); ++ status = DSP_PeripheralClocks_Enable(pDevContext, NULL); ++ ++ /* Enabling Dppll in lock mode */ ++ temp = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwCmBase) + 0x34)); ++ temp = (temp & 0xFFFFFFFE) | 0x1; ++ *((REG_UWORD32 *) ((u32) (resources.dwCmBase) + 0x34)) = ++ (u32) temp; ++ temp = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwCmBase) + 0x4)); ++ temp = (temp & 0xFFFFFC8) | 0x37; ++ ++ *((REG_UWORD32 *) ((u32) (resources.dwCmBase) + 0x4)) = ++ (u32) temp; ++ ++ udelay(10); ++ if (DSP_SUCCEEDED(status)) { ++ /* Send a message to DSP to wake up */ ++ CHNLSM_InterruptDSP2(pDevContext, MBX_PM_DSPWAKEUP); ++ HW_PWR_IVA2StateGet(resources.dwPrmBase, HW_PWR_DOMAIN_DSP, ++ &pwrState); ++ DBG_Trace(DBG_LEVEL7, ++ "\nWakeDSP: Power State After sending Interrupt " ++ "to DSP %x\n", pwrState); ++ /* set the device state to RUNNIG */ ++ pDevContext->dwBrdState = BRD_RUNNING; ++ } else { ++ DBG_Trace(DBG_LEVEL6, "WakeDSP: FAILED\n"); ++ } ++#endif ++ return status; ++} ++ ++/* ++ * ======== DSPPeripheralClkCtrl ======== ++ * Enable/Disable the DSP peripheral clocks as needed.. ++ */ ++DSP_STATUS DSPPeripheralClkCtrl(struct WMD_DEV_CONTEXT *pDevContext, ++ IN void *pArgs) ++{ ++ u32 extClk = 0; ++ u32 extClkId = 0; ++ u32 extClkCmd = 0; ++ u32 clkIdIndex = MBX_PM_MAX_RESOURCES; ++ u32 tmpIndex; ++ u32 dspPerClksBefore; ++ DSP_STATUS status = DSP_SOK; ++ DSP_STATUS status1 = DSP_SOK; ++ ++ DBG_Trace(DBG_ENTER, "Entering DSPPeripheralClkCtrl \n"); ++ dspPerClksBefore = pDevContext->uDspPerClks; ++ DBG_Trace(DBG_ENTER, "DSPPeripheralClkCtrl : uDspPerClks = 0x%x \n", ++ dspPerClksBefore); ++ ++ extClk = (u32)*((u32 *)pArgs); ++ ++ DBG_Trace(DBG_LEVEL3, "DSPPeripheralClkCtrl : extClk+Cmd = 0x%x \n", ++ extClk); ++ ++ extClkId = extClk & MBX_PM_CLK_IDMASK; ++ ++ /* process the power message -- TODO, keep it in a separate function */ ++ for (tmpIndex = 0; tmpIndex < MBX_PM_MAX_RESOURCES; tmpIndex++) { ++ if (extClkId == BPWR_CLKID[tmpIndex]) { ++ clkIdIndex = tmpIndex; ++ break; ++ } ++ } ++ /* TODO -- Assert may be a too hard restriction here.. May be we should ++ * just return with failure when the CLK ID does not match */ ++ /* DBC_Assert(clkIdIndex < MBX_PM_MAX_RESOURCES);*/ ++ if (clkIdIndex == MBX_PM_MAX_RESOURCES) { ++ DBG_Trace(DBG_LEVEL7, ++ "DSPPeripheralClkCtrl : Could n't get clock Id for" ++ "clkid 0x%x \n", clkIdIndex); ++ /* return with a more meaningfull error code */ ++ return DSP_EFAIL; ++ } ++ extClkCmd = (extClk >> MBX_PM_CLK_CMDSHIFT) & MBX_PM_CLK_CMDMASK; ++ switch (extClkCmd) { ++ case BPWR_DisableClock: ++ /* Call BP to disable the needed clock */ ++ DBG_Trace(DBG_LEVEL3, ++ "DSPPeripheralClkCtrl : Disable CLK for \n"); ++ status1 = CLK_Disable(BPWR_Clks[clkIdIndex].intClk); ++ status = CLK_Disable(BPWR_Clks[clkIdIndex].funClk); ++ DSPClkWakeupEventCtrl(BPWR_Clks[clkIdIndex].clkId, false); ++ if ((DSP_SUCCEEDED(status)) && (DSP_SUCCEEDED(status1))) { ++ (pDevContext->uDspPerClks) &= ++ (~((u32) (1 << clkIdIndex))); ++ } else { ++ DBG_Trace(DBG_LEVEL7, "DSPPeripheralClkCtrl : Failed " ++ "to disable clk\n"); ++ } ++ break; ++ case BPWR_EnableClock: ++ DBG_Trace(DBG_LEVEL3, ++ "DSPPeripheralClkCtrl : Enable CLK for \n"); ++ status1 = CLK_Enable(BPWR_Clks[clkIdIndex].intClk); ++ status = CLK_Enable(BPWR_Clks[clkIdIndex].funClk); ++ DSPClkWakeupEventCtrl(BPWR_Clks[clkIdIndex].clkId, true); ++ if ((DSP_SUCCEEDED(status)) && (DSP_SUCCEEDED(status1))) { ++ (pDevContext->uDspPerClks) |= (1 << clkIdIndex); ++ } else { ++ DBG_Trace(DBG_LEVEL7, ++ "DSPPeripheralClkCtrl:Failed to Enable clk\n"); ++ } ++ break; ++ default: ++ DBG_Trace(DBG_LEVEL3, ++ "DSPPeripheralClkCtrl : Unsupported CMD \n"); ++ /* unsupported cmd */ ++ /* TODO -- provide support for AUTOIDLE Enable/Disable ++ * commands */ ++ } ++ return status; ++} ++ ++/* ++ * ========PreScale_DSP======== ++ * Sends prescale notification to DSP ++ * ++ */ ++DSP_STATUS PreScale_DSP(struct WMD_DEV_CONTEXT *pDevContext, IN void *pArgs) ++{ ++#ifdef CONFIG_BRIDGE_DVFS ++ u32 level; ++ u32 voltage_domain; ++ ++ voltage_domain = *((u32 *)pArgs); ++ level = *((u32 *)pArgs + 1); ++ ++ DBG_Trace(DBG_LEVEL7, "PreScale_DSP: voltage_domain = %x, level = " ++ "0x%x\n", voltage_domain, level); ++ if ((pDevContext->dwBrdState == BRD_HIBERNATION) || ++ (pDevContext->dwBrdState == BRD_RETENTION) || ++ (pDevContext->dwBrdState == BRD_DSP_HIBERNATION)) { ++ DBG_Trace(DBG_LEVEL7, "PreScale_DSP: IVA in sleep. " ++ "No notification to DSP\n"); ++ return DSP_SOK; ++ } else if ((pDevContext->dwBrdState == BRD_RUNNING)) { ++ /* Send a prenotificatio to DSP */ ++ DBG_Trace(DBG_LEVEL7, ++ "PreScale_DSP: Sent notification to DSP\n"); ++ CHNLSM_InterruptDSP2(pDevContext, MBX_PM_SETPOINT_PRENOTIFY); ++ return DSP_SOK; ++ } else { ++ DBG_Trace(DBG_LEVEL7, "PreScale_DSP: Failed - DSP BRD" ++ " state in wrong state"); ++ return DSP_EFAIL; ++ } ++#endif /* #ifdef CONFIG_BRIDGE_DVFS */ ++ return DSP_SOK; ++} ++ ++/* ++ * ========PostScale_DSP======== ++ * Sends postscale notification to DSP ++ * ++ */ ++DSP_STATUS PostScale_DSP(struct WMD_DEV_CONTEXT *pDevContext, IN void *pArgs) ++{ ++#ifdef CONFIG_BRIDGE_DVFS ++ u32 level; ++ u32 voltage_domain; ++ struct IO_MGR *hIOMgr; ++ DSP_STATUS status = DSP_SOK; ++ ++ status = DEV_GetIOMgr(pDevContext->hDevObject, &hIOMgr); ++ ++ voltage_domain = *((u32 *)pArgs); ++ level = *((u32 *)pArgs + 1); ++ DBG_Trace(DBG_LEVEL7, ++ "PostScale_DSP: voltage_domain = %x, level = 0x%x\n", ++ voltage_domain, level); ++ if ((pDevContext->dwBrdState == BRD_HIBERNATION) || ++ (pDevContext->dwBrdState == BRD_RETENTION) || ++ (pDevContext->dwBrdState == BRD_DSP_HIBERNATION)) { ++ /* Update the OPP value in shared memory */ ++ IO_SHMsetting(hIOMgr, SHM_CURROPP, &level); ++ DBG_Trace(DBG_LEVEL7, ++ "PostScale_DSP: IVA in sleep. Wrote to shared " ++ "memory \n"); ++ return DSP_SOK; ++ } else if ((pDevContext->dwBrdState == BRD_RUNNING)) { ++ /* Update the OPP value in shared memory */ ++ IO_SHMsetting(hIOMgr, SHM_CURROPP, &level); ++ /* Send a post notification to DSP */ ++ CHNLSM_InterruptDSP2(pDevContext, MBX_PM_SETPOINT_POSTNOTIFY); ++ DBG_Trace(DBG_LEVEL7, ++ "PostScale_DSP: Wrote to shared memory Sent post" ++ " notification to DSP\n"); ++ return DSP_SOK; ++ } else { ++ DBG_Trace(DBG_LEVEL7, "PostScale_DSP: Failed - DSP BRD state " ++ "in wrong state"); ++ return DSP_EFAIL; ++ } ++#endif /* #ifdef CONFIG_BRIDGE_DVFS */ ++ return DSP_SOK; ++} ++ ++/* ++ * ========DSP_PeripheralClocks_Disable======== ++ * Disables all the peripheral clocks that were requested by DSP ++ */ ++DSP_STATUS DSP_PeripheralClocks_Disable(struct WMD_DEV_CONTEXT *pDevContext, ++ IN void *pArgs) ++{ ++ ++ u32 clkIdx; ++ DSP_STATUS status = DSP_SOK; ++ ++ for (clkIdx = 0; clkIdx < MBX_PM_MAX_RESOURCES; clkIdx++) { ++ if (((pDevContext->uDspPerClks) >> clkIdx) & 0x01) { ++ /* Disables the interface clock of the peripheral */ ++ status = CLK_Disable(BPWR_Clks[clkIdx].intClk); ++ if (DSP_FAILED(status)) { ++ DBG_Trace(DBG_LEVEL7, ++ "Failed to Enable the DSP Peripheral" ++ "Clk 0x%x \n", BPWR_Clks[clkIdx]); ++ } ++ /* Disables the functional clock of the periphearl */ ++ status = CLK_Disable(BPWR_Clks[clkIdx].funClk); ++ if (DSP_FAILED(status)) { ++ DBG_Trace(DBG_LEVEL7, ++ "Failed to Enable the DSP Peripheral" ++ "Clk 0x%x \n", BPWR_Clks[clkIdx]); ++ } ++ } ++ } ++ return status; ++} ++ ++/* ++ * ========DSP_PeripheralClocks_Enable======== ++ * Enables all the peripheral clocks that were requested by DSP ++ */ ++DSP_STATUS DSP_PeripheralClocks_Enable(struct WMD_DEV_CONTEXT *pDevContext, ++ IN void *pArgs) ++{ ++ u32 clkIdx; ++ DSP_STATUS int_clk_status = DSP_EFAIL, fun_clk_status = DSP_EFAIL; ++ ++ for (clkIdx = 0; clkIdx < MBX_PM_MAX_RESOURCES; clkIdx++) { ++ if (((pDevContext->uDspPerClks) >> clkIdx) & 0x01) { ++ /* Enable the interface clock of the peripheral */ ++ int_clk_status = CLK_Enable(BPWR_Clks[clkIdx].intClk); ++ /* Enable the functional clock of the periphearl */ ++ fun_clk_status = CLK_Enable(BPWR_Clks[clkIdx].funClk); ++ } ++ } ++ if ((int_clk_status | fun_clk_status) != DSP_SOK) ++ return DSP_EFAIL; ++ return DSP_SOK; ++} ++ ++void DSPClkWakeupEventCtrl(u32 ClkId, bool enable) ++{ ++ struct CFG_HOSTRES resources; ++ DSP_STATUS status = DSP_SOK; ++ u32 iva2_grpsel; ++ u32 mpu_grpsel; ++ ++ status = CFG_GetHostResources( ++ (struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); ++ if (DSP_FAILED(status)) ++ return; ++ ++ switch (ClkId) { ++ case BPWR_GPTimer5: ++ iva2_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerPmBase) + 0xA8)); ++ mpu_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerPmBase) + 0xA4)); ++ if (enable) { ++ iva2_grpsel |= OMAP3430_GRPSEL_GPT5; ++ mpu_grpsel &= ~OMAP3430_GRPSEL_GPT5; ++ } else { ++ mpu_grpsel |= OMAP3430_GRPSEL_GPT5; ++ iva2_grpsel &= ~OMAP3430_GRPSEL_GPT5; ++ } ++ *((REG_UWORD32 *) ((u32) (resources.dwPerPmBase) + 0xA8)) ++ = iva2_grpsel; ++ *((REG_UWORD32 *) ((u32) (resources.dwPerPmBase) + 0xA4)) ++ = mpu_grpsel; ++ break; ++ case BPWR_GPTimer6: ++ iva2_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerPmBase) + 0xA8)); ++ mpu_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerPmBase) + 0xA4)); ++ if (enable) { ++ iva2_grpsel |= OMAP3430_GRPSEL_GPT6; ++ mpu_grpsel &= ~OMAP3430_GRPSEL_GPT6; ++ } else { ++ mpu_grpsel |= OMAP3430_GRPSEL_GPT6; ++ iva2_grpsel &= ~OMAP3430_GRPSEL_GPT6; ++ } ++ *((REG_UWORD32 *) ((u32) (resources.dwPerPmBase) + 0xA8)) ++ = iva2_grpsel; ++ *((REG_UWORD32 *) ((u32) (resources.dwPerPmBase) + 0xA4)) ++ = mpu_grpsel; ++ break; ++ case BPWR_GPTimer7: ++ iva2_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerPmBase) + 0xA8)); ++ mpu_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerPmBase) + 0xA4)); ++ if (enable) { ++ iva2_grpsel |= OMAP3430_GRPSEL_GPT7; ++ mpu_grpsel &= ~OMAP3430_GRPSEL_GPT7; ++ } else { ++ mpu_grpsel |= OMAP3430_GRPSEL_GPT7; ++ iva2_grpsel &= ~OMAP3430_GRPSEL_GPT7; ++ } ++ *((REG_UWORD32 *) ((u32) (resources.dwPerPmBase) + 0xA8)) ++ = iva2_grpsel; ++ *((REG_UWORD32 *) ((u32) (resources.dwPerPmBase) + 0xA4)) ++ = mpu_grpsel; ++ break; ++ case BPWR_GPTimer8: ++ iva2_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerPmBase) + 0xA8)); ++ mpu_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerPmBase) + 0xA4)); ++ if (enable) { ++ iva2_grpsel |= OMAP3430_GRPSEL_GPT8; ++ mpu_grpsel &= ~OMAP3430_GRPSEL_GPT8; ++ } else { ++ mpu_grpsel |= OMAP3430_GRPSEL_GPT8; ++ iva2_grpsel &= ~OMAP3430_GRPSEL_GPT8; ++ } ++ *((REG_UWORD32 *) ((u32) (resources.dwPerPmBase) + 0xA8)) ++ = iva2_grpsel; ++ *((REG_UWORD32 *) ((u32) (resources.dwPerPmBase) + 0xA4)) ++ = mpu_grpsel; ++ break; ++ case BPWR_MCBSP1: ++ iva2_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwCorePmBase) + 0xA8)); ++ mpu_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwCorePmBase) + 0xA4)); ++ if (enable) { ++ iva2_grpsel |= OMAP3430_GRPSEL_MCBSP1; ++ mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP1; ++ } else { ++ mpu_grpsel |= OMAP3430_GRPSEL_MCBSP1; ++ iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP1; ++ } ++ *((REG_UWORD32 *) ((u32) (resources.dwCorePmBase) + 0xA8)) ++ = iva2_grpsel; ++ *((REG_UWORD32 *) ((u32) (resources.dwCorePmBase) + 0xA4)) ++ = mpu_grpsel; ++ break; ++ case BPWR_MCBSP2: ++ iva2_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerPmBase) + 0xA8)); ++ mpu_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerPmBase) + 0xA4)); ++ if (enable) { ++ iva2_grpsel |= OMAP3430_GRPSEL_MCBSP2; ++ mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP2; ++ } else { ++ mpu_grpsel |= OMAP3430_GRPSEL_MCBSP2; ++ iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP2; ++ } ++ *((REG_UWORD32 *) ((u32) (resources.dwPerPmBase) + 0xA8)) ++ = iva2_grpsel; ++ *((REG_UWORD32 *) ((u32) (resources.dwPerPmBase) + 0xA4)) ++ = mpu_grpsel; ++ break; ++ case BPWR_MCBSP3: ++ iva2_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerPmBase) + 0xA8)); ++ mpu_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerPmBase) + 0xA4)); ++ if (enable) { ++ iva2_grpsel |= OMAP3430_GRPSEL_MCBSP3; ++ mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP3; ++ } else { ++ mpu_grpsel |= OMAP3430_GRPSEL_MCBSP3; ++ iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP3; ++ } ++ *((REG_UWORD32 *) ((u32) (resources.dwPerPmBase) + 0xA8)) ++ = iva2_grpsel; ++ *((REG_UWORD32 *) ((u32) (resources.dwPerPmBase) + 0xA4)) ++ = mpu_grpsel; ++ break; ++ case BPWR_MCBSP4: ++ iva2_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerPmBase) + 0xA8)); ++ mpu_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwPerPmBase) + 0xA4)); ++ if (enable) { ++ iva2_grpsel |= OMAP3430_GRPSEL_MCBSP4; ++ mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP4; ++ } else { ++ mpu_grpsel |= OMAP3430_GRPSEL_MCBSP4; ++ iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP4; ++ } ++ *((REG_UWORD32 *) ((u32) (resources.dwPerPmBase) + 0xA8)) ++ = iva2_grpsel; ++ *((REG_UWORD32 *) ((u32) (resources.dwPerPmBase) + 0xA4)) ++ = mpu_grpsel; ++ break; ++ case BPWR_MCBSP5: ++ iva2_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwCorePmBase) + 0xA8)); ++ mpu_grpsel = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwCorePmBase) + 0xA4)); ++ if (enable) { ++ iva2_grpsel |= OMAP3430_GRPSEL_MCBSP5; ++ mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP5; ++ } else { ++ mpu_grpsel |= OMAP3430_GRPSEL_MCBSP5; ++ iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP5; ++ } ++ *((REG_UWORD32 *) ((u32) (resources.dwCorePmBase) + 0xA8)) ++ = iva2_grpsel; ++ *((REG_UWORD32 *) ((u32) (resources.dwCorePmBase) + 0xA4)) ++ = mpu_grpsel; ++ break; ++ } ++} +diff --git a/drivers/dsp/bridge/wmd/tiomap_io.c b/drivers/dsp/bridge/wmd/tiomap_io.c +new file mode 100644 +index 0000000..6121e8f +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/tiomap_io.c +@@ -0,0 +1,427 @@ ++/* ++ * tiomap_io.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== _tiomap_io.c ======== ++ * Description: ++ * Implementation for the io read/write routines. ++ * ++ *! Revision History ++ *! ================ ++ *! 16-Feb-2004 vp: Fixed warning in WriteDspData function. ++ *! 16-Apr-2003 vp: Added support for TC word swap ++ *! 26-Feb-2003 vp: Fixed issue with EXT_BEG and EXT_END address. ++ *! 24-Feb-2003 vp: Ported to Linux platform ++ *! 08-Oct-2002 rr: Created. ++ */ ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++ ++/* ----------------------------------- specific to this file */ ++#include "_tiomap.h" ++#include "_tiomap_pwr.h" ++#include "tiomap_io.h" ++ ++static u32 ulExtBase; ++static u32 ulExtEnd; ++ ++static u32 ulShm0End; ++static u32 ulDynExtBase; ++static u32 ulTraceSecBeg; ++static u32 ulTraceSecEnd; ++static u32 ulShmBaseVirt; ++ ++bool bSymbolsReloaded = true; ++ ++/* ++ * ======== ReadExtDspData ======== ++ * Copies DSP external memory buffers to the host side buffers. ++ */ ++DSP_STATUS ReadExtDspData(struct WMD_DEV_CONTEXT *hDevContext, ++ OUT u8 *pbHostBuf, u32 dwDSPAddr, ++ u32 ulNumBytes, u32 ulMemType) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct WMD_DEV_CONTEXT *pDevContext = hDevContext; ++ u32 offset; ++ u32 ulTLBBaseVirt = 0; ++ u32 ulShmOffsetVirt = 0; ++ u32 dwExtProgVirtMem; ++ u32 dwBaseAddr = pDevContext->dwDspExtBaseAddr; ++ bool bTraceRead = false; ++ ++ DBG_Trace(DBG_ENTER, "ReadExtDspData," ++ "hDevContext: 0x%x\n\t\tpbHostBuf: 0x%x" ++ "\n\t\tdwDSPAddr: 0x%x\n\t\tulNumBytes: 0x%x\n\t\t" ++ "ulMemType: 0x%x\n", pDevContext, pbHostBuf, dwDSPAddr, ++ ulNumBytes, ulMemType); ++ ++ if (!ulShmBaseVirt) { ++ status = DEV_GetSymbol(pDevContext->hDevObject, ++ SHMBASENAME, &ulShmBaseVirt); ++ } ++ DBC_Assert(ulShmBaseVirt != 0); ++ ++ /* Check if it is a read of Trace section */ ++ if (!ulTraceSecBeg) { ++ status = DEV_GetSymbol(pDevContext->hDevObject, ++ DSP_TRACESEC_BEG, &ulTraceSecBeg); ++ } ++ DBC_Assert(ulTraceSecBeg != 0); ++ ++ if (DSP_SUCCEEDED(status) && !ulTraceSecEnd) { ++ status = DEV_GetSymbol(pDevContext->hDevObject, ++ DSP_TRACESEC_END, &ulTraceSecEnd); ++ } ++ DBC_Assert(ulTraceSecEnd != 0); ++ ++ if (DSP_SUCCEEDED(status)) { ++ if ((dwDSPAddr <= ulTraceSecEnd) && ++ (dwDSPAddr >= ulTraceSecBeg)) { ++ DBG_Trace(DBG_LEVEL5, "Reading from DSP Trace" ++ "section 0x%x \n", dwDSPAddr); ++ bTraceRead = true; ++ } ++ } ++ ++ /* If reading from TRACE, force remap/unmap */ ++ if ((bTraceRead) && dwBaseAddr) { ++ dwBaseAddr = 0; ++ pDevContext->dwDspExtBaseAddr = 0; ++ } ++ ++ if (!dwBaseAddr) { ++ /* Initialize ulExtBase and ulExtEnd */ ++ ulExtBase = 0; ++ ulExtEnd = 0; ++ ++ /* Get DYNEXT_BEG, EXT_BEG and EXT_END.*/ ++ if (DSP_SUCCEEDED(status) && !ulDynExtBase) { ++ status = DEV_GetSymbol(pDevContext->hDevObject, ++ DYNEXTBASE, &ulDynExtBase); ++ } ++ DBC_Assert(ulDynExtBase != 0); ++ ++ if (DSP_SUCCEEDED(status)) { ++ status = DEV_GetSymbol(pDevContext->hDevObject, ++ EXTBASE, &ulExtBase); ++ } ++ DBC_Assert(ulExtBase != 0); ++ ++ if (DSP_SUCCEEDED(status)) { ++ status = DEV_GetSymbol(pDevContext->hDevObject, ++ EXTEND, &ulExtEnd); ++ } ++ DBC_Assert(ulExtEnd != 0); ++ ++ /* Trace buffer is right after the SHM SEG0, ++ * so set the base address to SHMBASE */ ++ if (bTraceRead) { ++ ulExtBase = ulShmBaseVirt; ++ ulExtEnd = ulTraceSecEnd; ++ } ++ ++ DBC_Assert(ulExtEnd != 0); ++ DBC_Assert(ulExtEnd > ulExtBase); ++ ++ if (ulExtEnd < ulExtBase) ++ status = DSP_EFAIL; ++ ++ if (DSP_SUCCEEDED(status)) { ++ ulTLBBaseVirt = ++ pDevContext->aTLBEntry[0].ulDspVa * DSPWORDSIZE; ++ DBC_Assert(ulTLBBaseVirt <= ulShmBaseVirt); ++ dwExtProgVirtMem = pDevContext->aTLBEntry[0].ulGppVa; ++ ++ if (bTraceRead) { ++ DBG_Trace(DBG_LEVEL7, "ReadExtDspData: " ++ "GPP VA pointing to SHMMEMBASE 0x%x \n", ++ dwExtProgVirtMem); ++ } else { ++ ulShmOffsetVirt = ulShmBaseVirt - ulTLBBaseVirt; ++ ulShmOffsetVirt += PG_ALIGN_HIGH(ulExtEnd - ++ ulDynExtBase + 1, ++ HW_PAGE_SIZE_64KB); ++ dwExtProgVirtMem -= ulShmOffsetVirt; ++ dwExtProgVirtMem += (ulExtBase - ulDynExtBase); ++ DBG_Trace(DBG_LEVEL7, "ReadExtDspData: " ++ "GPP VA pointing to EXTMEMBASE 0x%x \n", ++ dwExtProgVirtMem); ++ pDevContext->dwDspExtBaseAddr = ++ dwExtProgVirtMem; ++ ++ /* This dwDspExtBaseAddr will get cleared only when the board is ++ * stopped. */ ++ if (!pDevContext->dwDspExtBaseAddr) { ++ status = DSP_EFAIL; ++ DBG_Trace(DBG_LEVEL7, "ReadExtDspData: " ++ "failed to Map the program memory\n"); ++ } ++ } ++ ++ dwBaseAddr = dwExtProgVirtMem; ++ } ++ } ++ ++ if (!dwBaseAddr || !ulExtBase || !ulExtEnd) { ++ DBG_Trace(DBG_LEVEL7, ++ "Symbols missing for Ext Prog reading \n"); ++ status = DSP_EFAIL; ++ } ++ ++ offset = dwDSPAddr - ulExtBase; ++ ++ if (DSP_SUCCEEDED(status)) ++ memcpy(pbHostBuf, (u8 *)dwBaseAddr+offset, ulNumBytes); ++ ++ return status; ++} ++/* ++ * ======== WriteDspData ======== ++ * purpose: ++ * Copies buffers to the DSP internal/external memory. ++ */ ++DSP_STATUS WriteDspData(struct WMD_DEV_CONTEXT *hDevContext, IN u8 *pbHostBuf, ++ u32 dwDSPAddr, u32 ulNumBytes, u32 ulMemType) ++{ ++ u32 offset; ++ u32 dwBaseAddr = hDevContext->dwDspBaseAddr; ++ struct CFG_HOSTRES resources; ++ DSP_STATUS status; ++ u32 base1, base2, base3; ++ base1 = OMAP_DSP_MEM1_SIZE; ++ base2 = OMAP_DSP_MEM2_BASE - OMAP_DSP_MEM1_BASE; ++ base3 = OMAP_DSP_MEM3_BASE - OMAP_DSP_MEM1_BASE; ++ DBG_Trace(DBG_ENTER, "Entered WriteDspData \n"); ++ ++ status = CFG_GetHostResources( ++ (struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); ++ ++ offset = dwDSPAddr - hDevContext->dwDSPStartAdd; ++ if (offset < base1) { ++ dwBaseAddr = MEM_LinearAddress(resources.dwMemBase[2], ++ resources.dwMemLength[2]); ++ } else if (offset > base1 && offset < base2+OMAP_DSP_MEM2_SIZE) { ++ dwBaseAddr = MEM_LinearAddress(resources.dwMemBase[3], ++ resources.dwMemLength[3]); ++ offset = offset - base2; ++ } else if (offset >= base2+OMAP_DSP_MEM2_SIZE && ++ offset < base3 + OMAP_DSP_MEM3_SIZE) { ++ dwBaseAddr = MEM_LinearAddress(resources.dwMemBase[4], ++ resources.dwMemLength[4]); ++ offset = offset - base3; ++ } else{ ++ status = DSP_EFAIL; ++ return status; ++ } ++ if (ulNumBytes) ++ memcpy((u8 *) (dwBaseAddr+offset), pbHostBuf, ulNumBytes); ++ else ++ *((u32 *) pbHostBuf) = dwBaseAddr+offset; ++ ++ return status; ++} ++ ++/* ++ * ======== WriteExtDspData ======== ++ * purpose: ++ * Copies buffers to the external memory. ++ * ++ */ ++DSP_STATUS WriteExtDspData(struct WMD_DEV_CONTEXT *pDevContext, ++ IN u8 *pbHostBuf, u32 dwDSPAddr, u32 ulNumBytes, ++ u32 ulMemType, bool bDynamicLoad) ++{ ++ u32 dwBaseAddr = pDevContext->dwDspExtBaseAddr; ++ u32 dwOffset = 0; ++ u8 bTempByte1, bTempByte2; ++ u8 remainByte[4]; ++ s32 i; ++ DSP_STATUS retVal = DSP_SOK; ++ u32 dwExtProgVirtMem; ++ u32 ulTLBBaseVirt = 0; ++ u32 ulShmOffsetVirt = 0; ++ struct CFG_HOSTRES hostRes; ++ bool bTraceLoad = false; ++ bTempByte1 = 0x0; ++ bTempByte2 = 0x0; ++ ++ DBG_Trace(DBG_ENTER, "Entered WriteExtDspData dwDSPAddr 0x%x " ++ "ulNumBytes 0x%x \n", dwDSPAddr, ulNumBytes); ++ if (bSymbolsReloaded) { ++ /* Check if it is a load to Trace section */ ++ retVal = DEV_GetSymbol(pDevContext->hDevObject, ++ DSP_TRACESEC_BEG, &ulTraceSecBeg); ++ if (DSP_SUCCEEDED(retVal)) ++ retVal = DEV_GetSymbol(pDevContext->hDevObject, ++ DSP_TRACESEC_END, &ulTraceSecEnd); ++ } ++ if (DSP_SUCCEEDED(retVal)) { ++ if ((dwDSPAddr <= ulTraceSecEnd) && ++ (dwDSPAddr >= ulTraceSecBeg)) { ++ DBG_Trace(DBG_LEVEL5, "Writing to DSP Trace " ++ "section 0x%x \n", dwDSPAddr); ++ bTraceLoad = true; ++ } ++ } ++ ++ /* If dynamic, force remap/unmap */ ++ if ((bDynamicLoad || bTraceLoad) && dwBaseAddr) { ++ dwBaseAddr = 0; ++ MEM_UnmapLinearAddress((void *)pDevContext->dwDspExtBaseAddr); ++ pDevContext->dwDspExtBaseAddr = 0x0; ++ } ++ if (!dwBaseAddr) { ++ if (bSymbolsReloaded) ++ /* Get SHM_BEG EXT_BEG and EXT_END. */ ++ retVal = DEV_GetSymbol(pDevContext->hDevObject, ++ SHMBASENAME, &ulShmBaseVirt); ++ DBC_Assert(ulShmBaseVirt != 0); ++ if (bDynamicLoad) { ++ if (DSP_SUCCEEDED(retVal)) { ++ if (bSymbolsReloaded) ++ retVal = DEV_GetSymbol(pDevContext-> ++ hDevObject, DYNEXTBASE, ++ &ulExtBase); ++ } ++ DBC_Assert(ulExtBase != 0); ++ if (DSP_SUCCEEDED(retVal)) { ++ /* DR OMAPS00013235 : DLModules array may be ++ * in EXTMEM. It is expected that DYNEXTMEM and ++ * EXTMEM are contiguous, so checking for the ++ * upper bound at EXTEND should be Ok. */ ++ if (bSymbolsReloaded) ++ retVal = DEV_GetSymbol(pDevContext-> ++ hDevObject, EXTEND, &ulExtEnd); ++ } ++ } else { ++ if (bSymbolsReloaded) { ++ if (DSP_SUCCEEDED(retVal)) ++ retVal = DEV_GetSymbol(pDevContext-> ++ hDevObject, EXTBASE, ++ &ulExtBase); ++ DBC_Assert(ulExtBase != 0); ++ if (DSP_SUCCEEDED(retVal)) ++ retVal = DEV_GetSymbol(pDevContext-> ++ hDevObject, EXTEND, &ulExtEnd); ++ } ++ } ++ /* Trace buffer it right after the SHM SEG0, so set the ++ * base address to SHMBASE */ ++ if (bTraceLoad) ++ ulExtBase = ulShmBaseVirt; ++ ++ DBC_Assert(ulExtEnd != 0); ++ DBC_Assert(ulExtEnd > ulExtBase); ++ if (ulExtEnd < ulExtBase) ++ retVal = DSP_EFAIL; ++ ++ if (DSP_SUCCEEDED(retVal)) { ++ ulTLBBaseVirt = pDevContext->aTLBEntry[0].ulDspVa * ++ DSPWORDSIZE; ++ DBC_Assert(ulTLBBaseVirt <= ulShmBaseVirt); ++ ++ if (bSymbolsReloaded) { ++ if (DSP_SUCCEEDED(retVal)) { ++ retVal = DEV_GetSymbol(pDevContext-> ++ hDevObject, DSP_TRACESEC_END, ++ &ulShm0End); ++ } ++ if (DSP_SUCCEEDED(retVal)) { ++ retVal = DEV_GetSymbol(pDevContext-> ++ hDevObject, DYNEXTBASE, ++ &ulDynExtBase); ++ } ++ } ++ ulShmOffsetVirt = ulShmBaseVirt - ulTLBBaseVirt; ++ if (bTraceLoad) { ++ dwExtProgVirtMem = pDevContext->aTLBEntry[0]. ++ ulGppVa; ++ } else { ++ CFG_GetHostResources( ++ (struct CFG_DEVNODE *) ++ DRV_GetFirstDevExtension(), &hostRes); ++ dwExtProgVirtMem = hostRes.dwMemBase[1]; ++ dwExtProgVirtMem += (ulExtBase - ulDynExtBase); ++ } ++ DBG_Trace(DBG_LEVEL7, "WriteExtDspData: GPP VA " ++ "pointing to EXTMEMBASE 0x%x \n", ++ dwExtProgVirtMem); ++ ++ pDevContext->dwDspExtBaseAddr = ++ (u32)MEM_LinearAddress((void *) ++ TO_VIRTUAL_UNCACHED(dwExtProgVirtMem), ulExtEnd ++ - ulExtBase); ++ dwBaseAddr += pDevContext->dwDspExtBaseAddr; ++ /* This dwDspExtBaseAddr will get cleared only when ++ * the board is stopped. */ ++ if (!pDevContext->dwDspExtBaseAddr) { ++ retVal = DSP_EFAIL; ++ DBG_Trace(DBG_LEVEL7, "WriteExtDspData: failed " ++ "to Map the program memory\n"); ++ } ++ } ++ } ++ if (!dwBaseAddr || !ulExtBase || !ulExtEnd) { ++ DBG_Trace(DBG_LEVEL7, "Symbols missing for Ext Prog loading\n"); ++ retVal = DSP_EFAIL; ++ } ++ if (DSP_SUCCEEDED(retVal)) { ++ for (i = 0; i < 4; i++) ++ remainByte[i] = 0x0; ++ ++ dwOffset = dwDSPAddr - ulExtBase; ++ /* Also make sure the dwDSPAddr is < ulExtEnd */ ++ if (dwDSPAddr > ulExtEnd || dwOffset > dwDSPAddr) { ++ DBG_Trace(DBG_LEVEL7, "We can not load at this address " ++ "dwDSPAddr=0x%x, ulExt/DynBase=0x%x, " ++ "ulExtEnd=0x%x\n", dwDSPAddr, ulExtBase, ++ ulExtEnd); ++ retVal = DSP_EFAIL; ++ } ++ } ++ if (DSP_SUCCEEDED(retVal)) { ++ if (ulNumBytes) ++ memcpy((u8 *) dwBaseAddr + dwOffset, pbHostBuf, ++ ulNumBytes); ++ else ++ *((u32 *) pbHostBuf) = dwBaseAddr+dwOffset; ++ } ++ /* Unmap here to force remap for other Ext loads */ ++ if ((bDynamicLoad || bTraceLoad) && pDevContext->dwDspExtBaseAddr) { ++ MEM_UnmapLinearAddress((void *) pDevContext->dwDspExtBaseAddr); ++ pDevContext->dwDspExtBaseAddr = 0x0; ++ } ++ bSymbolsReloaded = false; ++ return retVal; ++} ++ +diff --git a/drivers/dsp/bridge/wmd/tiomap_io.h b/drivers/dsp/bridge/wmd/tiomap_io.h +new file mode 100644 +index 0000000..84a7553 +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/tiomap_io.h +@@ -0,0 +1,112 @@ ++/* ++ * tiomap_io.h ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== _tiomap_io.h ======== ++ * Description: ++ * Definitions, types and function prototypes for the io ++ * (r/w external mem). ++ * ++ *! Revision History ++ *! ================ ++ *! 08-Oct-2002 rr: Created. ++ */ ++ ++#ifndef _TIOMAP_IO_ ++#define _TIOMAP_IO_ ++ ++/* ++ * Symbol that defines beginning of shared memory. ++ * For OMAP (Helen) this is the DSP Virtual base address of SDRAM. ++ * This will be used to program DSP MMU to map DSP Virt to GPP phys. ++ * (see dspMmuTlbEntry()). ++ */ ++#define SHMBASENAME "SHM_BEG" ++#define EXTBASE "EXT_BEG" ++#define EXTEND "_EXT_END" ++#define DYNEXTBASE "_DYNEXT_BEG" ++#define DYNEXTEND "_DYNEXT_END" ++#define IVAEXTMEMBASE "_IVAEXTMEM_BEG" ++#define IVAEXTMEMEND "_IVAEXTMEM_END" ++ ++ ++#define DSP_TRACESEC_BEG "_BRIDGE_TRACE_BEG" ++#define DSP_TRACESEC_END "_BRIDGE_TRACE_END" ++ ++#define SYS_PUTCBEG "_SYS_PUTCBEG" ++#define SYS_PUTCEND "_SYS_PUTCEND" ++#define BRIDGE_SYS_PUTC_current "_BRIDGE_SYS_PUTC_current" ++ ++ ++#define WORDSWAP_ENABLE 0x3 /* Enable word swap */ ++ ++/* ++ * ======== ReadExtDspData ======== ++ * Reads it from DSP External memory. The external memory for the DSP ++ * is configured by the combination of DSP MMU and SHM Memory manager in the CDB ++ */ ++extern DSP_STATUS ReadExtDspData(struct WMD_DEV_CONTEXT *pDevContext, ++ OUT u8 *pbHostBuf, u32 dwDSPAddr, ++ u32 ulNumBytes, u32 ulMemType); ++ ++/* ++ * ======== WriteDspData ======== ++ */ ++extern DSP_STATUS WriteDspData(struct WMD_DEV_CONTEXT *pDevContext, ++ OUT u8 *pbHostBuf, u32 dwDSPAddr, ++ u32 ulNumBytes, u32 ulMemType); ++ ++/* ++ * ======== WriteExtDspData ======== ++ * Writes to the DSP External memory for external program. ++ * The ext mem for progra is configured by the combination of DSP MMU and ++ * SHM Memory manager in the CDB ++ */ ++extern DSP_STATUS WriteExtDspData(struct WMD_DEV_CONTEXT *pDevContext, ++ IN u8 *pbHostBuf, u32 dwDSPAddr, ++ u32 ulNumBytes, u32 ulMemType, ++ bool bDynamicLoad); ++ ++/* ++ * ======== WriteExt32BitDspData ======== ++ * Writes 32 bit data to the external memory ++ */ ++extern inline void WriteExt32BitDspData(IN const ++ struct WMD_DEV_CONTEXT *pDevContext, IN u32 dwDSPAddr, ++ IN u32 val) ++{ ++ *(u32 *)dwDSPAddr = ((pDevContext->tcWordSwapOn) ? (((val << 16) & ++ 0xFFFF0000) | ((val >> 16) & 0x0000FFFF)) : val); ++} ++ ++/* ++ * ======== ReadExt32BitDspData ======== ++ * Reads 32 bit data from the external memory ++ */ ++extern inline u32 ReadExt32BitDspData(IN const struct WMD_DEV_CONTEXT ++ *pDevContext, IN u32 dwDSPAddr) ++{ ++ u32 retVal; ++ retVal = *(u32 *)dwDSPAddr; ++ ++ retVal = ((pDevContext->tcWordSwapOn) ? (((retVal << 16) ++ & 0xFFFF0000) | ((retVal >> 16) & 0x0000FFFF)) : retVal); ++ return retVal; ++} ++ ++#endif /* _TIOMAP_IO_ */ ++ +diff --git a/drivers/dsp/bridge/wmd/tiomap_sm.c b/drivers/dsp/bridge/wmd/tiomap_sm.c +new file mode 100644 +index 0000000..a6d5d62 +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/tiomap_sm.c +@@ -0,0 +1,195 @@ ++/* ++ * tiomap_sm.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "_tiomap.h" ++#include "_tiomap_pwr.h" ++ ++#define MAILBOX_FIFOSTATUS(m) (0x80 + 4 * (m)) ++ ++static inline unsigned int fifo_full(void __iomem *mbox_base, int mbox_id) ++{ ++ return __raw_readl(mbox_base + MAILBOX_FIFOSTATUS(mbox_id)) & 0x1; ++} ++ ++DSP_STATUS CHNLSM_EnableInterrupt(struct WMD_DEV_CONTEXT *pDevContext) ++{ ++ DSP_STATUS status = DSP_SOK; ++ u32 numMbxMsg; ++ u32 mbxValue; ++ struct CFG_HOSTRES resources; ++ u32 devType; ++ struct IO_MGR *hIOMgr; ++ ++ DBG_Trace(DBG_ENTER, "CHNLSM_EnableInterrupt(0x%x)\n", pDevContext); ++ ++ /* Read the messages in the mailbox until the message queue is empty */ ++ ++ CFG_GetHostResources((struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), ++ &resources); ++ DEV_GetDevType(pDevContext->hDevObject, &devType); ++ status = DEV_GetIOMgr(pDevContext->hDevObject, &hIOMgr); ++ if (devType == DSP_UNIT) { ++ HW_MBOX_NumMsgGet(resources.dwMboxBase, ++ MBOX_DSP2ARM, &numMbxMsg); ++ while (numMbxMsg != 0) { ++ HW_MBOX_MsgRead(resources.dwMboxBase, ++ MBOX_DSP2ARM, ++ &mbxValue); ++ numMbxMsg--; ++ } ++ /* clear the DSP mailbox as well...*/ ++ HW_MBOX_NumMsgGet(resources.dwMboxBase, ++ MBOX_ARM2DSP, &numMbxMsg); ++ while (numMbxMsg != 0) { ++ HW_MBOX_MsgRead(resources.dwMboxBase, ++ MBOX_ARM2DSP, &mbxValue); ++ numMbxMsg--; ++ udelay(10); ++ ++ HW_MBOX_EventAck(resources.dwMboxBase, MBOX_ARM2DSP, ++ HW_MBOX_U1_DSP1, ++ HW_MBOX_INT_NEW_MSG); ++ } ++ /* Enable the new message events on this IRQ line */ ++ HW_MBOX_EventEnable(resources.dwMboxBase, ++ MBOX_DSP2ARM, ++ MBOX_ARM, ++ HW_MBOX_INT_NEW_MSG); ++ } ++ ++ return status; ++} ++ ++DSP_STATUS CHNLSM_DisableInterrupt(struct WMD_DEV_CONTEXT *pDevContext) ++{ ++ struct CFG_HOSTRES resources; ++ ++ DBG_Trace(DBG_ENTER, "CHNLSM_DisableInterrupt(0x%x)\n", pDevContext); ++ ++ CFG_GetHostResources((struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), ++ &resources); ++ HW_MBOX_EventDisable(resources.dwMboxBase, MBOX_DSP2ARM, ++ MBOX_ARM, HW_MBOX_INT_NEW_MSG); ++ return DSP_SOK; ++} ++ ++DSP_STATUS CHNLSM_InterruptDSP2(struct WMD_DEV_CONTEXT *pDevContext, ++ u16 wMbVal) ++{ ++#ifdef CONFIG_BRIDGE_DVFS ++ struct dspbridge_platform_data *pdata = ++ omap_dspbridge_dev->dev.platform_data; ++ u32 opplevel = 0; ++#endif ++ struct CFG_HOSTRES resources; ++ DSP_STATUS status = DSP_SOK; ++ unsigned long timeout; ++ u32 temp; ++ ++ status = CFG_GetHostResources((struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), ++ &resources); ++ if (DSP_FAILED(status)) ++ return DSP_EFAIL; ++#ifdef CONFIG_BRIDGE_DVFS ++ if (pDevContext->dwBrdState == BRD_DSP_HIBERNATION || ++ pDevContext->dwBrdState == BRD_HIBERNATION) { ++ if (pdata->dsp_get_opp) ++ opplevel = (*pdata->dsp_get_opp)(); ++ if (opplevel == 1) { ++ if (pdata->dsp_set_min_opp) ++ (*pdata->dsp_set_min_opp)(opplevel+1); ++ } ++ } ++#endif ++ ++ if (pDevContext->dwBrdState == BRD_DSP_HIBERNATION || ++ pDevContext->dwBrdState == BRD_HIBERNATION) { ++ /* Restore mailbox settings */ ++ /* Restart the peripheral clocks that were disabled only ++ * in DSP initiated Hibernation case.*/ ++ if (pDevContext->dwBrdState == BRD_DSP_HIBERNATION) { ++ DSP_PeripheralClocks_Enable(pDevContext, NULL); ++ /* Enabling Dpll in lock mode*/ ++ temp = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwCmBase) + 0x34)); ++ temp = (temp & 0xFFFFFFFE) | 0x1; ++ *((REG_UWORD32 *) ((u32) (resources.dwCmBase) + 0x34)) = ++ (u32) temp; ++ temp = (u32) *((REG_UWORD32 *) ++ ((u32) (resources.dwCmBase) + 0x4)); ++ temp = (temp & 0xFFFFFC8) | 0x37; ++ ++ *((REG_UWORD32 *) ((u32) (resources.dwCmBase) + 0x4)) = ++ (u32) temp; ++ } ++ HW_MBOX_restoreSettings(resources.dwMboxBase); ++ ++ /* Access MMU SYS CONFIG register to generate a short wakeup */ ++ temp = (u32) *((REG_UWORD32 *) ((u32) ++ (resources.dwDmmuBase) + 0x10)); ++ ++ pDevContext->dwBrdState = BRD_RUNNING; ++ } ++ timeout = jiffies + msecs_to_jiffies(1); ++ while (fifo_full((void __iomem *) resources.dwMboxBase, 0)) { ++ if (time_after(jiffies, timeout)) { ++ printk(KERN_ERR "dspbridge: timed out waiting for mailbox\n"); ++ return WMD_E_TIMEOUT; ++ } ++ } ++ DBG_Trace(DBG_LEVEL3, "writing %x to Mailbox\n", ++ wMbVal); ++ ++ HW_MBOX_MsgWrite(resources.dwMboxBase, MBOX_ARM2DSP, ++ wMbVal); ++ return DSP_SOK; ++} ++ ++bool CHNLSM_ISR(struct WMD_DEV_CONTEXT *pDevContext, bool *pfSchedDPC, ++ u16 *pwIntrVal) ++{ ++ struct CFG_HOSTRES resources; ++ u32 numMbxMsg; ++ u32 mbxValue; ++ ++ DBG_Trace(DBG_ENTER, "CHNLSM_ISR(0x%x)\n", pDevContext); ++ ++ CFG_GetHostResources((struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), &resources); ++ ++ HW_MBOX_NumMsgGet(resources.dwMboxBase, MBOX_DSP2ARM, &numMbxMsg); ++ ++ if (numMbxMsg > 0) { ++ HW_MBOX_MsgRead(resources.dwMboxBase, MBOX_DSP2ARM, &mbxValue); ++ ++ HW_MBOX_EventAck(resources.dwMboxBase, MBOX_DSP2ARM, ++ HW_MBOX_U0_ARM, HW_MBOX_INT_NEW_MSG); ++ ++ DBG_Trace(DBG_LEVEL3, "Read %x from Mailbox\n", mbxValue); ++ *pwIntrVal = (u16) mbxValue; ++ } ++ /* Set *pfSchedDPC to true; */ ++ *pfSchedDPC = true; ++ return true; ++} +diff --git a/drivers/dsp/bridge/wmd/ue_deh.c b/drivers/dsp/bridge/wmd/ue_deh.c +new file mode 100644 +index 0000000..d5551cb +--- /dev/null ++++ b/drivers/dsp/bridge/wmd/ue_deh.c +@@ -0,0 +1,329 @@ ++/* ++ * ue_deh.c ++ * ++ * DSP-BIOS Bridge driver support functions for TI OMAP processors. ++ * ++ * Copyright (C) 2005-2006 Texas Instruments, Inc. ++ * ++ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++ ++/* ++ * ======== ue_deh.c ======== ++ * Description: ++ * Implements upper edge DSP exception handling (DEH) functions. ++ * ++ *! Revision History: ++ *! ================ ++ *! 03-Jan-2005 hn: Support for IVA DEH. ++ *! 05-Jan-2004 vp: Updated for the 24xx HW library. ++ *! 19-Feb-2003 vp: Code review updates. ++ *! - Cosmetic changes. ++ *! 18-Oct-2002 sb: Ported to Linux platform. ++ *! 10-Dec-2001 kc: Updated DSP error reporting in DEBUG mode. ++ *! 10-Sep-2001 kc: created. ++ */ ++ ++/* ----------------------------------- Host OS */ ++#include ++ ++/* ----------------------------------- DSP/BIOS Bridge */ ++#include ++#include ++#include ++ ++/* ----------------------------------- Trace & Debug */ ++#include ++#include ++ ++/* ----------------------------------- OS Adaptation Layer */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ----------------------------------- Link Driver */ ++#include ++ ++/* ----------------------------------- Platform Manager */ ++#include ++#include ++ ++/* ------------------------------------ Hardware Abstraction Layer */ ++#include ++#include ++ ++/* ----------------------------------- This */ ++#include "mmu_fault.h" ++#include "_tiomap.h" ++#include "_deh.h" ++#include "_tiomap_mmu.h" ++#include "_tiomap_pwr.h" ++#include ++ ++static struct HW_MMUMapAttrs_t mapAttrs = { HW_LITTLE_ENDIAN, ++ HW_ELEM_SIZE_16BIT, ++ HW_MMU_CPUES} ; ++#define VirtToPhys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET) ++/* ++ * ======== WMD_DEH_Create ======== ++ * Creates DEH manager object. ++ */ ++DSP_STATUS WMD_DEH_Create(OUT struct DEH_MGR **phDehMgr, ++ struct DEV_OBJECT *hDevObject) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEH_MGR *pDehMgr = NULL; ++ struct CFG_HOSTRES cfgHostRes; ++ struct CFG_DEVNODE *hDevNode; ++ struct WMD_DEV_CONTEXT *hWmdContext = NULL; ++ ++ DBG_Trace(DBG_LEVEL1, "Entering DEH_Create: 0x%x\n", phDehMgr); ++ /* Message manager will be created when a file is loaded, since ++ * size of message buffer in shared memory is configurable in ++ * the base image. */ ++ /* Get WMD context info. */ ++ DEV_GetWMDContext(hDevObject, &hWmdContext); ++ DBC_Assert(hWmdContext); ++ /* Allocate IO manager object: */ ++ MEM_AllocObject(pDehMgr, struct DEH_MGR, SIGNATURE); ++ if (pDehMgr == NULL) { ++ status = DSP_EMEMORY; ++ } else { ++ /* Create an NTFY object to manage notifications */ ++ if (DSP_SUCCEEDED(status)) ++ status = NTFY_Create(&pDehMgr->hNtfy); ++ ++ /* Create a DPC object. */ ++ status = DPC_Create(&pDehMgr->hMmuFaultDpc, MMU_FaultDpc, ++ (void *)pDehMgr); ++ if (DSP_SUCCEEDED(status)) ++ status = DEV_GetDevNode(hDevObject, &hDevNode); ++ ++ if (DSP_SUCCEEDED(status)) ++ status = CFG_GetHostResources(hDevNode, &cfgHostRes); ++ ++ if (DSP_SUCCEEDED(status)) { ++ /* Fill in context structure */ ++ pDehMgr->hWmdContext = hWmdContext; ++ pDehMgr->errInfo.dwErrMask = 0L; ++ pDehMgr->errInfo.dwVal1 = 0L; ++ pDehMgr->errInfo.dwVal2 = 0L; ++ pDehMgr->errInfo.dwVal3 = 0L; ++ /* Install ISR function for DSP MMU fault */ ++ if ((request_irq(INT_DSP_MMU_IRQ, MMU_FaultIsr, 0, ++ "DspBridge\tiommu fault", (void *)pDehMgr)) == 0) ++ status = DSP_SOK; ++ else ++ status = DSP_EFAIL; ++ } ++ } ++ if (DSP_FAILED(status)) { ++ /* If create failed, cleanup */ ++ WMD_DEH_Destroy((struct DEH_MGR *)pDehMgr); ++ *phDehMgr = NULL; ++ } else { ++ *phDehMgr = (struct DEH_MGR *)pDehMgr; ++ DBG_Trace(DBG_LEVEL1, "ISR_IRQ Object 0x%x \n", ++ pDehMgr); ++ } ++ DBG_Trace(DBG_LEVEL1, "Exiting DEH_Create.\n"); ++ return status; ++} ++ ++/* ++ * ======== WMD_DEH_Destroy ======== ++ * Destroys DEH manager object. ++ */ ++DSP_STATUS WMD_DEH_Destroy(struct DEH_MGR *hDehMgr) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEH_MGR *pDehMgr = (struct DEH_MGR *)hDehMgr; ++ ++ DBG_Trace(DBG_LEVEL1, "Entering DEH_Destroy: 0x%x\n", pDehMgr); ++ if (MEM_IsValidHandle(pDehMgr, SIGNATURE)) { ++ /* If notification object exists, delete it */ ++ if (pDehMgr->hNtfy) ++ (void)NTFY_Delete(pDehMgr->hNtfy); ++ /* Disable DSP MMU fault */ ++ free_irq(INT_DSP_MMU_IRQ, pDehMgr); ++ (void)DPC_Destroy(pDehMgr->hMmuFaultDpc); ++ /* Deallocate the DEH manager object */ ++ MEM_FreeObject(pDehMgr); ++ } ++ DBG_Trace(DBG_LEVEL1, "Exiting DEH_Destroy.\n"); ++ return status; ++} ++ ++/* ++ * ======== WMD_DEH_RegisterNotify ======== ++ * Registers for DEH notifications. ++ */ ++DSP_STATUS WMD_DEH_RegisterNotify(struct DEH_MGR *hDehMgr, u32 uEventMask, ++ u32 uNotifyType, ++ struct DSP_NOTIFICATION *hNotification) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEH_MGR *pDehMgr = (struct DEH_MGR *)hDehMgr; ++ ++ DBG_Trace(DBG_LEVEL1, "Entering WMD_DEH_RegisterNotify: 0x%x\n", ++ pDehMgr); ++ ++ if (MEM_IsValidHandle(pDehMgr, SIGNATURE)) { ++ status = NTFY_Register(pDehMgr->hNtfy, hNotification, ++ uEventMask, uNotifyType); ++ } ++ DBG_Trace(DBG_LEVEL1, "Exiting WMD_DEH_RegisterNotify.\n"); ++ return status; ++} ++ ++ ++/* ++ * ======== WMD_DEH_Notify ======== ++ * DEH error notification function. Informs user about the error. ++ */ ++void WMD_DEH_Notify(struct DEH_MGR *hDehMgr, u32 ulEventMask, ++ u32 dwErrInfo) ++{ ++ struct DEH_MGR *pDehMgr = (struct DEH_MGR *)hDehMgr; ++ struct WMD_DEV_CONTEXT *pDevContext; ++ DSP_STATUS status = DSP_SOK; ++ u32 memPhysical = 0; ++ u32 HW_MMU_MAX_TLB_COUNT = 31; ++ u32 extern faultAddr; ++ struct CFG_HOSTRES resources; ++ u32 dummyVaAddr; ++ HW_STATUS hwStatus; ++ ++ status = CFG_GetHostResources( ++ (struct CFG_DEVNODE *)DRV_GetFirstDevExtension(), ++ &resources); ++ if (DSP_FAILED(status)) ++ DBG_Trace(DBG_LEVEL7, ++ "**Failed to get Host Resources in MMU ISR **\n"); ++ ++ DBG_Trace(DBG_LEVEL1, "Entering WMD_DEH_Notify: 0x%x, 0x%x\n", pDehMgr, ++ ulEventMask); ++ if (MEM_IsValidHandle(pDehMgr, SIGNATURE)) { ++ printk(KERN_INFO "WMD_DEH_Notify: ********** DEVICE EXCEPTION " ++ "**********\n"); ++ pDevContext = (struct WMD_DEV_CONTEXT *)pDehMgr->hWmdContext; ++ ++ switch (ulEventMask) { ++ case DSP_SYSERROR: ++ /* reset errInfo structure before use */ ++ pDehMgr->errInfo.dwErrMask = DSP_SYSERROR; ++ pDehMgr->errInfo.dwVal1 = 0L; ++ pDehMgr->errInfo.dwVal2 = 0L; ++ pDehMgr->errInfo.dwVal3 = 0L; ++ pDehMgr->errInfo.dwVal1 = dwErrInfo; ++ printk(KERN_ERR "WMD_DEH_Notify: DSP_SYSERROR, errInfo " ++ "= 0x%x\n", dwErrInfo); ++ break; ++ case DSP_MMUFAULT: ++ /* MMU fault routine should have set err info ++ * structure */ ++ pDehMgr->errInfo.dwErrMask = DSP_MMUFAULT; ++ printk(KERN_INFO "WMD_DEH_Notify: DSP_MMUFAULT," ++ "errInfo = 0x%x\n", dwErrInfo); ++ printk(KERN_INFO "WMD_DEH_Notify: DSP_MMUFAULT, High " ++ "Address = 0x%x\n", ++ (unsigned int)pDehMgr->errInfo.dwVal1); ++ printk(KERN_INFO "WMD_DEH_Notify: DSP_MMUFAULT, Low " ++ "Address = 0x%x\n", ++ (unsigned int)pDehMgr->errInfo.dwVal2); ++ printk(KERN_INFO "WMD_DEH_Notify: DSP_MMUFAULT, fault " ++ "address = 0x%x\n", (unsigned int)faultAddr); ++ dummyVaAddr = (u32)MEM_Calloc(sizeof(char) * 0x1000, ++ MEM_PAGED); ++ memPhysical = (u32)MEM_Calloc(sizeof(char) * 0x1000, ++ MEM_PAGED); ++ dummyVaAddr = PG_ALIGN_LOW((u32)dummyVaAddr, ++ PG_SIZE_4K); ++ memPhysical = VirtToPhys(dummyVaAddr); ++ DBG_Trace(DBG_LEVEL6, "WMD_DEH_Notify: DSP_MMUFAULT, " ++ "mem Physical= 0x%x\n", memPhysical); ++ pDevContext = (struct WMD_DEV_CONTEXT *) ++ pDehMgr->hWmdContext; ++ /* Reset the dynamic mmu index to fixed count if it ++ * exceeds 31. So that the dynmmuindex is always ++ * between the range of standard/fixed entries ++ * and 31. */ ++ if (pDevContext->numTLBEntries > ++ HW_MMU_MAX_TLB_COUNT) { ++ pDevContext->numTLBEntries = pDevContext-> ++ fixedTLBEntries; ++ } ++ DBG_Trace(DBG_LEVEL6, "Adding TLB Entry %d: VA: 0x%x, " ++ "PA: 0x%x\n", pDevContext-> ++ numTLBEntries, faultAddr, memPhysical); ++ if (DSP_SUCCEEDED(status)) { ++ hwStatus = HW_MMU_TLBAdd(resources.dwDmmuBase, ++ memPhysical, faultAddr, ++ HW_PAGE_SIZE_4KB, 1, &mapAttrs, ++ HW_SET, HW_SET); ++ } ++ /* send an interrupt to DSP */ ++ HW_MBOX_MsgWrite(resources.dwMboxBase, MBOX_ARM2DSP, ++ MBX_DEH_CLASS | MBX_DEH_EMMU); ++ /* Clear MMU interrupt */ ++ HW_MMU_EventAck(resources.dwDmmuBase, ++ HW_MMU_TRANSLATION_FAULT); ++ break; ++ default: ++ DBG_Trace(DBG_LEVEL6, ++ "WMD_DEH_Notify: Unknown Error, errInfo = " ++ "0x%x\n", dwErrInfo); ++ break; ++ } ++ /* Set the Board state as ERROR */ ++ pDevContext->dwBrdState = BRD_ERROR; ++ /* Disable all the clocks that were enabled by DSP */ ++ (void)DSP_PeripheralClocks_Disable(pDevContext, NULL); ++ /* Call DSP Trace Buffer */ ++ PrintDspTraceBuffer(hDehMgr->hWmdContext); ++ ++ /* Signal DSP error/exception event. */ ++ NTFY_Notify(pDehMgr->hNtfy, ulEventMask); ++ } ++ DBG_Trace(DBG_LEVEL1, "Exiting WMD_DEH_Notify\n"); ++ ++} ++ ++/* ++ * ======== WMD_DEH_GetInfo ======== ++ * Retrieves error information. ++ */ ++DSP_STATUS WMD_DEH_GetInfo(struct DEH_MGR *hDehMgr, ++ struct DSP_ERRORINFO *pErrInfo) ++{ ++ DSP_STATUS status = DSP_SOK; ++ struct DEH_MGR *pDehMgr = (struct DEH_MGR *)hDehMgr; ++ ++ DBC_Require(pDehMgr); ++ DBC_Require(pErrInfo); ++ ++ DBG_Trace(DBG_LEVEL1, "Entering WMD_DEH_GetInfo: 0x%x\n", hDehMgr); ++ ++ if (MEM_IsValidHandle(pDehMgr, SIGNATURE)) { ++ /* Copy DEH error info structure to PROC error info ++ * structure. */ ++ pErrInfo->dwErrMask = pDehMgr->errInfo.dwErrMask; ++ pErrInfo->dwVal1 = pDehMgr->errInfo.dwVal1; ++ pErrInfo->dwVal2 = pDehMgr->errInfo.dwVal2; ++ pErrInfo->dwVal3 = pDehMgr->errInfo.dwVal3; ++ } ++ ++ DBG_Trace(DBG_LEVEL1, "Exiting WMD_DEH_GetInfo\n"); ++ ++ return status; ++} +-- +1.5.6.3 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dss2-blank-rotate-accessible-by-user.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dss2-blank-rotate-accessible-by-user.patch new file mode 100644 index 0000000000..d75f15d00a --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dss2-blank-rotate-accessible-by-user.patch @@ -0,0 +1,20 @@ +--- a/drivers/video/fbsysfs.c 2009-07-17 12:23:16.000000000 -0700 ++++ b/drivers/video/fbsysfs.c 2009-07-17 12:24:32.000000000 -0700 +@@ -489,7 +489,7 @@ + * fbdev to use configfs instead of sysfs */ + static struct device_attribute device_attrs[] = { + __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp), +- __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank), ++ __ATTR(blank, S_IRUGO|S_IWUGO, show_blank, store_blank), + __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console), + __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor), + __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode), +@@ -498,7 +498,7 @@ + __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual), + __ATTR(name, S_IRUGO, show_name, NULL), + __ATTR(stride, S_IRUGO, show_stride, NULL), +- __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate), ++ __ATTR(rotate, S_IRUGO|S_IWUGO, show_rotate, store_rotate), + __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate), + #ifdef CONFIG_FB_BACKLIGHT + __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve), diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dss2-export-status.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dss2-export-status.patch new file mode 100644 index 0000000000..eaa78e28c7 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dss2-export-status.patch @@ -0,0 +1,44 @@ +From c78da49092fd206943f77aa9ebc3fa2c1301ac5a Mon Sep 17 00:00:00 2001 +From: Tim Yamin +Date: Thu, 23 Apr 2009 18:19:32 -0700 +Subject: [PATCH] DSS2: Export display status to sysfs + +It is useful to know from userspace if the display has been turned off, +e.g. to toggle display power from userspace. + +Signed-off-by: Tim Yamin +--- + drivers/video/omap2/omapfb/omapfb-sysfs.c | 10 ++++++++++ + 1 files changed, 10 insertions(+), 0 deletions(-) + +diff --git a/drivers/video/omap2/omapfb/omapfb-sysfs.c b/drivers/video/omap2/omapfb/omapfb-sysfs.c +index 2c88718..28438fb 100644 +--- a/drivers/video/omap2/omapfb/omapfb-sysfs.c ++++ b/drivers/video/omap2/omapfb/omapfb-sysfs.c +@@ -326,6 +326,15 @@ static ssize_t show_virt(struct device *dev, + return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region.vaddr); + } + ++static ssize_t show_display_state(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fb_info *fbi = dev_get_drvdata(dev); ++ struct omap_display *display = fb2display(fbi); ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", display->state); ++} ++ + static struct device_attribute omapfb_attrs[] = { + __ATTR(rotate_type, S_IRUGO, show_rotate_type, NULL), + __ATTR(mirror, S_IRUGO | S_IWUSR, show_mirror, store_mirror), +@@ -333,6 +342,7 @@ static struct device_attribute omapfb_attrs[] = { + __ATTR(overlays, S_IRUGO | S_IWUSR, show_overlays, store_overlays), + __ATTR(phys_addr, S_IRUGO, show_phys, NULL), + __ATTR(virt_addr, S_IRUGO, show_virt, NULL), ++ __ATTR(display_state, S_IRUGO, show_display_state, NULL), + }; + + int omapfb_create_sysfs(struct omapfb2_device *fbdev) +-- +1.5.6.3 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dss2-fix-XY-coordinates-when-rotating.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dss2-fix-XY-coordinates-when-rotating.patch new file mode 100644 index 0000000000..98a82c8a35 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dss2-fix-XY-coordinates-when-rotating.patch @@ -0,0 +1,177 @@ +From a9cc890ffea21fa492678b1755a263120cbddf0e Mon Sep 17 00:00:00 2001 +From: Tim Yamin +Date: Mon, 20 Apr 2009 20:29:11 -0700 +Subject: [PATCH] DSS2: OMAPFB: Translate X/Y coordinates for the video planes when rotating. + +When rotating the video planes, translate the X/Y coordinates such that +a [0,0] from userspace always maps to the correct upper left corner of +the display. This patch assumes that you rotate plane 0 before rotating +plane 1. Patch also corrects the scaling parameters so that the video is +displayed in the correct orientation (vertically, instead of horizontally) +when rotating by 90 / 270 degrees. + +Signed-off-by: Tim Yamin +--- + drivers/video/omap2/dss/dispc.c | 16 ++++++++++++---- + drivers/video/omap2/dss/overlay.c | 6 ++++++ + drivers/video/omap2/omapfb/omapfb-ioctl.c | 28 ++++++++++++++++++++++++++++ + 3 files changed, 46 insertions(+), 4 deletions(-) + +diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c +index 7e551c2..bece91d 100644 +--- a/drivers/video/omap2/dss/dispc.c ++++ b/drivers/video/omap2/dss/dispc.c +@@ -1580,10 +1580,18 @@ static int _dispc_setup_plane(enum omap_plane plane, + _dispc_set_pic_size(plane, width, height); + + if (plane != OMAP_DSS_GFX) { +- _dispc_set_scaling(plane, width, height, +- out_width, out_height, +- ilace, five_taps, fieldmode); +- _dispc_set_vid_size(plane, out_width, out_height); ++ if (rotation == 1 || rotation == 3) { ++ _dispc_set_scaling(plane, width, height, ++ out_height, out_width, ++ ilace, five_taps, fieldmode); ++ _dispc_set_vid_size(plane, out_height, out_width); ++ } else { ++ _dispc_set_scaling(plane, width, height, ++ out_width, out_height, ++ ilace, five_taps, fieldmode); ++ _dispc_set_vid_size(plane, out_width, out_height); ++ } ++ + _dispc_set_vid_color_conv(plane, cconv); + } + +diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c +index c047206..a1a02b5 100644 +--- a/drivers/video/omap2/dss/overlay.c ++++ b/drivers/video/omap2/dss/overlay.c +@@ -344,6 +344,20 @@ + outh = info->out_height; + } + ++ if ((ovl->supported_modes & info->color_mode) == 0) { ++ DSSERR("overlay doesn't support mode %d\n", info->color_mode); ++ return -EINVAL; ++ } ++ ++ if (ovl->id != OMAP_DSS_GFX && (info->rotation == 1 || ++ info->rotation == 3)) { ++ if(outw > dh || outh > dw) ++ return -EINVAL; ++ ++ /* If coordinates are invalid, they will be clipped later... */ ++ return 0; ++ } ++ + if (dw < info->pos_x + outw) { + DSSDBG("check_overlay failed 1: %d < %d + %d\n", + dw, info->pos_x, outw); +@@ -356,11 +370,6 @@ + return -EINVAL; + } + +- if ((ovl->supported_modes & info->color_mode) == 0) { +- DSSERR("overlay doesn't support mode %d\n", info->color_mode); +- return -EINVAL; +- } +- + return 0; + } + +diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c +index 79d8916..b548f62 100644 +--- a/drivers/video/omap2/dss/manager.c ++++ b/drivers/video/omap2/dss/manager.c +@@ -400,7 +400,7 @@ + struct omap_overlay *ovl; + bool ilace = 0; + int outw, outh; +- int r; ++ int r, pos_x = 0, pos_y = 0; + int num_planes_enabled = 0; + + DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name); +@@ -451,11 +451,51 @@ + else + outh = ovl->info.out_height; + ++ if (ovl->id != OMAP_DSS_GFX && ovl->info.rotation != 0) { ++ /* We need to rotate pos_x and pos_y with respect ++ to OMAP_DSS_GFX */ ++ ++ u16 dw, dh; ++ display->get_resolution(display, &dw, &dh); ++ ++ DSSDBG("plane pos was: (%d, %d), %dx%d, scr: %dx%d \n", ovl->info.pos_x, ++ ovl->info.pos_y, outw, outh, dw, dh); ++ ++ switch (ovl->info.rotation) { ++ case 1: ++ pos_y = ovl->info.pos_x; ++ pos_x = dw - ovl->info.pos_y - outh; ++ break; ++ case 2: ++ pos_x = dw - ovl->info.pos_x - outw; ++ pos_y = dh - ovl->info.pos_y - outh; ++ break; ++ case 3: ++ pos_x = ovl->info.pos_y; ++ pos_y = dh - ovl->info.pos_x - outw; ++ break; ++ } ++ ++ /* Check sanity */ ++ if (ovl->info.rotation != 2) { ++ if (dw < pos_x + outh) ++ pos_x = pos_y = 0; ++ else if (dh < pos_y + outw) ++ pos_x = pos_y = 0; ++ } else if ( (dw < ovl->info.pos_x + outw) || (dh < ovl->info.pos_y + outh) ) ++ pos_x = pos_y = 0; ++ ++ DSSDBG("pos_x is %d, pos_y is %d\n", pos_x, pos_y); ++ } else { ++ pos_x = ovl->info.pos_x; ++ pos_y = ovl->info.pos_y; ++ } ++ + r = dispc_setup_plane(ovl->id, ovl->manager->id, + ovl->info.paddr, + ovl->info.screen_width, +- ovl->info.pos_x, +- ovl->info.pos_y, ++ pos_x, ++ pos_y, + ovl->info.width, + ovl->info.height, + outw, +diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c +index 79d8916..b548f62 100644 +--- a/drivers/video/omap2/omapfb/omapfb-main.c ++++ b/drivers/video/omap2/omapfb/omapfb-main.c +@@ -484,22 +484,7 @@ + + if (var->rotate != fbi->var.rotate) { + DBG("rotation changing\n"); +- + ofbi->rotation = var->rotate; +- +- if (abs(var->rotate - fbi->var.rotate) != 2) { +- int tmp; +- DBG("rotate changing 90/270 degrees. " +- "swapping x/y res\n"); +- +- tmp = var->yres; +- var->yres = var->xres; +- var->xres = tmp; +- +- tmp = var->yres_virtual; +- var->yres_virtual = var->xres_virtual; +- var->xres_virtual = tmp; +- } + } + + xres_min = OMAPFB_PLANE_XRES_MIN; diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dss2-fix-scaling-when-rotating.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dss2-fix-scaling-when-rotating.patch new file mode 100644 index 0000000000..7cb8a7d9bf --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dss2-fix-scaling-when-rotating.patch @@ -0,0 +1,52 @@ +From 19a31ba4e8408ce80a4dbb96af489304c5e8128f Mon Sep 17 00:00:00 2001 +From: Tim Yamin +Date: Fri, 22 May 2009 18:58:33 -0700 +Subject: [PATCH] Fix scaling checks when rotation is 90 or 270 degrees. + +--- + drivers/video/omap2/dss/dispc.c | 25 +++++++++++++++++++------ + 1 files changed, 19 insertions(+), 6 deletions(-) + +diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c +index 088d353..77ca81b 100644 +--- a/drivers/video/omap2/dss/dispc.c ++++ b/drivers/video/omap2/dss/dispc.c +@@ -1508,16 +1508,29 @@ static int _dispc_setup_plane(enum omap_plane plane, + } + } else { + /* video plane */ +- ++ u8 error = 0; + unsigned long fclk = 0; + +- if (out_width < width / maxdownscale || +- out_width > width * 8) +- return -EINVAL; ++ if(rotation == 1 || rotation == 3) ++ { ++ if (out_width < height / maxdownscale || out_width > height * 8) ++ error = 1; ++ ++ if (out_height < width / maxdownscale || out_height > width * 8) ++ error = 1; ++ } else { ++ if (out_width < width / maxdownscale || out_width > width * 8) ++ error = 1; + +- if (out_height < height / maxdownscale || +- out_height > height * 8) ++ if (out_height < height / maxdownscale || out_height > height * 8) ++ error = 1; ++ } ++ ++ if(error != 0) ++ { ++ printk("DSS: Unable to down/up scale video plane\n"); + return -EINVAL; ++ } + + switch (color_mode) { + case OMAP_DSS_COLOR_RGB16: +-- +1.5.6.3 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/ehci.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/ehci.patch new file mode 100644 index 0000000000..5a8c84471b --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/ehci.patch @@ -0,0 +1,131 @@ +Index: git/arch/arm/mach-omap2/board-omap3beagle.c +=================================================================== +--- git.orig/arch/arm/mach-omap2/board-omap3beagle.c ++++ git/arch/arm/mach-omap2/board-omap3beagle.c +@@ -154,6 +154,7 @@ static int beagle_twl_gpio_setup(struct + * power switch and overcurrent detect + */ + ++#if 0 /* TODO: This needs to be modified to not rely on u-boot */ + gpio_request(gpio + 1, "EHCI_nOC"); + gpio_direction_input(gpio + 1); + +@@ -163,7 +164,7 @@ static int beagle_twl_gpio_setup(struct + + /* TWL4030_GPIO_MAX + 1 == ledB, PMU_STAT (out, active low LED) */ + gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1; +- ++#endif + return 0; + } + +Index: git/arch/arm/mach-omap2/usb-ehci.c +=================================================================== +--- git.orig/arch/arm/mach-omap2/usb-ehci.c ++++ git/arch/arm/mach-omap2/usb-ehci.c +@@ -147,9 +147,11 @@ static void setup_ehci_io_mux(void) + + void __init usb_ehci_init(void) + { ++#if 0 /* TODO: Setup Pin IO MUX for EHCI - moved this temporarily to U-boot */ + /* Setup Pin IO MUX for EHCI */ + if (cpu_is_omap34xx()) + setup_ehci_io_mux(); ++#endif + + if (platform_device_register(&ehci_device) < 0) { + printk(KERN_ERR "Unable to register HS-USB (EHCI) device\n"); +Index: git/drivers/usb/host/ehci-omap.c +=================================================================== +--- git.orig/drivers/usb/host/ehci-omap.c ++++ git/drivers/usb/host/ehci-omap.c +@@ -48,16 +48,25 @@ + * to get the PHY state machine in working state + */ + #define EXTERNAL_PHY_RESET ++#ifdef CONFIG_MACH_OMAP3_BEAGLE ++#define EXT_PHY_RESET_GPIO_PORT2 (147) ++#else + #define EXT_PHY_RESET_GPIO_PORT1 (57) + #define EXT_PHY_RESET_GPIO_PORT2 (61) ++#endif + #define EXT_PHY_RESET_DELAY (10) + ++#define PHY_STP_PULLUP_ENABLE (0x10) ++#define PHY_STP_PULLUP_DISABLE (0x90) ++ + /* ISSUE2: + * USBHOST supports External charge pump PHYs only + * Use the VBUS from Port1 to power VBUS of Port2 externally + * So use Port2 as the working ULPI port + */ ++#ifndef CONFIG_MACH_OMAP3_BEAGLE + #define VBUS_INTERNAL_CHARGEPUMP_HACK ++#endif + + #endif /* CONFIG_OMAP_EHCI_PHY_MODE */ + +@@ -225,14 +234,43 @@ static int omap_start_ehc(struct platfor + + #ifdef EXTERNAL_PHY_RESET + /* Refer: ISSUE1 */ ++#ifndef CONFIG_MACH_OMAP3_BEAGLE + gpio_request(EXT_PHY_RESET_GPIO_PORT1, "USB1 PHY reset"); + gpio_direction_output(EXT_PHY_RESET_GPIO_PORT1, 0); ++#endif + gpio_request(EXT_PHY_RESET_GPIO_PORT2, "USB2 PHY reset"); + gpio_direction_output(EXT_PHY_RESET_GPIO_PORT2, 0); ++ gpio_set_value(EXT_PHY_RESET_GPIO_PORT2, 0); + /* Hold the PHY in RESET for enough time till DIR is high */ + udelay(EXT_PHY_RESET_DELAY); + #endif + ++ /* ++ * The PHY register 0x7 - Interface Control register is ++ * configured to disable the integrated STP pull-up resistor ++ * used for interface protection. ++ * ++ * May not need to be here. ++ */ ++ omap_writel((0x7 << EHCI_INSNREG05_ULPI_REGADD_SHIFT) |/* interface reg */ ++ (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT) |/* Write */ ++ (1 << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT) |/* Port1 */ ++ (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT) |/* Start */ ++ (PHY_STP_PULLUP_DISABLE), ++ EHCI_INSNREG05_ULPI); ++ ++ while (!(omap_readl(EHCI_INSNREG05_ULPI) & (1<usbtll_fck_clk = clk_get(&dev->dev, USBHOST_TLL_FCLK); + if (IS_ERR(ehci_clocks->usbtll_fck_clk)) +@@ -307,7 +345,9 @@ static int omap_start_ehc(struct platfor + * Hold the PHY in RESET for enough time till PHY is settled and ready + */ + udelay(EXT_PHY_RESET_DELAY); ++#ifndef CONFIG_MACH_OMAP3_BEAGLE + gpio_set_value(EXT_PHY_RESET_GPIO_PORT1, 1); ++#endif + gpio_set_value(EXT_PHY_RESET_GPIO_PORT2, 1); + #endif + +@@ -393,7 +433,9 @@ static void omap_stop_ehc(struct platfor + + + #ifdef EXTERNAL_PHY_RESET ++#ifndef CONFIG_MACH_OMAP3_BEAGLE + gpio_free(EXT_PHY_RESET_GPIO_PORT1); ++#endif + gpio_free(EXT_PHY_RESET_GPIO_PORT2); + #endif + diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/logo_linux_clut224.ppm b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/logo_linux_clut224.ppm new file mode 100644 index 0000000000..76b746bf91 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/logo_linux_clut224.ppm @@ -0,0 +1,773 @@ +P3 +171 35 +255 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 204 204 204 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 136 136 136 0 0 0 0 0 0 0 0 0 +85 85 85 255 255 255 255 255 255 255 255 255 255 255 255 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 204 204 204 255 255 255 119 119 119 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 170 170 170 204 204 204 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 204 204 204 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 187 187 187 0 0 0 0 0 0 +85 85 85 255 255 255 255 255 255 255 255 255 255 255 255 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 204 204 204 255 255 255 119 119 119 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 238 238 238 255 255 255 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 119 119 119 255 255 255 +119 119 119 0 0 0 0 0 0 34 34 34 221 221 221 255 255 255 68 68 68 0 0 0 +0 0 0 0 0 0 0 0 0 204 204 204 255 255 255 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 238 238 238 255 255 255 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 119 119 119 255 255 255 +119 119 119 0 0 0 0 0 0 0 0 0 136 136 136 255 255 255 119 119 119 0 0 0 +0 0 0 0 0 0 0 0 0 204 204 204 255 255 255 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 136 136 136 238 238 238 255 255 255 255 255 255 +187 187 187 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 119 119 119 187 187 187 +255 255 255 255 255 255 255 255 255 187 187 187 85 85 85 0 0 0 0 0 0 0 0 0 +0 0 0 17 17 17 153 153 153 255 255 255 255 255 255 255 255 255 238 238 238 255 255 255 +85 85 85 0 0 0 0 0 0 0 0 0 85 85 85 187 187 187 255 255 255 255 255 255 +238 238 238 136 136 136 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 102 102 102 +255 255 255 255 255 255 255 255 255 119 119 119 0 0 0 0 0 0 187 187 187 255 255 255 +255 255 255 255 255 255 68 68 68 0 0 0 119 119 119 187 187 187 255 255 255 255 255 255 +255 255 255 187 187 187 85 85 85 0 0 0 0 0 0 0 0 0 0 0 0 204 204 204 +255 255 255 255 255 255 255 255 255 136 136 136 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 153 153 153 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 102 102 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 119 119 119 255 255 255 +119 119 119 0 0 0 0 0 0 85 85 85 238 238 238 255 255 255 51 51 51 0 0 0 +0 0 0 0 0 0 0 0 0 204 204 204 255 255 255 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 17 17 17 204 204 204 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 102 102 102 0 0 0 0 0 0 0 0 0 204 204 204 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 51 51 51 0 0 0 0 0 0 +0 0 0 221 221 221 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +119 119 119 0 0 0 0 0 0 119 119 119 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 204 204 204 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 102 102 102 +255 255 255 255 255 255 255 255 255 119 119 119 0 0 0 0 0 0 187 187 187 255 255 255 +255 255 255 255 255 255 68 68 68 0 0 0 204 204 204 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 51 51 51 0 0 0 0 0 0 0 0 0 204 204 204 +255 255 255 255 255 255 255 255 255 136 136 136 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 153 153 153 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 102 102 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 119 119 119 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 153 153 153 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 204 204 204 255 255 255 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 153 153 153 255 255 255 170 170 170 17 17 17 0 0 0 0 0 0 +102 102 102 221 221 221 238 238 238 17 17 17 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 204 204 204 255 255 255 102 102 102 0 0 0 0 0 0 +34 34 34 255 255 255 238 238 238 17 17 17 0 0 0 0 0 0 136 136 136 255 255 255 +85 85 85 0 0 0 34 34 34 255 255 255 221 221 221 102 102 102 0 0 0 0 0 0 +17 17 17 170 170 170 255 255 255 136 136 136 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +119 119 119 255 255 255 119 119 119 68 68 68 255 255 255 221 221 221 0 0 0 187 187 187 +255 255 255 102 102 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 204 204 204 255 255 255 102 102 102 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 85 85 85 255 255 255 136 136 136 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 238 238 238 255 255 255 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 119 119 119 255 255 255 +255 255 255 255 255 255 255 255 255 221 221 221 119 119 119 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 204 204 204 255 255 255 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 238 238 238 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 102 102 102 0 0 0 0 0 0 51 51 51 170 170 170 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 119 119 119 0 0 0 0 0 0 +0 0 0 170 170 170 255 255 255 255 255 255 255 255 255 187 187 187 136 136 136 51 51 51 +0 0 0 0 0 0 119 119 119 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 221 221 221 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +68 68 68 255 255 255 187 187 187 153 153 153 255 255 255 255 255 255 85 85 85 238 238 238 +255 255 255 34 34 34 0 0 0 0 0 0 51 51 51 170 170 170 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 119 119 119 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 85 85 85 255 255 255 136 136 136 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 238 238 238 255 255 255 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 119 119 119 255 255 255 +119 119 119 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 204 204 204 255 255 255 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 238 238 238 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 119 119 119 0 0 0 68 68 68 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 119 119 119 0 0 0 0 0 0 +0 0 0 0 0 0 51 51 51 136 136 136 136 136 136 136 136 136 255 255 255 255 255 255 +102 102 102 0 0 0 119 119 119 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 238 238 238 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 238 238 238 221 221 221 238 238 238 255 255 255 255 255 255 204 204 204 255 255 255 +221 221 221 0 0 0 0 0 0 68 68 68 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 119 119 119 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 85 85 85 255 255 255 136 136 136 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 238 238 238 255 255 255 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 119 119 119 255 255 255 +119 119 119 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 204 204 204 255 255 255 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 170 170 170 255 255 255 153 153 153 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 170 170 170 255 255 255 136 136 136 +0 0 0 0 0 0 51 51 51 204 204 204 255 255 255 119 119 119 0 0 0 0 0 0 +102 102 102 255 255 255 153 153 153 0 0 0 0 0 0 0 0 0 102 102 102 255 255 255 +204 204 204 0 0 0 51 51 51 255 255 255 204 204 204 85 85 85 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 204 204 204 255 255 255 255 255 255 170 170 170 221 221 221 255 255 255 255 255 255 +153 153 153 0 0 0 0 0 0 170 170 170 255 255 255 136 136 136 0 0 0 0 0 0 +51 51 51 204 204 204 255 255 255 119 119 119 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 85 85 85 255 255 255 136 136 136 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 238 238 238 255 255 255 51 51 51 0 0 0 0 0 0 +119 119 119 238 238 238 119 119 119 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 204 204 204 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 102 102 102 0 0 0 0 0 0 0 0 0 17 17 17 +238 238 238 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +85 85 85 0 0 0 17 17 17 221 221 221 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 102 102 102 0 0 0 136 136 136 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 204 204 204 0 0 0 +136 136 136 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +136 136 136 0 0 0 0 0 0 119 119 119 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 221 221 221 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 153 153 153 255 255 255 255 255 255 85 85 85 119 119 119 255 255 255 255 255 255 +102 102 102 0 0 0 0 0 0 136 136 136 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 204 204 204 0 0 0 136 136 136 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 204 204 204 0 0 0 +0 0 0 0 0 0 0 0 0 170 170 170 255 255 255 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 136 136 136 0 0 0 0 0 0 0 0 0 17 17 17 221 221 221 +255 255 255 102 102 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 68 68 68 255 255 255 238 238 238 34 34 34 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 68 68 68 255 255 255 238 238 238 34 34 34 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 204 204 204 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 102 102 102 0 0 0 0 0 0 0 0 0 17 17 17 +238 238 238 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +85 85 85 0 0 0 0 0 0 17 17 17 136 136 136 255 255 255 255 255 255 255 255 255 +255 255 255 187 187 187 136 136 136 17 17 17 0 0 0 0 0 0 119 119 119 255 255 255 +255 255 255 255 255 255 136 136 136 187 187 187 255 255 255 255 255 255 204 204 204 0 0 0 +102 102 102 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 204 204 204 119 119 119 +0 0 0 0 0 0 0 0 0 0 0 0 102 102 102 187 187 187 255 255 255 255 255 255 +255 255 255 255 255 255 136 136 136 102 102 102 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 102 102 102 255 255 255 221 221 221 0 0 0 17 17 17 238 238 238 255 255 255 +34 34 34 0 0 0 0 0 0 0 0 0 119 119 119 255 255 255 255 255 255 255 255 255 +136 136 136 187 187 187 255 255 255 255 255 255 204 204 204 0 0 0 136 136 136 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 204 204 204 0 0 0 +0 0 0 0 0 0 0 0 0 17 17 17 170 170 170 255 255 255 255 255 255 255 255 255 +153 153 153 102 102 102 0 0 0 0 0 0 0 0 0 0 0 0 17 17 17 221 221 221 +255 255 255 102 102 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 68 68 68 255 255 255 238 238 238 34 34 34 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 68 68 68 255 255 255 238 238 238 34 34 34 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/memory-move-malloc-end.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/memory-move-malloc-end.patch new file mode 100644 index 0000000000..9000642104 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/memory-move-malloc-end.patch @@ -0,0 +1,9 @@ +--- git/arch/arm/plat-omap/include/mach/vmalloc.h 2009-06-09 15:14:54.000000000 -0700 ++++ git/arch/arm/plat-omap/include/mach/vmalloc.h 2009-06-09 15:06:35.000000000 -0700 +@@ -17,5 +17,5 @@ + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +-#define VMALLOC_END (PAGE_OFFSET + 0x18000000) ++#define VMALLOC_END (PAGE_OFFSET + 0x28000000) + diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/screen-backlight-accessible-by-user.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/screen-backlight-accessible-by-user.patch new file mode 100644 index 0000000000..14e0934468 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/screen-backlight-accessible-by-user.patch @@ -0,0 +1,11 @@ +--- a/drivers/video/backlight/backlight.c 2009-07-17 12:20:05.000000000 -0700 ++++ b/drivers/video/backlight/backlight.c 2009-07-17 12:20:46.000000000 -0700 +@@ -205,7 +205,7 @@ + + static struct device_attribute bl_device_attributes[] = { + __ATTR(bl_power, 0644, backlight_show_power, backlight_store_power), +- __ATTR(brightness, 0644, backlight_show_brightness, ++ __ATTR(brightness, 0666, backlight_show_brightness, + backlight_store_brightness), + __ATTR(actual_brightness, 0444, backlight_show_actual_brightness, + NULL), diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/screen-backlight.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/screen-backlight.patch new file mode 100644 index 0000000000..65b551f1de --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/screen-backlight.patch @@ -0,0 +1,10 @@ +--- a/arch/arm/mach-omap2/Kconfig ++++ b/arch/arm/mach-omap2/Kconfig +@@ -128,6 +128,7 @@ config MACH_OMAP3EVM + config MACH_OMAP3_BEAGLE + bool "OMAP3 BEAGLE board" + depends on ARCH_OMAP3 && ARCH_OMAP34XX ++ select BACKLIGHT_CLASS_DEVICE + + config MACH_OVERO + bool "Gumstix Overo board" diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/sound-headphone-detection.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/sound-headphone-detection.patch new file mode 100644 index 0000000000..ef8b3626bc --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/sound-headphone-detection.patch @@ -0,0 +1,93 @@ +From db8f1eba9154789c45c6a92413bbbd94f5d9c7f5 Mon Sep 17 00:00:00 2001 +From: Tim Yamin +Date: Wed, 29 Apr 2009 17:30:25 -0700 +Subject: [PATCH] Touch Book: turn on/off the class D amplifier depending on whether the + headphones are plugged into the jack or not. + +Signed-off-by: Tim Yamin +--- + sound/soc/omap/omap3beagle.c | 33 +++++++++++++++++++++++++++++++++ + 1 files changed, 33 insertions(+), 0 deletions(-) + +diff --git a/sound/soc/omap/omap3beagle.c b/sound/soc/omap/omap3beagle.c +index fd24a4a..1236638 100644 +--- a/sound/soc/omap/omap3beagle.c ++++ b/sound/soc/omap/omap3beagle.c +@@ -20,7 +20,10 @@ + */ + + #include ++#include ++#include + #include ++ + #include + #include + #include +@@ -35,6 +38,9 @@ + #include "omap-pcm.h" + #include "../codecs/twl4030.h" + ++#define TB_HEADPHONE_GPIO 56 ++#define TB_HEADPHONE_IRQ OMAP_GPIO_IRQ(TB_HEADPHONE_GPIO) ++ + static int omap3beagle_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) + { +@@ -103,6 +109,33 @@ static struct snd_soc_device omap3beagle_snd_devdata = { + + static struct platform_device *omap3beagle_snd_device; + ++static void jack_work_func(struct work_struct *wq) ++{ ++ struct snd_soc_device *socdev = platform_get_drvdata(omap3beagle_snd_device); ++ struct snd_soc_codec *codec = socdev->codec; ++ ++ snd_soc_dapm_sync(codec); ++} ++DECLARE_WORK(jack_work, jack_work_func); ++ ++static irqreturn_t touchbook_headphone_event(int irq, void *snd) ++{ ++ int status = gpio_get_value(TB_HEADPHONE_GPIO); ++ struct snd_soc_device *socdev = platform_get_drvdata(omap3beagle_snd_device); ++ struct snd_soc_codec *codec = socdev->codec; ++ ++ if(status) { ++ snd_soc_dapm_disable_pin(codec, "HFL"); ++ snd_soc_dapm_disable_pin(codec, "HFR"); ++ } else { ++ snd_soc_dapm_enable_pin(codec, "HFL"); ++ snd_soc_dapm_enable_pin(codec, "HFR"); ++ } ++ ++ schedule_work(&jack_work); ++ return IRQ_HANDLED; ++} ++ + static int __init omap3beagle_soc_init(void) + { + int ret; +@@ -123,10 +156,22 @@ static int __init omap3beagle_soc_init(void) + omap3beagle_snd_devdata.dev = &omap3beagle_snd_device->dev; + *(unsigned int *)omap3beagle_dai.cpu_dai->private_data = 1; /* McBSP2 */ + ++ /* Touch Book -- headphone jack sensor */ ++ omap_set_gpio_debounce(TB_HEADPHONE_GPIO, 1); ++ omap_set_gpio_debounce_time(TB_HEADPHONE_GPIO, 0xff); ++ ++ ret = request_irq(TB_HEADPHONE_IRQ, touchbook_headphone_event, IRQF_TRIGGER_RISING | ++ IRQF_TRIGGER_FALLING, "touchbook_headphone", omap3beagle_snd_device); ++ if (ret < 0) ++ goto err1; ++ + ret = platform_device_add(omap3beagle_snd_device); + if (ret) + goto err1; + ++ /* Detect headphone status */ ++ touchbook_headphone_event(0, omap3beagle_snd_device); ++ + return 0; + + err1: diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/tincantools-puppy.diff b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/tincantools-puppy.diff new file mode 100644 index 0000000000..c7856731e5 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/tincantools-puppy.diff @@ -0,0 +1,66 @@ +--- /tmp/board-omap3beagle.c 2009-07-01 01:06:44.000000000 +0200 ++++ git/arch/arm/mach-omap2/board-omap3beagle.c 2009-07-01 01:06:50.000000000 +0200 +@@ -125,6 +125,13 @@ + .wires = 8, + .gpio_wp = 29, + }, ++ { ++ .mmc = 2, ++ .wires = 4, ++ .gpio_wp = 141, ++ .gpio_cd = 162, ++ .transceiver = true, ++ }, + {} /* Terminator */ + }; + +@@ -132,6 +139,11 @@ + .supply = "vmmc", + }; + ++static struct regulator_consumer_supply beagle_vmmc2_supply = { ++ .supply = "vmmc", ++}; ++ ++ + static struct regulator_consumer_supply beagle_vsim_supply = { + .supply = "vmmc_aux", + }; +@@ -148,6 +160,7 @@ + + /* link regulators to MMC adapters */ + beagle_vmmc1_supply.dev = mmc[0].dev; ++ beagle_vmmc2_supply.dev = mmc[1].dev; + beagle_vsim_supply.dev = mmc[0].dev; + + /* REVISIT: need ehci-omap hooks for external VBUS +@@ -209,6 +222,21 @@ + .consumer_supplies = &beagle_vmmc1_supply, + }; + ++/* VMMC2 for MMC2 pins CMD, CLK, DAT0..DAT3 (max 100 mA) */ ++static struct regulator_init_data beagle_vmmc2 = { ++ .constraints = { ++ .min_uV = 2700000, ++ .max_uV = 3150000, ++ .valid_modes_mask = REGULATOR_MODE_NORMAL ++ | REGULATOR_MODE_STANDBY, ++ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE ++ | REGULATOR_CHANGE_MODE ++ | REGULATOR_CHANGE_STATUS, ++ }, ++ .num_consumer_supplies = 1, ++ .consumer_supplies = &beagle_vmmc2_supply, ++}; ++ + /* VSIM for MMC1 pins DAT4..DAT7 (2 mA, plus card == max 50 mA) */ + static struct regulator_init_data beagle_vsim = { + .constraints = { +@@ -284,6 +312,7 @@ + .gpio = &beagle_gpio_data, + .power = &beagle_power_data, + .vmmc1 = &beagle_vmmc1, ++ .vmmc2 = &beagle_vmmc2, + .vsim = &beagle_vsim, + .vdac = &beagle_vdac, + .vpll2 = &beagle_vpll2, diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/touchscreen-ads7846-export-settings.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/touchscreen-ads7846-export-settings.patch new file mode 100644 index 0000000000..f8353afee5 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/touchscreen-ads7846-export-settings.patch @@ -0,0 +1,87 @@ +diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c +index 7c3897f..0c62ace 100644 +--- a/drivers/input/touchscreen/ads7846.c ++++ b/drivers/input/touchscreen/ads7846.c +@@ -491,9 +491,82 @@ static ssize_t ads7846_disable_store(struct device *dev, + + static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store); + ++static ssize_t show_debounce_max(struct device *dev, struct device_attribute *attr, char *buf) { ++ struct ads7846 *ts = dev_get_drvdata(dev); ++ return sprintf(buf, "%u\n", ts->debounce_max); ++} ++ ++static ssize_t show_debounce_tol(struct device *dev, struct device_attribute *attr, char *buf) { ++ struct ads7846 *ts = dev_get_drvdata(dev); ++ return sprintf(buf, "%u\n", ts->debounce_tol); ++} ++ ++static ssize_t show_debounce_rep(struct device *dev, struct device_attribute *attr, char *buf) { ++ struct ads7846 *ts = dev_get_drvdata(dev); ++ return sprintf(buf, "%u\n", ts->debounce_rep); ++} ++ ++static ssize_t show_x_plate_ohms(struct device *dev, struct device_attribute *attr, char *buf) { ++ struct ads7846 *ts = dev_get_drvdata(dev); ++ return sprintf(buf, "%u\n", ts->x_plate_ohms); ++} ++ ++static ssize_t write_debounce_max(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { ++ struct ads7846 *ts = dev_get_drvdata(dev); ++ unsigned long i; ++ ++ if (strict_strtoul(buf, 10, &i)) ++ return -EINVAL; ++ ++ ts->debounce_max = i; ++ return count; ++} ++ ++static ssize_t write_debounce_tol(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { ++ struct ads7846 *ts = dev_get_drvdata(dev); ++ unsigned long i; ++ ++ if (strict_strtoul(buf, 10, &i)) ++ return -EINVAL; ++ ++ ts->debounce_tol = i; ++ return count; ++} ++ ++static ssize_t write_debounce_rep(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { ++ struct ads7846 *ts = dev_get_drvdata(dev); ++ unsigned long i; ++ ++ if (strict_strtoul(buf, 10, &i)) ++ return -EINVAL; ++ ++ ts->debounce_rep = i; ++ return count; ++} ++ ++static ssize_t write_x_plate_ohms(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { ++ struct ads7846 *ts = dev_get_drvdata(dev); ++ unsigned long i; ++ ++ if (strict_strtoul(buf, 10, &i)) ++ return -EINVAL; ++ ++ ts->x_plate_ohms = i; ++ return count; ++} ++ ++static DEVICE_ATTR(debounce_max, S_IRUGO | S_IWUGO, show_debounce_max, write_debounce_max); ++static DEVICE_ATTR(debounce_tol, S_IRUGO | S_IWUGO, show_debounce_tol, write_debounce_tol); ++static DEVICE_ATTR(debounce_rep, S_IRUGO | S_IWUGO, show_debounce_rep, write_debounce_rep); ++static DEVICE_ATTR(x_plate_ohms, S_IRUGO | S_IWUGO, show_x_plate_ohms, write_x_plate_ohms); ++ + static struct attribute *ads784x_attributes[] = { + &dev_attr_pen_down.attr, + &dev_attr_disable.attr, ++ &dev_attr_debounce_max.attr, ++ &dev_attr_debounce_tol.attr, ++ &dev_attr_debounce_rep.attr, ++ &dev_attr_x_plate_ohms.attr, + NULL, + }; + diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/touchscreen-ads7846-rotation-support.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/touchscreen-ads7846-rotation-support.patch new file mode 100644 index 0000000000..6196716f23 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/touchscreen-ads7846-rotation-support.patch @@ -0,0 +1,108 @@ +diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c +index 0c62ace..8f6e83c 100644 +--- a/drivers/input/touchscreen/ads7846.c ++++ b/drivers/input/touchscreen/ads7846.c +@@ -127,6 +127,7 @@ struct ads7846 { + void (*filter_cleanup)(void *data); + int (*get_pendown_state)(void); + int gpio_pendown; ++ int rotate; + }; + + /* leave chip selected when we're done, for quicker re-select? */ +@@ -511,6 +512,11 @@ static ssize_t show_x_plate_ohms(struct device *dev, struct device_attribute *at + return sprintf(buf, "%u\n", ts->x_plate_ohms); + } + ++static ssize_t show_rotate(struct device *dev, struct device_attribute *attr, char *buf) { ++ struct ads7846 *ts = dev_get_drvdata(dev); ++ return sprintf(buf, "%u\n", ts->rotate); ++} ++ + static ssize_t write_debounce_max(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct ads7846 *ts = dev_get_drvdata(dev); + unsigned long i; +@@ -555,10 +561,22 @@ static ssize_t write_x_plate_ohms(struct device *dev, struct device_attribute *a + return count; + } + ++static ssize_t write_rotate(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { ++ struct ads7846 *ts = dev_get_drvdata(dev); ++ unsigned long i = 0; ++ ++ if (strict_strtoul(buf, 10, &i) || i > 3) ++ return -EINVAL; ++ ++ ts->rotate = i; ++ return count; ++} ++ + static DEVICE_ATTR(debounce_max, S_IRUGO | S_IWUGO, show_debounce_max, write_debounce_max); + static DEVICE_ATTR(debounce_tol, S_IRUGO | S_IWUGO, show_debounce_tol, write_debounce_tol); + static DEVICE_ATTR(debounce_rep, S_IRUGO | S_IWUGO, show_debounce_rep, write_debounce_rep); + static DEVICE_ATTR(x_plate_ohms, S_IRUGO | S_IWUGO, show_x_plate_ohms, write_x_plate_ohms); ++static DEVICE_ATTR(rotate, S_IRUGO | S_IWUGO, show_rotate, write_rotate); + + static struct attribute *ads784x_attributes[] = { + &dev_attr_pen_down.attr, +@@ -567,6 +585,7 @@ static struct attribute *ads784x_attributes[] = { + &dev_attr_debounce_tol.attr, + &dev_attr_debounce_rep.attr, + &dev_attr_x_plate_ohms.attr, ++ &dev_attr_rotate.attr, + NULL, + }; + +@@ -596,6 +615,8 @@ static void ads7846_rx(void *ads) + { + struct ads7846 *ts = ads; + struct ads7846_packet *packet = ts->packet; ++ struct ads7846_platform_data *pdata = ts->spi->dev.platform_data; ++ + unsigned Rt; + u16 x, y, z1, z2; + +@@ -657,6 +678,7 @@ static void ads7846_rx(void *ads) + * timer by reading the pen signal state (it's a GPIO _and_ IRQ). + */ + if (Rt) { ++ int t; + struct input_dev *input = ts->input; + + if (!ts->pendown) { +@@ -666,6 +688,27 @@ static void ads7846_rx(void *ads) + dev_dbg(&ts->spi->dev, "DOWN\n"); + #endif + } ++ ++ switch(ts->rotate) ++ { ++ case 0: ++ x = pdata->x_max - x + pdata->x_min; ++ y = pdata->y_max - y + pdata->y_min; ++ break; ++ case 1: ++ t = x; ++ x = pdata->x_max - (((y - pdata->y_min) * (pdata->x_max - pdata->x_min)) / (pdata->y_max - pdata->y_min)); ++ y = pdata->y_min + (((t - pdata->x_min) * (pdata->y_max - pdata->y_min)) / (pdata->x_max - pdata->x_min)); ++ break; ++ case 2: ++ break; ++ case 3: ++ t = x; ++ x = pdata->x_min + (((y - pdata->y_min) * (pdata->x_max - pdata->x_min)) / (pdata->y_max - pdata->y_min)); ++ y = pdata->y_max - (((t - pdata->x_min) * (pdata->y_max - pdata->y_min)) / (pdata->x_max - pdata->x_min)); ++ break; ++ } ++ + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_PRESSURE, Rt); +@@ -980,6 +1023,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) + + dev_set_drvdata(&spi->dev, ts); + ++ ts->rotate = 0; + ts->packet = packet; + ts->spi = spi; + ts->input = input_dev; diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/usb-lower-current-consumption-upon-insertion.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/usb-lower-current-consumption-upon-insertion.patch new file mode 100644 index 0000000000..e2df6cf83d --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/usb-lower-current-consumption-upon-insertion.patch @@ -0,0 +1,30 @@ +--- a/drivers/usb/core/hub.c 2009-07-28 08:47:00.000000000 -0700 ++++ b/drivers/usb/core/hub.c 2009-07-28 08:32:20.000000000 -0700 +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -3129,9 +3130,18 @@ + USB_PORT_FEAT_C_RESET); + } + +- if (connect_change) ++ if (connect_change) { ++ if(portstatus & USB_PORT_STAT_CONNECTION) { ++ gpio_set_value(176,0); ++ mdelay(25); ++ } + hub_port_connect_change(hub, i, + portstatus, portchange); ++ if(portstatus & USB_PORT_STAT_CONNECTION) { ++ mdelay(75); ++ gpio_set_value(176,1); ++ } ++ } + } /* end for i */ + + /* deal with hub status changes */ diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/usb-otg-pc-connection.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/usb-otg-pc-connection.patch new file mode 100644 index 0000000000..2bcfbdcc2f --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/usb-otg-pc-connection.patch @@ -0,0 +1,36 @@ +--- git/drivers/usb/gadget/file_storage.c 2009-06-07 12:18:05.000000000 -0700 ++++ git/drivers/usb/gadget/file_storage.c 2009-06-07 12:08:36.000000000 -0700 +@@ -283,8 +283,8 @@ + * + * DO NOT REUSE THESE IDs with any other driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. */ +-#define DRIVER_VENDOR_ID 0x0525 // NetChip +-#define DRIVER_PRODUCT_ID 0xa4a5 // Linux-USB File-backed Storage Gadget ++#define DRIVER_VENDOR_ID 0xa110 // Always Innovating, Inc. ++#define DRIVER_PRODUCT_ID 0x0001 // Tablet product + + + /* +@@ -2025,9 +2025,9 @@ + { + u8 *buf = (u8 *) bh->buf; + +- static char vendor_id[] = "Linux "; ++ static char vendor_id[] = "AI, Inc."; + static char product_disk_id[] = "File-Stor Gadget"; +- static char product_cdrom_id[] = "File-CD Gadget "; ++ static char product_cdrom_id[] = "Touch Book "; + + if (!fsg->curlun) { // Unsupported LUNs are okay + fsg->bad_lun_okay = 1; +@@ -3671,8 +3671,8 @@ + } + if (num_sectors < min_sectors) { + LINFO(curlun, "file too small: %s\n", filename); +- rc = -ETOOSMALL; +- goto out; ++// rc = -ETOOSMALL; ++// goto out; + } + + get_file(filp); -- cgit 1.2.3-korg