linux驱动学习-4th
2014-03-13 22:24
169 查看
接下来安装LDD3的惯例写一个global_mem 的字符驱动程序,这次实现目标,分配一个4k大小的内存实现读写操作。
采用class结构体用于mdev(udev)自动创建设备结点。
采用class结构体用于mdev(udev)自动创建设备结点。
/*最简单字符类global_mem驱动模版*/ /*[0].必须的头文件*/ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/kernel.h> /* printk() */ #include <linux/slab.h> /* kmalloc() */ #include <linux/fs.h> /* everything... */ #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/proc_fs.h> #include <linux/fcntl.h> /* O_ACCMODE */ #include <linux/seq_file.h> #include <linux/cdev.h> #include <linux/platform_device.h> #include <asm/system.h> /* cli(), *_flags */ #include <asm/uaccess.h> /* copy_*_user */ /*********************************************************************/ #define __DEBUG__ #ifdef __DEBUG__ #define DEBUG(format,...) printk("[%05d] ""%s:""%s. " format "",__LINE__, __FILE__, __FUNCTION__, ##__VA_ARGS__) #else #define DEBUG(format,...) #endif /*********************************************************************/ /*[1].定义设备名*/ #define DEVICE_NAME "global_mem_dev" /*[2].定义主设备号 0:动态分配 (!0):手动分配找一个kernel没有使用的*/ #define DEVICE_MAJOR 0 /*[3].定义次设备号个数*/ #define MINOR_COUNT 1 /*[4].定义自己的设备global_mem_dev结构体(包含了基本的cdev字符设结构体)*/ struct global_mem_dev { struct cdev cdev; /*c_dev结构体*/ unsigned char mem[4*1024]; }; /*********************************************************************/ /*[5].定义一个全局变量记录主设备号*/ static int major = DEVICE_MAJOR; /*[6].定义起始次设备号*/ static int first_minor = 0; /*[7].定义次设备号个数*/ static int minor_count = MINOR_COUNT; /*[8].定义一个global_mem_dev结构体指针,其实是一个数组*/ struct global_mem_dev *global_mem_dev_array = NULL; /*[9].定义一个class结构体用于mdev自动创建设备结点*/ struct class *global_mem_dev_class; /*********************************************************************/ static int global_mem_dev_open(struct inode *inode, struct file *filep); static int global_mem_dev_release(struct inode *inode, struct file *filep); static ssize_t global_mem_dev_read(struct file *filep, char __user *buf, size_t count, loff_t *off); static ssize_t global_mem_dev_write(struct file *filep, char __user *buf, size_t count, loff_t *off); static ssize_t global_mem_dev_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg); static int global_mem_dev_setup_cdev(struct global_mem_dev *mydev, int minor_index); static int __init global_mem_dev_init_module(void); static void __exit global_mem_dev_cleanup_module(void); /*********************************************************************/ /*打开设备,对应于用户空间的open系统调用*/ static int global_mem_dev_open(struct inode *inode, struct file *filep) { #if 0 int minor = MINOR(inode->i_rdev); //MINOR(inode->i_cdev); switch(minor) { case 0: /* /dev/global_mem */ printk("/dev/global_mem opened!\n"); break; case 1: /* /dev/global_mem1 */ printk("/dev/global_mem1 opened!\n"); break; case 2: /* /dev/global_mem2 */ printk("/dev/global_mem2 opened!\n"); break; case 3: /* /dev/global_mem1 */ printk("/dev/global_mem3 opened!\n"); break; } #endif /*将设备结构体指针赋值给文件私有数据指针*/ filep->private_data = global_mem_dev_array; printk("/dev/%s opened!\n", DEVICE_NAME); //填充一些资源的初始化 return 0; } /*关闭设备,对应于用户空间的close系统调用*/ static int global_mem_dev_release(struct inode *inode, struct file *filep) { printk("/dev/%s closed!\n", DEVICE_NAME); //填充一些资源的释放 return 0; } /*实现读功能,对应于用户空间的read系统调用*/ static ssize_t global_mem_dev_read(struct file *filep, char __user *buf, size_t count, loff_t *off) { struct global_mem_dev *dev = filep->private_data; /*获得设备结构体指针*/ copy_to_user(buf, (void *)dev->mem, count); return 0; } /*实现写功能,对应于用户空间的write系统调用*/ static ssize_t global_mem_dev_write(struct file *filep, char __user *buf, size_t count, loff_t *off) { struct global_mem_dev *dev = filep->private_data; /*获得设备结构体指针*/ copy_from_user(dev->mem, buf, count); return 0; } /*实现控制功能,对应于用户空间的ioctl系统调用*/ static ssize_t global_mem_dev_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg) { return 0; } /*设置file_operations 结构体,提供给应用空间的接口*/ static struct file_operations global_mem_dev_fops = { .owner = THIS_MODULE, .open = global_mem_dev_open, .release = global_mem_dev_release, .read = global_mem_dev_read, .write = global_mem_dev_write, .ioctl = global_mem_dev_ioctl, }; /*设备建立子函数,被global_mem_dev_init_module函数调用*/ static int global_mem_dev_setup_cdev(struct global_mem_dev *mydev, int minor_index) /*minor_index*/ { int err; dev_t devno = MKDEV(major, first_minor + minor_index); cdev_init(&mydev->cdev, &global_mem_dev_fops); mydev->cdev.owner = THIS_MODULE; //这里要用. mydev->cdev.ops = &global_mem_dev_fops; //这里要用. err = cdev_add(&mydev->cdev, devno, 1); if (err) { printk("[errno = %d] Faiglobal_mem to add %s , minor_index=%d\n", err, DEVICE_NAME, minor_index); } return err; } /*驱动模块的初始化*/ static int __init global_mem_dev_init_module(void) { int result; int i; dev_t dev_no = 0; /*设备号 高12bits:major 低20bits:minor*/ /*注册设备号*/ if (major) /*静态分配主设备号的注册*/ { dev_no = MKDEV(major, first_minor); result = register_chrdev_region(dev_no, /*分配设备号起始值*/ minor_count, /*请求的连续设备号的个数*/ DEVICE_NAME);/*设备号范围内关联的设备名称 *出现在/proc/devices和sysfs中*/ } else /*动态分配主设备号的注册*/ { result = alloc_chrdev_region(&dev_no, first_minor, minor_count, /*请求的连续设备号的个数*/ DEVICE_NAME); major = MAJOR(dev_no);/*保存动态分配的主设备号*/ } if (result < 0) { printk("%s : Faiglobal_mem to get major %d\n", DEVICE_NAME, major); result = -1; goto fail; /*失败了,应该回滚前面的操作撒*/ } else { DEBUG("%s : Successed to get major %d\n", DEVICE_NAME, major); } /*字符设备文件的注册*/ global_mem_dev_array = kmalloc(minor_count * sizeof(struct global_mem_dev), GFP_KERNEL); if (!global_mem_dev_array) { result = -ENOMEM; printk("%s : Faiglobal_mem to kmalloc *global_mem_dev_array(%d * sizeof(struct %s))\n", DEVICE_NAME, minor_count, DEVICE_NAME); goto fail; /* Make this more graceful */ } memset(global_mem_dev_array, 0, minor_count * sizeof(struct global_mem_dev)); for(i = 0; i < minor_count; i++) { result = global_mem_dev_setup_cdev(&global_mem_dev_array[i], i); if (result < 0) goto fail; } /*mdev动态的在/dev/下创建DEVICE_NAME节点*/ global_mem_dev_class = class_create(THIS_MODULE, DEVICE_NAME); device_create(global_mem_dev_class, NULL, dev_no, NULL, DEVICE_NAME); DEBUG("%s : Successed to init!\n", DEVICE_NAME); return 0; fail: global_mem_dev_cleanup_module(); return result; } /*驱动模块的清除*/ static void __exit global_mem_dev_cleanup_module(void) { int i; dev_t devno = MKDEV(major, first_minor); if (global_mem_dev_array) { for (i = 0; i < minor_count; i++) { cdev_del(&global_mem_dev_array[i].cdev); //注意是. } kfree(global_mem_dev_array); } /* cleanup_module is never calglobal_mem if registering faiglobal_mem */ unregister_chrdev_region(devno, 1); device_destroy(global_mem_dev_class, devno); class_destroy(global_mem_dev_class); DEBUG("%s : successed to exit!\n", DEVICE_NAME); } /**/ MODULE_AUTHOR("onejacky wanshijie@126.com"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("simple cdev driver module for global_mem"); module_init(global_mem_dev_init_module); //insmod module_exit(global_mem_dev_cleanup_module); //rmmod
相关文章推荐
- LINUX 网络编程---多路复用和信号驱动I/O(王德仙)2012-04-07 客户端和服务器端编写完成,明天开始学习poll 和epoll
- linux驱动学习笔记(1)
- linux网络设备应用与驱动编程学习3——lpc3250以太网控制器
- Linux内核驱动学习(一)----内核简介 | 配置 | 编译| 安装(PC平台下)
- Linux驱动学习笔记----------input输入子系统(基本概念与流程)
- linux驱动开发中断例程学习
- 学习笔记 --- LINUX 驱动调试之printk
- 学习笔记 --- LINUX 驱动调试之根据出错PC来分析
- Linux驱动学习笔记之一——高精度定时器(1)
- Linux学习:简单字符设备驱动
- linux驱动之定时器的介绍和内核时间的学习
- 【转载】学习linux驱动经典书籍
- Linux设备驱动模型学习笔记(kobject,kset,subsystem子系统)
- Linux驱动编写的方法学习
- linux驱动学习:1.内核树的建立
- linux 驱动学习-1th
- linux 驱动学习之ioctl 命令_IOC _ 相关宏
- Smart210学习记录-------Linux设备驱动结构
- Smart210学习记录-------linux驱动中断
- linux驱动学习--第十七天:第十章 Linux 中断与时钟 (二)概念、流程和框架(2013-06-19 22:31)