侵权投诉
搜索
更多>> 热门搜索:
订阅
纠错
加入自媒体

Linux字符设备架构是如何实现的?

2020-12-18 14:12
一口Linux
关注

   //从上一次文件位置指针的位置开始写入数据
   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



<上一页  1  2  3  4  
声明: 本文由入驻维科号的作者撰写,观点仅代表作者本人,不代表OFweek立场。如有侵权或其他问题,请联系举报。

发表评论

0条评论,0人参与

请输入评论内容...

请输入评论/评论长度6~500个字

您提交的评论过于频繁,请输入验证码继续

暂无评论

暂无评论

工控 猎头职位 更多
文章纠错
x
*文字标题:
*纠错内容:
联系邮箱:
*验 证 码:

粤公网安备 44030502002758号