📌一、分析

什么是misc设备
[[misc设备]]


📌二、实现

1. 添加驱动代码

只是简单的验证功能,没有处理越界及加锁
`kernel/drivers/hello/hello.c`
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/uaccess.h>

#define DEVICE_NAME "hello"
static char my_data[100] = "Hello, this is my_misc_device!\n";

static ssize_t my_read(struct file *file, char __user *buf, size_t count,
		       loff_t *ppos)
{
	if (copy_to_user(buf, my_data, count)) {
		return -EFAULT;
	}

	return count;
}

static ssize_t my_write(struct file *file, const char __user *buf, size_t count,
			loff_t *ppos)
{
	if (copy_from_user(my_data, buf, count)) {
		return -EFAULT;
	}

	return count;
}

static const struct file_operations my_fops = {
	.owner = THIS_MODULE,
	.read = my_read,
	.write = my_write,
};

static struct miscdevice my_misc_device = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &my_fops,
};

static int __init my_init(void)
{
	int ret = misc_register(&my_misc_device);
	if (ret) {
		pr_err("Failed to register misc device\n");
		return ret;
	}

	return 0;
}

static void __exit my_exit(void)
{
	misc_deregister(&my_misc_device);
}

module_init(my_init);
module_exit(my_exit);

MODULE_DESCRIPTION("hello misc device");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lucius");

2. 修改Kconfig和Makfile

3. 编译后添加模块

将生成的hello.ko,推送到开发板后insmod,可以查看到设备节点`/dev/hello`

📌三、测试

1. NDK

test-misc_hello.c

#include <stdio.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>

int main(int argc, char *argv[])
{
    char *tTempBuff = (char *)malloc(100);
    int tTempFd = -1;

    tTempBuff[99] = '\0';

    if (argc < 2)
    {
        return 0;
    }

    tTempFd = open("/dev/hello", O_RDWR);
    if (tTempFd < 0)
    {
        printf("open /dev/mycdev err\n");
        return -1;
    }

    if (!strcmp("write", argv[1]))
    {
        write(tTempFd, argv[2], strlen(argv[2]));
        printf("write %s to /dev/hello buf\n\n", argv[2]);
    }
    else if (!strcmp("read", argv[1]))
    {
        read(tTempFd, tTempBuff, 99);
        printf("read data form /dev/hello : %s\n\n", tTempBuff);
    }
    else
    {
        printf("please use write or read cmd\n");
    }

    close(tTempFd);

    return 0;
}

编译后推送到开发板验证./test-misc_hello read

2. APP

2.1. jni动态库
test-misc_hello-jni.c

#include <jni.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

static int fd = -1;

JNIEXPORT jint JNICALL Java_com_example_helloservice_HelloNative_openDevice(JNIEnv *env, jclass cls)
{
    if (fd >= 0)
        return fd;
    fd = open("/dev/hello", O_RDWR);
    if (fd < 0)
        return -1;
    return fd;
}

JNIEXPORT jint JNICALL Java_com_example_helloservice_HelloNative_writeDevice(JNIEnv *env, jclass cls, jbyteArray arr)
{
    if (fd < 0)
        return -1;
    jsize len = (*env)->GetArrayLength(env, arr);
    jbyte *buf = (*env)->GetByteArrayElements(env, arr, NULL);
    int w = write(fd, buf, len);
    (*env)->ReleaseByteArrayElements(env, arr, buf, 0);
    return w;
}

JNIEXPORT jbyteArray JNICALL Java_com_example_helloservice_HelloNative_readDevice(JNIEnv *env, jclass cls, jint maxlen)
{
    if (fd < 0)
        return NULL;
    char tmp[256];
    int r = read(fd, tmp, maxlen > 255 ? 255 : maxlen);
    if (r <= 0)
        return NULL;
    jbyteArray out = (*env)->NewByteArray(env, r);
    (*env)->SetByteArrayRegion(env, out, 0, r, (jbyte *)tmp);
    return out;
}

JNIEXPORT void JNICALL Java_com_example_helloservice_HelloNative_closeDevice(JNIEnv *env, jclass cls)
{
    if (fd >= 0)
    {
        close(fd);
        fd = -1;
    }
}

2.2. Android studio工程

  1. HelloNative.java
package com.example.app;

public class HelloNative {
    /* 加载JNI库 */
    static {
        System.loadLibrary("test-misc_hello_jni");
    }
    /* 声明本地方法 */
    public static native int openDevice();
    public static native int writeDevice(byte[] data);
    public static native byte[] readDevice(int maxlen);
    public static native void closeDevice();
}
	
  1. MainActivity.java
package com.example.app;

import static com.example.app.HelloNative.closeDevice;
import static com.example.app.HelloNative.readDevice;
import static com.example.app.HelloNative.writeDevice;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView textView = findViewById(R.id.textViewHello);

        int fd = HelloNative.openDevice();
        if (fd >= 0) {
            textView.setText("Hello misc");
        } else {
            textView.setText("Open /dev/hello failed");
        }

        String msg = "Hello Kernel!";
        byte[] data = msg.getBytes();

        int written = writeDevice(data);
        System.out.println("写入字节数: " + written);

        byte[] readData = readDevice(128);
        if (readData != null) {
            System.out.println("读取到数据: " + new String(readData));
        } else {
            System.out.println("读取失败或无数据");
        }

        closeDevice();
        System.out.println("设备已关闭");
    }
}

📁四、附言

参考文章

rk3566-Android11 从驱动到 app 第一章添加驱动程序_rk3566 安卓11-CSDN博客

代码

https://git.luciusxu.cn/25Oct30-Test-MISC_Hello/25Oct30-Test-MISC_Hello.git