导航:首页 > 器材知识 > 字符设备驱动注册后如何使用

字符设备驱动注册后如何使用

发布时间:2023-04-15 06:46:21

『壹』 请问Linux驱动程序中,字符设备驱动,块设备驱动以及网络驱动的区别和比较,学的时候需要注意些什么

可以讲字符设备和块设备归为一类,它们都是可以顺序/随机地进行读取和存储的单元,二者驱动主要在于块设备需要具体的burst实现,对访问也有一定的边界要求。其他的没有什么不同。
网络设备是特殊设备的驱动,它负责接收和发送帧数据,可能是物理帧,也可能是ip数据包,这些特性都有网络驱动决定。它并不存在于/dev下面,所以与一般的设备不同。网络设备是一个net_device结构,并通过register_netdev注册到系统里,最后通过ifconfig -a的命令就能看到。
不论是什么设备,设备级的数据传输都是基本类似的,内核里的数据表示只是一部分,更重要的是总线的访问,例如串行spi,i2c,并行dma等。

『贰』 嵌入式开发(七):linux字符型设备驱动初步

姓名:王芷若    学号:19020100180

学院:电子工程学院

【嵌牛导读】:本篇文章整理Linux知识点—Linux字符型设备驱动初步。

【嵌牛鼻子】:Linux设备类型,结构体,驱动模块

【嵌牛提问】:Linux设备有什么类型?关键函数有哪些?

【嵌牛内容】–linux字符型设备驱动初步

一、Linux字符设备驱动初步

1、Linux设备类型

(1)字符设备:只能一个字节一个字节的读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后顺序进行。字符设备是面向流的设备,常见的字符设备如鼠标、键盘、串口、控制台、LED等。

(2)块设备:是指可以从设备的任意位置读取一定长度的数据设备。块设备如硬盘、磁盘、U盘和SD卡等存储设备。

(3)网络设备:网络设备比较特殊,不在是对文件进行操作,而是由专门的网络接口来实现。应用程序不能直接访问网络设备驱动程序。在/dev目录下也没有文件来表示网络设备。

2、开发流程

在这里插入图片描述

3、关键函数讲解(以2.6以下版本内核为例)

(1)驱动模块注册register_chrdev()函数

原型:register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);

major:主设备号,该值为 0 时,自动运行分配。而实际值不是 0 ;

name:设备名称;

fops:操作函数,实现驱动定义的open、read、write、close等内核函数与应用程序调用的open、read、write、close间的映射;

返回值:

major 值为 0 ,正常注册后,返回分配的主设备号。如果分配失败,返回 EBUSY 的负值 ( -EBUSY ) 。major 值若大于 linux/major.h (2.4内核)中声明的最大值 (#define MAX_CHRDEV 255) ,则返回EINVAL 的负值 (-EINVAL) 。指定 major 值后,若有注册的设备,返回 EBUSY 的负值 (-EBUSY)。若正常注册,则返回 0 值

(2)驱动注销unregister_chrdev()函数

原型:

#include <linux.fs.h>

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

变量:

major 主设备号

name 设备文件

返回值:

major 值若大于 linux/major.h (2.4 内核)中声明的最大值 (#define MAX_CHRDEV 255),返回 EINVAL的负值 (-EINVAL)。指定了 major的值后,若将要注销的 major 值并不是注册的设备驱动程序,返回 EINVAL的负值 ( -EINVAL )。正常注销则返回 0值。

(3)File_operation结构体

file_operations结构是建立驱动程序和设备编号的连接,内部是一组函数指针,每个打开的文件,也就是file结构,和一组函数关联,这些操作主要用来实现系统调用的

struct file_operations {

struct mole *owner;//拥有该结构的模块的指针,一般为THIS_MODULES

loff_t (*llseek) (struct file *, loff_t, int);//用来修改文件当前的读写位置

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//从设备中同步读取数据

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//向设备发送数据

ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步的读取操作

ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步的写入操作

int (*readdir) (struct file *, void *, filldir_t);//仅用于读取目录,对于设备文件,该字段为NULL

unsigned int (*poll) (struct file *, struct poll_table_struct *); //轮询函数,判断目前是否可以进行非阻塞的读写或写入

int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); //执行设备I/O控制命令

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); //不使用BLK文件系统,将使用此种函数指针代替ioctl

long (*compat_ioctl) (struct file *, unsigned int, unsigned long); //在64位系统上,32位的ioctl调用将使用此函数指针代替

int (*mmap) (struct file *, struct vm_area_struct *); //用于请求将设备内存映射到进程地址空间

int (*open) (struct inode *, struct file *); //打开

int (*flush) (struct file *, fl_owner_t id);

int (*release) (struct inode *, struct file *); //关闭

int (*fsync) (struct file *, struct dentry *, int datasync); //刷新待处理的数据

int (*aio_fsync) (struct kiocb *, int datasync); //异步刷新待处理的数据

int (*fasync) (int, struct file *, int); //通知设备FASYNC标志发生变化

int (*lock) (struct file *, int, struct file_lock *);

ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);

