Linux 字符设备驱动例子

Stella981
• 阅读 765

编写好驱动,通过挂载的方法将驱动程序挂载到内核里面,大致步骤如下:

一:  1>建立以.c为后缀的c语言程序文件 (里面包含了设备名及设备号等)
     2>建立Makefile文件(作用是通过make来产生设备文件*.ko文件,里面可以建立自己的平台所需的设备文件如:arm等).make 产生相应的设备文件

二: 要在/dev下建立相应的设备结点(设备名),用insomd *.ko命令将相应的驱动设备文件挂载到内核中.

三:编写测试文件(.c文件)用来测试内核是否已近成功挂载到内核.(编写好相应的测试文件后,用gcc –o Filename Filename.c(测试文件名) 来产生相应的可执行文件).

四:如果设备驱动挂载成功,当执行测试文件(./Filename)时会产生相应的结果.
 
五:可能用到的相关命令:
1.       lsmod:列出内核已经载入模块的专题.
    输出:
    Module(模块名)            size(大小)            used by (被..使用)
2.       demop:分析可加载模块的依赖性,生成modules.dep文件和映射文件
3.       uname –r   显示内核版本(在编写Makefile时使用到)
4.       modprobe : linux 内核添加和删除模块(相关参数请查看man帮助文档)
5.       modinfo:显示内核模块的信息.
6.       insmod: 向linux内核中加载一个模块,用法:insmod  [filename] [module options…]
7.       rmmod: 删除内核中的模块, 用法: rmmod [-f,w,s,v]       [modulename]
8.       dmesg: 显示内核缓冲区,内核的各种信息,内核启动时的信息会写入到/var/log/下.

六.例子

第一步:增加头文件和宏定义

#include <linux/fs.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/module>
#include <linux/kernel>

第二步:添加与字符设备定义及注册有关的数据成员

//定义设备名称
#define DEVICE_NAME "test"        //设备名
#define BUF_SIZE       1024
static char tmpbuf[BUF_SIZE];
 
//定义主次设备号
static unsigned int TestMajor=0;                     //主
static unsigned int TestMinor=0;                     //次
 
 
static struct cdev *test_cdev;
static dev_t dev;

第三步:增加open/release函数

static int test_chardev_open(struct inode *inode,struct file *file)
{
    printk("open major=%d, minor=%d\n", imajor(inode),
                                     iminor(inode));
       return 0;
}

static int test_chardev_release(struct inode *inode,struct file *file)
{
      printk("close major=%d,minor=%d\n",imajor(inode), 
                                      iminor(inode));
       return 0;
}

第四步:增加read函数

static ssize_t test_chardev_read(struct file *file,char __user *buf,
                            size_t const count,loff_t *offset)
{
       if(count < BUF_SIZE)
       {
              if(copy_to_user(buf,tmpbuf,count))
              {
                printk("copy to user fail \n");
               return -EFAULT;
              }
       }else{
              printk("read size must be less than %d\n", BUF_SIZE);
              return -EINVAL;
       }
       *offset += count;
              return count;
}
 

第五步:增加write函数

static ssize_t test_chardev_write(struct file *file, const char __user  *buf,size_t const count,loff_t *offset)
{
       if(count < BUF_SIZE)
       {
              if(copy_from_user(tmpbuf,buf,count))
              {
                printk("copy from user fail \n");
               return -EFAULT;
               }
       }else{
      
              printk("size must be less than %d\n", BUF_SIZE);
              return -EINVAL;
       }           
       *offset += count;
       return count;
}

第六步:添加增加file_operations 成员

static struct file_operations chardev_fops={
       .owner = THIS_MODULE,
       .read = test_chardev_read,
       .write = test_chardev_write,
       .open = test_chardev_open,
       .release = test_chardev_release,
};

第七步:在模块的入口添加设备的设备号获取及设备注册

