mcp251xfd test driver crashes - linux-kernel

Edit: It works now,
https://github.com/minima34/mcp251xfd_test
I wrote my first linux module using SPI to just test mcp2158fd chip writing and reading the OSC register but it bugs my system on run and I can't unload it with modprobe -rf or rmmod -f.
When I write reboot and press Enter it hangs at some point and can't reboot. The problem is I work remotely from another town and can't do hw reboot when my colleauges are not there. I dont have the log because its on serial console and I cant go upward to see.
It spams some "sdio" message which I supress with dmesg -n 1 command.
Im very interested in this topic. Its very interesting to write drivers.
What causes the crash?
This is my code:
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <asm/unaligned.h>
// by Dv: compatibility
#include <linux/version.h>
// by Dv: compatibility /
//#include <linux/iopoll.h>
#include "mcp251xfd.h"
#define DEVICE_NAME "mcptest"
#define TID_CAN_CLK 20000000
#define TID_SPI_FREQ 4000000
#define my_read_poll_timeout(op, val, cond, sleep_us, timeout_us, \
sleep_before_read, args...) \
({ \
u64 __timeout_us = (timeout_us); \
unsigned long __sleep_us = (sleep_us); \
ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \
might_sleep_if((__sleep_us) != 0); \
if (sleep_before_read && __sleep_us) \
usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
for (;;) { \
(val) = op(args); \
if (cond) \
break; \
if (__timeout_us && \
ktime_compare(ktime_get(), __timeout) > 0) { \
(val) = op(args); \
break; \
} \
if (__sleep_us) \
usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
} \
(cond) ? 0 : -ETIMEDOUT; \
})
#define my_regmap_read_poll_timeout(myspi, addr, val, cond, sleep_us, timeout_us) \
({ \
int __ret, __tmp; \
__tmp = my_read_poll_timeout(my_regmap_read, __ret, __ret || (cond), \
sleep_us, timeout_us, false, (myspi), (addr), &(val)); \
__ret ?: __tmp; \
})
static const struct mcp251xfd_devtype_data mcp251xfd_devtype_data_mcp2517fd = {
.quirks = MCP251XFD_QUIRK_MAB_NO_WARN | MCP251XFD_QUIRK_CRC_REG |
MCP251XFD_QUIRK_CRC_RX | MCP251XFD_QUIRK_CRC_TX |
MCP251XFD_QUIRK_ECC,
.model = MCP251XFD_MODEL_MCP2517FD,
};
static const struct mcp251xfd_devtype_data mcp251xfd_devtype_data_mcp2518fd = {
.quirks = MCP251XFD_QUIRK_CRC_REG | MCP251XFD_QUIRK_CRC_RX |
MCP251XFD_QUIRK_CRC_TX | MCP251XFD_QUIRK_ECC,
.model = MCP251XFD_MODEL_MCP2518FD,
};
/* Autodetect model, start with CRC enabled. */
static const struct mcp251xfd_devtype_data mcp251xfd_devtype_data_mcp251xfd = {
.quirks = MCP251XFD_QUIRK_CRC_REG | MCP251XFD_QUIRK_CRC_RX |
MCP251XFD_QUIRK_CRC_TX | MCP251XFD_QUIRK_ECC,
.model = MCP251XFD_MODEL_MCP251XFD,
};
u16 rx_reg;
u32 rx_val;
static int my_regmap_write(struct spi_device *spi, u16 reg, u32 val)
{
struct spi_transfer xfer[2];
//uint8_t *reg_bytes[2] = (void *) ®
//uint8_t *val_bytes[4] = (void *) &val;
reg|=MCP251XFD_SPI_INSTRUCTION_WRITE;
xfer[0].tx_buf = ®
xfer[0].rx_buf = &rx_reg;
xfer[0].len = 2;
xfer[1].tx_buf = &val;
xfer[1].rx_buf = &rx_val;
xfer[1].len = 4;
printk(KERN_NOTICE "%s(%d) writing 0x%04x => 0x%08xx\nrx_reg => 0x%04x", __FUNCTION__, __LINE__, reg, val, rx_reg);
return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
}
static int my_regmap_read(struct spi_device *spi, u16 reg, u32 *val)
{
struct spi_transfer xfer[2];
reg|=MCP251XFD_SPI_INSTRUCTION_READ;
xfer[0].tx_buf = ®
xfer[0].rx_buf = &rx_reg;
xfer[0].len = 2;
u32 klokvam_nuli = 0x00000000;
xfer[1].tx_buf = &klokvam_nuli;
xfer[1].rx_buf = val;
xfer[1].len = 4;
printk(KERN_NOTICE "%s(%d) reading 0x%04x => 0x%08xx\nrx_reg => 0x%04x", __FUNCTION__, __LINE__, reg, *val, rx_reg);
return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
}
static int vikam_te(struct spi_device *spi)
{
u32 osc, osc_reference, osc_mask;
/* Set Power On Defaults for "Clock Output Divisor" and remove
* "Oscillator Disable" bit.
*/
printk (KERN_NOTICE "mask=0x%08x; clkdiv10=0x%08x;\n",MCP251XFD_REG_OSC_CLKODIV_MASK,MCP251XFD_REG_OSC_CLKODIV_10);
osc = FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, MCP251XFD_REG_OSC_CLKODIV_10);
osc_reference = MCP251XFD_REG_OSC_OSCRDY;
osc_mask = MCP251XFD_REG_OSC_OSCRDY;
//osc_mask = MCP251XFD_REG_OSC_OSCRDY | MCP251XFD_REG_OSC_PLLRDY;
/* Note:
*
* If the controller is in Sleep Mode the following write only
* removes the "Oscillator Disable" bit and powers it up. All
* other bits are unaffected.
*/
my_regmap_write(spi, MCP251XFD_REG_OSC, 0x00000060);
printk(KERN_NOTICE, "%s(%d) w1 osc=0x%08x\n", __FUNCTION__, __LINE__, osc);
pr_info("[WR] rx_reg = 0x%04X \nrx_val = 0x%08X \n", rx_reg, rx_val);
int err = my_regmap_read_poll_timeout(spi, MCP251XFD_REG_OSC, osc, (osc & osc_mask) == osc_reference,
MCP251XFD_OSC_STAB_SLEEP_US,
MCP251XFD_OSC_STAB_TIMEOUT_US);
printk(KERN_NOTICE, "%s(%d) r0 osc=0x%08x\n", __FUNCTION__, __LINE__, osc);
pr_info("[RD] rx_reg = 0x%04X \nrx_val = 0x%08X \n", rx_reg, rx_val);
return err;
}
static int mcptest_probe(struct spi_device *spi)
{
int err;
struct mcp251xfd_priv *priv;
u32 freq = TID_CAN_CLK;
printk(KERN_NOTICE "YOU'RE WELCOME!, my_freq_is_%u\n",freq);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0))
if(!spi->irq)
{
return dev_err_probe(&spi->dev, -ENXIO, "No IRQ specified (maybe node \"interrupts-extended\" in DT missing)!\n");
}
#else
if(!spi->irq)
{
dev_err(&spi->dev, "No IRQ specified (maybe node \"interrupts-extended\" in DT missing)!\n");
return -ENXIO;
}
#endif
//struct device_node *np;
//np = spi->dev.of_node;
spi_set_drvdata(spi, priv);
priv->spi = spi;
//priv->rx_int = rx_int;
//priv->clk = clk;
//
// CHECK
const void *match = of_device_get_match_data(&spi->dev);
//
if (match)
priv->devtype_data = *(struct mcp251xfd_devtype_data *)match;
else
priv->devtype_data = *(struct mcp251xfd_devtype_data *)spi_get_device_id(spi)->driver_data;
//
// or
//priv->devtype_data = *(struct mcptest_devtype_data *)
// spi_get_device_id(spi)->driver_data;
priv->spi_max_speed_hz_orig = spi->max_speed_hz;
#ifndef TID_SPI_FREQ
spi->max_speed_hz = min(spi->max_speed_hz, freq / 2 / 1000 * 850);
#else
spi->max_speed_hz = min(TID_SPI_FREQ, freq / 2 / 1000 * 850);
#endif
spi->bits_per_word = 8;
/////////////////////////////////////////zgrep FORCE_UNLOAD /proc/config.gz
dev_err( &spi->dev, "%s():%d spi-> max_speed_hz:%d \n", __FUNCTION__, __LINE__, spi->max_speed_hz);
err = spi_setup(spi);
dev_err( &spi->dev, "%s():%d spi_setup:%d \n", __FUNCTION__, __LINE__, err);
if (err)
goto remove;
//err = mcptest_regmap_init(priv);
//dev_err( &spi->dev, "%s():%d spi_setup:%d \n", __FUNCTION__, __LINE__, err);
//if (err)
// goto remove;
//err = mcptest_register(priv);
//dev_err( &spi->dev, "%s():%d spi_setup:%d \n", __FUNCTION__, __LINE__, err);
//if (err)
// goto remove;
err = vikam_te(spi);
if(err)
goto remove;
//
//int spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers, unsigned int num_xfers)
//spi – a device with which data will be exchanged.
//xfers – An array of spi_transfers.
//num_xfers – Number of items in the xfer array.
//
//int spi_async(struct spi_device *spi, struct spi_message *message)
//spi – a device with which data will be exchanged.
//message – describes the data transfers, including completion callback.
//
//
//This API is used to write the data and followed by a read. This is synchronous.
//
//int spi_write_then_read(struct spi_device * spi, const void * txbuf, unsigned n_tx, void * rxbuf, unsigned n_rx)
//
//spi – a device with which data will be exchanged.
//txbuf – data to be written.
//n_tx – size of txbuf (in bytes).
//rxbuf – the buffer into which data will be read.
//n_rx – size of rxbuf (in bytes).
//
return 0;
remove:
spi->max_speed_hz = priv->spi_max_speed_hz_orig;
if(spi)
{
spi_unregister_device(spi); // Unregister the SPI slave
pr_info("spi device unregistered :) happy hacking :P\nIm waiting the next test :P\n");
}
return err;
}
/*
static void __exit etx_spi_exit(void)
{
}
module_init(etx_spi_init);
module_exit(etx_spi_exit); // https://embetronicx.com/tutorials/linux/device-drivers/linux-kernel-spi-device-driver-tutorial/
*/
static int mcptest_remove(struct spi_device *spi)
{
struct mcp251xfd_priv *priv = spi_get_drvdata(spi);
spi->max_speed_hz = priv->spi_max_speed_hz_orig;
if(spi)
{
spi_unregister_device(spi); // Unregister the SPI slave
pr_info("spi device unregistered :) happy hacking :P\nIm waiting the next test :P\n");
}
return 0;
}
/*
static const struct dev_pm_ops mcptest_pm_ops = {
SET_RUNTIME_PM_OPS(mcptest_runtime_suspend,
mcptest_runtime_resume, NULL)
};
*/
static const struct spi_device_id mcptest_id_table[] = {
{
.name = "mcp2517fd",
.driver_data = (kernel_ulong_t)&mcp251xfd_devtype_data_mcp2517fd,
}, {
.name = "mcp2518fd",
.driver_data = (kernel_ulong_t)&mcp251xfd_devtype_data_mcp2518fd,
}, {
.name = "mcp251xfd",
.driver_data = (kernel_ulong_t)&mcp251xfd_devtype_data_mcp251xfd,
}, {
/* sentinel */
},
};
MODULE_DEVICE_TABLE(spi, mcptest_id_table);
static const struct of_device_id mcptest_of_match[] = {
{
.compatible = "microchip,mcp2517fd",
.data = &mcp251xfd_devtype_data_mcp2517fd,
}, {
.compatible = "microchip,mcp2518fd",
.data = &mcp251xfd_devtype_data_mcp2518fd,
}, {
.compatible = "microchip,mcp251xfd",
.data = &mcp251xfd_devtype_data_mcp251xfd,
}, {
/* sentinel */
},
};
MODULE_DEVICE_TABLE(of, mcptest_of_match);
static struct spi_driver mcptest_driver = {
.driver = {
.name = DEVICE_NAME,
//.pm = &mcptest_pm_ops,
.of_match_table = mcptest_of_match,
},
.probe = mcptest_probe,
.remove = mcptest_remove,
.id_table = mcptest_id_table,
};
module_spi_driver(mcptest_driver);
MODULE_AUTHOR("Acho # T&D Engineering");
MODULE_DESCRIPTION("mcptest test");
MODULE_LICENSE("GPL v2");
Edit:
This is the log of the crash:
root#helperboard:/# dmesg -C && insmod mcptest.ko && dmesg && dmesg -n 1
[ 1038.960898] Internal error: Accessing user space memory outside uaccess.h routines: 96000045 [#1] PREEMPT SMP
[ 1038.972067] Modules linked in: mcptest(+) vin_v4l2 ov8858_r2a_4lane vin_io videobuf2_dma_contig gt9xxnew_ts sprdbt_tty mcp251x qcaspi mcp251xfd sprdwl_ng uwe5622_bsp_sdio pvrsrvkm(O) sunxi_gmac tid_irq_test
[ 1038.992839] CPU: 3 PID: 7052 Comm: insmod Tainted: G O 4.9.170 #13
[ 1039.001060] Hardware name: sun50iw10 (DT)
[ 1039.005572] task: ffffffc03cdb9d00 task.stack: ffffffc03e19c000
[ 1039.012254] PC is at mcptest_probe+0x6c/0x2f0 [mcptest]
[ 1039.018147] LR is at mcptest_probe+0x48/0x2f0 [mcptest]
[ 1039.024026] pc : [<ffffff8000d9b2ec>] lr : [<ffffff8000d9b2c8>] pstate: 80400145
[ 1039.032342] sp : ffffffc03e19f9d0
[ 1039.036069] x29: ffffffc03e19f9d0 x28: ffffffc0364b4d00
[ 1039.042057] x27: 0000000000000001 x26: 0000000000000000
[ 1039.048046] x25: ffffff800920df10 x24: 000000f1e6e21461
[ 1039.054033] x23: 000000000000002f x22: ffffff8009469000
[ 1039.060021] x21: 0000000000000000 x20: ffffff8000d9bd30
[ 1039.066009] x19: ffffffc03d423000 x18: 000000000000000a
[ 1039.071996] x17: 0000007fa74532a8 x16: ffffff80080a9090
[ 1039.077984] x15: 0000000001312d00 x14: ffffff8009418788
[ 1039.083971] x13: ffffffffffffffff x12: 0000000000000020
....
[ 1040.059641] f8e0: ffffffc03f6eba90 0000000005f5e0ff ffffff80094183ce 00000000fffffff4
[ 1040.068389] f900: 0000000000000020 ffffffffffffffff
[ 1040.073841] [<ffffff8000d9b2ec>] mcptest_probe+0x6c/0x2f0 [mcptest]
[ 1040.080849] [<ffffff80085cabd4>] spi_drv_probe+0x88/0xb0
[ 1040.086789] [<ffffff8008539c50>] driver_probe_device+0x1dc/0x41c
[ 1040.093501] [<ffffff8008539f24>] __driver_attach+0x94/0x108
[ 1040.099731] [<ffffff80085379f4>] bus_for_each_dev+0x8c/0xcc
[ 1040.105954] [<ffffff8008539530>] driver_attach+0x30/0x3c
[ 1040.111885] [<ffffff8008538ef4>] bus_add_driver+0xf8/0x248
[ 1040.118012] [<ffffff800853ac80>] driver_register+0x98/0xe4
[ 1040.124139] [<ffffff80085caae8>] __spi_register_driver+0x6c/0x78
[ 1040.130851] [<ffffff8000d9e014>] mcptest_driver_init+0x14/0x30 [mcptest]
[ 1040.138340] [<ffffff8008083a30>] do_one_initcall+0xb4/0x164
[ 1040.144571] [<ffffff800819d3fc>] do_init_module+0x64/0x1c0
[ 1040.150710] [<ffffff80081324b8>] load_module+0x1c0c/0x2060
[ 1040.156842] [<ffffff8008132b98>] SyS_finit_module+0xa8/0xfc
[ 1040.163066] [<ffffff8008083540>] el0_svc_naked+0x34/0x38
[ 1040.169011] Code: 95de6857 14000061 d2800000 f9005260 (f9014013)
[ 1040.175828] ---[ end trace 5ec42185a80c910f ]---
root#helperboard:/# ^C
root#helperboard:/# reboot
[ OK ] Stopped Create Static Device Nodes in /dev.
[ OK ] Reached target Shutdown.
[ OK ] Reached target Final Step.
Starting Reboot...
[ 1987.095794] systemd-shutdow: 23 output lines suppressed due to ratelimiting
[ 1987.124360] systemd-shutdown[1]: Syncing filesystems and block devices.
[ 1987.152188] systemd-shutdown[1]: Sending SIGTERM to remaining processes...
[ 1987.177346] systemd-journald[1192]: Received SIGTERM from PID 1 (systemd-shutdow).
[ 1987.221361] systemd-shutdown[1]: Sending SIGKILL to remaining processes...
[ 1987.244437] systemd-shutdown[1]: Unmounting file systems.
[ 1987.272053] EXT4-fs (mmcblk0p4): re-mounted. Opts: nobarrier,noauto_da_alloc,data=ordered
[ 1987.290797] systemd-shutdown[1]: All filesystems unmounted.
[ 1987.297107] systemd-shutdown[1]: Deactivating swaps.
[ 1987.302961] systemd-shutdown[1]: All swaps deactivated.
[ 1987.308893] systemd-shutdown[1]: Detaching loop devices.
[ 1987.318052] systemd-shutdown[1]: All loop devices detached.
[ 1987.324325] systemd-shutdown[1]: Detaching DM devices.
[ 2012.905670] WCN_ERR: log_rx_callback open /data/unisoc_cp2log_1.txt error no.-30 retry:1
And hangs here when I do reboot

I found out the problem,
it was args mismatch of my read_poll_timeout macros in their usages.
These are my new macros:
#define tid_poll_timeout(cond, sleep_us, timeout_us, sleep_before_read, op, args...) \
({ \
ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \
might_sleep_if(sleep_us); \
if(sleep_before_read) \
usleep_range((sleep_us >> 2) + 1, sleep_us); \
for (;;) { \
op(args); \
if (cond) \
break; \
if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \
op(args); \
break; \
} \
if (sleep_us) \
usleep_range((sleep_us >> 2) + 1, sleep_us); \
} \
(cond) ? 0 : -ETIMEDOUT; \
})
#define my_regmap_read_poll_timeout(cond, sleep_us, timeout_us, op, spi, addr, val) \
({ \
int __ret, __tmp; \
__tmp = tid_poll_timeout(val || (cond), sleep_us, timeout_us, false, op, spi, addr, &(val)); \
__ret ?: __tmp; \
})
And this where its used:
int err = my_regmap_read_poll_timeout((osc & osc_mask) == osc_reference, MCP251XFD_OSC_STAB_SLEEP_US, MCP251XFD_OSC_STAB_TIMEOUT_US, my_regmap_read, spi, MCP251XFD_REG_OSC, osc);
And this is my new code if somebody is interested for testing the spi connection to MCP251xFD family
https://github.com/minima34/mcp251xfd_test
git clone https://github.com/minima34/mcp251xfd_test.git
Another thing I edited was that im not using the priv structure, so I commented out these lines related to the structure:
//spi_set_drvdata(spi, priv);
//priv->spi = spi;
//priv->rx_int = rx_int;
//priv->clk = clk;
//
//
// CHECK
//const void *match = of_device_get_match_data(&spi->dev);
//
//if (match)
// priv->devtype_data = *(struct mcp251xfd_devtype_data *)match;
//else
// priv->devtype_data = *(struct mcp251xfd_devtype_data *)spi_get_device_id(spi)->driver_data;
//
// or
//priv->devtype_data = *(struct mcptest_devtype_data *)
// spi_get_device_id(spi)->driver_data;
//
//priv->spi_max_speed_hz_orig = spi->max_speed_hz;
This is it, driver works now and I found out that my spi connection works. yea, happy smile :)
I write 0x00000060 and read 0x00000060
so.. => everything [OK]
Thanks family.