int (*check_flags)(int);

int (*flock) (struct file *, int, struct file_lock *);

ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);

ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);

int (*setlease)(struct file *, long, struct file_lock **);

};

『叁』 linux字符设备驱动简述-1

Linux系统中,应用程序访问外设是通过文件的形式来进行的,Linux将所有的外设都看做文件,统一存放在/dev目录下。
应用程序使用内核提供的标准系统调用来与内核中的驱动程序进行通讯,这些系统调用有:
open(), read(), write(), ioctl(), close() 等等。

file_operations重要的成员

struct inode 结构代表一个实实在在文件,每个文件只对应一个inode;
struct file 结构代表一个打开的文件,同一个文件可以对应多个file结构;
struct file_operations结构代表底层操作硬件函数的集合

找到 first_drv的主设备号是249,如下图

进阶字符设备写法请看下一篇文章

『肆』 求助字符驱动linux

你这个有点乱哦。。看得头晕得很。。 还有你这里错误的主要地方是。汪孙编写字符设备模块的时候不需要再制定交叉编译器。因为实行makemoles 的时候孝陵衫会找到你内核里面顶层makefile 里面的交叉编译器。

我这里有一个简单的字符设备模块。不妨可以借鉴看一下。需要自己手动添加一个设备节点。我程序里面使用的是主设备号是240 次设备号是0.
原文地址 通过测试:不过还有一点小问题,就是 模块的卸载需要使用rmmod mymod 不加.ko 具体原因我也没弄明白。这里面包含了 字符设备file_operations 函数巧腔。但是用户层的应用需要你自己完成。
http://www.ebhou.com/post/chrcdev.html
#include <linux/init.h>
#include <linux/mole.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
//设备驱动应该包含的头文件
#define MYMODE_SIZE 0x1000
#define MYMODE_MAJOR 240
#define MEM_CLEAR 0x1

static int mymode_major = MYMODE_MAJOR; //设备主节点号

struct mymode_dev{ //定义 设备结构体
struct cdev cdev;
unsigned char mem[MYMODE_SIZE];
};

struct mymode_dev *mymode; //设备结构体指针

static ssize_t mymode_read(struct file * filp,char __user *buf,size_t size,loff_t *ppos) //设备读操作函数
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct mymode_dev *modedev = filp->private_data;
if(p>MYMODE_SIZE)
{
printk(KERN_INFO"err of poss %d\n",p);
return 0;
}

if(count > (MYMODE_SIZE-p))
count = MYMODE_SIZE-p;
if(_to_user(buf,(void *)(modedev->mem+p),count)){
ret = - EFAULT;
}else{
*ppos +=count;
ret = count;
printk(KERN_INFO"read %d bytes from %d\n",count,p);
}
return ret;
}
static ssize_t mymode_write(struct file *filp,const char __user *buf,size_t size,loff_t *ppos) //设备写操作函数
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct mymode_dev *modedev = filp->private_data;

