Linux驱动直接读写节点的方法总结
本文主要描述Linux内核在使用过程中需要直接通过命令读写某个变量的值或者执行某个函数的方法,实现用户空间与内核空间的直接交互。
命令输入方式包括串口输入和adb输入。
内核版本 | 平台 |
---|---|
kernel-4.19/kernel-5.10 | RK3399/RK356X/RK3588 |
1、直接修改某个数据的值
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "debug level (0-3)");
该节点直接生成在/sys/module/xxxx/parameter,例如:
echo 1 > sys/module/lt7911uxc/parameters/debug
2、proc文件系统读写
创建一个proc虚拟文件,应用层通过读写该文件,即可实现与内核的交互。可用于驱动升级固件。例如:
#include <linux/proc_fs.h>
#include <linux/firmware.h>
static ssize_t lt7911uxc_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *pos)
{
u8 data[3]= { 0xff };
const size_t size = sizeof(data);
struct device *dev = &g_lt7911uxc->i2c_client->dev;
dev_info(dev, "++ lt7911uxc_proc_write ++");
if (count > size) //count为输入字符串长度
return -EINVAL;
if (copy_from_user(data, buffer, count)) //用户空间写入数据,echo 1 > /proc/lt7911uxc/fw_update
return -EFAULT;
do {
unsigned long val = simple_strtoul(data, NULL, 10); //字符串转long型
switch(val) {
case 1:
do_fw_upgrade(g_lt7911uxc); //载入固件
break;
default:
dev_err(dev, "unknow value: %lu\n", val);
return -EINVAL;
}
} while(0);
dev_info(dev, "-- lt7911uxc_proc_write end --");
// lt7911d_update_runtime_info(&this_state->sd, false);
// _reset_ic();
return count;
}
static const struct proc_ops proc_fops = {
.proc_write = lt7911uxc_proc_write,
};
void lt7911uxc_init_procfs(struct lt7911uxc *lt7911uxc)
{
struct proc_dir_entry *parent;
struct device *dev = <7911uxc->i2c_client->dev;
g_lt7911uxc = lt7911uxc;
parent = proc_mkdir("lt7911uxc", NULL); //创建虚拟文件
if (!parent) {
dev_err(dev, "lt7911uxc init procfs mkdir fail!");
return;
}
proc_create("fw-upgrade", S_IWUSR | S_IWGRP, parent, &proc_fops); //创建proc
dev_info(dev, "-- lt7911uxc_init_procfs --");
}
static void do_fw_upgrade(struct lt7911uxc *lt7911uxc)
{
struct device *dev = <7911uxc->i2c_client->dev;
const struct firmware *fw_p = 0;
// u8 hdcp_key[286] = {0};
dev_info(dev, "++ do_fw_upgrade ++");
lt7911uxc_reset_ic(lt7911uxc);
// Step1 : Read chip ID to check I2C connection
do {
int nChipID = 0;
u8 bA000, bA001;
WriteI2C_Byte(lt7911uxc, 0xFF, 0xe0);
WriteI2C_Byte(lt7911uxc, 0xEE, 0x01);
WriteI2C_Byte(lt7911uxc, 0xff, 0xe1);
bA000 = HDMI_ReadI2C_Byte(lt7911uxc, 0x00);
bA001 = HDMI_ReadI2C_Byte(lt7911uxc, 0x01);
nChipID = ((bA000 << 8) | bA001);
dev_info(dev, "fw: nChipID: %04x\n", nChipID);
} while(0);
do {
/*读取固件:/vendor/etc/firmware/LT7911UXC_FW.bin */
int res = request_firmware(&fw_p, "LT7911UXC_FW.bin", dev);
dev_info(dev, "request-firmware %d", res);
if (fw_p) {
const u8 *d = fw_p->data;
dev_info(dev, "fw size: %zu", fw_p->size);
dev_info(dev, "fw header: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
d[0], d[1],d[2],d[3],d[4],d[5],d[6],d[7]);
} else {
dev_err(dev, "request firmware fail !");
return;
}
} while(0);
/*Step2 : Initial Settings*/
......
/*Step3: Block Erase*/
......
/*Step4 : Write firmware data into flash*/
......
/*Step5 : Read the data in the flash and compare it with original data*/
......
/*step6 : reset ic*/
......
}
3、创建class/group/ 进行读写
读:cat sys/class/lt7911uxc/lt7911uxc/lp_esc
写:echo 1 2 3 4 > sys/class/lt7911uxc/lt7911uxc/lp_esc
传递进入内核空间的数据是char *buf
-
除了使用sscanf获取用户空间的数据外,还有其他内核已定义的读取函数,如fgets、kstrtoint、kstrtou16、kstrtou8、kstrtob等,参考/lib/kstrtox.c
-
或者simple_strtol() ,simple_strtoul(),strnicmp,strcasecmp等,参考/lib/string.c和/lib/vsprintf.c
-
也可使用memset,memcpy,memmove,memcmp,memscan,strstr,memchr等内存相关操作函数
#include <linux/module.h>
static struct class *lt7911uxc_class;
static void lt7911uxc_unregister_class_device(void *data)
{
struct lt7911uxc *lt7911uxc = data;
struct device *dev = lt7911uxc->classdev;
device_unregister(dev);
}
static ssize_t lp_esc_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
//struct lt7911uxc *lt7911uxc = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "lp_escclk:{%d, %d, %d, %d}\n",
rk3588_dcphy_4K_param.lp_escclk_pol_sel[0],
rk3588_dcphy_4K_param.lp_escclk_pol_sel[1],
rk3588_dcphy_4K_param.lp_escclk_pol_sel[2],
rk3588_dcphy_4K_param.lp_escclk_pol_sel[3]);
}
static ssize_t lp_esc_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct lt7911uxc *lt7911uxc = dev_get_drvdata(dev);
struct device *device = <7911uxc->i2c_client->dev;
int value[4], i = 0;
if (!lt7911uxc)
return -EINVAL;
sscanf(buf, "%d %d %d %d", &value[0], &value[1], &value[2], &value[3]); //用户空间获取数据
for (i = 0; i < 4; i++) {
if (value[i] == 0 || value[i] == 1) {
rk3588_dcphy_4K_param.lp_escclk_pol_sel[i] = value[i];
} else {
dev_err(device, "get input lp_escclk_pol_sel err!\n");
return -EINVAL;
}
}
return count;
}
static DEVICE_ATTR_RW(lp_esc); //设置device属性,读写RW,只读RO,只写WO
static struct attribute *lt7911uxc_attrs[] = {
&dev_attr_lp_esc.attr, //根据需要添加节点
NULL
};
ATTRIBUTE_GROUPS(lt7911uxc);
static int lt7911uxc_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
...
lt7911uxc->classdev = device_create_with_groups(lt7911uxc_class, //创建group、class
dev, MKDEV(0, 0),
lt7911uxc,
lt7911uxc_groups,
"lt7911uxc");
if (IS_ERR(lt7911uxc->classdev))
dev_err(dev, "device creat lt7911uxc class failed!\n");
err = devm_add_action_or_reset(dev, lt7911uxc_unregister_class_device, lt7911uxc);
if (err)
dev_err(dev, "device unregister lt7911uxc class failed!\n");
...
}
4、创建debug file system
读:cat /d/hdmirx/ctrl
#include <linux/seq_file.h>
#include <linux/fs.h>
struct rk_hdmirx_dev {
...
struct dentry *debugfs_dir;
...
};
static int hdmirx_status_show(struct seq_file *s, void *v)
{
struct rk_hdmirx_dev *hdmirx_dev = s->private;
struct v4l2_dv_timings timings = hdmirx_dev->timings;
struct v4l2_bt_timings *bt = &timings.bt;
bool plugin;
u32 htot, vtot, fps;
u32 val;
plugin = tx_5v_power_present(hdmirx_dev);
seq_printf(s, "status: %s\n", plugin ? "plugin" : "plugout");
if (!plugin)
return 0;
}
static int hdmirx_status_open(struct inode *inode, struct file *file)
{
return single_open(file, hdmirx_status_show, inode->i_private);
}
static const struct file_operations hdmirx_status_fops = {
.owner = THIS_MODULE,
.open = hdmirx_status_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void hdmirx_register_debugfs(struct device *dev,
struct rk_hdmirx_dev *hdmirx_dev)
{
hdmirx_dev->debugfs_dir = debugfs_create_dir("hdmirx", NULL); //新建路径 /d/hdmirx/
if (IS_ERR(hdmirx_dev->debugfs_dir))
return;
debugfs_create_file("status", 0600, hdmirx_dev->debugfs_dir, //新建文件夹 /d/hdmirx/ctrl
hdmirx_dev, &hdmirx_status_fops);
}
5、创建misc_dev写入数据
读:cat /sys/class/misc/it66353_dev/it66353_hdmirxsel
写:echo 1 > /sys/class/misc/it66353_dev/it66353_hdmirxsel
#include <linux/miscdevice.h>
#include <linux/fs.h>
static ssize_t it66353_hdmirxsel_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct it66353_dev *it66353 = g_it66353;
dev_info(it66353->dev, "%s: hdmi rx select state: %d\n",
__func__, g_it66353->hdmi_rx_sel);
return sprintf(buf, "%d\n", g_it66353->hdmi_rx_sel);
}
static ssize_t it66353_hdmirxsel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct it66353_dev *it66353 = g_it66353;
u32 hdmirxstate = 0;
int ret;
mutex_lock(&it66353->port_lock);
ret = kstrtouint(buf, 10, &hdmirxstate);
if (!ret && hdmirxstate >= 0 && hdmirxstate <= RX_PORT_COUNT) {
it66353->hdmi_rx_sel = hdmirxstate;
dev_info(it66353->dev, "%s: state: %d\n", __func__, hdmirxstate);
/*
* _rx_set_hpd(hdmirxstate, 0, TERM_FOLLOW_HPD);
* msleep(200);
* _rx_set_hpd(hdmirxstate, 1, TERM_FOLLOW_HPD);
*/
it66353_set_active_port(hdmirxstate);
} else {
dev_info(it66353->dev, "%s: write hdmi_rx_sel failed!!!, hdmirxstate:%d \n",
__func__, hdmirxstate);
}
mutex_unlock(&it66353->port_lock);
return count;
}
static DEVICE_ATTR_RW(it66353_hdmirxsel);
static const struct file_operations it66353_fops = {
.owner = THIS_MODULE,
.read = it66353_read,
.write = it66353_write,
.unlocked_ioctl = it66353_ioctl,
};
static struct miscdevice it66353_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "it66353_dev",
.fops = &it66353_fops,
};
static int probe()
{
...
ret = misc_register(&it66353_miscdev);
if (ret) {
dev_err(it66353->dev,
"it66353 ERROR: could not register it66353 device\n");
return ret;
}
ret = device_create_file(it66353_miscdev.this_device,
&dev_attr_it66353_hdmirxsel);
if (ret) {
dev_err(it66353->dev, "failed to create attr hdmirxsel!\n");
goto err0;
}
err0:
misc_deregister(&it66353_miscdev);
...
}