第一步:my74hc595.c

#include <linux/module.h> //模块所需的大量符号和函数定义
#include <linux/init.h> //指定初始化和清除函数
#include <linux/fs.h> //文件系统相关的函数和头文件
#include <linux/cdev.h> //cdev结构的头文件
#include <asm/uaccess.h> //在内核和用户空间中移动数据的函数
#include <linux/slab.h>
#include <linux/device.h>

MODULE_LICENSE("GPL"); //指定代码使用的许可证

//文件操作函数的声明
int my74hc595_open(struct inode *, struct file *);
int my74hc595_release(struct inode *, struct file *);
ssize_t my74hc595_read(struct file *, char *, size_t, loff_t *);
ssize_t my74hc595_write(struct file *, const char *, size_t, loff_t *);

int dev_major = 1253; //指定主设备号
int dev_minor = 0; //指定次设备号

static struct class *firstdrv_class;
static struct device *firstdrv_class_dev;

struct cdev *my74hc595_cdev; //内核中表示字符设备的结构
int *gp_testdata;//测试用数据

struct file_operations my74hc595_fops= //将文件操作与分配的设备号相连
{
  owner: THIS_MODULE, //指向拥有该模块结构的指针
  open: my74hc595_open,
  release: my74hc595_release,
  read: my74hc595_read,
  write: my74hc595_write,
};

static void __exit my74hc595_exit(void) //退出模块时的操作
{
  dev_t devno=MKDEV(dev_major, dev_minor); //dev_t是用来表示设备编号的结构

  cdev_del(my74hc595_cdev); //从系统中移除一个字符设备
  kfree(my74hc595_cdev); //释放自定义的设备结构
  kfree(gp_testdata);
  unregister_chrdev_region(devno, 1); //注销已注册的驱动程序

  device_unregister(firstdrv_class_dev); //删除/dev下对应的字符设备节点
  class_destroy(firstdrv_class);

  printk("my74hc595 unregister success\n");
}

static int __init my74hc595_init(void) //初始化模块的操作
{
  int ret, err;
  dev_t devno;
#if 1
  //动态分配设备号,次设备号已经指定
  ret=alloc_chrdev_region(&devno, dev_minor, 1, "my74hc595");
  //保存动态分配的主设备号
  dev_major=MAJOR(devno);

#else
  //根据期望值分配设备号
  devno=MKDEV(dev_major, dev_minor);
  ret=register_chrdev_region(devno, 1, "my74hc595");
#endif

  if(ret<0)
  {
    printk("my74hc595 register failure\n");
    //my74hc595_exit(); //如果注册设备号失败就退出系统
    return ret;
  }
  else
  {
    printk("my74hc595 register success\n");
  }

  gp_testdata = kmalloc(sizeof(int), GFP_KERNEL);
#if 0//两种初始化字符设备信息的方法
  my74hc595_cdev = cdev_alloc();//调试时,此中方法在rmmod后会出现异常,原因未知
  my74hc595_cdev->ops = &my74hc595_fops;
#else
  my74hc595_cdev = kmalloc(sizeof(struct cdev), GFP_KERNEL);
  cdev_init(my74hc595_cdev, &my74hc595_fops);
#endif

  my74hc595_cdev->owner = THIS_MODULE; //初始化cdev中的所有者字段

  err=cdev_add(my74hc595_cdev, devno, 1); //向内核添加这个cdev结构的信息
  if(err<0)
    printk("add device failure\n"); //如果添加失败打印错误消息

  firstdrv_class = class_create(THIS_MODULE, "my74hc595");
  firstdrv_class_dev = device_create(firstdrv_class, NULL, MKDEV(dev_major, 0), NULL,"my74hc595-%d", 0);//在/dev下创建节点

  printk("register my74hc595 dev OK\n");

  return 0;
}
//打开设备文件系统调用对应的操作
int my74hc595_open(struct inode *inode, struct file *filp)
{
  //将file结构中的private_data字段指向已分配的设备结构
  filp->private_data = gp_testdata;
  printk("open my74hc595 dev OK\n");
  return 0;
}
//关闭设备文件系统调用对应的操作
int my74hc595_release(struct inode *inode, struct file *filp)
{
  printk("close my74hc595 dev OK\n");
  return 0;
}
//读设备文件系统调用对应的操作
ssize_t my74hc595_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
  //获取指向已分配数据的指针
  unsigned int *p_testdata = filp->private_data;
  //将设备变量值复制到用户空间
  if(copy_to_user(buf, p_testdata, sizeof(int)))
  {
    return -EFAULT;
  }
  printk("read my74hc595 dev OK\n");
  return sizeof(int); //返回读取数据的大小
}
//写设备文件系统调用对应的操作
ssize_t my74hc595_write(struct file *filp, const char *buf, size_t len, loff_t *off)
{
  //获取指向已分配数据的指针
  unsigned int *p_testdata = filp->private_data;
  //从用户空间复制数据到内核中的设备变量
  if(copy_from_user(p_testdata, buf, sizeof(int)))
  {
    return -EFAULT;
  }
  printk("write my74hc595 dev OK\n");
  return sizeof(int); //返回写数据的大小
}

module_init(my74hc595_init); //模块被装载时调用my74hc595_init
module_exit(my74hc595_exit); //模块被卸载时调用my74hc595_exit

第二步:Makefile

按如下内容编写一个Makefile文件,然后输入make就可以开始自动编译了。编译之后得到了一个名为my74hc595.ko的模块文件,这就是我们需要的设备驱动文件。

