开发板:tiny6410

内核:linux2.6.38

要写led驱动首先要复习的是混杂设备驱动的设计流程、 内核file_ops结构体、硬件通信IO端口和IO内存、 ioctl系统调用的实现步骤和方法

按照顺序来对已经学习的内容复习 并把led模块驱动写出来

第一步:复习ioctl系统调用函数

一、什么是ioctl系统调用函数,驱动中的iodtl函数是什么样子的?为什么需要ioctl函数?》

虽然file_ops结构体提供了相当多的文件操作函数,但是要想对硬件操作,这个file_ops结构体无法完成了,所以操作系统又提供了另一个对硬件操作的函数,就是ioctl,ioctl分为在应用层 和 在驱动层。ioctl应用层的函数传递命令,从用户态将命令传递到内核态,调用驱动的ioctl函数来实现对硬件的操作。

上图::从网上找的

用户空间中的ioctl 和 驱动的ioctl有点不一样

用户空间的ioctl函数形式为:

int ioctl(int fd, unsigned long cmd,  ...);//参数的意义:::第一个代表:文件描述符,第二个代表命令 第三个为可选的参数

驱动中的ioctl从2.6.36开始改名称了-----改为unlocked_ioctl

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

参数意义:第一个 为用户空间传递下来的 文件指针,

第二个为参数的命令 cmd这个命令的定义和获取 接下来要详细的讲解一下

第三个为  用户空间传递 的 可选的参数

二、在写ioctl代码之前首先要搞懂如何定义命令,为了防止对错误的设备使用正确的命令,命令号应该在系统范围内是唯一的。。。

ioctl命令编码被划分为几个段,在include/asm/ioctl.h 中定义了这些字段:类型(幻数),基数,传送方向,参数大小等。在Documentation/ioctl-number.txt文件中 罗列了在内核中已经使用的幻数。

定义ioct命令的正确方法是 使用四个位段::

Type(8)-------------类型(幻数)表明是哪个设备的命令,要参考Documentation/ioctl-number.txt文档后钻出合适的命令

Number(8)----------序号,表明设备命令中的第几个,8位宽

Direction(2)--------数据传送的方向,可能的值是 ———_IOC_NONE(没有数据传输),_IOC_READ,_IOC_WRITE.数据的传送方向是从应用程序的观点来看待的,_IOC_READ意为从设备中读取数据

Size(8~14)----------用户数据的大小(8-14位宽,视处理器而定)。

上面是对一个命令所包含信息的解析,一个命令应该包含什么信息。

那么如何定义一个命令呢,内核提供了下列宏来定义命令

_IO(type,nr);//没有参数的命令

_IOR(type,nr,datatype);//从驱动中读数据

_IOW(type,nr,datatype);//向驱动中写入数据

_IOWR(type,nr,datatype);//双向传送

内核还定义了用来解开宏的字段

_IOC_DIR(nr);

_IOC_TYPE(nr);

_IOC_NR(nr);

_IOC_SIZE(nr); nr为要解析的命令

三、ioctl函数中如何实现命令

1、一般在ioctl函数的实现通常是根据命令执行的一个switch语句。但是当命令不能匹配任何一个设备所持有的命令的时候,通常返回-EINVAL.

2、ioctl函数的第三个参数arg的使用

如果arg是一个整数,那么可以直接使用,如果是指针,我们必须确保这个用户地址是有效的,因此使用前需要进行正确性检查,使用下面的函数对arg参数的正确性进行检查

int access_ok(int type, const void* addr, unsigned long size);//第一个是类型 参数为VERIFY_READ 或者VERIFY_WRITE,用来表明读用户内存 还是 写用户内存。addr参数是要操作的用户内存地址,size 是操作的长度。

如果需要同时读写 则使用参数VERIFY_WRITE。

access_ok()返回一个布尔类型的值:1 是成功(存取没问题) 0是失败(存取有问题)。

第二步:复习file_ops结构体

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 (*write) (struct file *, const char __user *, size_t, loff_t *);//文件的写入函数

ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//

ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

int (*readdir) (struct file *, void *, filldir_t);//读取文件夹

unsigned int (*poll) (struct file *, struct poll_table_struct *);//对应select系统调用

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);//硬件操作函数

long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

int (*mmap) (struct file *, struct vm_area_struct *);//内存映射

int (*open) (struct inode *, struct file *);//打开

int (*flush) (struct file *, fl_owner_t id);

int (*release) (struct inode *, struct file *);//文件释放

int (*fsync) (struct file *, 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 (*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);

int (*check_flags)(int);

int (*flock) (struct file *, int, struct file_lock *);

ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);

ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);