static int __init chrdev_init(void)
{      
  int result;
 
  if(TestMajor)
  {
        dev=MKDEV(TestMajor,TestMinor);//创建设备编号
        result=register_chrdev_region(dev,1,DEVICE_NAME);
  } else {
        result=alloc_chrdev_region(&dev,TestMinor,1,DEVICE_NAME);
        TestMajor=MAJOR(dev);
   }
   if(result<0)
   {
        printk(KERN_WARNING"LED: cannot get major %d \n",TestMajor);
        return result;
    }
 
  test_cdev=cdev_alloc();
  cdev_init(test_cdev,&chardev_fops);
  //test_cdev->ops=&chardev_fops;
  test_cdev->owner=THIS_MODULE;
  result=cdev_add(test_cdev,dev,1);
  if(result)
        printk("<1>Error %d while register led device!\n",result);
 
   return 0;
}

第八步:在模块的出口函数增加设备设备号释放及设备注销函数

 unregister_chrdev_region(MKDEV(TestMajor,TestMinor),1);
 cdev_del(test_cdev);

第九步:编译并加载该模块

第十步:根据设备号的设置,在文件系统中建立对应的设备节点

$mknod /dev/test  c  XXX(主设备号)  XX(次设备号)

完整例子

驱动文件

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
 
#define DEVICENAME  "ccccc"
 
unsigned int major=221;
unsigned int minor=0;
struct cdev *abc;
dev_t dev;
static char bufrh[1024]="read success!";
 
static int aaaaa_open(struct inode *inodep, struct file *filep)
{
       printk("read success!\n");
      return 0;
}
 
int aaaaa_release(struct inode *inodep, struct file *filep)
{
      return 0;
}
static ssize_t aaaaa_read (struct file *filep, char __user *buf, size_t  count, loff_t *offset)
{
       if(copy_to_user(buf, bufrh, 1))
              {
                     printk("copy_to_user fail!\n");
              }
       return 0;
}
 
ssize_t aaaaa_write (struct file *filep, const char __user *buf,  size_t count, loff_t *offse)
{
       printk("write!\n");
       return 0;
}
 
 
static const struct  file_operations  fops = {
       .owner = THIS_MODULE,
       .open = aaaaa_open,
       .release = aaaaa_release,
       .read = aaaaa_read,
       .write = aaaaa_write,
      
      
};
 
 
 
 
static int __init aaaaa_init(void)
{
       int a;
       dev=MKDEV(major, minor);
       a=register_chrdev_region(dev, 1, DEVICENAME);
        
       abc=cdev_alloc();
       abc->owner=THIS_MODULE;
       cdev_init(abc, &fops);
      
       cdev_add(abc, dev, 1);
 
      
       return 0;
}
 
static void __exit  aaaaa_cleanup(void)
{
       cdev_del(abc);
       unregister_chrdev_region(dev, 1);
}
 
module_init(aaaaa_init);
module_exit(aaaaa_cleanup);
MODULE_LICENSE("GPL ");

Makefile文件

obj-m += firstqd.o(相应设备文件名)
 
KERDIR = /usr/src/linux-headers-2.6.32-24-generic   #x86平台
PWD=$(shell pwd)
 
modules:
       $(MAKE) -C $(KERDIR) M=$(PWD)  modules
 
pc:
       gcc -o fristqd firstqd.c
arm:
       arm-linux-gcc -o fristqd firstqd.c
 
clean:
        rm -rf *.o *~core *.depend *.cmd *.ko *.mod.c *.tmp_versions

用户空间测试代码

#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
 
char buf[1024];
char bufw[1024]="write success";
int main()
{
      int fd,m,n;
      fd=open("/dev/aaa",O_RDWR);
      if (fd)
        {
            m=read(fd,buf,100);
            printf("read kernel:%s\n",buf);
 
            n=write(fd,bufw,10);
         }
      //printf("ni hao");
      return  0;
}
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
3年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这