//从上一次文件位置指针的位置开始写入数据
kbuf = mycd->buffer + *ppos;
//拷贝数据到内核空间
ret = copy_from_user(kbuf, ubuf, n);
if(ret != 0)
return -EFAULT;
//更新文件位置指针的值
*ppos += n;
//更新dev_fifo.len
mycd->len += n;
printk("dev_fifo_write success!");
return n;
}
//linux 内核在2.6以后,已经废弃了ioctl函数指针结构,取而代之的是
long dev_fifo_unlocked_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int ret = 0;
struct mycdev *mycd = file->private_data;
if(_IOC_TYPE(cmd)!=DEV_FIFO_TYPE){
pr_err("cmd %u,bad magic 0x%x/0x%x.",cmd,_IOC_TYPE(cmd),DEV_FIFO_TYPE);
return-ENOTTY;
}
if(_IOC_DIR(cmd)&_IOC_READ)
ret=!access_ok(VERIFY_WRITE,(void __user*)arg,_IOC_SIZE(cmd));
else if( _IOC_DIR(cmd)&_IOC_WRITE )
ret=!access_ok(VERIFY_READ,(void __user*)arg,_IOC_SIZE(cmd));
if(ret){
pr_err("bad access %ld.",ret);
return-EFAULT;
}
switch(cmd)
{
case DEV_FIFO_CLEAN:
printk("CMD:CLEAN");
memset(mycd->buffer, 0, sizeof(mycd->buffer));
break;
case DEV_FIFO_SETVALUE:
printk("CMD:SETVALUE");
mycd->len = arg;
break;
case DEV_FIFO_GETVALUE:
printk("CMD:GETVALUE");
ret = put_user(mycd->len, (int *)arg);
break;
default:
return -EFAULT;
}
return ret;
}
//设备操作函数接口
static const struct file_operations fifo_operations = {
.owner = THIS_MODULE,
.open = dev_fifo_open,
.read = dev_fifo_read,
.write = dev_fifo_write,
.unlocked_ioctl = dev_fifo_unlocked_ioctl,
};
//模块入口
int __init dev_fifo_init(void)
{
int i = 0;
int n = 0;
int ret;
struct device *device;
gcd = kzalloc(ndevices * sizeof(struct mycdev), GFP_KERNEL);
if(!gcd){
return -ENOMEM;
}
//设备号 : 主设备号(12bit) | 次设备号(20bit)
dev_num = MKDEV(MAJOR_NUM, 0);
//静态注册设备号
ret = register_chrdev_region(dev_num,ndevices,"dev_fifo");
if(ret < 0){
//静态注册失败,进行动态注册设备号
ret =alloc_chrdev_region(&dev_num,0,ndevices,"dev_fifo");
if(ret < 0){
printk("Fail to register_chrdev_region");
goto err_register_chrdev_region;
}
}
//创建设备类
cls = class_create(THIS_MODULE, "dev_fifo");
if(IS_ERR(cls)){
ret = PTR_ERR(cls);
goto err_class_create;
}
printk("ndevices : %d",ndevices);
for(n = 0;n < ndevices;n ++)
{
//初始化字符设备
cdev_init(&gcd[n].cdev,&fifo_operations);
//添加设备到操作系统
ret = cdev_add(&gcd[n].cdev,dev_num + n,1);
if (ret < 0)
{
goto err_cdev_add;
}
//导出设备信息到用户空间(/sys/class/类名/设备名)
device = device_create(cls,NULL,dev_num +n,NULL,"dev_fifo%d",n);
if(IS_ERR(device)){
ret = PTR_ERR(device);
printk("Fail to device_create");
goto err_device_create;
}
}
printk("Register dev_fito to system,ok!");
return 0;
err_device_create:
//将已经导出的设备信息除去
for(i = 0;i < n;i ++)
{
device_destroy(cls,dev_num + i);
}
err_cdev_add:
//将已经添加的全部除去
for(i = 0;i < n;i ++)
{
cdev_del(&gcd[i].cdev);
}
err_class_create:
unregister_chrdev_region(dev_num, ndevices);
err_register_chrdev_region:
return ret;
}
void __exit dev_fifo_exit(void)
{
int i;
//删除sysfs文件系统中的设备
for(i = 0;i < ndevices;i ++)
{
device_destroy(cls,dev_num + i);
}
//删除系统中的设备类
class_destroy(cls);
//从系统中删除添加的字符设备
for(i = 0;i < ndevices;i ++)
{
cdev_del(&gcd[i].cdev);
}
//释放申请的设备号
unregister_chrdev_region(dev_num, ndevices);
return;
}
module_init(dev_fifo_init);
module_exit(dev_fifo_exit);
头文件内容:
dev_fifo_head.h
#ifndef _DEV_FIFO_HEAD_H
#define _DEV_FIFO_HEAD_H
#define DEV_FIFO_TYPE 'k'
#define DEV_FIFO_CLEAN _IO(DEV_FIFO_TYPE,0x10)
#define DEV_FIFO_GETVALUE _IOR(DEV_FIFO_TYPE,0x11,int)
#define DEV_FIFO_SETVALUE _IOW(DEV_FIFO_TYPE,0x12,int)
#endif
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
应用程序:
#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 ;
int n;
char buf[1024] = "hello word";
fd = open("/dev/dev_fifo0",O_RDWR);
if(fd < 0){
perror("Fail ot open");
return -1;
}
printf("open successful ,fd = %d",fd);
n = write(fd,buf,strlen(buf));
if(n < 0){
perror("Fail to write");
return -1;
}
printf("write %d bytes!",n);
n = write(fd,buf,strlen(buf));
if(n < 0){
perror("Fail to write");
return -1;
}
printf("write %d bytes!",n);
return 0;
}
测试步骤:
(1) 加载模块
sudo insmod hello.ko
(2) 创建设备节点
sudo mknod /dev/hello c 250 0
如果代码中增加了自动创建设备节点的功能,这个步骤不要执行。
(3) 测试字符设备
gcc test.c -o run
sudo ./run