Related

ESP32-C3-DevKitC-02 Getting Junk Data on UART for AT commands

I am interfacing A7672S 4G module with ESP32-C3-DevKitC-02 over Uart0
The problem i am facing is for any AT command published,i am getting lot of junk data
I have tried publising AT to the same 4G module using arduino ,i am able to get the correct response only with this ESP module i am seeing such issues
void uart_init(void)
{
const uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
// We won't use a buffer for sending data.
uart_driver_install(UART_NUM_0, RX_BUF_SIZE * 2, 0, 0, NULL, 0);
uart_param_config(UART_NUM_0, &uart_config);
uart_set_pin(UART_NUM_0, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}
#define RX_BUF_SIZE 1024
int sendData( const char *data)
{
static const char *TX_TASK_TAG = "TX_TASK";
esp_log_level_set(TX_TASK_TAG, ESP_LOG_VERBOSE);
const int len = strlen(data);
const int txBytes = uart_write_bytes(UART_NUM_0, data, len);
ESP_LOGI(TX_TASK_TAG, "Wrote %d bytes", txBytes);
return txBytes;
}
static uint8_t receiveData(uint8_t *data,unsigned int delay_ms)
{
uint8_t rxBytes = 0;
static const char *RX_TASK_TAG = "RX_TASK";
esp_log_level_set(RX_TASK_TAG, ESP_LOG_VERBOSE);
rxBytes = uart_read_bytes(UART_NUM_0, data,RX_BUF_SIZE, delay_ms / portTICK_PERIOD_MS);
data[rxBytes]='\0';
ESP_LOGI(RX_TASK_TAG, "Received %d bytes:\nRxData:%s\n", rxBytes,data);
return rxBytes;
}
uint8_t *data = (uint8_t *)malloc(RX_BUF_SIZE + 1);
uint8_t len=0;
static const char *MAIN_TAG = "MAIN_FUNCTION";
esp_log_level_set(MAIN_TAG, ESP_LOG_VERBOSE);
memset(data, 0, RX_BUF_SIZE + 1);
uart_init();
vTaskDelay(20);
uart_flush(UART_NUM_0);
sendData("AT+CGMI\r\n");
len = receiveData(data,2000);
if(OK == check_response(data,(unsigned char*)"OK"))
{
ESP_LOGI(MAIN_TAG,"Incorporated AT Response Received");
}
else
{
ESP_LOGI(MAIN_TAG,"AT Response Not Received");
}

i.MX8 mini GPT implementation issue

I'm trying to use GPT to measuring frequency on a gpio. After some research on the google and Linux's documentation, unfortunately I couldn't see any compatible GPT driver in kernel. I found a patch on google and applied it. But I have a getting clock error now. There is a function which It gets the clock but it's never executing.
P.S : I prepared a platform driver to use exported functions (e.g. mxc_request_input_capture) in timer-imx-gpt.c.
My dts nodes
/*These nodes are child node if aips1*/
gpt1: gpt#302d0000 {
compatible = "fsl,imx8mm-gpt";
reg = <0x302d0000 0x10000>;
interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk IMX8MM_CLK_GPT1>,
<&clk IMX8MM_CLK_GPT1_ROOT>,
<&clk IMX8MM_CLK_GPT_3M>;
clock-names = "ipg", "per", "osc_per";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpt_input_capture0>;
status = "okay";
};
iomuxc: pinctrl#30330000 {
compatible = "fsl,imx8mm-iomuxc";
reg = <0x30330000 0x10000>;
pinctrl_gpt_input_capture0: gptinputcapture0grp {
fsl,pins = <
MX8MM_IOMUXC_SAI3_RXFS_GPT1_CAPTURE1 0xd6 /*0x00000000*/
>;
};
};
request gpt input capture func (in timer-imx-gpt.c)
int mxc_request_input_capture(unsigned int chan, mxc_icap_handler_t handler,
unsigned long capflags, void *dev_id)
{
struct imx_timer *imxtm;
struct icap_channel *ic;
unsigned long flags;
u64 start_cycles;
int ret = 0;
u32 mode;
/* we only care about rising and falling flags */
capflags &= (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING);
if (chan > 1 || !handler || !capflags)
return -EINVAL;
printk("mxc_request_input_capture 1\n");
ic = &icap_channel_arr[chan];
imxtm = ic->imxtm;
printk("mxc_request_input_capture 2\n");
if (!imxtm->gpt->gpt_ic_enable)
return -ENODEV; //THIS ERROR CODE RETURN
printk("mxc_request_input_capture 3\n");
spin_lock_irqsave(&icap_lock, flags);
if (ic->handler) {
ret = -EBUSY;
goto out;
}
printk("mxc_request_input_capture 4\n");
ic->handler = handler;
ic->dev_id = dev_id;
switch (capflags) {
case IRQF_TRIGGER_RISING:
mode = V2_IM_RISING;
break;
case IRQF_TRIGGER_FALLING:
mode = V2_IM_FALLING;
break;
default:
mode = V2_IM_BOTH;
break;
}
printk("mxc_request_input_capture 4\n");
/* ack any pending input capture interrupt before enabling */
imxtm->gpt->gpt_ic_irq_acknowledge(ic);
/*
* initialize the cyclecounter. The input capture is capturing
* from the mxc clocksource, so it has the same mask/shift/mult.
*/
memset(&ic->cc, 0, sizeof(ic->cc));
ic->cc.read = gpt_ic_read;
ic->cc.mask = clocksource_mxc.mask;
ic->cc.shift = clocksource_mxc.shift;
ic->cc.mult = clocksource_mxc.mult;
printk("mxc_request_input_capture 5\n");
/* initialize a timecounter for the input capture */
start_cycles = mxc_read_sched_clock();
timecounter_init(&ic->tc, &ic->cc, ktime_get_ns());
printk("mxc_request_input_capture 6\n");
/*
* timecounter_init() read the last captured timer count, but
* that's not the start cycle counter, so update it with the
* real start cycles.
*/
ic->tc.cycle_last = start_cycles;
imxtm->gpt->gpt_ic_enable(ic, mode);
imxtm->gpt->gpt_ic_irq_enable(ic);
printk("mxc_request_input_capture 7\n");
out:
spin_unlock_irqrestore(&icap_lock, flags);
printk("mxc_request_input_capture 8\n");
return ret;
}
EXPORT_SYMBOL_GPL(mxc_request_input_capture);
I realized that imxtm->gpt->gpt_ic_enable variable seems NULL. imxtm structure gets filled in the end of static int __init _mxc_timer_init function. But the function returns error before filling the struct. The error is happening checking the clock. The function which related to getting clock is mxc_timer_init_dt but it never executes.
log:
root:~# dmesg | grep gpt
[ 0.000174] gpt imx6dl_timer_init_dt HEAD
[ 0.000240] gpt _mxc_timer_init
[ 0.000244] gpt imxtm->type 3
[ 0.000249] gpt imxtm->type GPT_TYPE_IMX6DL
[ 0.000252] imx-gpt is NOT null
[ 0.000260] gpt GPT_TYPE_IMX6DL 3
[ 0.000263] gpt mxc_timer_init_dt error -517
[ 16.837810] mxc-timer 302d0000.gpt: GPT Timer Probe Function head
[ 16.843920] mxc-timer 302d0000.gpt: GPT Timer Probe Function end
It start with executing imx6dl_timer_init_dt. mxc_timer_init_dt function must be execute after imx6dl_timer_init_dt to get the clock but it is not executing. Due to failing on getting clock, I am not able to use the gpt module.
some parts of timer-imx-gpt.c
static int __init _mxc_timer_init(struct imx_timer *imxtm)
{
struct icap_channel *ic;
int i, ret;
printk(KERN_INFO "gpt _mxc_timer_init\n");
printk(KERN_INFO "gpt imxtm->type %d \n", imxtm->type);
switch (imxtm->type) {
case GPT_TYPE_IMX1:
imxtm->gpt = &imx1_gpt_data;
printk(KERN_INFO "gpt imxtm->type GPT_TYPE_IMX1 \n");
break;
case GPT_TYPE_IMX21:
imxtm->gpt = &imx21_gpt_data;
printk(KERN_INFO "gpt imxtm->type GPT_TYPE_IMX21 \n");
break;
case GPT_TYPE_IMX31:
imxtm->gpt = &imx31_gpt_data;
printk(KERN_INFO "gpt imxtm->type GPT_TYPE_IMX31 \n");
break;
case GPT_TYPE_IMX6DL:
imxtm->gpt = &imx6dl_gpt_data;
printk(KERN_INFO "gpt imxtm->type GPT_TYPE_IMX6DL \n");
break;
default:
printk(KERN_INFO "gpt imxtm->type DEFAULT \n");
return -EINVAL;
}
imxtm->gpt = &imx6dl_gpt_data;
if(!imxtm->gpt)
printk(KERN_INFO "imx-gpt is null");
printk(KERN_INFO "imx-gpt is NOT null");
if (IS_ERR(imxtm->clk_per)) {
pr_err("i.MX timer: unable to get clk\n"); //I SAW THIS LOG IN LINUX LOGS
return PTR_ERR(imxtm->clk_per); //Function return error at this line
}
printk(KERN_INFO "gpt clk_per success\n");
if (!IS_ERR(imxtm->clk_ipg))
clk_prepare_enable(imxtm->clk_ipg);
printk(KERN_INFO "gpt clk_ipg success\n");
clk_prepare_enable(imxtm->clk_per);
printk(KERN_INFO "gpt clk_prepare_enable\n");
/*
* Initialise to a known state (all timers off, and timing reset)
*/
writel_relaxed(0, imxtm->base + MXC_TCTL);
writel_relaxed(0, imxtm->base + MXC_TPRER); /* see datasheet note */
imxtm->gpt->gpt_oc_setup_tctl(imxtm);
/* init and register the timer to the framework */
ret = mxc_clocksource_init(imxtm);
if (ret)
return ret;
ret = mxc_clockevent_init(imxtm);
if (ret)
return ret;
/*Filling the imx structure. But never reach here*/
for (i = 0; i < 2; i++) {
ic = &icap_channel_arr[i];
ic->imxtm = imxtm;
}
printk(KERN_INFO, "_mxc_timer_init RETURN SUCCESS\n");
return 0;
}
void __init mxc_timer_init(unsigned long pbase, int irq, enum imx_gpt_type type)
{
struct imx_timer *imxtm;
printk(KERN_INFO, "gpt mxc_timer_init HEAD\n");
imxtm = kzalloc(sizeof(*imxtm), GFP_KERNEL);
BUG_ON(!imxtm);
imxtm->clk_per = clk_get_sys("imx-gpt.0", "per");
imxtm->clk_ipg = clk_get_sys("imx-gpt.0", "ipg");
imxtm->base = ioremap(pbase, SZ_4K);
BUG_ON(!imxtm->base);
imxtm->type = type;
imxtm->irq = irq;
_mxc_timer_init(imxtm);
}
/*
* a platform driver is needed in order to acquire pinmux
* for input capture pins. The probe call is also useful
* for setting up the input capture channel structures.
*/
static int mxc_timer_probe(struct platform_device *pdev)
{
struct icap_channel *ic;
int i;
dev_info(&pdev->dev, "GPT Timer Probe Function head\n");
/* setup the input capture channels */
for (i = 0; i < 2; i++) {
ic = &icap_channel_arr[i];
ic->chan = i;
if (i == 0) {
ic->cnt_reg = V2_TCAP1;
ic->irqen_bit = V2_IR_IF1;
ic->status_bit = V2_TSTAT_IF1;
ic->mode_bit = V2_TCTL_IM1_BIT;
} else {
ic->cnt_reg = V2_TCAP2;
ic->irqen_bit = V2_IR_IF2;
ic->status_bit = V2_TSTAT_IF2;
ic->mode_bit = V2_TCTL_IM2_BIT;
}
}
dev_info(&pdev->dev, "GPT Timer Probe Function end\n");
return 0;
}
static int mxc_timer_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id timer_of_match[] = {
{ .compatible = "fsl,imx1-gpt" },
{ .compatible = "fsl,imx21-gpt" },
{ .compatible = "fsl,imx27-gpt" },
{ .compatible = "fsl,imx31-gpt" },
{ .compatible = "fsl,imx25-gpt" },
{ .compatible = "fsl,imx50-gpt" },
{ .compatible = "fsl,imx51-gpt" },
{ .compatible = "fsl,imx53-gpt" },
{ .compatible = "fsl,imx6q-gpt" },
{ .compatible = "fsl,imx6dl-gpt" },
{ .compatible = "fsl,imx6sl-gpt" },
{ .compatible = "fsl,imx6sx-gpt" },
{ .compatible = "fsl,imx8mm-gpt" },
{ },
};
MODULE_DEVICE_TABLE(of, timer_of_match);
static struct platform_driver mxc_timer_pdrv = {
.probe = mxc_timer_probe,
.remove = mxc_timer_remove,
.driver = {
.name = "mxc-timer",
.owner = THIS_MODULE,
.of_match_table = timer_of_match,
},
};
module_platform_driver(mxc_timer_pdrv);
static int __init mxc_timer_init_dt(struct device_node *np, enum imx_gpt_type type)
{
struct imx_timer *imxtm;
static int initialized;
int ret;
printk(KERN_INFO, "gpt mxc_timer_init_dt head\n");
/* Support one instance only */
if (initialized)
return 0;
imxtm = kzalloc(sizeof(*imxtm), GFP_KERNEL);
if (!imxtm)
return -ENOMEM;
imxtm->base = of_iomap(np, 0);
if (!imxtm->base)
return -ENXIO;
imxtm->irq = irq_of_parse_and_map(np, 0);
if (imxtm->irq <= 0)
return -EINVAL;
imxtm->clk_ipg = of_clk_get_by_name(np, "ipg");
/* Try osc_per first, and fall back to per otherwise */
imxtm->clk_per = of_clk_get_by_name(np, "osc_per");
if (IS_ERR(imxtm->clk_per))
imxtm->clk_per = of_clk_get_by_name(np, "per");
imxtm->type = type;
printk(KERN_INFO, "gpt mxc_timer_init_dt line 883\n");
ret = _mxc_timer_init(imxtm);
if (ret)
return ret;
initialized = 1;
printk(KERN_INFO, "gpt mxc_timer_init_dt end\n");
return 0;
}
static int __init imx1_timer_init_dt(struct device_node *np)
{
return mxc_timer_init_dt(np, GPT_TYPE_IMX1);
}
static int __init imx21_timer_init_dt(struct device_node *np)
{
return mxc_timer_init_dt(np, GPT_TYPE_IMX21);
}
static int __init imx31_timer_init_dt(struct device_node *np)
{
enum imx_gpt_type type = GPT_TYPE_IMX31;
printk(KERN_INFO "gpt imx31_timer_init_dt HEAD");
/*
* We were using the same compatible string for i.MX6Q/D and i.MX6DL/S
* GPT device, while they actually have different programming model.
* This is a workaround to keep the existing i.MX6DL/S DTBs continue
* working with the new kernel.
*/
if (of_machine_is_compatible("fsl,imx6dl"))
type = GPT_TYPE_IMX6DL;
return mxc_timer_init_dt(np, type);
}
static int __init imx6dl_timer_init_dt(struct device_node *np)
{
printk(KERN_INFO "gpt imx6dl_timer_init_dt HEAD");
return mxc_timer_init_dt(np, GPT_TYPE_IMX6DL); //This function must execute to get clock
}
TIMER_OF_DECLARE(imx1_timer, "fsl,imx1-gpt", imx1_timer_init_dt);
TIMER_OF_DECLARE(imx21_timer, "fsl,imx21-gpt", imx21_timer_init_dt);
TIMER_OF_DECLARE(imx27_timer, "fsl,imx27-gpt", imx21_timer_init_dt);
TIMER_OF_DECLARE(imx31_timer, "fsl,imx31-gpt", imx31_timer_init_dt);
TIMER_OF_DECLARE(imx25_timer, "fsl,imx25-gpt", imx31_timer_init_dt);
TIMER_OF_DECLARE(imx50_timer, "fsl,imx50-gpt", imx31_timer_init_dt);
TIMER_OF_DECLARE(imx51_timer, "fsl,imx51-gpt", imx31_timer_init_dt);
TIMER_OF_DECLARE(imx53_timer, "fsl,imx53-gpt", imx31_timer_init_dt);
TIMER_OF_DECLARE(imx6q_timer, "fsl,imx6q-gpt", imx31_timer_init_dt);
TIMER_OF_DECLARE(imx6dl_timer, "fsl,imx6dl-gpt", imx6dl_timer_init_dt);
TIMER_OF_DECLARE(imx6sl_timer, "fsl,imx6sl-gpt", imx6dl_timer_init_dt);
TIMER_OF_DECLARE(imx6sx_timer, "fsl,imx6sx-gpt", imx6dl_timer_init_dt);
TIMER_OF_DECLARE(imx8mm_timer, "fsl,imx8mm-gpt", imx6dl_timer_init_dt); //This line added after applying patch
I will be very grateful for your help

