static void wcnss_post_bootup(struct work_struct *work)
{
    pr_info("[WCNSS]%s: Cancel APPS vote for Iris & Riva\n", __func__);


    wcnss_wlan_power(&penv->pdev->dev, &penv->wlan_config,
                     WCNSS_WLAN_SWITCH_OFF);
}
static void wcnss_post_bootup(struct work_struct *work)
{
	pr_info("%s: Cancel APPS vote for Iris & Riva\n", __func__);

	/* Since Riva is up, cancel any APPS vote for Iris & Riva VREGs  */
	wcnss_wlan_power(&penv->pdev->dev, &penv->wlan_config,
		WCNSS_WLAN_SWITCH_OFF);
}
Example #3
0
static void riva_post_bootup(struct work_struct *work)
{
	struct platform_device *pdev = wcnss_get_platform_device();
	struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();

	pr_debug(MODULE_NAME ": Cancel APPS vote for Iris & Riva\n");

	wcnss_wlan_power(&pdev->dev, pwlanconfig,
		WCNSS_WLAN_SWITCH_OFF);
}
static void wcnss_post_bootup(struct work_struct *work)
{
	pr_info("%s: Cancel APPS vote for Iris & Riva\n", __func__);

    //ASUS_BSP+++ "for /data/log/ASUSEvtlog"
    ASUSEvtlog("[wcnss]: wcnss_post_bootup, Cancel APPS vote for Iris & Riva.\n");
    //ASUS_BSP--- "for /data/log/ASUSEvtlog"

	/* Since Riva is up, cancel any APPS vote for Iris & Riva VREGs  */
	wcnss_wlan_power(&penv->pdev->dev, &penv->wlan_config,
		WCNSS_WLAN_SWITCH_OFF);
}
Example #5
0
/* Subsystem handlers */
static int riva_shutdown(const struct subsys_data *subsys)
{
	struct platform_device *pdev = wcnss_get_platform_device();
	struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
	int    ret = -1;

	pil_force_shutdown("wcnss");

	/* proxy vote on behalf of Riva */
	if (pdev && pwlanconfig)
		ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
					WCNSS_WLAN_SWITCH_OFF);
	return ret;
}
static void riva_post_bootup(struct work_struct *work)
{
	struct platform_device *pdev = wcnss_get_platform_device();
	struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();

    pr_debug(MODULE_NAME ": riva_post_bootup, Cancel APPS vote for Iris & Riva\n");

    //ASUS_BSP+++ "for /data/log/ASUSEvtlog"
    ASUSEvtlog("[wcnss]: riva_post_bootup, Cancel APPS vote for Iris & Riva.\n");
    //ASUS_BSP--- "for /data/log/ASUSEvtlog"

	wcnss_wlan_power(&pdev->dev, pwlanconfig,
		WCNSS_WLAN_SWITCH_OFF);
}
Example #7
0
static void wcnss_post_bootup(struct work_struct *work)
{
	if (do_not_cancel_vote == 1) {
		pr_info("%s: Keeping APPS vote for Iris & WCNSS\n", __func__);
		return;
	}

	pr_info("%s: Cancel APPS vote for Iris & WCNSS\n", __func__);

	/* Since WCNSS is up, cancel any APPS vote for Iris & WCNSS VREGs  */
	wcnss_wlan_power(&penv->pdev->dev, &penv->wlan_config,
		WCNSS_WLAN_SWITCH_OFF);

}
static void pil_pronto_remove_proxy_vote(struct pil_desc *pil)
{
	struct pronto_data *drv = dev_get_drvdata(pil->dev);
	struct platform_device *pdev = wcnss_get_platform_device();
	struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();

	regulator_disable(drv->vreg);
	clk_disable_unprepare(drv->cxo);
	if (pdev && pwlanconfig) {
		/* Temporary workaround as pronto sends interrupt that
		 * it is capable of voting for it's resources too early. */
		msleep(20);
		wcnss_wlan_power(&pdev->dev, pwlanconfig,
					WCNSS_WLAN_SWITCH_OFF, NULL);
	}
}
Example #9
0
static int riva_powerup(const struct subsys_data *subsys)
{
	struct platform_device *pdev = wcnss_get_platform_device();
	struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
	int    ret = -1;

	if (pdev && pwlanconfig)
		ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
					WCNSS_WLAN_SWITCH_ON);
	if (!ret)
		pil_force_boot("wcnss");

	ss_restart_inprogress = false;
	enable_irq(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ);

	return ret;
}
Example #10
0
static int riva_powerup(const struct subsys_data *subsys)
{
	struct platform_device *pdev = wcnss_get_platform_device();
	struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
	int    ret = -1;

	if (pdev && pwlanconfig)
		ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
					WCNSS_WLAN_SWITCH_ON);
	if (!ret) {
		msleep(1000);
		pil_force_boot("wcnss");
	}
	ss_restart_inprogress = false;
	enable_irq(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ);
	schedule_delayed_work(&cancel_vote_work, msecs_to_jiffies(5000));

	return ret;
}
Example #11
0
static void wcnss_post_bootup(struct work_struct *work)
{
	pr_info("%s: Cancel APPS vote for Iris & Riva\n", __func__);

	/* Since Riva is up, cancel any APPS vote for Iris & Riva VREGs  */
	wcnss_wlan_power(&penv->pdev->dev, &penv->wlan_config,
		WCNSS_WLAN_SWITCH_OFF);

#if defined(CONFIG_MACH_MELIUS_SKT) || defined(CONFIG_MACH_MELIUS_KTT) || \
	defined(CONFIG_MACH_MELIUS_LGT)
#if !defined(CONFIG_RADIO_IRIS) && defined(CONFIG_IR_REMOCON_FPGA)
	/* workaround about conflict with qualcomm FM radio gpio */
	gpio_tlmm_config(GPIO_CFG(GPIO_IRDA_SDA, 0,
		GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA), 1);
	gpio_tlmm_config(GPIO_CFG(GPIO_IRDA_SCL, 0,
		GPIO_CFG_INPUT,	GPIO_CFG_NO_PULL, GPIO_CFG_8MA), 1);
#endif
#endif
}
static int riva_powerup(const struct subsys_desc *desc)
{
	struct riva_data *drv;
	struct platform_device *pdev = wcnss_get_platform_device();
	struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
	int ret = 0;

	drv = container_of(desc, struct riva_data, subsys_desc);
	if (pdev && pwlanconfig) {
		ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
					WCNSS_WLAN_SWITCH_ON, NULL);
		if (!ret)
			pil_boot(&drv->pil_desc);
	}
	drv->rst_in_progress = 0;
	enable_irq(drv->irq);
	schedule_delayed_work(&drv->cancel_work, msecs_to_jiffies(5000));

	return ret;
}
static int riva_powerup(const struct subsys_data *subsys)
{
	struct platform_device *pdev = wcnss_get_platform_device();
	struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
	int    ret = -1;

	if (pdev && pwlanconfig)
		ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
					WCNSS_WLAN_SWITCH_ON);
	/* delay PIL operation, this SSR may be happening soon after kernel
	 * resumes because of a SMSM RESET by Riva when APPS was suspended.
	 * PIL fails to locate the images without this delay */
	if (!ret) {
		msleep(1000);
		pil_force_boot("wcnss");
	}
	ss_restart_inprogress = false;
	enable_irq(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ);

	return ret;
}
static int wcnss_powerup(const struct subsys_desc *subsys)
{
	struct pronto_data *drv = subsys_to_drv(subsys);
	struct platform_device *pdev = wcnss_get_platform_device();
	struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
	int    ret = -1;

	if (pdev && pwlanconfig)
		ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
					WCNSS_WLAN_SWITCH_ON, NULL);
	if (!ret) {
		msleep(1000);
		ret = pil_boot(&drv->desc);
		if (ret)
			return ret;
	}
	drv->restart_inprogress = false;
	enable_irq(drv->irq);
	schedule_delayed_work(&drv->cancel_vote_work, msecs_to_jiffies(5000));

	return 0;
}
static int riva_powerup(const struct subsys_desc *subsys)
{
	struct platform_device *pdev = wcnss_get_platform_device();
	struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
	int    ret = -1;
	pr_info(MODULE_NAME "%s + \n", __FUNCTION__);

	if (pdev && pwlanconfig)
		ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
					WCNSS_WLAN_SWITCH_ON);
	/* delay PIL operation, this SSR may be happening soon after kernel
	 * resumes because of a SMSM RESET by Riva when APPS was suspended.
	 * PIL fails to locate the images without this delay */
	if (!ret) {
		msleep(1000);
		pil_force_boot("wcnss");
	}
	ss_restart_inprogress = false;
	enable_irq(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ);
	schedule_delayed_work(&cancel_vote_work, msecs_to_jiffies(5000));
	pr_info(MODULE_NAME "%s - \n", __FUNCTION__);

	return ret;
}
Example #16
0
static int
wcnss_trigger_config(struct platform_device *pdev)
{
	int ret;
	struct qcom_wcnss_opts *pdata;

	/* make sure we are only triggered once */
	if (penv->triggered)
		return 0;
	penv->triggered = 1;

	/* initialize the WCNSS device configuration */
	pdata = pdev->dev.platform_data;
	if (WCNSS_CONFIG_UNSPECIFIED == has_48mhz_xo)
		has_48mhz_xo = pdata->has_48mhz_xo;
	penv->wlan_config.use_48mhz_xo = has_48mhz_xo;

	penv->thermal_mitigation = 0;

	penv->gpios_5wire = platform_get_resource_byname(pdev, IORESOURCE_IO,
							"wcnss_gpios_5wire");

	/* allocate 5-wire GPIO resources */
	if (!penv->gpios_5wire) {
		dev_err(&pdev->dev, "insufficient IO resources\n");
		ret = -ENOENT;
		goto fail_gpio_res;
	}

	/* Configure 5 wire GPIOs */
	ret = wcnss_gpios_config(penv->gpios_5wire, true);
	if (ret) {
		dev_err(&pdev->dev, "WCNSS gpios config failed.\n");
		goto fail_gpio_res;
	}

	/* power up the WCNSS */
	ret = wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
					WCNSS_WLAN_SWITCH_ON);
	if (ret) {
		dev_err(&pdev->dev, "WCNSS Power-up failed.\n");
		goto fail_power;
	}

	/* trigger initialization of the WCNSS */
	penv->pil = pil_get(WCNSS_PIL_DEVICE);
	if (IS_ERR(penv->pil)) {
		dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
		ret = PTR_ERR(penv->pil);
		penv->pil = NULL;
		goto fail_pil;
	}

	/* allocate resources */
	penv->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
							"wcnss_mmio");
	penv->tx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
							"wcnss_wlantx_irq");
	penv->rx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
							"wcnss_wlanrx_irq");

	if (!(penv->mmio_res && penv->tx_irq_res && penv->rx_irq_res)) {
		dev_err(&pdev->dev, "insufficient resources\n");
		ret = -ENOENT;
		goto fail_res;
	}

	/* register sysfs entries */
	ret = wcnss_create_sysfs(&pdev->dev);
	if (ret)
		goto fail_sysfs;

	return 0;

