dev_fifo.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#define MAJOR_NUM 250
struct MycDev{
int len;
unsigned char buff[50];
struct cdev cdev;
};
static dev_t g_pDevNum = {0};
struct MycDev *g_pcDev;
struct class *g_pCla;
//register device num
static int m_Ndevices = 3;
module_param(m_Ndevices, int, 0644);
MODULE_PARM_DESC(m_Ndevices, "The number of devices for register.\n");
static int mydev_fifo_open(struct inode *inode, struct file *file);
static ssize_t mydev_llseek(struct file *file, loff_t offset, int whence);
static ssize_t mydev_fifo_read(struct file *file, char __user *ubuf, size_t size, loff_t *ppos);
static ssize_t mydev_fifo_write(struct file *file, const char __user *ubuf, size_t size, loff_t *ppos);
long mydev_fifo_unlocked_ioctl(struct file *file, unsigned int cmd,unsigned long arg);
int __init MyDev_Fifo_Init(void);
void __exit MyDev_Fifo_Exit(void);
//打开设备
static int mydev_fifo_open(struct inode *inode, struct file *file)
{
printk("dev_fifo_open success!\n");
struct MycDev *cdev ;
cdev = container_of(inode->i_cdev, struct MycDev, cdev);
file->private_data = cdev;
return 0;
}
static ssize_t mydev_llseek(struct file *file, loff_t offset, int whence)
{
long newpos;
switch(whence) {
case 0:
newpos = offset;
break;
case 1:
newpos = file->f_pos + offset;
break;
case 2:
newpos = 50 -1 + offset;
break;
default:
return -EINVAL;
}
if ((newpos<0) || (newpos> 50))
return -EINVAL;
file->f_pos = newpos;
return 0;
}
static ssize_t mydev_fifo_read(struct file *file, char __user *ubuf, size_t size, loff_t *ppos)
{
char *kbuf = NULL;
int len = 0, ret = 0;
struct MycDev *mycdv = file->private_data;
printk("read: *ppos %lld\n", *ppos);
// 读到末尾
if(*ppos == mycdv->len){
return 0;
}
//请求大大小 > buffer剩余的字节数 :读取实际记得字节数
if(size > mycdv->len - *ppos){
len = mycdv->len - *ppos;
}
else{
len = size;
}
//从上一次文件位置指针的位置开始读取数据
kbuf = mycdv->buff + *ppos;
// 拷贝数据到用户空间
ret = copy_to_user(ubuf, kbuf, len);
if(ret != 0){
return -EFAULT;
}else{
printk("read ok\n");
}
//更新文件位置指针的值
*ppos +=len;
printk("read data success \n");
return len;
}
static ssize_t mydev_fifo_write(struct file *file, const char __user *ubuf, size_t size, loff_t *ppos)
{
int len = 0;
char *writepos = NULL;
struct MycDev *mycdv = file->private_data;
printk("write: ppos: %lld ubuf: %s \n", *ppos, ubuf);
if(*ppos == sizeof(mycdv->buff) || NULL == ubuf){
return -1;
}
if(size > sizeof(mycdv->buff) - *ppos){
len = sizeof(mycdv->buff) - *ppos;
}
else{
len = size;
}
//从上一次文件位置指针的位置开始写入数据
writepos = mycdv->buff + *ppos;
//拷贝数据到内核空间
int ret = copy_from_user(writepos, ubuf, len);
if(ret != 0){
return -EFAULT;
}
//更新文件位置指针的值
*ppos +=len;
//更新dev_fifo.len
mycdv->len +=len;
printk("write data success \n");
return len;
}
long mydev_fifo_unlocked_ioctl(struct file *file, unsigned int cmd,unsigned long arg)
{
return 0;
}
//设备操作函数接口
static const struct file_operations fifo_operations = {
.owner = THIS_MODULE,
.open = mydev_fifo_open,
.llseek = mydev_llseek,
.read = mydev_fifo_read,
.write = mydev_fifo_write,
.unlocked_ioctl = mydev_fifo_unlocked_ioctl,
};
int __init MyDev_Fifo_Init(void)
{
printk("\n\n************************************syw*********************************\n\n");
int ret;
int i =0;
int j = 0;
struct device *device;
g_pcDev = kzalloc(m_Ndevices * sizeof(struct MycDev), GFP_KERNEL);
if(!g_pcDev){
return -ENOMEM;
printk(KERN_NOTICE "Error add cdevdemo");
}
//设备号 : 主设备号(12bit) | 次设备号(20bit)
g_pDevNum = MKDEV(MAJOR_NUM, 0);
//静态注册设备号
ret = register_chrdev_region(g_pDevNum, m_Ndevices, "dev_fifo");
if(ret < 0){
//静态注册失败,进行动态注册设备号
ret = alloc_chrdev_region(&g_pDevNum,0,m_Ndevices,"dev_fifo");
if(ret < 0){
printk("Fail to register_chrdev_region\n");
goto err_register_chrdev_region;
}
}
//创建设备类
g_pCla = class_create(THIS_MODULE, "dev_fifo");
if(IS_ERR(g_pCla)){
ret = PTR_ERR(g_pCla);
goto err_class_create;
}
for(i=0; i < m_Ndevices; i++){
//init 字符设备
cdev_init(&g_pcDev[i].cdev, &fifo_operations);
//添加设备到操作系统
ret = cdev_add(&g_pcDev[i].cdev,g_pDevNum + i,1);
if (ret < 0){
goto err_cdev_add;
}
//导出设备信息到用户空间(/sys/class/类名/设备名)
device = device_create(g_pCla,NULL,g_pDevNum + i,NULL,"dev_fifo%d",i);
if(IS_ERR(device)){
ret = PTR_ERR(device);
printk("Fail to device_create\n");
goto err_device_create;
}
}
printk("Register dev_fito to system,ok!\n");
return 0;
err_register_chrdev_region:
return ret;
err_class_create:
unregister_chrdev_region(g_pDevNum, m_Ndevices);
err_cdev_add:
//将已经添加的全部除去
for(j=0; j < i; j++){
cdev_del(&g_pcDev[j].cdev);
}
err_device_create:
//将已经导出的设备信息除去
for(j = 0;j < i; j++){
device_destroy(g_pCla,g_pDevNum + j);
}
}
void __exit MyDev_Fifo_Exit(void)
{
int i;
//删除sysfs文件系统中的设备
for(i = 0;i < m_Ndevices;i ++){
device_destroy(g_pCla,g_pDevNum + i);
}
//删除系统中的设备类
class_destroy(g_pCla);
//从系统中删除添加的字符设备
for(i = 0;i < m_Ndevices;i ++){
cdev_del(&g_pcDev[i].cdev);
}
//释放申请的设备号
unregister_chrdev_region(g_pDevNum, m_Ndevices);
return;
}
MODULE_LICENSE("GPL");
module_init(MyDev_Fifo_Init);
module_exit(MyDev_Fifo_Exit);
Makefile
ifeq ($(KERNELRELEASE),)
KERNEL_DIR ?=/lib/modules/$(shell uname -r)/build
PWD :=$(shell pwd)
modules:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
.PHONY:modules clean
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean
else
obj-m := dev_fifo.o
endif
main.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, const char *argv[])
{
int fd = 0;
int n;
char buf[1024] = "hello word";
char tmp[1024] = {0};
fd = open("/dev/dev_fifo0",O_RDWR);
if(fd < 0){
perror("Fail ot open");
return -1;
}
printf("open successful ,fd = %d\n",fd);
n = write(fd,buf,strlen(buf));
if(n < 0){
perror("Fail to write");
return -1;
}
printf("write %d bytes!\n",n);
n = write(fd,buf,strlen(buf));
if(n < 0){
perror("Fail to write");
return -1;
}
printf("write %d bytes!\n",n);
n = write(fd,buf,strlen(buf));
if(n < 0){
perror("Fail to write");
return -1;
}
printf("write %d bytes!\n",n);
/************************************************/
lseek(fd, 0, SEEK_SET);
n = read(fd, tmp, 30);
printf("read: %s\n", tmp);
return 0;
}
测试结果:
yw@yw-VirtualBox:~/tmp/demo/charDrvice/demo2$ sudo ./a.out
open successful ,fd = 3
write 10 bytes!
write 10 bytes!
write 10 bytes!
read: hello wordhello wordhello word