Doubly linked list task_list in Binder why is is empty? how to print it?

Modified binder.c code based on this.
https://github.com/S3NEO/android_kernel_samsung_s3ve3g/blob/24c9b3454fddc6a5b332989cb4f4455c21867a42/drivers/staging/android/binder.c#L3222
I want to print the doubly linked list task_list:
static int binder_free_thread(struct binder_proc *proc,
struct binder_thread *thread)
{
struct binder_transaction *t;
struct binder_transaction *send_reply = NULL;
int active_transactions = 0;
static const size_t memberOffset = offsetof(binder_thread, wait);
wait_queue_head_t *wqhptr = &thread->wait;
struct list_head *n1,*p1;
wait_queue_head_t *my2;
printk(KERN_INFO "iovec str size:%d",sizeof(iovec));
printk(KERN_INFO "thread->task_list:%p",(void *)&wqhptr->task_list);
list_for_each_safe(p1,n1, &wqhptr->task_list){
my2 = list_entry(p1, wait_queue_head_t, task_list);
printk (KERN_INFO "my2 t= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
}
It should be wait_queue head of the list, so I would assume it should contain entries.... however it shows nothing?
Questions:
How to populate it?
Why it is empty?
FYI The pointer &wqhptr->task_list points to some memory.
Code triggering:
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <unistd.h>
#define BINDER_THREAD_EXIT 0x40046208ul
int main()
{
int fd, epfd;
struct epoll_event event = { .events = EPOLLIN };
fd = open("/dev/binder0", O_RDONLY);
epfd = epoll_create(1000);
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
ioctl(fd, BINDER_THREAD_EXIT, NULL);
}
This is related to this:
https://bugs.chromium.org/p/project-zero/issues/detail?id=1942
Here is Kernel Source I work with (searchable via GitHub):
https://github.com/S3NEO/android_kernel_samsung_s3ve3g/
Update 1:
static void ep_remove_wait_queue(struct eppoll_entry *pwq)
{
wait_queue_head_t *whead;
wait_queue_t *strptr;
struct list_head *n1,*p1;
wait_queue_t *my2;
rcu_read_lock();
/* If it is cleared by POLLFREE, it should be rcu-safe */
whead = rcu_dereference(pwq->whead);
if (whead)
{
strptr=&pwq->wait;
list_for_each_safe(p1,n1, &strptr->task_list){
my2 = list_entry(p1, wait_queue_t, task_list);
printk (KERN_INFO "my2= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
}
This one print entries.
I though thread->wait will be the same as &pwq->wait ..... it is?
Questions:
yes or no?
how to fill thread->wait from Binder to have entries and so that they correspond with &pwq->wait, since that's whe Use After Free should happen....
Thanks,
Update 2:
static void ep_remove_wait_queue(struct eppoll_entry *pwq)
{
wait_queue_head_t *whead;
wait_queue_t *strptr;
struct list_head *n1,*p1;
wait_queue_t *my2;
rcu_read_lock();
/* If it is cleared by POLLFREE, it should be rcu-safe */
whead = rcu_dereference(pwq->whead);
printk(KERN_INFO "whead before");
if (whead)
{
strptr=&pwq->wait;
list_for_each_safe(p1,n1, &pwq->whead){
my2 = list_entry(p1, wait_queue_t, task_list);
printk (KERN_INFO "my2= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
}
Should above print it than?
Thanks,
Update 3:
static void ep_remove_wait_queue(struct eppoll_entry *pwq)
{
wait_queue_head_t *whead;
wait_queue_t *strptr;
struct list_head *n1,*p1;
wait_queue_t *my2;
rcu_read_lock();
/* If it is cleared by POLLFREE, it should be rcu-safe */
whead = rcu_dereference(pwq->whead);
printk(KERN_INFO "whead before");
if (whead)
{
strptr=&pwq->wait;
list_for_each_safe(p1,n1, &pwq->whead->task_list){
my2 = list_entry(p1, wait_queue_t, task_list);
printk (KERN_INFO "my2= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
}
Trying the above ... recompinig...
Update 4:
binder.c
static int binder_free_thread(struct binder_proc *proc,
struct binder_thread *thread)
{
struct binder_transaction *t;
struct binder_transaction *send_reply = NULL;
int active_transactions = 0;
static const size_t memberOffset = offsetof(binder_thread, wait);
wait_queue_head_t *wqhptr = &thread->wait;
wait_queue_head_t *pwqhptr = &proc->wait;
struct list_head *n1,*p1;
wait_queue_t *my2;
printk(KERN_INFO "iovec str size:%d",sizeof(iovec));
printk(KERN_INFO "thread->task_list:%p",(void *)&wqhptr->task_list);
printk(KERN_INFO "proc->task_list:%p",(void *)&pwqhptr->task_list);
list_for_each_safe(p1,n1, &pwqhptr->task_list){
my2 = list_entry(p1, wait_queue_t, task_list);
printk (KERN_INFO "p list= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
}
list_for_each_safe(p1,n1, &wqhptr->task_list){
my2 = list_entry(p1, wait_queue_t, task_list);
printk (KERN_INFO "t list= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
}
eventpoll.c
static void ep_remove_wait_queue(struct eppoll_entry *pwq)
{
wait_queue_head_t *whead;
wait_queue_t *strptr;
struct list_head *n1,*p1;
wait_queue_t *my2;
rcu_read_lock();
/* If it is cleared by POLLFREE, it should be rcu-safe */
whead = rcu_dereference(pwq->whead);
printk(KERN_INFO "whead before");
if (whead)
{
strptr=&pwq->wait;
list_for_each_safe(p1,n1, &pwq->whead->task_list){
my2 = list_entry(p1, wait_queue_t, task_list);
printk (KERN_INFO "my2= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
}
remove_wait_queue(whead, &pwq->wait);
printk(KERN_INFO "remove wait queue:%p", (void*)&pwq->wait);
printk(KERN_INFO "remove wait queue task list:%p", (void*)&strptr->task_list);
I see the list is printed.....but during Android Bootup not my PoC:
During Android start
[ 84.747753] binder_ioctl: 1878:2371 40046208 0
[ 84.747765] iovec str size:8
[ 84.747771] thread->task_list:e4fb2e30
[ 84.747777] proc->task_list:e57d866c
[ 84.747784] p list= e57d866c e7fffe7c
[ 84.747790] p list= e656de7c e57d866c
[ 84.747797] binder_free_thread size:252 worker_off:44
[ 84.747804] freed thread:e4fb2e00
I see proc->task_list ... will free() of 0xe4fb2e00 affect it?
My PoC:
[ 642.254192] wq queue:e7ce8798
[ 642.254201] epoll struct:e7ce8780
[ 642.254214] wq queue:e7ce8f98
[ 642.254220] epoll struct:e7ce8f80
[ 642.254230] wq queue:e7ce8718
[ 642.254236] epoll struct:e7ce8700
[ 642.254266] binder_ioctl: 7392:7392 40046208 0
[ 642.254274] iovec str size:8
[ 642.254280] thread->task_list:e5389b30
[ 642.254286] proc->task_list:c309d86c
[ 642.254292] binder_free_thread size:252 worker_off:44
[ 642.254299] freed thread:e5389b00
[ 642.254736] ep_unregister_pollwait struct:e7ce8780 epi struct:e51d0480
[ 642.254792] ep_unregister_pollwait struct:e7ce8f80 epi struct:e51d0a80
[ 642.254799] ep_unregister_pollwait list not empty
[ 642.254805] whead before
[ 642.254811] my2= c0f50cc4 c0f50cc4
[ 642.254817] remove wait queue:e734b994
[ 642.254823] remove wait queue task list:e734b9a0
[ 642.254830] ep_unregister_pollwait list not empty
[ 642.254835] whead before
[ 642.254841] my2= c0f50cd0 c0f50cd0
[ 642.254847] remove wait queue:e734bb24
[ 642.254852] remove wait queue task list:e734bb30
[ 642.254863] ep_free
[ 642.254873] ep_free
[ 642.254881] ep_free
My PoC:
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#define BINDER_THREAD_EXIT 0x40046208ul
#define BINDER_VERSION 0xc0046209ul
int main()
{
int fd,fd1,fd2, epfd,epfd1,epfd2;
struct epoll_event event = { .events = EPOLLOUT };
fd = open("/dev/binder", O_RDONLY);
fd1 = open("/dev/random", O_RDONLY);
epfd = epoll_create(1000);
epfd1 = epoll_create(1000);
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event)) err(1, "epoll_add");
if (epoll_ctl(epfd1, EPOLL_CTL_ADD, fd1, &event)) err(1, "epoll_add");
//ioctl(fd, BINDER_VERSION, NULL);
ioctl(fd, BINDER_THREAD_EXIT, NULL);
printf("Finished here.");
}
Anybody have idea why?
Update 5:
I narrowed it down ... so I want to replicate behavior of com.cyanogenmod.lockclock
It behaves like I want it to see:
s3ve3g:/ # ps | grep 2140
u0_a50 2140 257 845744 36336 sys_epoll_ b4ed9114 S com.cyanogenmod.lockclock
Source:
https://github.com/LineageOS/android_packages_apps_LockClock
[ 53.617686] binder_ioctl: 2140:2401 40046208 0
[ 53.617697] iovec str size:8
[ 53.617704] thread->task_list:e5b2c030
[ 53.617710] proc->task_list:e609206c
[ 53.617716] p list= e609206c e50c3e7c
[ 53.617722] p list= e50c5e7c e609206c
[ 53.617729] binder_free_thread size:252 worker_off:44
[ 53.617736] freed thread:e5b2c000
[ 53.617755] ep_unregister_pollwait struct:e5f5c680 epi struct:e5f4c280
[ 53.617762] ep_unregister_pollwait list not empty
[ 53.617768] whead before
[ 53.617773] my2= e8b10308 e8b10308
[ 53.617779] remove wait queue:e5fd755c
[ 53.617785] remove wait queue task list:e5fd7568
[ 53.617803] ep_free
I think Binder is used here:
https://github.com/LineageOS/android_packages_apps_LockClock/blob/5239d22272aa2b7a2bcf2c45482395da3e163289/src/org/lineageos/lockclock/DeviceStatusService.java
Any idea how to replicate this using C (native) code?

request_mem_region fails on Raspberry Pi

The call to request_mem_region is failing (returns null).
I would say that the memory region I'm trying to access (GPIO starting at 0x3f20000) is being used.
I removed (rmmod) the module bcm28795_gpio but the request is still failing.
The modules I have loaded are (lsmod):
Module Size Used by
cfg80211 427817 0
rfkill 16018 1 cfg80211
snd_bcm2835 20511 0
snd_pcm 75890 1 snd_bcm2835
snd_timer 19160 1 snd_pcm
snd 51908 3 snd_bcm2835,snd_timer,snd_pcm
bcm2835_wdt 3225 0
uio_pdrv_genirq 3164 0
uio 8000 1 uio_pdrv_genirq
i2c_dev 5859 0
ipv6 347473 30`
cat /proc/iomem is returning the following:
00000000-3affffff : System RAM
00008000-007e7483 : Kernel code
00860000-0098e1ab : Kernel data
3f006000-3f006fff : dwc_otg
3f007000-3f007eff : /soc/dma#7e007000
3f00b840-3f00b84e : /soc/vchiq
3f00b880-3f00b8bf : /soc/mailbox#7e00b800
3f200000-3f2000b3 : /soc/gpio#7e200000
3f201000-3f201fff : /soc/uart#7e201000
3f201000-3f201fff : /soc/uart#7e201000
3f202000-3f2020ff : /soc/sdhost#7e202000
3f980000-3f98ffff : dwc_otg`
I think this issue is related to the Device Tree, but I'm not sure what do next.
The driver code:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/of.h>
#include <linux/of_address.h>
#define DRIVER_NAME "tsgpio"
struct tsgpio_dev {
struct resource res;
void __iomem *virtbase;
} dev;
static const struct file_operations tsgpio_fops = {
.owner = THIS_MODULE,
};
static struct miscdevice tsgpio_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = DRIVER_NAME,
.fops = &tsgpio_fops,
};
static const struct of_device_id tsgpio_of_match[] = {
{ .compatible = "brcm,bcm2835-gpiomem" },
{},
};
MODULE_DEVICE_TABLE(of, tsgpio_of_match);
int __init tsgpio_probe(struct platform_device *pdev)
{
int ret;
ret = misc_register(&tsgpio_misc_device);
if (ret)
return ENODEV;
// Find address range in device tree
ret = of_address_to_resource(pdev->dev.of_node, 0, &dev.res);
if (ret) {
ret = ENOENT;
goto out_deregister;
}
// Request access to memory
if (request_mem_region(dev.res.start, resource_size(&dev.res),
DRIVER_NAME) == NULL) {
ret = EBUSY;
goto out_deregister;
}
/* Arrange access to our registers (calls ioremap) */
dev.virtbase = of_iomap(pdev->dev.of_node, 0);
if (dev.virtbase == NULL) {
ret = ENOMEM;
goto out_release_mem_region;
}
return 0;
out_release_mem_region:
release_mem_region(dev.res.start, resource_size(&dev.res));
out_deregister:
misc_deregister(&tsgpio_misc_device);
return ret;
}
int tsgpio_remove(struct platform_device *pdev)
{
iounmap(dev.virtbase);
release_mem_region(dev.res.start, resource_size(&dev.res));
misc_deregister(&tsgpio_misc_device);
return 0;
}
static struct platform_driver tsgpio_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(tsgpio_of_match),
},
.remove = __exit_p(tsgpio_remove),
};
static int __init tsgpio_init(void)
{
return platform_driver_probe(&tsgpio_driver, tsgpio_probe);
}
static void __exit tsgpio_exit(void)
{
platform_driver_unregister(&tsgpio_driver);
}
module_init(tsgpio_init);
module_exit(tsgpio_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Emanuel Oliveira");
Output from dmesg:
[ 1745.126636] tsgpio: init
[ 1745.130025] tsgpio: probe of 3f200000.gpiomem failed with error 16
Thank you!

How to safely exit from a kthread, that executes a function periodically, on removing a module?

I am starting a kthread, which will execute a function periodically, from a loadable kernel module.
int start_fwd_filter(struct bloom_filter *bflt)
{
fwd_bflt_thread =
kthread_run((void *)timed_fwd_filter, (void *)bflt, "fwd_bflt");
if(fwd_bflt_thread == NULL)
return -1;
return 0;
}
int timed_fwd_filter(void* data)
{
struct bloom_filter *bflt = (struct bloom_filter *)data;
allow_signal(SIGKILL|SIGSTOP);
while(1)
{
__set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(delay*HZ);
if(kthread_should_stop())
{
pr_info(" *** mtp | 1.timed_fwd_filter thread"
" stopped | timed_fwd_filter *** \n");
timed_fwd_filter_stopped = 1;
return 0;
}
if(signal_pending(current))
goto exit_timed_fwd_filter;
if(tcp_client_fwd_filter(bflt) < 0)
{
pr_info(" *** mtp | tcp_client_fwd_filter 2 attmepts "
"failed | timed_fwd_filter *** \n");
}
if(kthread_should_stop())
{
pr_info(" *** mtp | 2.timed_fwd_filter thread"
" stopped | timed_fwd_filter *** \n");
timed_fwd_filter_stopped = 1;
return 0;
}
if(signal_pending(current))
{
goto exit_timed_fwd_filter;
}
}
exit_timed_fwd_filter:
timed_fwd_filter_stopped = 1;
do_exit(0);
}
And when the module is unloaded, I am issuing a call to kthread_stop() to stop this thread:
static void __exit network_server_exit(void)
{
int ret;
int id;
if(fwd_bflt_thread != NULL)
{
if(!timed_fwd_filter_stopped)
{
ret = kthread_stop(fwd_bflt_thread);
if(!ret)
pr_info(" *** mtp | timed forward filter thread"
" stopped | network_server_exit *** \n");
}
}
...
}
But I am unable to remove module as rmmod is getting hung:
[ 599.823825] INFO: task rmmod:2359 blocked for more than 120 seconds.
[ 599.823845] Tainted: G OE 3.16.0-45-generic #60~14.04.1-Ubuntu
[ 599.823863] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[ 599.823882] rmmod D ffff88022f3530c0 0 2359 2358 0x00000004
[ 599.823884] ffff880216eb3da0 0000000000000082 ffff8802209d3d20 ffff880216eb3fd8
[ 599.823885] 00000000000130c0 00000000000130c0 ffff8802239a3d20 ffff8800bb91ff10
[ 599.823887] ffff8800bb91ff18 7fffffffffffffff ffff8802209d3d20 00007fe3a7de9200
[ 599.823888] Call Trace:
[ 599.823893] [<ffffffff8176a2d9>] schedule+0x29/0x70
[ 599.823894] [<ffffffff817696b9>] schedule_timeout+0x229/0x2a0
[ 599.823897] [<ffffffff810c9198>] ? console_unlock+0x1f8/0x440
[ 599.823899] [<ffffffff8176add6>] wait_for_completion+0xa6/0x160
[ 599.823901] [<ffffffff810a1b30>] ? wake_up_state+0x20/0x20
[ 599.823904] [<ffffffff810917da>] kthread_stop+0x4a/0xe0
[ 599.823907] [<ffffffffc052b6b0>] network_server_exit+0x83/0x9d3 [local_tcp]
[ 599.823910] [<ffffffff810ebb92>] SyS_delete_module+0x162/0x200
[ 599.823912] [<ffffffff81013057>] ? do_notify_resume+0x97/0xb0
[ 599.823915] [<ffffffff8176e34d>] system_call_fastpath+0x1a/0x1f
Suspecting that my explicit call to schedule_timeout(), to periodically call tcp_client_fwd_filter(bflt), might be causing a problem as kthread_stop() also eventually calls schedule_timeout() with MAX_SCHEDULE_TIMEOUT to let kthread->exited->done to be true, I removed schedule_timeout() call and put in:
int timed_fwd_filter(void* data)
{
struct bloom_filter *bflt = (struct bloom_filter *)data;
DECLARE_WAIT_QUEUE_HEAD(fwdwait);
allow_signal(SIGKILL|SIGSTOP);
while(1)
{
wait_event_timeout( fwdwait, (kthread_should_stop() == true), delay*HZ)
if(kthread_should_stop())
{
pr_info(" *** mtp | 1.timed_fwd_filter thread"
" stopped | timed_fwd_filter *** \n");
timed_fwd_filter_stopped = 1;
return 0;
}
...
Still I was facing the same problem and also I realized that wait_event_timeout() also calls schedule_timeout(). I think I am doing something wrong here, but unable to figure out what exactly. Can someone help me out with us?
EDIT:
I cleaned up the code and made !kthread_should_stop()check the while loop condition, as it was found in many examples, still no luck.
int timed_fwd_filter(void* data)
{
unsigned long jleft = 0;
struct bloom_filter *bflt = (struct bloom_filter *)data;
allow_signal(SIGKILL|SIGSTOP);
set_current_state(TASK_INTERRUPTIBLE);
while(!kthread_should_stop())
{
jleft = schedule_timeout(delay*HZ);
__set_current_state(TASK_RUNNING);
if(signal_pending(current))
{
goto exit_timed_fwd_filter;
}
if(tcp_client_fwd_filter(bflt) < 0)
{
pr_info(" *** mtp | tcp_client_fwd_filter 2 attmepts "
"failed | timed_fwd_filter *** \n");
}
set_current_state(TASK_INTERRUPTIBLE);
}
__set_current_state(TASK_RUNNING);
exit_timed_fwd_filter:
timed_fwd_filter_stopped = 1;
do_exit(0);
}

Resources