先看一下转载:http://blog.chinaunix.net/u3/110644/showart_2208979.html
|
上面的内容说的很有用。
此前,我按照《linux设备驱动开发详解》的例程学习,发现驱动并不能正常工作。鄙人实在是太菜了,甚至没想过用手工去创建设备节点,所以常常为设备节点的事苦恼。
下面,我结合自己的实践,分享下我的实验成果:
驱动源码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#define MAJOR_NUM 251
/*此处要根据自己的/proc/device/目录下的情况,选一下不重复的主设备号就好*/
#define DEVSIZE 0X1000
MODULE_LICENSE("GPL");
/*定义设备结构体,其中包括分配cdev*/
struct globalvar_dev{
struct cdev cdev;
unsigned char mem[DEVSIZE];
};
struct globalvar_dev dev;
struct class *myclass;
static ssize_t globalvar_read(struct file *filp,char*buf,size_t count,loff_t *ppos)
{
unsigned long p=*ppos;
int ret=0;
/*分析和获取有效的读长度*/
if(p>=DEVSIZE)//要读的字节数太大
return 0;
if(count>DEVSIZE-p)//要求的字节数太大
count=DEVSIZE-p;
if(copy_to_user(buf,(void *)(dev.mem+p),count)){
ret=-EFAULT;
}
else{
*ppos +=count;
ret = count;
printk(KERN_INFO "read %d bytes from %ld\n",count,p);
}
return ret;
}
static ssize_t globalvar_write(struct file *filp,const char*buf,size_t count,loff_t *ppos)
{
unsigned long p = *ppos;
int ret = 0;
/*分析和获取有效的读长度*/
if(p>=DEVSIZE)//要读的字节数太大
return 0;
if(count>DEVSIZE-p)//要求的字节数太大
count=DEVSIZE-p;
if(copy_from_user(dev.mem+p,buf,count))
ret = -EFAULT;
else{
*ppos += count;
ret = count;
printk(KERN_INFO "written %d byte from %ld\n",count,p);
}
return ret;
}
static loff_t globalvar_llseek(struct file *filp,loff_t offset,int orig)
{
loff_t ret;
switch(orig)
{
case 0:
if (offset<0){
ret=-EINVAL;
break;
}
if((unsigned int)offset >DEVSIZE){
ret=-EINVAL;
break;
}
filp->f_pos=(unsigned int)offset;
ret=filp->f_pos;
break;
case 1:
if (filp->f_pos+offset<0){
ret=-EINVAL;
break;
}
if(filp->f_pos+(unsigned int)offset >DEVSIZE){
ret=-EINVAL;
break;
}
filp->f_pos+=(unsigned int)offset;
ret=filp->f_pos;
break;
case 2:
if (DEVSIZE-1+offset<0){
ret=-EINVAL;
break;
}
if(DEVSIZE-1+(unsigned int)offset >DEVSIZE){
ret=-EINVAL;
break;
}
filp->f_pos=DEVSIZE-1+(unsigned int)offset;
ret=filp->f_pos;
break;
default:
ret = -EINVAL;
}
return ret;
}
struct file_operations globalvar_fops=
{
.owner=THIS_MODULE,
.write=globalvar_write,
.read=globalvar_read,
.llseek=globalvar_llseek,
};
static int __init globalvar_init(void)
{
int err;
int result;
//第一步:取得设备号
dev_t devno = MKDEV(MAJOR_NUM,0);/*主,次设备号*/
if(MAJOR_NUM)
/*register_chrdev_region(设备号指针,设备号数目,设备名)*/
result=register_chrdev_region(devno,1,"globalvar");
else
{
/*alloc_chrdev_region(分配到的设备号指针,起始次设备号,
需要分配的设备号数目,设备名称)*/
result = alloc_chrdev_region(&devno,0,1,"globalvar");
//MAJOR_NUM=MAJOR(devno);
}
/*第二步:初始化cdev*/
cdev_init(&dev.cdev,&globalvar_fops);/*建立cdev和file_operations之间的连接*/
dev.cdev.owner=THIS_MODULE;
dev.cdev.ops=&globalvar_fops;
/*第三步:注册字符设备*/
/*cdev_add(cdev结构指针,设备号,设备数目)*/
err=cdev_add(&dev.cdev,devno,1);
if(err)
printk(KERN_NOTICE "Error %d adding globalvar",err);
/*增加对udev的支持*/
myclass = class_create(THIS_MODULE, "globalvar");
device_create(myclass, NULL, MKDEV(MAJOR_NUM, 0), NULL, "globalvar");
printk("globalvar installs success");
return 0;
}
static void __exit globalvar_exit(void)
{
class_destroy(myclass);
device_destroy(myclass,MKDEV(MAJOR_NUM,0));
cdev_del(&dev.cdev);/*注销cdev结构*/
unregister_chrdev_region(MKDEV(MAJOR_NUM,0),1);/*注销设备号*/
printk("globalvar exits success");
}
module_init(globalvar_init);
module_exit(globalvar_exit);
上面引用的文章已经说的很清楚,如果你手工创建节点,就不需要红色字体的内容。加上红色字体内容后,如果你的平台已经有了udev ,那么它就会自动创建设备节点。
测试源码:
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
void main()
{
FILE *fp0 =NULL;
char Buf[4096];
strcpy(Buf,"globalvar is char dev!");
printf("BUF:%s\n",Buf);
fp0=fopen("/dev/globalvar","r+");
if(fp0==NULL){
printf("Open globalvar Error!\n");
return -1;
}
fwrite(Buf,sizeof(Buf),1,fp0);
fseek(fp0,0,SEEK_SET);
strcpy(Buf,"Buf is NULL!");
printf("BUF:%s\n",Buf);
fread(Buf,sizeof(Buf),1,fp0);
printf("BUF: %s\n",Buf);
return 0;
}
以上源码,我已测试通过。只是还有一点不毛病:
看到了吗?本来就是
BUF:glogalvar is char dev!
Read 4096 bytes from
……
我不知道怎么搞的,它们混到一块儿了,不过,这个问题应该和驱动没有关系。如果哪们仁兄知道怎么回事,还请告诉我一下。好了,基本上就这样了。