int (*setlease)(struct file *, long, struct file_lock **);

long (*fallocate)(struct file *file, int mode, loff_t offset,

loff_t len);

};

在这个结构体中并不是所有的函数都需要实现,只实现需要的函数即可。

第三部:复习硬件操作--IO内存和IO端口

真正要实现设备的操作时必须要设计到硬件的操作的,设备驱动程序是软件概念和硬件概念的一个抽象层,因此都要讨论。

每种外设都通过寄存器来进行控制。大部分外设都设有几个寄存器,不管是在内存地址,还是在IO地址空间,这些寄存器的访问地址都是连续的。

在硬件层,内存区域和IO区域没有概念上的区别:他们都是通过向地址总线和控制总线发送电平信号进行访问,再通过数据总线读写数据。

详细请参考下面的连接::

IO端口、IO内存详细解析

第四步:混杂设备驱动程序设计

在linux系统 中,存在一类字符设备,他们共享一个主设备号(10),但次设备号不同,我们称这类设备为混杂设备(miscdevice),所有混杂设备形成一个链表,对设备访问时内核根据次设备号查找到相应的miscdevice设备。

描述混杂设备的结构体:

struct miscdevice  {

int minor;//次设备号--一般是有设备自动分配的--MISC_DYNANIC_MINOR

const char *name;//设备名称

const struct file_operations *fops;//设备对应的 文件操作集

struct list_head list;//

struct device *parent;

struct device *this_device;

const char *nodename;

mode_t mode;

};

如何使用混杂设备:

在初始化的时候注册 混杂设备就好

int misc_register(struct miscdevice * misc);

在模块卸载的时候将混杂设备注销:

misc_deregister(struct miscdevice *misc);

上LED模块驱动的例程:::

/**************************************************************************/

/***************************led.h**********************************************/

#ifndef _LED_H_

#define _LED_H_

#include/*涉及到ioctl命令的定义方法的问题*/

#define DEVICE_NAME "tiny6410-led"

/**/

#define LED_IOC_MAGIC 'l'/*定义命令类型*/

/**/

#define LED_IOCGETDATA _IOR(LED_IOC_MAGIC,1,int)/*利用宏来辅助定义命令--定义从驱动中获得数据*/

#define LED_IOCSETDATA _IOW(LED_IOC_MAGIC,2,int)/*利用宏来辅助定义命令--定义向驱动中写入数据*/

#define LED_IOC_MAXNR 2

#endif

/**************************************************************************/

/***************************led.c**********************************************/

#include#include#include#include#include#include#include#include#include#include#include#include#include/*要了解各个头文件中所包含的函数*/

#include"led.h"

/*led对应GPIOK虚拟地址*/

unsigned long GPIOK_VA_BASE;

#define GPIOK_CON0_VA GPIOK_VA_BASE

#define GPIOK_CON1_VA GPIOK_VA_BASE+0x4

#define GPIOK_DAT_VA  GPIOK_VA_BASE+0x8

#define GPIO_PUD_VA   GPIOK_VA_BASE+0xc

/*led对应的物理地址*/

#define GPIOK_PA_BASE 0x7f008800

/*linux 采用resource描述挂接在cpu总线上的结构实体*/

struct resource tiny6410_led_resource =

{

.name  = "led io-mem",

.start = GPIOK_PA_BASE,

.end = GPIOK_PA_BASE + 0x10,

.flags = IORESOURCE_MEM,

};

static void tiny6410_led_pin_setup(void)

{

unsigned long start = tiny6410_led_resource.start;

unsigned long size = tiny6410_led_resource.end - tiny6410_led_resource.start;

unsigned long tmp;

/*申请io内存*/

request_mem_region(start,size,tiny6410_led_resource.name);

/*映射io内存*/

GPIOK_VA_BASE = (unsigned long)ioremap(start,size);//映射的地址 由系统自动分配

printk("映射的虚拟地址 GPIOK_VA_BASE = 0x%lxn",GPIOK_VA_BASE);

/*对应管教设置为输出*/

tmp = readl(GPIOK_CON0_VA);

tmp = (tmp & ((0xffffU<<16)|(0x1111U<<16)));

writel(tmp,GPIOK_CON0_VA);

/*对应管脚置高--使led全灭*/

tmp = readl(GPIOK_DAT_VA);//将寄存器在内存中的虚拟地址的数据读出来

tmp |= (0xf<<4);

writel(tmp,GPIOK_DAT_VA);

}

/****************************************************/

static void tiny6410_led_pin_release(void)