fail_sysfs:
fail_res:
	if (penv->pil)
		pil_put(penv->pil);
fail_pil:
	wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
				WCNSS_WLAN_SWITCH_OFF);
fail_power:
	wcnss_gpios_config(penv->gpios_5wire, false);
fail_gpio_res:
	kfree(penv);
	penv = NULL;
	return ret;
}
static int
wcnss_trigger_config(struct platform_device *pdev)
{
    int ret;
    struct qcom_wcnss_opts *pdata;


    if (penv->triggered)
        return 0;
    penv->triggered = 1;


    pdata = pdev->dev.platform_data;
    if (WCNSS_CONFIG_UNSPECIFIED == has_48mhz_xo)
        has_48mhz_xo = pdata->has_48mhz_xo;
    penv->wlan_config.use_48mhz_xo = has_48mhz_xo;

    penv->thermal_mitigation = 0;

    penv->gpios_5wire = platform_get_resource_byname(pdev, IORESOURCE_IO,
                        "wcnss_gpios_5wire");


    if (!penv->gpios_5wire) {
        dev_err(&pdev->dev, "insufficient IO resources\n");
        ret = -ENOENT;
        goto fail_gpio_res;
    }


    ret = wcnss_gpios_config(penv->gpios_5wire, true);
    if (ret) {
        dev_err(&pdev->dev, "WCNSS gpios config failed.\n");
        goto fail_gpio_res;
    }


    ret = wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
                           WCNSS_WLAN_SWITCH_ON);
    if (ret) {
        dev_err(&pdev->dev, "WCNSS Power-up failed.\n");
        goto fail_power;
    }


    penv->pil = pil_get(WCNSS_PIL_DEVICE);
    if (IS_ERR(penv->pil)) {
        dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
        ret = PTR_ERR(penv->pil);
        penv->pil = NULL;
        goto fail_pil;
    }


    penv->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                     "wcnss_mmio");
    penv->tx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
                       "wcnss_wlantx_irq");
    penv->rx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
                       "wcnss_wlanrx_irq");

    if (!(penv->mmio_res && penv->tx_irq_res && penv->rx_irq_res)) {
        dev_err(&pdev->dev, "insufficient resources\n");
        ret = -ENOENT;
        goto fail_res;
    }


    ret = wcnss_create_sysfs(&pdev->dev);
    if (ret)
        goto fail_sysfs;

    wake_lock_init(&penv->wcnss_wake_lock, WAKE_LOCK_SUSPEND, "wcnss");

    penv->msm_wcnss_base = ioremap(MSM_RIVA_PHYS, SZ_256);
    if (!penv->msm_wcnss_base) {
        pr_err("%s: ioremap wcnss physical failed\n", __func__);
        goto fail_wake;
    }

    return 0;