if(count > (MYMODE_SIZE-p))
count = MYMODE_SIZE-p;
if(_from_user((modedev->mem+p),buf,count)){
ret = - EFAULT;
}else{
*ppos +=count;
ret = count;
printk(KERN_INFO"write %d bytes from %d\n",count,p);
}
return ret;
}
static loff_t mymode_llseek(struct file *filep,loff_t offset,int orig) //设置偏移定位操作函数
{
loff_t ret = 0;
switch(orig)
{
case 0:
if(offset < 0 )
{
printk(KERN_INFO"ERR OFFSET\n");
ret = - EINVAL;
break;
}
if(offset >= MYMODE_SIZE)
{
filep->f_pos = MYMODE_SIZE-1;
ret = MYMODE_SIZE-1;
break;
}
filep->f_pos = offset;
ret = offset;

break;
case 1:
if(filep->f_pos + offset > MYMODE_SIZE-1)
{
filep->f_pos = MYMODE_SIZE-1;
ret = MYMODE_SIZE-1;
break;

}else if(filep->f_pos + offset <0){
filep->f_pos = 0;
ret = 0 ;
break;

}
filep->f_pos += offset;
ret = offset;
break;
case 2:
if(filep->f_pos + offset > MYMODE_SIZE-1)
{
filep->f_pos = MYMODE_SIZE-1;
ret = MYMODE_SIZE-1;
break;

}else if(filep->f_pos + offset <0){
filep->f_pos = 0;
ret = 0 ;
break;

}
filep->f_pos += offset;
ret = offset;
break;
default:
break;
}
}
static int mymode_ioctl(struct inode *inodep,struct file *filep,unsigned int cmd ,unsigned long arg) //设备ioctl 函数只简单的实现了对内存区域的清0 当然你也可以自己添加一些你想要实现的功能。不管怎样你得电脑你做主。
{
struct mymode_dev *modedev = filep->private_data;
switch(cmd)
{
case MEM_CLEAR:
memset(modedev->mem,0,MYMODE_SIZE);
printk(KERN_INFO"Clear the mem to be zero\n");
break;
default:
printk(KERN_INFO"Clear the mem to be zero\n");
break;
}
return 0;
}
static int mymode_open(struct inode *inodep,struct file *filep)
{
filep->private_data = mymode;
return 0;
;
}
static int mymode_release(struct inode *inodep,struct file *filep)
{
return 0;
}
static const struct file_operations mymode_fops={ // cdev 设备的file_operations 结构体 主要实现了 开关 读写 设置偏移量 和ioctl 函数
.owner = THIS_MODULE,
.llseek = mymode_llseek,
.read = mymode_read,
.write = mymode_write,
.ioctl = mymode_ioctl,
.open = mymode_open,
.release = mymode_release,
};
int mymode_init(void) //模块加载初始化函数。设备的入口函数
{
int err,res;
dev_t devno = MKDEV(mymode_major,0);
if(mymode_major)
res = register_chrdev_region(&devno,1,"mymode");//register the cdev no
else{
res = alloc_chrdev_region(&devno,0,1,"mymode");
mymode_major = MAJOR(devno);
}
if(res < 0)
return res;
mymode = kmalloc(sizeof(struct mymode_dev),GFP_KERNEL);
if(mymode == NULL)
{
printk(KERN_INFO"kmalloc failed\n");
unregister_chrdev_region(MKDEV(MYMODE_MAJOR,0),1);
return -ENOMEM;
}
memset(mymode,0,sizeof(struct mymode_dev));
cdev_init(&mymode->cdev,&mymode_fops);
mymode->cdev.owner = THIS_MODULE;
err = cdev_add(&mymode->cdev,devno,1);
if(err){
printk("Error %d add mymode ",err);
unregister_chrdev_region(MKDEV(MYMODE_MAJOR,0),1);

}
printk("mymode insert\n");
return 0;
}
void mymode_exit(void)//模块卸载函数 其主要内容是相对于设备初始化函数的反操作。
{
cdev_del(&mymode->cdev);
kfree(mymode);
unregister_chrdev_region(MKDEV(MYMODE_MAJOR,0),1);
printk("mymode exit\n");
}

mole_init(mymode_init);
mole_exit(mymode_exit);

MODULE_AUTHOR("pink hou ");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("First Mole");
MODULE_ALIAS("A simplest mole");

这个是完整的C代码 具体的makefile 参考我的blog借鉴一下吧。这是我通过验证的 绝对没问题。
原文地址:http://www.ebhou.com/post/chrcdev.html