{

/**首先要解除映射**/

iounmap((void*)GPIOK_VA_BASE);//参数是 映射到内存的地址

release_mem_region(tiny6410_led_resource.start,tiny6410_led_resource.end - tiny6410_led_resource.start);

}

/**************************************************************************************************/

static unsigned long tiny6410_led_getdata(void)

{

return ((readl(GPIOK_DAT_VA)>>4)&0xF);//返回虚拟io内存中寄存器的值

}

/*设置led对应的GPIO数据寄存器的值*/

static void tiny6410_led_setdata(int data)

{

unsigned long tmp;

tmp = readl(GPIOK_DAT_VA);

tmp = ~(0xF<<4) | ((data&0xF)<<4);

writel(tmp,GPIOK_DAT_VA);

}

/*****************************************************************************************************/

static long led_ioctl(struct file* filp,unsigned int cmd,unsigned long arg)

{

int ioarg,ret;

/*检测命令的有效性*/

if(_IOC_TYPE(cmd) != LED_IOC_MAGIC)//检测命令

return -EINVAL;

if(_IOC_NR(cmd) > LED_IOC_MAXNR)//检测命令号  如果大于最大的命令号

return -EINVAL;

/*根据命令来执行不同的操作*/

switch(cmd)  {

case LED_IOCGETDATA:{

ioarg = tiny6410_led_getdata();

ret = put_user(ioarg,(int*)arg);

break;

}

case LED_IOCSETDATA:{

ret = get_user(ioarg,(int*) arg);

tiny6410_led_setdata(ioarg);

break;

}

default:

return -EINVAL;

}

return ret;

}

static ssize_t led_read(struct file* filp,char __user* buf,size_t size,loff_t *ppos)

{

}

/********************************************************************************************/

static struct file_operations dev_fops =

{

.owner = THIS_MODULE,

.unlocked_ioctl = led_ioctl,

.read = led_read,

};

static struct miscdevice misc =

{

.minor = MISC_DYNAMIC_MINOR,//次设备号由系统动态分配

.name = DEVICE_NAME,

.fops = &dev_fops,//文件操作

};

/****************************************************************************************/

static int __init dev_init(void)

{

int ret;

tiny6410_led_pin_setup();//在板子初始化的时候 要将寄存器映射到内存中去

ret = misc_register(&misc);

printk("initialized minor =%dn",misc.minor);

return ret;

}

static void __exit dev_exit(void)

{

tiny6410_led_pin_release();

misc_deregister(&misc);//混杂字符设备驱动的解除

}

module_init(dev_init);

