博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux字符设备驱动
阅读量:5115 次
发布时间:2019-06-13

本文共 3972 字,大约阅读时间需要 13 分钟。

/*Linux字符设备驱动源代码scdd.c*/

#include <linux/init.h>   /*模块头文件*/

#include <linux/module.h>

#include <linux/types.h>  /*dev_t头文件*/

#include <linux/kdev_t.h> /*MAJOR和MINOR宏头文件*/

#include <linux/fs.h>     /*register_chrdev_region等函数

                                                 file_operations结构体*/

#include <linux/cdev.h>   /*struct cdev结构体*/

#include <asm/uaccess.h>  /*copy_to_user函数*/

 

#define DEVICE_NAME "scdd"  /*定义设备名*/

#define DEVICE_MAJOR 250

 

struct cdev my_cdev;

 

int scdd_open(struct inode *inode,structfile *filp)

{

       return0;

}

int scdd_close(struct inode *inode,structfile *filp)

{

       return0;

}

ssize_t scdd_read(struct file *filp,char__user *buff,size_t size,loff_t *offp)

{

       intleft;

       chardata=1;

      

       for(left=size;left>0;left--)

       {

              /*拷贝数据到用户空间*/

              copy_to_user(buff,&data,1);

              buff++;

       }

       returnsize;

}

ssize_t scdd_write(struct file *filp,char__user *buff,size_t size,loff_t *offp)

{

       return0;

}

/*file_operations结构体*/

struct file_operations scdd_fops={

       .owner=THIS_MODULE,

       .read=scdd_read,

       .write=scdd_write,

       .open=scdd_open,

       .release=scdd_close,

};

static int __init scdd_init(void)

{     /*模块初始化函数*/

       intsmajor;

       smajor=DEVICE_MAJOR;

       dev_tdev_n=MKDEV(smajor,0);

       /*申请设备号*/

       if(!register_chrdev_region(dev_n,1,DEVICE_NAME))

       {     /*静态申请*/

              printk("registersuccess\n");

       }else

       {

              gotoregister_error;

       }

       /*else

       {     /*动态申请*/

              /*alloc_chrdev_region(&dev_n,0,1,DEVICE_NAME);

              smajor=MAJOR(dev_n);

       }*/

      

       /*初始化cdev结构体*/

       cdev_init(&my_cdev,&scdd_fops);

       my_cdev.owner=THIS_MODULE;

       my_cdev.ops=&scdd_fops;

      

       /*注册字符设备*/

       cdev_add(&my_cdev,dev_n,1);

       return0;

      

register_error:

       unregister_chrdev_region(MKDEV(DEVICE_MAJOR,0),1);

       return0;

}

static void __exit scdd_exit(void)

{     /*模块卸载函数*/

       cdev_del(&my_cdev);

       unregister_chrdev_region(MKDEV(DEVICE_MAJOR,0),1);

}

 

module_init(scdd_init);

module_exit(scdd_exit);

MODULE_LICENSE("Dual BSD/GPL");

这个程序只是简单演示字符注册的一个完整过程,并不带有复杂的操作,调用read时向用户空间写全1

要点:

1.设备号,主设备号用来标识设备所对应的驱动程序,同一个驱动程序可以对应多个设备,次设备号就是用来区分采用同一个驱动程序的不同设备文件。

内核中采用dev_t来描述设备号,其实dev_t实质为unsigned int类型,其中高12位为主设备号,低20位为次设备号,其定义在linux/types.h中

从dev_t中分解出主设备号和次设备号

MAJOR(dev_t dev)

MINOR(dev_t dev)

将主设备号和次设备号转换成dev_t类型

MKDEV(intmajor,int minor)

2.申请设备号

静态申请,函数定义在linux/fs.h中

int register_chrdev_region(dev_t from,unsigned count,const char *name)

from是申请设备号起始值,from次设备号经常被置成0,count是所请求连续设备号个数,name是该设备号范围关联的名称,它将出现在/proc/device和sysfs中。

分配成功返回0,错误情况下,将返回一个负的错误码,并且不能使用所请求的设备号。

动态申请

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

申请的设备号将保存在dev中,baseminor为所申请的第一个次设备号

无论哪种方式申请设备号,不使用时要释放设备号

void unregister_chrdev_region(dev_t first,unsigned int count)

3.三个重要的结构

struct file_operations

struct file

struct inode

struct file_operations一个函数指针集合,定义能在设备上进行的操作,常用的有:

int (*open)(structinode *inode,stuuct file *filep)

void (*release)(structinode *inode,struct file *filep)

ssize_t(*read)(struct file *filep,char __user *buff,size_t size,loff_t *offp)

ssize_t(*write)(struct file *filep,char __user *buff,size_t seze,loff_t * offp)

int(*ioctl)(struct inode *inode,struct file *filep,unsigned int cmd,unsigned longarg) off_t(*llseek)(struct file *filp,loff_t,int)

struct file表示一个打开的文件,一个文件被打开十次,则有10个structfile结构

struct file结构成员有:

mode_t f_mode 文件模式,例如:可读可写

lofft_t f_pos 当前读/写位置

struct file_operations *f_op 与文件相关操作,执行open操作时对这个指针赋值

void *private_data在open时置为NULL,用于跨系统调用时保存非常有用的资源

struct inode用来记录文件的物理上的信息,一个文件被打开10次,但只有一个structinode结构,struct inode结构成员如下:

dev_t i_rdev 用来保存设备号

struct cdev*i_cdev 指向字符设备结构struct cdev的指针

4.字符设备的注册

内核使用struct cdev结构表示字符设备,定义在linux/cdev.h中。

初始化cdev结构体有两种方式

静态:

struct cdev my_cdev;

cdev_init(&my_cdev,&fops);

my_cdev.owner = THIS_MODULE;

动态(就是通过kmalloc去申请cdev结构):

struct cdev *my_cdev = cdev_alloc();

my_cdev->ops= &my_fops;

my_cdev.owner= THIS_MODULE;

注册

int cdev_add(struct cdev *dev,dev_t num,unsigned int count)

num是设备号,count经常取1

注销

void cdev_del(struct cdev *dev)

 

早起比较经典的字符设备驱动注册和注销方法

注册

int register_chrdev(unsigned int major,const char *name,struct file_operations*fops)

注销

int unregister_chrdev(unsigned int major,const char *name)

转载于:https://www.cnblogs.com/phonegap/archive/2011/12/09/2536134.html

你可能感兴趣的文章
Android-多线程AsyncTask
查看>>
第一个Spring冲刺周期团队进展报告
查看>>
红黑树 c++ 实现
查看>>
Android 获取网络链接类型
查看>>
linux中启动与终止lnmp的脚本
查看>>
gdb中信号的处理[转]
查看>>
LeetCode【709. 转换成小写字母】
查看>>
如何在Access2007中使用日期类型查询数据
查看>>
Jzoj4757 树上摩托
查看>>
CF992E Nastya and King-Shamans(线段树二分+思维)
查看>>
第一个Java Web程序
查看>>
树状数组_一维
查看>>
如果没有按照正常的先装iis后装.net的顺序,可以使用此命令重新注册一下:
查看>>
linux install ftp server
查看>>
嵌入式软件设计第8次实验报告
查看>>
算法和数据结构(三)
查看>>
Ubuntu下的eclipse安装subclipse遇到没有javahl的问题...(2天解决了)
查看>>
alter database databasename set single_user with rollback IMMEDIATE 不成功问题
查看>>
Repeater + Resources 列表 [原创][分享]
查看>>
WCF揭秘——使用AJAX+WCF服务进行页面开发
查看>>