『伍』 求助,linux字符设备驱动开发

一、Linux device driver 的概念系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口.设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程判梁序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作.设备驱动程序是内核的一部分,它完成以下的功能:
1、对设备初始化和释放;
2、把数据从内核传送到硬件和从硬件读取数据;
3、读取应用程序传送给设备文件的数据和回送应用程序请求的数据;
4、检测和处理设备出现的错误.
在Linux操作系统下枯闹有三类主要的设备文件类型,一是字符设备,二是块设备,三是网络设备.字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作.块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待.
已经提到,用户进程是通过设备文件来与实际的硬件打交道.每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备?另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们.设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序.
最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度.也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作.如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck.
二、实例剖析
我们来写一个最简单的字符设备驱动掘败运程序.虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理.把下面的C代码输入机器,你就会获得一个真正的设备驱动程序.
由于用户进程是通过设备文件同硬件打交道,对设备文件的操作方式不外乎就是一些系统调用,如 open,read,write,close…, 注意,不是fopen, fread,但是如何把系统调用和驱动程序关联起来呢?这需要了解一个非常关键的数据结构:
STruct file_operatiONs {
int (*seek) (struct inode * ,struct file *, off_t ,int);
int (*read) (struct inode * ,struct file *, char ,int);
int (*write) (struct inode * ,struct file *, off_t ,int);
int (*readdir) (struct inode * ,struct file *, struct dirent * ,int);
int (*select) (struct inode * ,struct file *, int ,select_table *);
int (*ioctl) (struct inode * ,struct file *, unsined int ,unsigned long);
int (*mmap) (struct inode * ,struct file *, struct vm_area_struct *);
int (*open) (struct inode * ,struct file *);
int (*release) (struct inode * ,struct file *);
int (*fsync) (struct inode * ,struct file *);
int (*fasync) (struct inode * ,struct file *,int);
int (*check_media_change) (struct inode * ,struct file *);
int (*revalidate) (dev_t dev);
}
这个结构的每一个成员的名字都对应着一个系统调用.用户进程利用系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数.这是linux的设备驱动程序工作的基本原理.既然是这样,则编写设备驱动程序的主要工作就是编写子函数,并填充file_operations的各个域.
下面就开始写子程序.
#include

『陆』 字符型设备驱动如何编译

字符设备驱动程序框架

1、写出open、write函数
2、告诉内核
1)、定义一个struct file_operations结构并填充好

static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_mole变量 */
.open = first_drv_open,
.write = first_drv_write,
};
2)、把struct file_operations结构体告诉内核
major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核
相关参数:第一个,设备号,0自动分配主设备号,否则为主设备号0-255
第二个:设备名
第二个:struct file_operations结构体

4)、register_chrdev由谁调用(入口函数调用)
static int first_drv_init(void)

5)、入口函数须使用内核宏来修饰
mole_init(first_drv_init);
mole_init会定义一个结构体,这个结构体里面有一个函数指针指向first_drv_init这个函数,当我们加载或安装一个驱动时,内核会自动找到这个结构体,然后调用里面的函数指针,这个函数指针指向first_drv_init这个函数,first_drv_init这个函数就是把struct file_operations结构体告诉内核

6)、有入口函数就有出口函数

mole_exit(first_drv_exit);
最后加上协议
MODULE_LICENSE("GPL");

3、mdev根据系统信息自动创建设备节点:

每次写驱动都要手动创建设备文件过于麻烦,使用设备管理文件系统则方便闷册虚很多。在2.6的内核以前一直使用的是devfs,但是它存在许多缺陷。它创建了大量的设备文件,其实这些设备更本不存在。而且设备与设备文件的映射具有不确定性,比如U盘即可能对应sda,又可能对应sdb。没有足够的主/辅设备号。2.6之后的内核引入了sysfs文件系统,它挂载在/sys上,配合udev使用,可以很好的完成devfs的功能,并弥补了那些缺点。(这里说一下,当今内核已经使用netlink了)。
udev是用户空间的一个应用程序,在嵌入式中用的是mdev,mdev在busybox中。mdev是udev的精简版。
首先在busybox中添加支持mdev的选项:
Linux System Utilities --->
[*] mdev
[*] Support /etc/mdev.conf
[*] Support subdirs/symlinks
[*] Support regular expressions substitutions when renaming device
[*] Support command execution at device addition/removal
然后修改/etc/init.d/rcS:
echo /sbin/mdev > /proc/sys/kernel/hotplug
/sbin/mdev -s
执行mdev -s :以‘-s’为参数调用位于 /sbin目录写的mdev(其实是个链接,作用是传递参数给/bin目录下的busybox程序并调用它),mdev扫描 /sys/蚂燃class 和 /sys/block 中所有的类设备目录,如果在目录中含有名为“dev”的文件姿氏,且文件中包含的是设备号,则mdev就利用这些信息为这个设备在/dev 下创建设备节点文件。一般只在启动时才执行一次 “mdev -s”。
热插拔事件:由于启动时运行了命 令:echo /sbin/mdev > /proc/sys/kernel/hotplug ,那么当有热插拔事件产生时,内核就会调用位于 /sbin目录的mdev。这时mdev通过环境变量中的 ACTION 和 DEVPATH,来确定此次热插拔事件的动作以及影响了/sys中的那个目录。接着会看看这个目录中是否“dev”的属性文件,如果有就利用这些信息为 这个设备在/dev 下创建设备节点文件
重新打包文件系统,这样/sys目录,/dev目录就有东西了
下面是create_class的原型:

#define class_create(owner, name) /
({ /
static struct lock_class_key __key; /
__class_create(owner, name, &__key); /
})
extern struct class * __must_check __class_create(struct mole *owner,
const char *name,
struct lock_class_key *key);

class_destroy的原型如下:
extern void class_destroy(struct class *cls);
device_create的原型如下:

extern struct device *device_create(struct class *cls, struct device *parent,
dev_t devt, void *drvdata,
const char *fmt, ...)
__attribute__((format(printf, 5, 6)));
device_destroy的原型如下:
extern void device_destroy(struct class *cls, dev_t devt);
具体使用如下,可参考后面的实例:
static struct class *firstdrv_class;
static struct class_device *firstdrv_class_dev;

firstdrv_class = class_create(THIS_MODULE, "firstdrv");
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */

class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);

下面再来看一下应用程序如何找到这个结构体的
在应用程序中我们使用open打开一个设备:如:open(/dev/xxx, O_RDWR);
xxx有一个属性,如字符设备为c,后面为读写权限,还有主设备名、次设备名,我们注册时 通过register_chrdev(0, "first_drv", &first_drv_fops)(有主设备号,设备名,struct file_operations结构体)将first_drv_fops结构体注册到内核数组chrdev中去的,结构体中有open,write函数,那么应用程序如何找到它的,事实上是根据打开的这个文件的属性中的设备类型及主设备号在内核数组chrdev里面找到我们注册的first_drv_fops,
实例代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static struct class *firstdrv_class;
static struct class_device *firstdrv_class_dev;
volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;
static int first_drv_open(struct inode *inode, struct file *file)
{
//printk("first_drv_open\n");
/* 配置GPF4,5,6为输出 */
*gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
*gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
return 0;
}
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val;
//printk("first_drv_write\n");
_from_user(&val, buf, count); // _to_user();
if (val == 1)
{
// 点灯
*gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
}
else
{
// 灭灯
*gpfdat |= (1<<4) | (1<<5) | (1<<6);
}

return 0;
}
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_mole变量 */
.open = first_drv_open,
.write = first_drv_write,
};
int major;
static int first_drv_init(void)
{
major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
gpfdat = gpfcon + 1;
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(major, "first_drv"); // 卸载
class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
iounmap(gpfcon);
}
mole_init(first_drv_init);
mole_exit(first_drv_exit);
MODULE_LICENSE("GPL");

编译用Makefile文件

KERN_DIR = /work/system/linux-2.6.22.6
all:
make -C $(KERN_DIR) M=`pwd` moles
clean:
make -C $(KERN_DIR) M=`pwd` moles clean
rm -rf moles.order
obj-m += first_drv.o

测试程序:

