Chinaunix首页 | 论坛 | 博客
  • 博客访问: 146360
  • 博文数量: 52
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 316
  • 用 户 组: 普通用户
  • 注册时间: 2015-05-19 22:20
文章分类
文章存档

2016年(43)

2015年(9)

我的朋友

分类: Android平台

2016-05-08 12:12:19

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define GLOBALMEM_SIZE 0x1000 //全局内存大小
#define MEM_CLEAR 0x1  //清全局内存
#define GLOBALMEM_MAJOR 250  //预设主设备号




static int globalmem_major = GLOBALMEM_MAJOR;
struct globalmen_dev{
struct cdev cdev;
unsigned char mem[GLOBALMEM_SIZE];//全局内存
};


struct globalmen_dev *dev_p;


static struct class *firstdrv_class;
static struct class_device *firstdrv_class_dev;




static int globalmem_open(struct inode *inode,struct file *filp)
{
filp->private_data = dev_p;
return 0;
}
static int globalmem_release(struct inode *inode,struct file *filp)
{
return 0;


}
static ssize_t globalmem_read(struct file *filp,char __user *buf,size_t count,loff_t *ppos)
{
unsigned long p = *ppos;
int ret = 0;
if(p >= GLOBALMEM_SIZE)
{
return 0;
}
if(count > GLOBALMEM_SIZE -p)
{
count = GLOBALMEM_SIZE -p;
}
struct globalmen_dev *pdev = filp->private_data;
//copy_to_user 数据从内存 空间拷贝到用户空间
if(copy_to_user(buf,(void*)(pdev->mem+p),count))
{
ret = -EFAULT;
}
else
{
*ppos += count;
ret = count;
printk(KERN_INFO "read %ld bytes(s) from %ld\n",(unsigned long)count,p);
}
return ret;
}




static ssize_t globalmem_write(struct file *filp,char __user *buf,size_t count,loff_t *ppos)
{
unsigned long p = *ppos;
int ret = 0;
if(p >= GLOBALMEM_SIZE)
{
return 0;
}
if(count > GLOBALMEM_SIZE - p)
{
count = GLOBALMEM_SIZE - p;
}
struct globalmen_dev *pdev = filp->private_data;

//copy_from_user  数据从用户空间拷贝到内核空间
if(copy_from_user(pdev->mem+p,buf,count))
{
ret = -EFAULT;
}
else
{
*ppos += count;
ret = count;
printk(KERN_INFO "write %ld bytes(s) from %ld\n",(unsigned long)count,p);
}
return ret;
}
static ssize_t globalmem_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 > GLOBALMEM_SIZE)
{
ret = -EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret  = filp->f_pos;
break;
case 1:
if((filp->f_pos+offset) > GLOBALMEM_SIZE)
{
ret = -EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default:
ret  = -EINVAL;

}
return ret; 
}
static int globalmem_ioctl(struct inode *inodep,struct file *filp,unsigned int cmd,unsigned long arg)
{
struct globalmen_dev *pdev = filp->private_data;
switch(cmd)
{
case MEM_CLEAR:
memset(pdev->mem,0,GLOBALMEM_SIZE);
printk(KERN_INFO"globalmem is set to zero\n");
break;
default:
return -EINVAL;
}
return 0;
}




static const struct file_operations globalmem_fops = 
{
.owner    = THIS_MODULE,
.open  = globalmem_open,
.llseek   = globalmem_llseek,
.read     = globalmem_read,
.write    = globalmem_write,
.ioctl  = globalmem_ioctl,
.release  = globalmem_release,
};


static void globalmem_setup_cdev(struct globalmen_dev *devp,int index)
{
int err,devno = MKDEV(globalmem_major,index);
cdev_init(&devp->cdev,&globalmem_fops);
devp->cdev.owner = THIS_MODULE;
err = cdev_add(&devp->cdev,devno,1);
if(err)
{
printk(KERN_NOTICE"Error %d adding golmem",err);
}
}
//globalmem驱动加载函数
int globalmem_init(void)
{
int result;
dev_t devno = MKDEV(globalmem_major,0);
//申请字符设备驱动区域
if(globalmem_major)
{
result = register_chrdev_region(devno,1,"globalmem");
}
else
{
//动态申请主设备号
result = alloc_chrdev_region(&devno,0,1,"globalmem");
globalmem_major = MAJOR(devno);
}
if(result < 0)
{
return result;
}
//动态申请设备结构体的内存
dev_p = kmalloc(sizeof(struct globalmen_dev),GFP_KERNEL);
if(!dev_p)
{
result = -ENOMEM;
goto fail_malloc;
}
globalmem_setup_cdev(dev_p,0);
//自动创建节点
firstdrv_class = class_create(THIS_MODULE, "firstdrv");     //mdev 根据这些信息自动创建
    firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(globalmem_major, 0), NULL, "xyz"); /* /dev/xyz */