fail_wake:
    wake_lock_destroy(&penv->wcnss_wake_lock);

fail_sysfs:
fail_res:
    if (penv->pil)
        pil_put(penv->pil);
fail_pil:
    wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
                     WCNSS_WLAN_SWITCH_OFF);
fail_power:
    wcnss_gpios_config(penv->gpios_5wire, false);
fail_gpio_res:
    kfree(penv);
    penv = NULL;
    return ret;
}
static int
wcnss_trigger_config(struct platform_device *pdev)
{
	int ret;
	struct qcom_wcnss_opts *pdata;

	/* make sure we are only triggered once */
	if (penv->triggered)
		return 0;
	penv->triggered = 1;

	/* initialize the WCNSS device configuration */
	pdata = pdev->dev.platform_data;
	if (WCNSS_CONFIG_UNSPECIFIED == has_48mhz_xo)
		has_48mhz_xo = pdata->has_48mhz_xo;
	penv->wlan_config.use_48mhz_xo = has_48mhz_xo;

	penv->thermal_mitigation = 0;
	strlcpy(penv->wcnss_version, "INVALID", WCNSS_VERSION_LEN);

	penv->gpios_5wire = platform_get_resource_byname(pdev, IORESOURCE_IO,
							"wcnss_gpios_5wire");

	/* allocate 5-wire GPIO resources */
	if (!penv->gpios_5wire) {
		dev_err(&pdev->dev, "insufficient IO resources\n");
		ret = -ENOENT;
		goto fail_gpio_res;
	}

	/* Configure 5 wire GPIOs */
	ret = wcnss_gpios_config(penv->gpios_5wire, true);
	if (ret) {
		dev_err(&pdev->dev, "WCNSS gpios config failed.\n");
		goto fail_gpio_res;
	}

	/* power up the WCNSS */
	ret = wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
					WCNSS_WLAN_SWITCH_ON);
	if (ret) {
		dev_err(&pdev->dev, "WCNSS Power-up failed.\n");
		goto fail_power;
	}

	/* trigger initialization of the WCNSS */
	penv->pil = pil_get(WCNSS_PIL_DEVICE);
	if (IS_ERR(penv->pil)) {
		dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
		ret = PTR_ERR(penv->pil);
		penv->pil = NULL;
		goto fail_pil;
	}

	/* allocate resources */
	penv->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
							"wcnss_mmio");
	penv->tx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
							"wcnss_wlantx_irq");
	penv->rx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
							"wcnss_wlanrx_irq");

	if (!(penv->mmio_res && penv->tx_irq_res && penv->rx_irq_res)) {
		dev_err(&pdev->dev, "insufficient resources\n");
		ret = -ENOENT;
		goto fail_res;
	}

	INIT_WORK(&penv->wcnssctrl_rx_work, wcnssctrl_rx_handler);
	INIT_WORK(&penv->wcnssctrl_version_work, wcnss_send_version_req);
	INIT_WORK(&penv->wcnssctrl_nvbin_dnld_work, wcnss_nvbin_dnld_main);

	wake_lock_init(&penv->wcnss_wake_lock, WAKE_LOCK_SUSPEND, "wcnss");

	penv->msm_wcnss_base = ioremap(MSM_RIVA_PHYS, SZ_256);
	if (!penv->msm_wcnss_base) {
		pr_err("%s: ioremap wcnss physical failed\n", __func__);
		goto fail_wake;
	}

	return 0;