#include
#include
#include
#include
/* firstdrvtest on
* firstdrvtest off
*/
int main(int argc, char **argv)
{
int fd;
int val = 1;
fd = open("/dev/xyz", O_RDWR);
if (fd < 0)
{
printf("can't open!\n");
}
if (argc != 2)
{
printf("Usage :\n");
printf("%s \n", argv[0]);
return 0;
}
if (strcmp(argv[1], "on") == 0)
{
val = 1;
}
else
{
val = 0;
}

write(fd, &val, 4);
return 0;
}

『柒』 arm上linux如何正确的注册字符设备的驱动程序

奇怪,看不出有问题

『捌』 字符设备和块设备

提供连续的数据流,是一个线性设备,应用程序可以顺序读取,通常不支持随机存取。相反,此类设备支持按字节/字符来读写数据。举例来说,调制解调器是典型的字符设备。
如:键盘、鼠标、显示屏

应用程序可以随机访问设备数据,程序可自行确定读取数据的位置。硬盘是典型的块设备,应用程序可以寻址磁盘上的任何位置,并由此读取数据。此外,数据的读写只能以块(通常是512B)的倍数进行。与字符设备不同,块设备并不支持基于字符的寻址。
如:硬盘、U盘
两种设备本身并没用严格的区分,主要是字符设备和块设备驱动程序提供的访问接口(file I/O API)是不一样的

Linux的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/dev目录下,称为设备文件。应用程序可以打开、关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样。为了管理这些设备,系统为设备编了号,每个设备号又分为主设备号和次设备号。 主设备号用来区分不同种类的设备,而次设备号用来区分同一类型的多个设备。 对于常用设备,Linux有约定俗成的编号,如硬盘的主设备号是3。
一个字符设备或者块设备都有一个主设备号和次设备号。主设备号和次设备号统称为设备号。主设备号用来表示一个特定的驱动程序。次设备号用来表示使用该驱动程序的各设备。 例如一个嵌入式系统,有两个LED指示灯,LED灯需要独立的打开或者关闭。那么,可以写一个LED灯的字符设备驱动程序,可以将其主设备号注册成5号设备,次设备号分别为1和2。这里,次设备号就分别表示两个LED灯。

一般的, 主设备号标识出与设备关联的设备驱动 。如 /dev/null 和 /dev/port 由 1 号驱动来管理。

现在的 Linux 内核允许多个驱动共享一个主设备号,但更多的设备都遵循一个驱动对一个主设备号的原则。

内核由 次设备号确定当前所指向的是哪个设备。 根据所编写的驱动程序,可以从内核那里得到一个直接指向设备的指针,或者使用次设备号作为一个设备本地数组的索引。但不论如何,内核自身几乎不知道次设备号的什么事情。

当静态分配设备号时,需要查看系统中已经存在的设备号,从而决定使用哪个新设备号。可以读取/proc/devices文件获得设备的设备号。/proc/devices文件包含字符设备和块设备的设备号,如下所示:

用于创建Linux中的字符设备文件和块设备文件。

『玖』 linux用mknod怎么创建设备怎么用

首先要明白什么是设备文件,简单的我们说,操作系统与外部设备(入磁盘驱动器,打印机,modern,终端 等等)都是通过设备文件来进行通信的,在Unix/Linux系统与外部设备通讯之前,这个设备必须首先要有一个设备文件,设备文件均放在/dev目录下。

一般情况下在安装系统的时候系统自动创建了很多已检测到的设备的设备文件,但有时候我们也需要自己手动创建,命令行生成设备文件的方式有 insf,mksf,mknod等等

根据mknod命令的使用参数来看【mknod Name { b | c } Major Minor 】,使用mknod之前,至少要明白以下几点:

  1. 设备文件类型:分为块设备和字符设备。ls -l /dev 结果显示第一个字段有b*** 和 c****,这里即标识了块设备和字符设备。

  2. 字符设备文件----字符设备文件传送数据给设备的时候,一次传送一个字符,终端,打印机,绘图仪,modern等设备都经过字符设备文件传送数据

  3. 块设备---系统通过块设备文件存取一个设备的时候,先从内存中的buffer中读或写数据,而不是直接传送数据到物理磁盘,这种方式能有效的提高磁盘和CD-ROMS的I/O性能。磁盘和CD-ROMS即可以使用字符设备文件也可使用块设备文件。

  4. 主号和次号

    主号:当在任意目录使用ls -l 时,结果的第5个字段就是主号,设备主号代表了这个设备使用的是哪个设备驱动程序

    次号:次号是一个24位的十六进制数字,定义了设个设备在系统中的物理的位置

