第一步: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. 由底层和逻辑说开去——c++之类与对象的深入剖析

    类是什么,对象是什么,  这两个问题在各个c++书里面都以一种抽象的描述方式,给了我们近乎完美的答案,然后我好像就知道什么是类什么是对象了,但是当扪心自问,类在哪儿,对象在哪儿,成员方法在哪儿,成员变 ...

  2. 2014年辛星完全解读Javascript第五节 break和continue与错误处理

    先说一下break和continue的主要用法吧,break用于跳出循环,continue用于跳过该循环中的一个迭代.简单的说,就是break直接从该语句跳出,但是continue不会跳出该循环语句, ...

  3. 十六、mysql 分区之 简单sql优化2

    .索引的分类 B-Tree 基本支持 HASH 只有memory支持 R-Tree myisam支持 Full-text myisam支持(全文索引) .Memory引擎下只有“=”条件才会使用索引 ...

  4. 使用maven 命令运行项目

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

  5. Svg 画图(电池)

    公司现在在做充电桩项目,其中要显示充电桩的电池充电情况,功能展示的时候要画图,之前做的时候准备使用HightChar来画,但是,hightchar好像没有这样的电池图形,最后,项目经理要我自己通过sv ...

  6. 【数位DP】bzoj1026: [SCOI2009]windy数

    1026: [SCOI2009]windy数 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 4163  Solved: 1864[Submit][Sta ...

  7. [转载]VS2012创建MVC3项目提示错误: 此模板尝试加载组件程序集 “NuGet.VisualStudio.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”。

    如果在没有安装vs2012 update3升级包的情况下,创建MVC3项目会出现下面的错误信息. 因为VS2012已经全面切换到使用NuGet这个第三方开源工具来管理项目包和引用模块了,使用VS201 ...

  8. textarea中限制输入字符长度

    要在textarea中限制输入字符的长度,比如在twitter中要限制字符为140个,可实现的方法有: 1. <textarea name="A" cols="45 ...

  9. spoj 237

    好牛的题  哈哈 #include <cstdio> #include <algorithm> #define S(n) scanf("%d",&n ...

  10. 如何优化 Java 性能?

    对于 Java 性能比较关心的同学大概都知道<Java Performance>这本书,一般而言,很多同学在日常写 Java Code 的时候很少去关心性能问题,但是在我们写 Code 的 ...