fail_wake:
	wake_lock_destroy(&penv->wcnss_wake_lock);

fail_res:
	if (penv->pil)
		pil_put(penv->pil);
fail_pil:
	wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
				WCNSS_WLAN_SWITCH_OFF);
fail_power:
	wcnss_gpios_config(penv->gpios_5wire, false);
fail_gpio_res:
	kfree(penv);
	penv = NULL;
	return ret;
}
Example #19
0
static int
wcnss_trigger_config(struct platform_device *pdev)
{
	int ret;
	struct qcom_wcnss_opts *pdata;
	unsigned long wcnss_phys_addr;
	int size = 0;
	int has_pronto_hw = of_property_read_bool(pdev->dev.of_node,
									"qcom,has_pronto_hw");

	/* make sure we are only triggered once */
	if (penv->triggered)
		return 0;
	penv->triggered = 1;

	/* initialize the WCNSS device configuration */
	pdata = pdev->dev.platform_data;
	if (WCNSS_CONFIG_UNSPECIFIED == has_48mhz_xo) {
		if (has_pronto_hw) {
			has_48mhz_xo = of_property_read_bool(pdev->dev.of_node,
										"qcom,has_48mhz_xo");
			penv->wcnss_hw_type = WCNSS_PRONTO_HW;
		} else {
			penv->wcnss_hw_type = WCNSS_RIVA_HW;
			has_48mhz_xo = pdata->has_48mhz_xo;
		}
	}
	penv->wlan_config.use_48mhz_xo = has_48mhz_xo;

	penv->thermal_mitigation = 0;
	strlcpy(penv->wcnss_version, "INVALID", WCNSS_VERSION_LEN);

	/* Configure 5 wire GPIOs */
	if (!has_pronto_hw) {
		penv->gpios_5wire = platform_get_resource_byname(pdev,
					IORESOURCE_IO, "wcnss_gpios_5wire");

		/* allocate 5-wire GPIO resources */
		if (!penv->gpios_5wire) {
			dev_err(&pdev->dev, "insufficient IO resources\n");
			ret = -ENOENT;
			goto fail_gpio_res;
		}
		ret = wcnss_gpios_config(penv->gpios_5wire, true);
	} else
		ret = wcnss_pronto_gpios_config(&pdev->dev, true);

	if (ret) {
		dev_err(&pdev->dev, "WCNSS gpios config failed.\n");
		goto fail_gpio_res;
	}

	/* power up the WCNSS */
	ret = wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
					WCNSS_WLAN_SWITCH_ON);
	if (ret) {
		dev_err(&pdev->dev, "WCNSS Power-up failed.\n");
		goto fail_power;
	}

	/* trigger initialization of the WCNSS */
	penv->pil = pil_get(WCNSS_PIL_DEVICE);
	if (IS_ERR(penv->pil)) {
		dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
		ret = PTR_ERR(penv->pil);
		penv->pil = NULL;
		goto fail_pil;
	}

	/* allocate resources */
	penv->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
							"wcnss_mmio");
	penv->tx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
							"wcnss_wlantx_irq");
	penv->rx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
							"wcnss_wlanrx_irq");

	if (!(penv->mmio_res && penv->tx_irq_res && penv->rx_irq_res)) {
		dev_err(&pdev->dev, "insufficient resources\n");
		ret = -ENOENT;
		goto fail_res;
	}
	INIT_WORK(&penv->wcnssctrl_rx_work, wcnssctrl_rx_handler);
	INIT_WORK(&penv->wcnssctrl_version_work, wcnss_send_version_req);

	wake_lock_init(&penv->wcnss_wake_lock, WAKE_LOCK_SUSPEND, "wcnss");

	if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
		size = 0x3000;
		wcnss_phys_addr = MSM_PRONTO_PHYS;
	} else {
		wcnss_phys_addr = MSM_RIVA_PHYS;
		size = SZ_256;
	}

	penv->msm_wcnss_base = ioremap(wcnss_phys_addr, size);
	if (!penv->msm_wcnss_base) {
		ret = -ENOMEM;
		pr_err("%s: ioremap wcnss physical failed\n", __func__);
		goto fail_wake;
	}

	return 0;

fail_wake:
	wake_lock_destroy(&penv->wcnss_wake_lock);
fail_res:
	if (penv->pil)
		pil_put(penv->pil);
fail_pil:
	wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
				WCNSS_WLAN_SWITCH_OFF);
fail_power:
	if (has_pronto_hw)
		wcnss_pronto_gpios_config(&pdev->dev, false);
	else
		wcnss_gpios_config(penv->gpios_5wire, false);
fail_gpio_res:
	kfree(penv);
	penv = NULL;
	return ret;
}