从内核中最简单的驱动程序入手,描述Linux驱动开发,主要文章目录如下(持续更新中):
01 - 第一个内核模块程序
02 - 注册字符设备驱动
03 - open & close 函数的应用
04 - read & write 函数的应用
05 - ioctl 的应用
06 - ioctl LED灯硬件分析
07 - ioctl 控制LED软件实现(寄存器操作)
08 - ioctl 控制LED软件实现(库函数操作)
09 - 注册字符设备的另一种方法(常用)
10 - 一个cdev实现对多个设备的支持
11 - 四个cdev控制四个LED设备
12 - 虚拟串口驱动
13 - I2C驱动
14 - SPI协议及驱动讲解
15 - SPI Linux驱动代码实现
16 - 非阻塞型I/O
17 - 阻塞型I/O
18 - I/O多路复用之 select
19 - I/O多路复用之 poll
20 - I/O多路复用之 epoll
21 - 异步通知
内核层 用户层
————————
| | 《======= write
| |
| FIFO |
| |
| | =======》read
————————
这一功能的实现主要是在驱动中实现一个FIFO,驱动接收用户传来的数据然后将其 放到FIFO,当应用层想要获取数据时,驱动将FIFO中的数据读出,然后复制给应用层。
原 型: DEFINE_KFIFO(fifo, type, size)
功 能: 定义并初始化一个FIFO
@param1: 声明的FIFO数据类型的名称
@param2: FIFO中成员的数据类型,eg: char int ...
@param3: FIFO中元素的数量,元素的个数必须是2的幂
原 型: kfifo_to_user(fifo, to, len, copied)
功 能: 将FIFO中的数据取出,复制到用户空间
@param1: 要使用的FIFO的地址
@param2: 复制数据的位置
@param3: 目标缓冲区的大小
@param4: 指向输出变量的指针,用于存储复制的字节数
@return: -EFAULT/0
原 型: kfifo_from_user(fifo, to, len, copied)
功 能: 将用户空间中的数据取出,复制到FIFO中
@param1: 要使用的FIFO的地址
@param2: 指向要添加的数据的指针
@param3: 要添加的数据的长度
@param4: 指向输出变量的指针,用于存储复制的字节数
@return: -EFAULT/0
原 型: kfifo_is_empty(fifo)
功 能: 判断fifo是否为空
@param1: 要判断的fifo
@return: fifo为空返回真
原 型: kfifo_is_full(fifo)
功 能: 判断fifo是否为满
@param1: 要判断的fifo
@return: fifo为满返回真
#include
#include
#include
#include
#include
#include
#include
#include
struct class *cls = NULL;
struct device *dev = NULL;
struct cdev test_cdev;
/*
原 型 : DEFINE_KFIFO(fifo, type, size)
功 能 : 定义并初始化一个FIFO
@param1: 声明的FIFO数据类型的名称
@param2: FIFO中成员的数据类型,eg: char int ...
@param3: FIFO中元素的数量,元素的个数必须是2的幂
*/
DEFINE_KFIFO(virtual_serial_fifo, char, 32);
int demo_open(struct inode *inode, struct file *filp)
{
printk("%s -- %d.\n", __FUNCTION__, __LINE__);
return 0;
}
int demo_release(struct inode *inode, struct file *filp)
{
printk("%s -- %d.\n", __FUNCTION__, __LINE__);
return 0;
}
/**
* __user : 目的是提醒驱动的开发者,这个内存空间属于用户空间
*
* 原型 : kfifo_to_user(fifo, to, len, copied)
* 功能 : 将FIFO中的数据取出,复制到用户空间
* @param1: 要使用的FIFO的地址
* @param2: 复制数据的位置
* @param3: 目标缓冲区的大小
* @param4: 指向输出变量的指针,用于存储复制的字节数
* @return: -EFAULT/0
*/
static ssize_t demo_read(struct file *filp, char __user *userbuf, size_t size, loff_t *offset)
{
unsigned int copied_num = 0;
unsigned int retval;
printk("%s -- %d.\n", __FUNCTION__, __LINE__);
retval = kfifo_to_user(&virtual_serial_fifo, userbuf, size, &copied_num);
if (retval == -EFAULT)
{
printk("kfifo_to_user failed.\n");
goto err0;
}
printk("copied_num = %d.\n", copied_num);
return copied_num;
err0:
return retval;
}
/**
* __user : 目的是提醒驱动的开发者,这个内存空间属于用户空间
*
* 原型 : kfifo_from_user(fifo, to, len, copied)
* 功能 : 将用户空间中的数据取出,复制到FIFO中
* @param1: 要使用的FIFO的地址
* @param2: 指向要添加的数据的指针
* @param3: 要添加的数据的长度
* @param4: 指向输出变量的指针,用于存储复制的字节数
* @return: -EFAULT/0
*/
static ssize_t demo_write(struct file *filp, const char __user *userbuf, size_t size, loff_t *offset)
{
unsigned int copied_num = 0;
unsigned int retval;
printk("%s -- %d.\n", __FUNCTION__, __LINE__);
retval = kfifo_from_user(&virtual_serial_fifo, userbuf, size, &copied_num);
if (retval == -EFAULT)
{
printk("kfifo_from_user failed.\n");
goto err0;
}
printk("copied_num = %d.\n", copied_num);
return copied_num;
err0:
return retval;
}
const struct file_operations fops = {
.open = demo_open,
.release = demo_release,
.read = demo_read,
.write = demo_write,
};
static int __init demo_init(void)
{
dev_t dev_no = MKDEV(255, 0);
int ret;
printk("%s -- %d.\n", __FUNCTION__, __LINE__);
ret = register_chrdev_region(dev_no, 1, "test_cdev");
if (ret)
{
printk("register_chrdev_region failed\n");
return ret;
}
cdev_init(&test_cdev, &fops);
ret = cdev_add(&test_cdev, dev_no, 1);
if (ret)
{
printk("cdev_add failed\n");
return ret;
}
cls = class_create(THIS_MODULE, "test_cls");
if (cls == NULL)
{
printk("class_create failed\n");
return -1;
}
dev = device_create(cls, NULL, dev_no, NULL, "test_dev");
if (dev == NULL)
{
printk("device_create failed\n");
return -1;
}
return 0;
}
static void __exit demo_exit(void)
{
dev_t dev_no = MKDEV(255, 0);
printk("%s -- %d.\n", __FUNCTION__, __LINE__);
device_destroy(cls, dev_no);
class_destroy(cls);
cdev_del(&test_cdev);
unregister_chrdev_region(dev_no, 1);
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
#include
#include
#include
#include
#include
#include
const char *pathname = "/dev/test_dev";
int main(int argc, const char *argv[])
{
int fd;
char buf[32];
if (argc < 2)
{
printf("please input argv[1]\n");
return -1;
}
fd = open(pathname, O_RDWR, 0666);
if (fd < 0)
{
printf("open failed\n");
return fd;
}
strcpy(buf, argv[1]);
write(fd, buf, sizeof(buf));
read(fd, buf, sizeof(buf));
printf("buf = %s\n", buf);
close(fd);
return 0;
}
KERNELDIR ?= /home/lzj/ti-processor-sdk-linux-am335x-evm-05.02.00.10/board-support/linux-4.14.79/
PWD := $(shell pwd)
all:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERNELDIR) M=$(PWD) modules
arm-linux-gnueabihf-gcc test.c -o app
install:
sudo cp *.ko app /tftpboot
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERNELDIR) M=$(PWD) clean
rm app
clean:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERNELDIR) M=$(PWD) clean
rm app
obj-m += demo.o
root@am335x-evm:~# insmod demo.ko
[ 469.997454] demo_init -- 119.
root@am335x-evm:~# ./app hello world
[ 475.559439] demo_open -- 28.
[ 475.562883] demo_write -- 90.
[ 475.565914] copied_num = 32.
[ 475.568839] demo_read -- 57.
[ 475.571751] copied_num = 32.
buf = hello
[ 475.581090] demo_release -- 35.
root@am335x-evm:~# rmmod demo.ko
[ 479.871196] demo_exit -- 158.
```12-