- Linux驱动学习(一、在Ubuntu主机实现)
- 一、需要的文件
- 1、驱动源代码hello.c
- 2、Makefile
- 3、应用程序app.c
- 二、加载驱动
- 三、执行app
Linux驱动学习(一、在Ubuntu主机实现)
Linux驱动学习(二、在Ubuntu主机的QEMU模拟平台实现)
这次学习会遇到很多问题:1、gcc版本不同导致的问题
2、编译平台不同
3、我不知道什么原因的原因
4、这篇教程没写完 希望能给大家提供一点思路,作者是个菜鸟,不知道是在ubuntu上实现还是qemu,如果在qemu上运行,就用交叉编译生成文件,再复制到文件系统中再去insmod。
这篇先简单介绍在ubuntu主机上测试驱动
一、需要的文件 1、驱动源代码hello.cprintk打印的东西不要全复制成一样的了
使用自动创建设备节点的方法,不用每次mknod了
调用了 __register_chrdev(major, 0, 256, name, fops) 函数: 这个函数不只帮我们注册了设备号,还帮我们做了cdev 的初始化以及cdev 的注册;
#include2、Makefile#include #include #include #include #include #include #include #include #include #include int major; static int minor = 0; static int hello_open(struct inode *inode, struct file *file); static ssize_t hello_read(struct file *file, char __user *buf, size_t count, loff_t *ppos); static ssize_t hello_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos); long hello_ioctl(struct file *file, unsigned int cmd, unsigned long arg); static dev_t devno; static struct class *cls; static struct device *test_device; static char test_buff[128]; struct file_operations hello_fileop = { .owner = THIS_MODULE, .open = hello_open, .read = hello_read, .write = hello_write, .unlocked_ioctl = hello_ioctl }; static int hello_open(struct inode *inode, struct file *file) { printk(KERN_alert"|----------------|n"); printk(KERN_alert"| |n"); printk(KERN_alert"| hello_open |n"); printk(KERN_alert"| *OvO* |n"); printk(KERN_alert"| |n"); printk(KERN_alert"|----------------|n"); return 0; } static ssize_t hello_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { strcat(test_buff, " from kernel!"); if (0 != copy_to_user(buf, test_buff, strlen(test_buff))) { printk(KERN_alert"ERROR: read error!"); } printk(KERN_alert"|----------------|n"); printk(KERN_alert"| |n"); printk(KERN_alert"| hello_read |n"); printk(KERN_alert"| *O3O* |n"); printk(KERN_alert"| |n"); printk(KERN_alert"|----------------|n"); return 0; } static ssize_t hello_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { memset(test_buff, 0, sizeof(test_buff)); if (0 != copy_from_user(test_buff, buf, count)) { printk(KERN_alert"ERROR: write error!"); } printk(KERN_alert"|----------------|n"); printk(KERN_alert"| |n"); printk(KERN_alert"| hello_write |n"); printk(KERN_alert"| *OxO* |n"); printk(KERN_alert"| |n"); printk(KERN_alert"|----------------|n"); return 0; } long hello_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { if (cmd == 1) { printk(KERN_alert"|----------------|n"); printk(KERN_alert"| |n"); printk(KERN_alert"| hello_ioctl |n"); printk(KERN_alert"| CMD 1 |n"); printk(KERN_alert"| success |n"); printk(KERN_alert"|----------------|n"); return 0; } if (cmd == 4) { printk(KERN_alert"|----------------|n"); printk(KERN_alert"| |n"); printk(KERN_alert"| hello_ioctl |n"); printk(KERN_alert"| CMD 2 |n"); printk(KERN_alert"| success |n"); printk(KERN_alert"|----------------|n"); return 0; } if (cmd == 3) { printk(KERN_alert"|----------------|n"); printk(KERN_alert"| |n"); printk(KERN_alert"| hello_ioctl |n"); printk(KERN_alert"| CMD 3 |n"); printk(KERN_alert"| success |n"); printk(KERN_alert"|----------------|n"); return 0; } printk("hello_ioctl errorn"); return -EFAULT; } static int hello_init(void) { printk(KERN_alert"|----------------|n"); printk(KERN_alert"| |n"); printk(KERN_alert"| hello_init |n"); printk(KERN_alert"| *OvO* |n"); major = register_chrdev(0, "hello", &hello_fileop); devno = MKDEV(major, minor); cls = class_create(THIS_MODULE, "hello_class"); if (IS_ERR(cls)) { unregister_chrdev(major, "hello"); printk(KERN_alert"| IS_ERR(cls) |n"); printk(KERN_alert"|----------------|n"); return -EBUSY; } test_device = device_create(cls, NULL, devno, NULL, "hello"); if (IS_ERR(test_device)) { class_destroy(cls); unregister_chrdev(major, "hello"); printk(KERN_alert"| IS_ERR(device)-|n"); printk(KERN_alert"|----------------|n"); return -EBUSY; } printk(KERN_alert"| steve |n"); printk(KERN_alert"|----------------|n"); return 0; } static void hello_exit(void) { printk(KERN_alert"|----------------|n"); printk(KERN_alert"| |n"); printk(KERN_alert"| hello_exit |n"); printk(KERN_alert"| mO.Om |n"); printk(KERN_alert"| |n"); printk(KERN_alert"|----------------|n"); device_destroy(cls, devno); class_destroy(cls); unregister_chrdev(major, "hello"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("STEVE");
KERN_DIR = /lib/modules/$(shell uname -r)/build all: make -C $(KERN_DIR) M=`pwd` modules clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order obj-m += hello.o3、应用程序app.c
#include二、加载驱动#include #include #include #include #include #include int main(int argc, char **argv) { int fd; int file; int val = 1; char data[256] = {0}; fd = open("/dev/hello", O_RDWR); //file = open("hellofile", O_RDWR); if (fd == -1) { printf("0-can't open...n"); exit(-1); } else { printf("1-open ok...use 'dmesg | tail -12' to see 12 lines printkn"); int retval; retval = write(fd, "steve", 6); if (retval == -1) { perror("write errorn"); close(fd); exit(-1); } else { printf("2-write ok...use 'vi 文件名' to see see what you writen"); retval = read(fd, data, 10); if (retval == -1) { perror("read errorn"); close(fd); exit(-1); } printf("3-read ok: %sn", data); unsigned int CMD; printf("请输入CMD:"); scanf("%d", &CMD); if(CMD==2){ retval = ioctl(fd, 4, 0); }else{ retval = ioctl(fd, CMD, 0); } if (retval == -1) { perror("ioctl errorn"); close(fd); exit(-1); } printf("4-ioctl CMD%d okn",CMD); close(fd); } } exit(EXIT_SUCCESS); return 0; }
app.c hello.c Makefile是必需的,hellofile只是我加上去的测试文件
先make出hello.ko文件
make
make执行成功,如下图所示
如果是要在上一篇linux教程中的qemu中测试驱动,那就要给make 加上一些参数 比如在makefile中这样写:make -C
(
K
D
I
R
)
M
=
(KDIR) M=
(KDIR)M=(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
insmod命令功能: 用于将指定模块加载到内核中
sudo insmod hello.ko
insmod成功,输出init函数打印的东东
dmesg | tail -6
查看当前有哪些设备
cat /proc/devices
可以看到字符设备已经有hello了,我这自动分配为243号
ls -l /dev/hello
hello.c代码已经帮我们生成好了/dev/hello
在应用层调用ioctl的时候,当传入的cmd=2时会出现错误。
ioctl返回值为-1。错误号errno:14, bad address。
上网查阅才知道需要使用_IOWR等宏来生成cmd命令,而不能自己写。
虽然自己写也可以,但很有可能和系统的其他cmd命令冲突。刚好当ioctl的cmd=2使就出现了错误。但我先用偏方实现了。以后再改
https://mjmwired.net/kernel/documentation/ioctl-number.txt
生成可执行文件
gcc -o app app.c
执行
sudo ./app
查看最近30条记录
dmesg | tail -30
跑路,明天再写
试一下把上面的驱动移植到qemu中,
因为第二次作业做的只是雏形,不适合在工程下使用,可能因为少了很多东西?所以下面遇到一些问题不好解决
把刚才的文件生成arm端的hello.ko 和 app,复制到qemu的文件系统中,发现报错 out-of-tree 原因是 其中,内核配置项:
CONFIG_MODULE_SIG=y
表示开启了签名机制。
CONFIG_MODULE_SIG_FORCE=y
则模块必须有正确的签名才能正常使用。
CONFIG_MODULE_SIG_ALL=y
内核在编译的时候,会主动去给模块签名。
这次init 并没有自动创建/dev/hello 可能是因为busybox配置?
当我给这个系统加读写权限后(mount -o remount rw /),loading out-of-tree 报错又没了。。