就拿我们常用的创建卷组来看:

先来看看mknod 命令,如果该设备文件你想放在一个特定的文件夹下当然就先创建文件夹

mknod 设备文件名[/dev/xyz] b/c 主号 次号

{ mkdir /dev/vg01

mknod /dev/vg01/group c 64 0X010000}

创建之后,就可以使用你想要创建的设备对于德创建命令了,如我现在的卷组的创建命令:

vgcreate /dev/vg01 /dev/dsk/c*t*d*

一直进行下去,之后的步骤根据不同的设备而不尽相同。

『拾』 Linux字符设备驱动的组成

在Linux中,字符设备驱动由如下几个部分组成。
1.字符设备驱动模块加载与卸载函数
在字符设备驱动模块加载函数中应该实现设备号的申请和cdev的注册,而在卸载函数中应实现设备号
的释放和cdev的注销。
Linux内核的编码习惯是为设备定义一个设备相关的结构体,该结构体包含设备所涉及的cdev、私有
数据及锁等信息。2.字符设备驱动的file_operations结构体中的成员函数
file_operations结构体中的成员函数是字符设备驱动与内核虚拟文件系统的接口,是用户空间对Linux
进行系统调用最终的落实者。设备驱动的读函数中,filp是文件结构体指针,buf是用户空间内存的地址,该地址在内核空间不宜直
接读写,count是要读的字节数,f_pos是读的位置相对于文件开头的偏移。
设备驱动的写函数中,filp是文件结构体指针,buf是用户空间内存的地址,该地址在内核空间不宜直
接读写,count是要写的字节数,f_pos是写的位置相对于文件开头的偏移。
由于用户空间不能直接访问内核空间的内存,因此借助了函数_from_user()完成用户空间缓冲
区到内核空间的复制,以及_to_user()完成内核空间到用户空间缓冲区的复制,见代码第6行和第14
行。
完成内核空间和用户空间内存复制的_from_user()和_to_user()的原型分别为:
unsigned long _from_user(void *to, const void _ _user *from, unsigned long count);
unsigned long _to_user(void _ _user *to, const void *from, unsigned long count);
上述函数均返回不能被复制的字节数,因此,如果完全复制成功,返回值为0。如果复制失败,则返
回负值。如果要复制的内存是简单类型,如char、int、long等,则可以使用简单的put_user()和
get_user()读和写函数中的_user是一个宏,表明其后的指针指向用户空间,实际上更多地充当了代码自注释的
功能。内核空间虽然可以访问用户空间的缓冲区,但是在访问之前,一般需要先检查其合法性,通过
access_ok(type,addr,size)进行判断,以确定传入的缓冲区的确属于用户空间。

阅读全文

与字符设备驱动注册后如何使用相关的资料

热点内容
东莞市确亮五金制品有限公司 浏览:547
上海丰合五金制品有限公司 浏览:201
装置艺术造梦派设计 浏览:5
so2的实验室制法的净化装置 浏览:430
北京o型圈视觉检测设备哪里有 浏览:126
校园广播设备怎么连接 浏览:960
中央空调的制冷模式是什么标识 浏览:696
液压马达铭牌轴承怎么看 浏览:753
什么是安全瓶仪器 浏览:246
用单摆法测量重力加速度实验装置 浏览:566
除尘器设备需要多少钱 浏览:236
东莞市健益五金制品 浏览:507
专用红木家具打磨电动工具 浏览:769
冰柜压缩机怎么用才能制冷 浏览:642
定期检查消防器材的什么 浏览:335
cad管道球阀门图库 浏览:456
山东断桥铝设备价格哪个品牌好 浏览:719
刚毕业机械行业多少工资合适 浏览:399
理光办公设备里面待遇怎么样 浏览:371
沼气装置设计图 浏览:966