Linux设备驱动开发详解学习笔记<一>

书名:《Linux设备驱动开发详解》第二版

主机环境:Linux version 2.6.25-14.fc9.i686@Fedora

arm-linux-gcc版本:version 4.4.1

开发板:mini2440-256M

版权信息:版权归作者M.Bing和博客园共同所有,可以随意转载,但请保留此条信息!

备注:由于作者水平有限,文章难免出错,欢迎大家指正,共同学习。

一、字符设备简介

  在Linux2.6内核中,使用cdev结构描述一个字符设备,cdev结构如下所示

struct cdev {struct kobject kobj;struct module *owner;struct file_operations *ops;  /*文件操作结构体*/struct list_head list;dev_t dev;   /*设备号 32位,其中12位主设备号,20次设备号*/unsigned int count;
};

  可以从dev_t获得主设备号和次设备号:

  MAJOR(dev_t dev)

  MINOR(dev_t dev)

  可以使用下面的宏通过主设备号和次设备号生成dev_t:

  MKDEV(int major, int minor)

  cdev结构内部还有一个重要的结构体:file_operations,其定义了字符设备驱动提供给虚拟文件系统的接口函数。

在Linux2.6内核中使用以下函数操作cdev结构体:

void cdev_init(struct cdev *, struct file_operationd *);/*这个函数将cdev和file_operations联合在一起*/
struct cdev *cdev_alloc(void);
void cdev_put(struct cdev *,dev_t, unsigned);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);

各个函数的功能从其函数名就可以看出。

二、file_operations结构体

  在上面一节说了file_operations是一个重要的结构体,下面看看它的内部构成:

struct file_operations {struct module *owner;loff_t(*llseek) (struct file *, loff_t, int);ssize_t(*read) (struct file *, char __user *, size_t, loff_t *);ssize_t(*aio_read) (struct kiocb *, char __user *, size_t, loff_t);ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *);ssize_t(*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);int (*readdir) (struct file *, void *, filldir_t);unsigned int (*poll) (struct file *, struct poll_table_struct *);int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, struct dentry *, int datasync);int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t(*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);ssize_t(*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);ssize_t(*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void __user *);ssize_t(*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area) (struct file *, unsigned long,unsigned long, unsigned long,unsigned long);
};

每个函数都对应一个用户操作,但你不必实现每一个函数,一般你只需要实现以下几个函数:

struct file_operations fops = {.read = device_read,.write = device_write,.open = device_open,.release = device_release
};

三、实现一个字符设备需要做哪些工作

  1、填充file_operations中需要的函数,并实现这些函数

  2、实现加载模块时调用的函数XXX_init();下面是一个常用的例子:

dev_t IO_MAJOR = 0;                 //这里是动态分配设备号和动态创建设备结点需要用到的
struct cdev  dev_c;
struct class *my_class;static int __init IO_init(void)
{int ret,err;ret = alloc_chrdev_region(&IO_MAJOR, 0, 1,DEVICE_NAME); //动态分配设备号if (ret) { printk("testmode register failure\n"); unregister_chrdev_region(IO_MAJOR,1);return ret;} else { printk("testmode register success\n"); } cdev_init(&dev_c, &s3c6410_IO_fops);err = cdev_add(&dev_c, IO_MAJOR, 1);if(err){printk(KERN_NOTICE "error %d adding FC_dev\n",err);unregister_chrdev_region(IO_MAJOR, 1);return err;}my_class = class_create(THIS_MODULE, DEVICE_NAME);//动态创建设备结点if(IS_ERR(my_class)){ printk("ERR:cannot create a cdev_class\n");  unregister_chrdev_region(IO_MAJOR, 1);return -1;}device_create(my_class,NULL, IO_MAJOR, 0, DEVICE_NAME);printk(DEVICE_NAME "initialized.\n");return 0;
}

要注意的是,在上面的函数中,没有驱动模块加载失败时的处理,所以在实际的应用过程在每一步加载失败时,跳转到相应的出错处理,在出错处理中将前面以注册的函数依次卸载。

  图3-1为字符设备驱动的结构,字符设备驱动与字符设备以及字符设备驱动与用户空间访问该设备的程序之间的关系:

图3-1 字符设备内部关系

四、示例代码

