Linux驱动直接读写节点的方法总结

Linux驱动直接读写节点的方法总结


本文主要描述Linux内核在使用过程中需要直接通过命令读写某个变量的值或者执行某个函数的方法,实现用户空间与内核空间的直接交互。
命令输入方式包括串口输入和adb输入。

内核版本平台
kernel-4.19/kernel-5.10RK3399/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 = &lt7911uxc->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 = &lt7911uxc->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 = &lt7911uxc->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);
    ...
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux 内核的 I2C 驱动中,通常会提供设备读写节点,以便用户空间程序可以通过文件系统接口来访问 I2C 设备。这些节点一般位于 `/dev` 目录下,命名规则为 `i2c-X`,其中 X 是 I2C 控制器的编号。 例如,如果系统中有一个名为 `i2c-1` 的 I2C 控制器,那么对应的设备节点为 `/dev/i2c-1`。 在 Android 上层,可以使用 Java 的 `FileInputStream` 和 `FileOutputStream` 类来读写 I2C 设备。需要先打开对应的设备节点,然后通过文件流进行读写操作。 以下是一个简单的读写示例: ```java import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class I2CExample { private static final String I2C_DEVICE = "/dev/i2c-1"; private static final int I2C_ADDRESS = 0x50; public static void main(String[] args) { try { // 打开 I2C 设备节点 FileInputStream input = new FileInputStream(I2C_DEVICE); FileOutputStream output = new FileOutputStream(I2C_DEVICE); // 写入数据 byte[] writeBuffer = { 0x00, 0x01, 0x02 }; output.write(writeBuffer); // 取数据 byte[] readBuffer = new byte[3]; input.read(readBuffer); // 关闭文件流 input.close(); output.close(); System.out.println("Read data: " + toHexString(readBuffer)); } catch (IOException e) { e.printStackTrace(); } } private static String toHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02X ", b)); } return sb.toString(); } } ``` 以上示例中,我们打开了 `/dev/i2c-1` 设备节点,并且使用地址 `0x50` 进行了一次写操作和一次操作。取到的数据会以十六进制字符串的形式输出到控制台。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值