return 0;
fail_malloc:
unregister_chrdev_region(devno,1);
return result;
}
//设备驱动卸载函数
void globalmem_exit()
{
cdev_del(&dev_p->cdev);
kfree(dev_p);
unregister_chrdev_region(MKDEV(globalmem_major,0),1);
class_device_unregister(firstdrv_class_dev);
    class_destroy(firstdrv_class);
}
module_init(globalmem_init);
module_exit(globalmem_exit);
MODULE_AUTHOR("chengyouliang2016/5/7");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("a test memuse");
MODULE_ALIAS("a simplest module");
#if 0
/***************************************************************************************************
                         linux字符设备驱动总结之:全自动创建设备及节点
***************************************************************************************************/
概览:
    第一步:注册设备号                                              信息#tail -f /var/log/message
        注册函数:
            register_chrdev_region() 或                             查看#lsmod
            alloc_chrdev_region()    或                             查看#cat /proc/devices
            register_chrdev()
        注销函数:
            unregist_chrdev_region() 或
            unregister_chrdev()
    
    第二步:初始化cdev并添加到系统
        初始化cdev
            静态初始化 cdev_init() 或
            动态初始化 cdev_alloc()
        添加到系统函数
            cdev_add()
        从系统删除函数
            cdev_del()
            
    第三步:创建设备节点
        创建类
            class_create()          将放于/sysfs                    查看#ls /sys/class
        删除类
            class_destroy()
        
        创建节点
            device_create() 或 class_device_create()  将存放于/dev  查看#ls /dev
        删除节点
            device_destroy() 或 class_device_destroy()
    


/***************************************************************************************************
                                   第一步:注册设备号
***************************************************************************************************/
Linux内核中所有已分配的字符设备编号都记录在一个名为 chrdevs 散列表里。
    该散列表中的每一个元素是一个 char_device_struct 结构,它的定义如下:
    static struct char_device_struct
    {
        struct char_device_struct *next;    // 指向散列冲突链表中的下一个元素的指针
        unsigned    int major;              // 主设备号
        unsigned    int baseminor;          // 起始次设备号
        int minorct;                        // 设备编号的范围大小
        char    name[64];                   // 处理该设备编号范围内的设备驱动的名称
        struct file_operations *fops;       // 没有使用
        struct cdev *cdev;                  // 指向字符设备驱动程序描述符的指针
    }*chrdevs[CHRDEV_MAJOR_HASH_SIZE];


    1 每一个主设备有一个会分配一个此结构,可以有多个次设备号。次设备是依次递增的。
    2 内核提供了5个函数来来管理字符设备编号。
    
            register_chrdev_region()        指定初始值
            alloc_chrdev_region()           动态分配
            register_chrdev()               指定设备号
        他们都会调用 __register_chrdev_region() 来注册一组设备编号范围(一个char_device_struct结构),我们使用其中一个即可。
            
            unregist_chrdev_region()        释放都用此函数
            unregister_chrdev()             都调用了 __unregister_chrdev_region() 来注销设备


        注册:
            register_chrdev_region(dev_t first,unsigned int count,char *name)
                first :要分配的设备编号范围的初始值(次设备号常设为0);
                count :连续编号范围.
                Name  :编号相关联的设备名称. (/proc/devices);




            int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);
                *dev        :存放返回的设备号
                firstminor  :第一个次设备号的号数,常为0;


            int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
                major :要注册的设备号, 若为0则自动分配一个
                name  :设备名
                *fops :以后再聊


        释放:
            void unregister_chrdev(unsigned int major, const char *name);
            void unregister_chrdev_region(dev_t from, unsigned count);
    3 示例:略
    4 参考:感谢原著 (有此6个函数的源码及解说)。
        http://blog.csdn.net/iLetLet/article/details/6180314


/***************************************************************************************************
                            第二步:初始化 cdev 并添加到系统
***************************************************************************************************/
1.内核中每个字符设备都对应一个 cdev 结构的变量,定义如下:
    linux-2.6.22/include/linux/cdev.h
    struct cdev 
    {
        struct kobject kobj;                //每个 cdev 都是一个 kobject
        struct module *owner;               //指向实现驱动的模块
        const struct file_operations *ops;  //操纵这个字符设备文件的方法
        struct list_head list;              //与 cdev 对应的字符设备文件的 inode->i_devices 的链表头
        dev_t dev;                          //起始设备编号
        unsigned int count;                 //设备范围号大小
    };


2. 初始化cdev :有两种定义初始化方式:
    
    方式1:静态内存定义初始化:
        struct cdev my_cdev;
        cdev_init(&my_cdev, &fops);
        my_cdev.owner = THIS_MODULE;
        
    方式2:动态内存定义初始化:
        struct cdev *my_cdev = cdev_alloc();
        my_cdev->ops = &fops;
        my_cdev->owner = THIS_MODULE;


3. 添加cdev到系统
    为此可以调用 cdev_add() 函数。传入cdev结构的指针,起始设备编号,以及设备编号范围。
    释放时使用 cdev_del()函数来释放cdev占用的内存。
 
4
/***************************************************************************************************
                            第三步:创建设备节点
***************************************************************************************************/
    方法一:利用mknod命令手动创建设备节点。
    方法二:实际上Linux内核为我们提供了一组函数,可以在模块加载的时候在/dev目录下创建相应设备节点,在卸载时可删除该节点。


    原理:
        1 内核中定义了struct class结构体,它对应一个类。
        2 先调用class_create()函数,可以用它来创建一个类,这个类将存放于sysfs下面.
        3 再调用device_create()函数,从而在/dev目录下创建相应的设备节点。
        4 卸载模块对应的函数是 device_destroy 和 class_destroy()
        注:2.6 以后的版本使用device_create(),之前的版本使用的class_device_create()。


#endif

阅读(1277) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~