字符设备驱动开发实验
该实验与正点原子教程一致,先将该实验视频过一遍,然后根据文档进行实验。
一、驱动框架
(1)注册驱动
//include/linux/fs.h
static int __init xxx_init(void){
/* 注册模块 */
int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);
return 0;
}
static void __exit xxx_exit(void){
/* 卸载模块*/
void unregister_chrdev(unsigned int major, const char *name);
}
module_init(xxx_init); //注册模块加载函数
module_exit(xxx_exit); //注册模块卸载函数
(2)操作函数集合
//include/linux/fs.h
/* 文件打开 */
static int xxx_open(struct inode *, struct file *){
}
/* 文件释放 */
static int xxx_release(struct inode *, struct file *){
}
/* 文件读 */
static ssize_t xxx_read(struct file *, char __user *, size_t, loff_t *){
}
/* 文件写 */
static ssize_t xxx_write(struct file *, const char __user *, size_t, loff_t *){
}
/* 文件操作函数集合 */
static struct file_operations xxx_fops = {
.owner = THGIS_MODULE,
.open = xxx_open,
.release = xxx_release,
.read = xxx_read,
.write = xxx_write,
}
(3)数据操作
#include <asm/uaccess.h>
long copy_to_user(void __user *to, const void *from, long n)
long copy_from_user(void *to, const void __user *from, long n)
(4)开源协议
MODULE_LICENSE("GPL");
MODULE_AUTHOR("XXX");
二、Makefile
三、测试程序
1 NAME 简短的指令、数据名称说明;
2 SYNOPSIS 简短的指令下达语法(Syntax)简介
3 Description 较为完整的说明,这部分最好仔细看看;
4 Options 针对SYNOPSIS 部分中,有列举的所有可用的选项和说明;
5 COMMANDS 当这个程序软件在执行的时候,可以在此程序中下达的指令;
6 FILES 这个程序或数据所使用或参考或连结到的某些档案;
7 SEE ALSO 可以参考的,跟这个指令或数据有相关的其他说明;
8 EXAMPLE 一些可以参考的范例;
9 BUGS 是否有相关的臭虫!
open:man 2 open
OPEN(2) Linux Programmer's Manual OPEN(2)
NAME
open, creat - open and possibly create a file or device
SYNOPSIS
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
DESCRIPTION
Given a pathname for a file, open() returns a file descriptor, a small, nonnegative integer for
use in subsequent system calls (read(2), write(2), lseek(2), fcntl(2), etc.). The file
descriptor returned by a successful call will be the lowest-numbered file descriptor not cur‐
rently open for the process.
By default, the new file descriptor is set to remain open across an execve(2) (i.e., the
FD_CLOEXEC file descriptor flag described in fcntl(2) is initially disabled; the O_CLOEXEC
write:man 2 write
WRITE(2) Linux Programmer's Manual WRITE(2)
NAME
write - write to a file descriptor
SYNOPSIS
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
DESCRIPTION
write() writes up to count bytes from the buffer pointed buf to the file referred to by the
file descriptor fd.
The number of bytes written may be less than count if, for example, there is insufficient space
on the underlying physical medium, or the RLIMIT_FSIZE resource limit is encountered (see setr‐
limit(2)), or the call was interrupted by a signal handler after having written less than count
bytes. (See also pipe(7).)
For a seekable file (i.e., one to which lseek(2) may be applied, for example, a regular file)
writing takes place at the current file offset, and the file offset is incremented by the num‐
ber of bytes actually written. If the file was open(2)ed with O_APPEND, the file offset is
first set to the end of the file before writing. The adjustment of the file offset and the
Manual page write(2) line 1 (press h for help or q to quit)
read:man 2 read
READ(2) Linux Programmer's Manual READ(2)
NAME
read - read from a file descriptor
SYNOPSIS
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
DESCRIPTION
read() attempts to read up to count bytes from file descriptor fd into the buffer starting at
buf.
If count is zero, read() returns zero and has no other results. If count is greater than
SSIZE_MAX, the result is unspecified.
RETURN VALUE
On success, the number of bytes read is returned (zero indicates end of file), and the file
position is advanced by this number. It is not an error if this number is smaller than the
number of bytes requested; this may happen for example because fewer bytes are actually avail‐
able right now (maybe because we were close to end-of-file, or because we are reading from a
pipe, or from a terminal), or because read() was interrupted by a signal. On error, -1 is
Manual page read(2) line 1 (press h for help or q to quit)
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
static char usrdata[] = {"usr data!"};
int main(int argc, char *argv[])
{
int fd,retvalue;
char *filename;
char readbuf[100],writebuf[100];
if(argc != 3)
{
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename,O_RDWR);
if(fd < 0){
printf("Can't open file %s\r\n",filename);
return -1;
}
if(atoi(argv[2]) == 1){
retvalue = read(fd,readbuf,50);
if(retvalue < 0){
printf("read file %s failed! \r\n",filename);
}else{
printf("read data:%s\r\n",readbuf);
}
}
if(atoi(argv[2]) == 2){
memcpy(writebuf,usrdata,sizeof(usrdata));
retvalue = write(fd,writebuf,50);
if(retvalue < 0){
printf("write file %s failed!\r\n",filename);
}
}
retvalue = close(fd);
if(retvalue < 0){
printf("Can't close file %s\r\n",filename);
return -1;
}
return 0;
}
四、过程总结
(1)代码按照正点原子例程来敲的,编译的Makefile文件按照酷客开发板例程里敲的,后面再抽空研究Makefile吧,内容有点多,就暂时不深究了。
(2)编译应用程序的时候,电脑里没有arm-linux-gnueabihf-gcc这个命令,直接使用arm-linux-gcc进行编译,编译成功后,一样可以运行。
(3)modprobe这个指令有,但是depmod指令没有,busybox可能需要后期需要重新编译一下,把这个指令加进去,暂时使用insmod和rmmod指令。
(4)卸载的时候,发现卸载不了,最后发现是执行rmmod chrdevbase.ko卸载不了,执行rmmod chrdevbase指令可以卸载,卸载的时候,不加.ko。
(5)代码用vi写的,有点艰难。Linux下的VS Code还没搭建。
附上实验成功的截图:
记录自己的学习过程,2022.04.07