#Makefile
CROSS_COMPILE=arm-linux-
ARCH:=arm
CC:=$(CROSS_COMPILE)gcc
LD:=$(CROSS_COMPILE)ld
obj-m = my74hc595.o
module-objs = my74hc595.o

KDIR = /home/zhang/at91/linux-at91
PWD = $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
rm -rf *.o *.ko *~

接下来运行如下代码,将驱动加入内核。

insmod my74hc595.ko

将自动在/dev目录下创建设备节点

rmmod my74hc595.ko

将自动删除节点

第三步:CharDevTest.c

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>

main()
{
  int fd, num;
  // fd=open("/dev/my74hc595", O_RDWR, S_IRUSR|S_IWUSR); //可读写方式打开设备文件
  fd=open("/dev/my74hc595-0", O_RDWR); //可读写方式打开设备文件
  if(fd!=-1)
  {
    read(fd, &num, sizeof(int)); //读取设备变量
    printf("The my74hc595 is %d\n", num);

    printf("Please input the num written to my74hc595\n");
    scanf("%d", &num);
    write(fd, &num, sizeof(int)); //写设备变量

    read(fd, &num, sizeof(int)); //再次读取刚才写的值
    printf("The my74hc595 is %d\n", num);

    close(fd); //关闭设备文件
  }
  else
  {
    printf("Device open failure\n");
    perror("open my74hc595");
  }
}

Linux_2.6字符设备驱动实例的更多相关文章

  1. Linux字符设备驱动实例—globalmem驱动

    1.globalmem虚拟设备实例 globalmem为“全局内存”的意思,在globalmem字符设备中会分配一片大小为GLOBALMEM_SIZE(4KB)的内存空间,并在驱动中提供对这片内存的读 ...

  2. LED字符设备驱动实例及测试代码

    驱动代码如下: #include <linux/kernel.h>//内核头文件 #include <linux/init.h>//__init等 #include <l ...

  3. Linux字符设备驱动框架

    字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l ...

  4. Linux字符设备驱动结构(一)--cdev结构体、设备号相关知识机械【转】

    本文转载自:http://blog.csdn.net/zqixiao_09/article/details/50839042 一.字符设备基础知识 1.设备驱动分类 linux系统将设备分为3类:字符 ...

  5. Linux驱动设计——字符设备驱动(一)

    Linux字符设别驱动结构 cdev结构体 struct cdev { struct kobject kobj; struct module *owner; const struct file_ope ...

  6. 【转】linux设备驱动程序之简单字符设备驱动

    原文网址:http://www.cnblogs.com/geneil/archive/2011/12/03/2272869.html 一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用 ...

  7. 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联

    转载自:http://www.kancloud.cn/yueqian_scut/emlinux/106829 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sy ...

  8. Linux字符设备驱动

    一.字符设备基础 字符设备 二.字符设备驱动与用户空间访问该设备的程序三者之间的关系 三.字符设备模型 1.Linux内核中,使用 struct cdev 来描述一个字符设备 动态申请(构造)cdev ...

  9. 字符设备驱动1:新的方式添加cdev + 在open函数中将文件私有数据指向设备结构体

    本例中,驱动入口处,使用cdev_add添加驱动,这点也可与字符设备驱动0:一个简单但完整的字符设备驱动程序对比一下. 另外主要讲xx_open实现文件私有数据指向设备结构体. 引子: 偶然看到,在j ...

随机推荐

  1. The content of element type "sqlMapConfig" is incomplete,

    The content of element type "sqlMapConfig" is incomplete, it must match "(properties? ...

  2. Win7 64bit 成功安装ArcView3.X

    本人参考 链接 已在Win7 64Bit 笔记本上成功安装ArcView3.3,于是记录以下心得。 Win7 64Bit安装不了ArcView3.X的原因在于: 1,ArcView3.X属于16Bit ...

  3. PHP - PDO 之 mysql 参数绑定

    <?php /* pdo 学习 */ $dsn = 'mysql:host=localhost;dbname=cswl';//构建连接dsn $db = new pdo($dsn,'root', ...

  4. @properties指针说明

    在iOS开发过程中,属性的定义往往与retain, assign, copy有关,我想大家都很熟悉了,在此我也不介绍,网上有很多相关文章. 现在我们看看iOS5中新的关键字strong, weak, ...

  5. 使用maven 命令运行项目

    安装好maven3 配置好环境变量后, 输入mvn -v 查看安装是否成功, 然后导入maven项目, 选择import 导入选择Exsting Maven Projects, 接下来就准备运行一下m ...

  6. 正则表达式通过Unicode属性匹配

    原文链接:http://zochen.iteye.com/blog/690716 Unicode 编码并不只是为某个字符简单定义了一个编码,而且还将其进行了归类. \pP 其中的小写 p 是 prop ...

  7. Qt单元测试

    单元测试之作用要完成测试用例,保证设计上的耦合依赖通过测试用例,保证覆盖率,提高程序质量 QTest一些有用的静态函数QTest::qExecQTest::qSleepQTest::qWait   例 ...

  8. Objective C 四舍五入,float处理

    NSLog(@"平方:%.f", pow(3,2) ); //result 9 NSLog(@"上舍入:%.f", ceil(3.000000000001)); ...

  9. eval()字符串转成对象

    var s = "{a:1,b:2}"; console.log(typeof s); s = eval("(" + s + ")"); c ...

  10. 团体程序设计天梯赛-练习集L2-006. 树的遍历

    L2-006. 树的遍历 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历 ...