/** =====================================================================================* Filename:  testmode.c* Description:* Version:  1.0* Created:  2013-5-29* Revision:  none* Compiler:  gcc* Author:  M.Bing* Company: Deviser of TinJing* =====================================================================================*/#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>#include <plat/regs-gpio.h>
#include <plat/s3c6410.h>
#include <plat/gpio-cfg.h>
#include <asm/gpio.h>
#include <plat/gpio-bank-l.h>
#include <plat/gpio-bank-n.h>#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>#define DEVICE_NAME        "io_mode"#define TESTK08K15      0   //Test GPIOK8--GPIOK15
#define TESTL00L07      1   //Test GPIOL0--GPIOL7unsigned int UserCmd = 0;/*Set GPK12 GPK13 GPK14 GPK15 INPUT*//**/
static int IO_open(struct inode *inode, struct file *file)
{int reg;
/**************Init GPIOK8--GPIOK15******************/reg = readl(S3C64XX_GPKCON1);reg &=(~0xffffffff);/*Use GPK8 ~GPK15  8PIN*//*Set GPK12 GPK13 GPK14 GPK15 INPUT*//*Set GPK8   GPK9   GPK10 GPK11 OUTPUT*/reg |=(0x00001111);writel(reg,S3C64XX_GPKCON1);reg = readl(S3C64XX_GPKPUD);reg &= (~0xff0000);reg |= (0xaa0000); //GPK8   GPK9   GPK10 GPK11  pull
    writel(reg,S3C64XX_GPKPUD);
/**************Init GPIOL0--GPIOL7******************/reg = readl(S3C64XX_GPLCON);reg &= (~0xffffffff);reg |= (0x00001111);writel(reg,S3C64XX_GPLCON);//L0-L3 OUTPUT L4-L7 INPUT
reg = readl(S3C64XX_GPLPUD);reg &=(~0x0000ffff);reg |=(0x000000aa);//L0-L3 PULL UP
    writel(reg,S3C64XX_GPLPUD);return 0;
}static int IO_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg)
{if (arg > 4){return -EINVAL;}UserCmd = cmd;switch(UserCmd){case TESTK08K15:printk("\n-----------UserCmd = TESTK08K15------------\n");break;case TESTL00L07:printk("\n-----------UserCmd = TESTL00L07------------\n");break;default:printk("\n-----------Unkonw UserCmd = %d------------\n",cmd);break;}return 0;
}static ssize_t IO_read(struct file *file,char __user *buff,size_t size,loff_t *loff)
{unsigned int reg;if(UserCmd == TESTK08K15){reg = readl(S3C64XX_GPKDAT);buff[0] = ((reg | 0xffff0fff) >> 12)&0x0f;//get K12 - K15 data//printk("IO_read buff[0] = %d\n",buff[0]);
}if(UserCmd == TESTL00L07){reg = readl(S3C64XX_GPLDAT);buff[0] = ((reg | 0xffffff0f) >> 4)&0x0f;//get L4 - L7 data
        }return 0;
}
static ssize_t IO_write(struct file *file,const char __user *buff,size_t size,loff_t *loff)
{unsigned int reg;if(UserCmd == TESTK08K15){//printk("IO_write buff[0] = %d\n",buff[0]); reg = readl(S3C64XX_GPKDAT);reg &= (~0xf00);reg |=( buff[0] << 8)& 0xf00;writel(reg,S3C64XX_GPKDAT);}if(UserCmd == TESTL00L07){reg = readl(S3C64XX_GPLDAT);reg &= (~0x0f);reg |= (buff[0]) & 0x0f;writel(reg,S3C64XX_GPLDAT);}return 0;
}static struct file_operations s3c6410_IO_fops =
{.owner    =    THIS_MODULE,.open    =    IO_open,.ioctl    =    IO_ioctl,.read    =     IO_read,.write    =     IO_write};dev_t IO_MAJOR = 0;                 //这里是动态分配设备号和动态创建设备结点需要用到的
struct cdev  dev_c;
struct class *my_class;static int __init IO_init(void)
{int ret,err;ret = alloc_chrdev_region(&IO_MAJOR, 0, 1,DEVICE_NAME); //动态分配设备号if (ret) { printk("testmode register failure\n"); unregister_chrdev_region(IO_MAJOR,1);return ret;} else { printk("testmode register success\n"); } cdev_init(&dev_c, &s3c6410_IO_fops);err = cdev_add(&dev_c, IO_MAJOR, 1);if(err){printk(KERN_NOTICE "error %d adding FC_dev\n",err);unregister_chrdev_region(IO_MAJOR, 1);return err;}my_class = class_create(THIS_MODULE, DEVICE_NAME);//动态创建设备结点if(IS_ERR(my_class)){ printk("ERR:cannot create a cdev_class\n");  unregister_chrdev_region(IO_MAJOR, 1);return -1;}device_create(my_class,NULL, IO_MAJOR, 0, DEVICE_NAME);printk(DEVICE_NAME "initialized.\n");return 0;
}static void __exit IO_exit(void)
{device_destroy(my_class, IO_MAJOR);class_destroy(my_class);unregister_chrdev_region(IO_MAJOR,1);printk("testmode_exit \n");}module_init(IO_init);
module_exit(IO_exit);MODULE_AUTHOR("M.Bing");
MODULE_DESCRIPTION("s3c6410 IO  test.");
MODULE_LICENSE("GPL");

转载于:https://www.cnblogs.com/M-Bing/p/3178107.html

《Linux设备驱动开发详解》学习笔记一相关推荐

  1. 《linux设备驱动开发详解》笔记——15 linux i2c驱动

    <linux设备驱动开发详解>笔记--15 linux i2c驱动 15.1 总体结构 如下图,i2c驱动分为如下几个重要模块 核心层core,完成i2c总线.设备.驱动模型,对用户提供s ...

  2. 《linux设备驱动开发详解》笔记——14 linux网络设备驱动

    14.1 网络设备驱动结构 网络协议接口层:硬件无关,标准收发函数dev_queue_xmit()和netif_rx();  注意,netif_rx是将接收到的数据给上层,有时也在驱动收到数据以后调用 ...

  3. c16语言延时函数delay,《linux设备驱动开发详解》笔记——10中断与时钟

    10.1 中断与定时器 中断一般有如下类型: 内部中断和外部中断:内部中断来自CPU,例如软件中断指令.溢出.除0错误等:外部中断有外部设备触发 可屏蔽中断和不可屏蔽中断 向量中断和非向量中断,ARM ...

  4. 《Linux 设备驱动开发详解(第2版)》——1.4 Linux设备驱动

    本节书摘来自异步社区<Linux 设备驱动开发详解(第2版)>一书中的第1章,第1.1节,作者:宋宝华著,更多章节内容可以访问云栖社区"异步社区"公众号查看 1.4 L ...

  5. Linux设备驱动开发详解 第3版 (即 Linux设备驱动开发详解 基于最新的Linux 4 0内核 )前言

    Linux从未停歇脚步.Linus Torvalds,世界上最伟大的程序员之一,Linux内核的创始人,Git的缔造者,仍然在没日没夜的合并补丁,升级内核.做技术,从来没有终南捷径,拼的就是坐冷板凳的 ...

  6. 《Linux设备驱动开发详解(第2版)》隆重出版

    Linux设备驱动开发详解(第2版)(前一版狂销3万册,畅销书最新升级) [新品] 点击看大图     基本信息 * 作者: 宋宝华       * 出版社:人民邮电出版社     * ISBN:97 ...

  7. 《Linux设备驱动开发详解(第3版)》(即《Linux设备驱动开发详解:基于最新的Linux 4.0内核》)网购链接

    <Linux设备驱动开发详解:基于最新的Linux 4.0内核> china-pub   天猫     dangdang   京东 China-pub 8月新书销售榜 推荐序一 技术日新月 ...

  8. 《Linux设备驱动开发详解 A》一一2.3 接口与总线

    本节书摘来华章计算机出版社<Linux设备驱动开发详解 A>一书中的第2章,第2.3节,作者:宋宝华 更多章节内容可以访问云栖社区"华章计算机"公众号查看.1 2.3 ...

  9. linux设备驱动开发详解源码,linux设备驱动开发详解光盘源码.rar

    压缩包 : linux设备驱动开发详解光盘源码.rar 列表 19/busybox源代码/busybox-1.2.1.tar.bz2 19/MTD工具/mtd-utils-1.0.0.tar.gz 1 ...

最新文章

  1. soul一直显示正在登录聊天服务器,soul这个软件,为什么有些人在玩的时间很久以后(两百天以上),就不会再主动和其他人打招呼了?...
  2. linux 查看发行版 名称 版本号
  3. air java_Air 调用本地化java程序
  4. PAT甲级1013 Battle Over Cities:[C++题解]并查集、结构体存边
  5. linux postfix 日志,linux – 如何计算Postfix的mailq的消息?
  6. Fedora 17 PHP编程环境配置
  7. 揭秘阿里云EB级大数据计算引擎MaxCompute
  8. 基于深度学习的手写数字识别、python实现
  9. android线程卡死,Android主线程为什么不因为Loop死循环卡死
  10. 2008安装完了找不到_7206BEP.进口轴承_玉溪SKF轴承安装指南
  11. 如何获取下拉列表框的值
  12. 熊猫烧香完整的病毒源代码
  13. Linux停服务器命令,使用linux的shutdown命令关闭服务器
  14. Windows11 专业版 体验分享
  15. Mirth Connect的简单使用
  16. DTOJ3026 geronimo
  17. 你还在为高速停车收费而烦恼吗?现已步入高速行ETC智慧交通时代
  18. day3----部署duboo微服务值部署zk和Jenkins(3)
  19. 调音台使用基础-增益结构与推子位置
  20. TCP/UDP 端口及部分端口的作用

热门文章

  1. 查看java运行时参数_查看JVM运行时参数
  2. fpga驱动oled iic显示代码_【接口时序】6、IIC总线的原理与Verilog实现
  3. 微信小程序 获取input输入的值
  4. java字符串构造函数的应用_构造函数中的参数0需要找不到类型为'java.lang.String'的bean...
  5. html代码js正则,过滤所有HTML代码和CSS,JS
  6. kubesphere_KubeSphere容器混合云一个人也能轻松运维的K8s
  7. css3转换图形展示,CSS3的常见transformation图形变化用法小结
  8. 计算机编程学英语词汇,计算机编程英语词汇大全
  9. oracle下定时删除归档脚本
  10. oracle 查看隐含参数的脚步