module_exit(dev_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("kong-hua-sheng");

/************************************************************************************/

/***********************************测试程序app-led.c************************************************/

#include#include#include#include#include#include#include#include"led.h"

#define DEV_NAME "/dev/"DEVICE_NAME

/*将传送进来的字符转换为int型数据*/

int binstr_to_int(char* binstr)

{

int ret =0,i=0;

char bnum[5];

memset(bnum,'0',4);

int len = strlen(binstr);

if(len>4)

{

strcpy(bnum,binstr+len-4);

}else

{

strcpy(bnum+4-len,binstr);

}

for(i=0;i<4;i++)

{

ret <<=1;

ret +=(bnum[i]=='0'? 1:0);

}

return ret;

}

int main(int argc,char* argv[])

{

if(argc>2)

{

printf("can shu > 2 error!!n");

_exit(EXIT_FAILURE);

}

int fd,ioarg;

if(-1 == (fd = open(DEV_NAME,O_RDWR)))

{

ioarg = ioctl(fd,LED_IOCGETDATA,&ioarg);

printf("canshu wei 1 shi duqu shu ju! %d n",ioarg);

}else{

ioarg = binstr_to_int(argv[1]);

ioctl(fd,LED_IOCSETDATA,&ioarg);

}

_exit(EXIT_SUCCESS);

}

/**********写好之后如何测试命令******************/

#arm-linux-gcc -o app_led app-led.c

#./app_led 1101 这样led就会根据你设置的数字来点亮对应的发光二极管

linux内核中led驱动的分布,Linux内核模块驱动之---led驱动相关推荐

  1. linux内核中kset是什么意思,Linux内核之设备驱动-底层数据结构kobject/kset

    Linux内核之设备驱动-底层数据结构kobject/kset kobject kobject是组成device.driver.bus.class的基本结构.如果把前者看成基类,则后者均为它的派生产物 ...

  2. Linux内核中的vfs,详解linux内核VFS

    虚拟文件系统(VFS) 在我看来, "虚拟"二字主要有两层含义: 1, 在同一个目录结构中, 可以挂载着若干种不同的文件系统. VFS隐藏了它们的实现细节, 为使用者提供统一的接口 ...

  3. linux内核中的GPIO系统之(4):pinctrl驱动的理解和总结

    1. 前言 本站之前的三篇文章[1][2][3]介绍了pin controller(对应的pin controller subsystem).gpio controller(对应的GPIO subsy ...

  4. Linux内核中的platform机制

    Linux内核中的platform机制 从Linux 2.6起引入了一套新的驱动管理和注册机制:platform_device和platform_driver.Linux中大部分的设备驱动,都可以使用 ...

  5. linux内核中锁有哪些,Linux内核中有哪些锁

    Linux内核中的各种锁 在LInux操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实象多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问.尤其是在多处理器系统上,更需 ...

  6. 深入浅出SCSI子系统(一)Linux 内核中的 SCSI 架构

    目录 深入浅出SCSI子系统(一)Linux 内核中的 SCSI 架构 1.Linux 内核中的 SCSI 架构描述 2.SCSI 较高层 3.SCSI 中间层 4.SCSI 较低层 深入浅出SCSI ...

  7. linux 内核flush,armv8(aarch64)linux内核中flush_dcache_all函数详细分析

    /* *  __flush_dcache_all() *  Flush the wholeD-cache. * Corrupted registers: x0-x7, x9-x11 */ ENTRY( ...

  8. 【Linux 内核】进程管理 ( 进程特殊形式 | 内核线程 | 用户线程 | C 标准库与 Linux 内核中进程相关概念 | Linux 查看进程命令及输出字段解析 )

    文章目录 一.进程特殊形式 ( 内核线程 | 用户线程 ) 二.C 标准库与 Linux 内核中进程相关概念 三.Linux 查看进程命令及输出字段解析 一.进程特殊形式 ( 内核线程 | 用户线程 ...

  9. TCP三次握手在linux内核中的实现

    TCP三次握手在linux内核中的实现 以下基于linux内核2.4.0源码(转自www.yuanma.org/) 以前一直使用的网络通讯的函数都是工作在阻塞模式.在看connect实现源码时,突然想 ...

  10. Linux内核中无名管道pipe和有名管道fifo的分析

    1.管道(pipe) 管道是进程间通信的主要手段之一.一个管道实际上就是个只存在于内存中的文件,对这个文件的操作要通过两个已经打开文件进行,它们分别代表管道的两端.管道是一种特殊的文件,它不属于某一种 ...

最新文章

  1. tomcat9扩展php 插件,Eclipse插件开发tomcat扩展
  2. ARM Exploitation
  3. MS-SQL CLR 直接读写文件,飞一般的感觉!
  4. 颜色空间,图像格式,彩色转灰度函数
  5. centos7.2 安装mysql5.6_Centos7安装mysql5.6
  6. sql GROUP BY子句使用实例
  7. mongodb集群数据同步及故障演练
  8. 1015.利用QxOrm库操作数据库示例代码
  9. 做风控的你,GPS数据有没有这样用?
  10. 苹果iPhone系列成交额5秒破亿;荣耀回应:没有安卓授权是假消息;魅族宣布将接入鸿蒙系统|极客头条...
  11. python植物大战僵尸辅助_Python自学入门,如何制作植物大战僵尸
  12. python图片提取文字软件_这款Python 库 4行代码提取图片中的文字
  13. python字符串转json(python字符串转浮点数)
  14. Python深度学习基础(二)——反向传递概念透彻解析以及Python手动实现
  15. 2020你还在烦恼网络硬盘选择吗?分享50个免费的云盘网盘服务——拥有无限储存空间
  16. google和edge浏览器崩溃,错误代码: STATUS_STACK_BUFFER_OVERRUN
  17. OpenCV-二值图像连通域分析
  18. [xiaoyi和你飞]博弈基础 ——[AB]
  19. for循环不执行的小坑
  20. lisp中窗选线_ssget怎样选择所有的多线lisp语言里面……

热门文章

  1. 5. MVC,MVP 和 MVVM
  2. 3.Magento的布局(Layout),块(Block)和模板(Template)
  3. 用css样式修改input控件和button控件
  4. QATF自动化测试框架
  5. Oracle中for update和for update nowait的区别
  6. 【linux】【git】git报错fatal: HTTP request failed
  7. hdu 6155 - Subsequence Count
  8. XidianOJ 1096 数的拆分
  9. 手动修改Sublime Text2 边栏Sidebar的样式
  10. 一次没想好,第二次就要想到做好