一、控制三盏灯的亮灭
头文件:
#ifndef __head_h__
#define __head_h__
typedef struct{
unsigned int moder;
unsigned int otyper;
unsigned int ospeedr;
unsigned int pupdr;
unsigned int idr;
unsigned int odr;
}gpio_t;
#define phy_led1_addr 0x50006000
#define phy_led2_addr 0x50007000
#define phy_led3_addr 0x50006000
#define phy_rcc_addr 0x50000a28
#endif
驱动程序:
#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/io.h>
#include"head.h"
int major;
char kbuf[128]={0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
int mycdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__file__,__func__,__line__);
return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
printk("%s:%s:%d\n",__file__,__func__,__line__);
unsigned long ret;
//向用户空间读取拷贝
if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
size=sizeof(kbuf);
ret=copy_to_user(ubuf,kbuf,size);
if(ret)//拷贝失败
{
printk("copy_to_user filed\n");
return ret;
}
return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{
unsigned long ret;
//从用户空间读取数据
if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
size=sizeof(kbuf);
ret=copy_from_user(kbuf,ubuf,size);
if(ret)//拷贝失败
{
printk("copy_to_user filed\n");
return ret;
}
switch(kbuf[0]){
case '1'://led1
if(kbuf[1]=='0')//关灯
vir_led1->odr &= (~(1<<10));
else//开灯
vir_led1->odr |= 1<<10;
break;
case '2'://led2
if(kbuf[1]=='0')//关灯
vir_led2->odr &= (~(1<<10));
else//开灯
vir_led2->odr |= 1<<10;
break;
case '3'://led3
if(kbuf[1]=='0')//关灯
vir_led3->odr &= (~(1<<8));
else//开灯
vir_led3->odr |= 1<<8;
break;
}
return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__file__,__func__,__line__);
return 0;
}
//定义操作方法结构体变量并赋值
struct file_operations fops={
.open=mycdev_open,
.read=mycdev_read,
.write=mycdev_write,
.release=mycdev_close,
};
int all_led_init(void)
{
//寄存器地址的映射
vir_led1=ioremap(phy_led1_addr,sizeof(gpio_t));
if(vir_led1==null)
{
printk("ioremap filed:%d\n",__line__);
return -enomem;
}
vir_led2=ioremap(phy_led2_addr,sizeof(gpio_t));
if(vir_led2==null)
{
printk("ioremap filed:%d\n",__line__);
return -enomem;
}
vir_led3=vir_led1;
vir_rcc=ioremap(phy_rcc_addr,4);
if(vir_rcc==null)
{
printk("ioremap filed:%d\n",__line__);
return -enomem;
}
printk("物理地址映射成功\n");
//寄存器的初始化
//rcc
(*vir_rcc) |= (3<<4);
//led1
vir_led1->moder &= (~(3<<20));
vir_led1->moder |= (1<<20);
vir_led1->odr &= (~(1<<10));
//led2
vir_led2->moder &= (~(3<<20));
vir_led2->moder |= (1<<20);
vir_led2->odr &= (~(1<<10));
//led3
vir_led3->moder &= (~(3<<16));
vir_led1->moder |= (1<<16);
vir_led1->odr &= (~(1<<8));
printk("寄存器初始化成功\n");
return 0;
}
static int __init mycdev_init(void)
{
//字符设备驱动注册
major=register_chrdev(0,"mychrdev",&fops);
if(major<0)
{
printk("字符设备驱动注册失败\n");
return major;
}
printk("字符设备驱动注册成功:major=%d\n",major);
//寄存器映射以及初始化
all_led_init();
return 0;
}
static void __exit mycdev_exit(void)
{
//取消地址映射
iounmap(vir_led1);
iounmap(vir_led2);
iounmap(vir_rcc);
//注销字符设备驱动
unregister_chrdev(major,"mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
module_license("gpl");
应用程序:
#include<stdlib.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
#include<string.h>
int main(int argc, char const *argv[])
{
char buf[128]={0};
int fd=open("/dev/mychrdev",o_rdwr);
if(fd<0)
{
printf("打开设备文件失败\n");
exit(-1);
}
while(1)
{
//从终端读取
printf("请输入两个字符\n");
printf("第一个字符:1(led1) 2(led2) 3(led3)\n");
printf("第二个字符:0(关灯) 1(开灯)\n");
printf("请输入>");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
//向设备文件中写
write(fd,buf,sizeof(buf));
}
close(fd);
return 0;
}
二、自动创建设备节点
1.创建设备节点的机制
2.udev创建设备节点的原理
3.创建设备节点的相关api
**************创建设备文件相关**********
#include<linux/device.h>
1.向上提交目录
struct class *class_create(struct module *owner, const char *name)
功能:向上提交目录信息,申请了一个struct class对象并初始化
参数:
owner:指向当前模块自身的一个指针,填写this_module
name:向上提交的目录名
返回值:成功返回申请到的class对象的首地址,失败返回一个指向内核顶层4k空间的指针
/*
关关于返回值判断问题:
只要判断指针的数值>4k预留空间起始值就说明函数调用失败
bool __must_check is_err(__force const void *ptr)
//#define is_err_value(x) (unsigned long)(void *)(x) >= 0xfffffff-4095
功能:判断指针是否指向内核4k预留空间,如果指针指向4k预留空间返回真,否则返回假
long __must_check ptr_err(__force const void *ptr)
功能:将一个指针转换成long类型错误码返回
*/
ex:
struct class *cls=class_create(this_module,"mychrdev");
if(is_err(cls))
{
printk("向上提交目录失败\n");
return -ptr_err(cls);
}
2.向上提交设备节点信息
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
功能:向上提交设备节点信息,申请一个struct device对象并初始化
参数:
class:class_create()得到的对象指针
parent:父节点指针,不知道就填null
devt:设备号 主设备号<<20|次设备号
/*
mkdev(主设备号,次设备号):根据主设备号和次设备号得到设备号
major(dev):根据设备号得到主设备号
minor(dev):根据设备号得到次设备号
*/
drvdata:当前对象的一个私有数据,填null
fmt:填要创建的设备节点名 video%d
...:不定长参数 i
返回值:成功返回创建成功的struct device对象指针,失败返回指针指向4k预留空间
************删除设备文件相关**********
销毁节点信息:
void device_destroy(struct class *class, dev_t devt)
功能:销毁节点信息
参数:
class:class_create()得到的对象指针
devt:向上提交设备节点是填写的设备号
销毁目录
void class_destroy(struct class *cls)
参数:class_create()得到的对象指针
返回值:无
4.实例
#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/io.h>
#include<linux/device.h>
#include"head.h"
int major;
char kbuf[128]={0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
int mycdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__file__,__func__,__line__);
return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
printk("%s:%s:%d\n",__file__,__func__,__line__);
unsigned long ret;
//向用户空间读取拷贝
if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
size=sizeof(kbuf);
ret=copy_to_user(ubuf,kbuf,size);
if(ret)//拷贝失败
{
printk("copy_to_user filed\n");
return ret;
}
return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{
unsigned long ret;
//从用户空间读取数据
if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
size=sizeof(kbuf);
ret=copy_from_user(kbuf,ubuf,size);
if(ret)//拷贝失败
{
printk("copy_to_user filed\n");
return ret;
}
switch(kbuf[0]){
case '1'://led1
if(kbuf[1]=='0')//关灯
vir_led1->odr &= (~(1<<10));
else//开灯
vir_led1->odr |= 1<<10;
break;
case '2'://led2
if(kbuf[1]=='0')//关灯
vir_led2->odr &= (~(1<<10));
else//开灯
vir_led2->odr |= 1<<10;
break;
case '3'://led3
if(kbuf[1]=='0')//关灯
vir_led3->odr &= (~(1<<8));
else//开灯
vir_led3->odr |= 1<<8;
break;
}
return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__file__,__func__,__line__);
return 0;
}
//定义操作方法结构体变量并赋值
struct file_operations fops={
.open=mycdev_open,
.read=mycdev_read,
.write=mycdev_write,
.release=mycdev_close,
};
int all_led_init(void)
{
//寄存器地址的映射
vir_led1=ioremap(phy_led1_addr,sizeof(gpio_t));
if(vir_led1==null)
{
printk("ioremap filed:%d\n",__line__);
return -enomem;
}
vir_led2=ioremap(phy_led2_addr,sizeof(gpio_t));
if(vir_led2==null)
{
printk("ioremap filed:%d\n",__line__);
return -enomem;
}
vir_led3=vir_led1;
vir_rcc=ioremap(phy_rcc_addr,4);
if(vir_rcc==null)
{
printk("ioremap filed:%d\n",__line__);
return -enomem;
}
printk("物理地址映射成功\n");
//寄存器的初始化
//rcc
(*vir_rcc) |= (3<<4);
//led1
vir_led1->moder &= (~(3<<20));
vir_led1->moder |= (1<<20);
vir_led1->odr &= (~(1<<10));
//led2
vir_led2->moder &= (~(3<<20));
vir_led2->moder |= (1<<20);
vir_led2->odr &= (~(1<<10));
//led3
vir_led3->moder &= (~(3<<16));
vir_led1->moder |= (1<<16);
vir_led1->odr &= (~(1<<8));
printk("寄存器初始化成功\n");
return 0;
}
static int __init mycdev_init(void)
{
//字符设备驱动注册
major=register_chrdev(0,"mychrdev",&fops);
if(major<0)
{
printk("字符设备驱动注册失败\n");
return major;
}
printk("字符设备驱动注册成功:major=%d\n",major);
//向上提交目录
cls=class_create(this_module,"mychrdev");
if(is_err(cls))
{
printk("向上提交目录失败\n");
return -ptr_err(cls);
}
printk("向上提交目录成功\n");
//向上提交设备节点信息
int i;//向上提交三次设备节点信息
for(i=0;i<3;i++)
{
dev=device_create(cls,null,mkdev(major,i),null,"myled%d",i);
if(is_err(dev))
{
printk("向上提交设备节点失败\n");
return -ptr_err(dev);
}
}
printk("向上提交设备节点成功\n");
//寄存器映射以及初始化
all_led_init();
return 0;
}
static void __exit mycdev_exit(void)
{
//取消地址映射
iounmap(vir_led1);
iounmap(vir_led2);
iounmap(vir_rcc);
//销毁设备节点信息
int i;
for(i=0;i<3;i++)
{
device_destroy(cls,mkdev(major,i));
}
//销毁目录
class_destroy(cls);
//注销字符设备驱动
unregister_chrdev(major,"mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
module_license("gpl");
三、ioctl函数的使用
1.引入ioctl函数的意义
2.ioctl函数的分析
*********系统调用函数的分析**********
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
功能:进行io功能的设置
参数:
fd:文件描述符
request:io控制的功能码
...:可以加,也可以不加。如果第三个参数传递数值,只能传递整型数据和指针
返回值:成功返回0,失败返回错误码
*********驱动中操作方法的分析********
long (*unlocked_ioctl) (struct file *file, unsigned int cmd, unsigned long arg)
{
参数分析:
file:文件指针
cmd:应用程序中的ioctl第二个参数传递过来
arg:应用程序中的ioctl第三个参数传递过来
}
3.ioctl功能码的构建
====== ==================================
bits meaning
====== ==================================
31-30 00 - no parameters: uses _io macro
10 - read: _ior
01 - write: _iow
11 - read/write: _iowr
29-16 size of arguments
15-8 ascii character supposedly
unique to each driver
7-0 function #
====== ==================================
#define _io(type,nr) _ioc(_ioc_none,(type),(nr),0)
#define _ior(type,nr,size) _ioc(_ioc_read,(type),(nr),sizeof(size))
#define _iow(type,nr,size) _ioc(_ioc_write,(type),(nr),sizeof(size))
#define _iowr(type,nr,size) _ioc(_ioc_read|_ioc_write,(type),(nr),sizeof(size)
#define _ioc(dir,type,nr,size) \
((unsigned int) \
(((dir) << _ioc_dirshift) | \
((type) << _ioc_typeshift) | \
((nr) << _ioc_nrshift) | \
((size) << _ioc_sizeshift)))
ex:构建led开关的功能码:ioctl函数无第三个参数
开灯 #define led_on _io('l',1)
关灯 #define led_off _io('l',0)
ex:构建led开关的功能码:ioctl函数有第三个参数
开灯 #define led_on _iow('l',1,int)
关灯 #define led_off _iow('l',0,int)
4.ioctl实例----不传递第三个参数
头文件:
#ifndef __head_h__
#define __head_h__
typedef struct
{
unsigned int moder;
unsigned int otyper;
unsigned int ospeedr;
unsigned int pupdr;
unsigned int idr;
unsigned int odr;
} gpio_t;
#define phy_led1_addr 0x50006000
#define phy_led2_addr 0x50007000
#define phy_led3_addr 0x50006000
#define phy_rcc_addr 0x50000a28
// 构建开灯关灯的功能码
#define led_on _io('l', 1)
#define led_off _io('l', 0)
#endif
驱动程序:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include "head.h"
int major;
char kbuf[128] = {0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
int mycdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __file__, __func__, __line__);
return 0;
}
long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch (cmd)
{
case led_on: // 开灯
vir_led1->odr |= (0x1 << 10);
vir_led2->odr |= (0x1 << 10);
vir_led3->odr |= (0x1 << 8);
break;
case led_off: // 关灯
vir_led1->odr &= (~(0x1 << 10));
vir_led2->odr &= (~(0x1 << 10));
vir_led3->odr &= (~(0x1 << 8));
break;
}
return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __file__, __func__, __line__);
return 0;
}
// 定义操作方法结构体变量并赋值
struct file_operations fops = {
.open = mycdev_open,
.unlocked_ioctl = mycdev_ioctl,
.release = mycdev_close,
};
int all_led_init(void)
{
// 寄存器地址的映射
vir_led1 = ioremap(phy_led1_addr, sizeof(gpio_t));
if (vir_led1 == null)
{
printk("ioremap filed:%d\n", __line__);
return -enomem;
}
vir_led2 = ioremap(phy_led2_addr, sizeof(gpio_t));
if (vir_led2 == null)
{
printk("ioremap filed:%d\n", __line__);
return -enomem;
}
vir_led3 = vir_led1;
vir_rcc = ioremap(phy_rcc_addr, 4);
if (vir_rcc == null)
{
printk("ioremap filed:%d\n", __line__);
return -enomem;
}
printk("物理地址映射成功\n");
// 寄存器的初始化
// rcc
(*vir_rcc) |= (3 << 4);
// led1
vir_led1->moder &= (~(3 << 20));
vir_led1->moder |= (1 << 20);
vir_led1->odr &= (~(1 << 10));
// led2
vir_led2->moder &= (~(3 << 20));
vir_led2->moder |= (1 << 20);
vir_led2->odr &= (~(1 << 10));
// led3
vir_led3->moder &= (~(3 << 16));
vir_led1->moder |= (1 << 16);
vir_led1->odr &= (~(1 << 8));
printk("寄存器初始化成功\n");
return 0;
}
static int __init mycdev_init(void)
{
// 字符设备驱动注册
major = register_chrdev(0, "mychrdev", &fops);
if (major < 0)
{
printk("字符设备驱动注册失败\n");
return major;
}
printk("字符设备驱动注册成功:major=%d\n", major);
// 向上提交目录
cls = class_create(this_module, "mychrdev");
if (is_err(cls))
{
printk("向上提交目录失败\n");
return -ptr_err(cls);
}
printk("向上提交目录成功\n");
// 向上提交设备节点信息
int i; // 向上提交三次设备节点信息
for (i = 0; i < 3; i++)
{
dev = device_create(cls, null, mkdev(major, i), null, "myled%d", i);
if (is_err(dev))
{
printk("向上提交设备节点失败\n");
return -ptr_err(dev);
}
}
printk("向上提交设备节点成功\n");
// 寄存器映射以及初始化
all_led_init();
return 0;
}
static void __exit mycdev_exit(void)
{
// 取消地址映射
iounmap(vir_led1);
iounmap(vir_led2);
iounmap(vir_rcc);
// 销毁设备节点信息
int i;
for (i = 0; i < 3; i++)
{
device_destroy(cls, mkdev(major, i));
}
// 销毁目录
class_destroy(cls);
// 注销字符设备驱动
unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
module_license("gpl");
应用程序:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "head.h"
int main(int argc, char const *argv[])
{
char buf[128] = {0};
int a;
int fd = open("/dev/myled0", o_rdwr);
if (fd < 0)
{
printf("打开设备文件失败\n");
exit(-1);
}
while (1)
{
// 从终端读取
printf("请输入要实现的功能 ");
printf("0(关灯) 1(开灯)\n");
printf("请输入>>>");
scanf("%d", &a);
switch (a)
{
case 1:
ioctl(fd, led_on);
break;
case 0:
ioctl(fd, led_off);
break;
}
}
close(fd);
return 0;
}
5.ioctl实例----传递第三个参数(传递整型)
头文件:
#ifndef __head_h__
#define __head_h__
typedef struct
{
unsigned int moder;
unsigned int otyper;
unsigned int ospeedr;
unsigned int pupdr;
unsigned int idr;
unsigned int odr;
} gpio_t;
#define phy_led1_addr 0x50006000
#define phy_led2_addr 0x50007000
#define phy_led3_addr 0x50006000
#define phy_rcc_addr 0x50000a28
// 构建开灯关灯的功能码
#define led_on _iow('l', 1, int)
#define led_off _iow('l', 0, int)
#endif
驱动程序:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include "head.h"
int major;
char kbuf[128] = {0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
int mycdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __file__, __func__, __line__);
return 0;
}
long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch (cmd)
{
case led_on: // 开灯
switch (arg)
{
case 1: // led1
vir_led1->odr |= (0x1 << 10);
break;
case 2:
vir_led2->odr |= (0x1 << 10);
break;
case 3:
vir_led3->odr |= (0x1 << 8);
break;
}
break;
case led_off: // 关灯
switch (arg)
{
case 1: // led1
vir_led1->odr &= (~(0x1 << 10));
break;
case 2:
vir_led2->odr &= (~(0x1 << 10));
break;
case 3:
vir_led3->odr &= (~(0x1 << 8));
break;
}
break;
}
return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __file__, __func__, __line__);
return 0;
}
// 定义操作方法结构体变量并赋值
struct file_operations fops = {
.open = mycdev_open,
.unlocked_ioctl = mycdev_ioctl,
.release = mycdev_close,
};
int all_led_init(void)
{
// 寄存器地址的映射
vir_led1 = ioremap(phy_led1_addr, sizeof(gpio_t));
if (vir_led1 == null)
{
printk("ioremap filed:%d\n", __line__);
return -enomem;
}
vir_led2 = ioremap(phy_led2_addr, sizeof(gpio_t));
if (vir_led2 == null)
{
printk("ioremap filed:%d\n", __line__);
return -enomem;
}
vir_led3 = vir_led1;
vir_rcc = ioremap(phy_rcc_addr, 4);
if (vir_rcc == null)
{
printk("ioremap filed:%d\n", __line__);
return -enomem;
}
printk("物理地址映射成功\n");
// 寄存器的初始化
// rcc
(*vir_rcc) |= (3 << 4);
// led1
vir_led1->moder &= (~(3 << 20));
vir_led1->moder |= (1 << 20);
vir_led1->odr &= (~(1 << 10));
// led2
vir_led2->moder &= (~(3 << 20));
vir_led2->moder |= (1 << 20);
vir_led2->odr &= (~(1 << 10));
// led3
vir_led3->moder &= (~(3 << 16));
vir_led1->moder |= (1 << 16);
vir_led1->odr &= (~(1 << 8));
printk("寄存器初始化成功\n");
return 0;
}
static int __init mycdev_init(void)
{
// 字符设备驱动注册
major = register_chrdev(0, "mychrdev", &fops);
if (major < 0)
{
printk("字符设备驱动注册失败\n");
return major;
}
printk("字符设备驱动注册成功:major=%d\n", major);
// 向上提交目录
cls = class_create(this_module, "mychrdev");
if (is_err(cls))
{
printk("向上提交目录失败\n");
return -ptr_err(cls);
}
printk("向上提交目录成功\n");
// 向上提交设备节点信息
int i; // 向上提交三次设备节点信息
for (i = 0; i < 3; i++)
{
dev = device_create(cls, null, mkdev(major, i), null, "myled%d", i);
if (is_err(dev))
{
printk("向上提交设备节点失败\n");
return -ptr_err(dev);
}
}
printk("向上提交设备节点成功\n");
// 寄存器映射以及初始化
all_led_init();
return 0;
}
static void __exit mycdev_exit(void)
{
// 取消地址映射
iounmap(vir_led1);
iounmap(vir_led2);
iounmap(vir_rcc);
// 销毁设备节点信息
int i;
for (i = 0; i < 3; i++)
{
device_destroy(cls, mkdev(major, i));
}
// 销毁目录
class_destroy(cls);
// 注销字符设备驱动
unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
module_license("gpl");
应用程序:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "head.h"
int main(int argc, char const *argv[])
{
char buf[128] = {0};
int a, b;
int fd = open("/dev/myled0", o_rdwr);
if (fd < 0)
{
printf("打开设备文件失败\n");
exit(-1);
}
while (1)
{
// 从终端读取
printf("请输入要实现的功能 ");
printf("0(关灯) 1(开灯)\n");
printf("请输入>>>");
scanf("%d", &a);
printf("请选择要控制的灯:1(led1)2(led2) 3(led3)\n");
printf("请输入>>>");
scanf("%d", &b);
switch (a)
{
case 1:
ioctl(fd, led_on, b);
break;
case 0:
ioctl(fd, led_off, b);
break;
}
}
close(fd);
return 0;
}
6.ioctl实例----传递第三个参数(传递地址)
头文件:
#ifndef __head_h__
#define __head_h__
typedef struct
{
unsigned int moder;
unsigned int otyper;
unsigned int ospeedr;
unsigned int pupdr;
unsigned int idr;
unsigned int odr;
} gpio_t;
#define phy_led1_addr 0x50006000
#define phy_led2_addr 0x50007000
#define phy_led3_addr 0x50006000
#define phy_rcc_addr 0x50000a28
// 构建开灯关灯的功能码
#define led_on _iow('l', 1,int)
#define led_off _iow('l', 0,int)
#endif
驱动程序:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include "head.h"
int major;
char kbuf[128] = {0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
int mycdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __file__, __func__, __line__);
return 0;
}
long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int which;
//获取应用程序中b的值
int ret= copy_from_user(&which,(void *)arg,4);
if(ret)
{
printk("copy_from_user filed\n");
return-eio;
}
switch (cmd)
{
case led_on: // 开灯
switch (which)
{
case 1: // led1
vir_led1->odr |= (0x1 << 10);
break;
case 2:
vir_led2->odr |= (0x1 << 10);
break;
case 3:
vir_led3->odr |= (0x1 << 8);
break;
}
break;
case led_off: // 关灯
switch (which)
{
case 1: // led1
vir_led1->odr &= (~(0x1 << 10));
break;
case 2:
vir_led2->odr &= (~(0x1 << 10));
break;
case 3:
vir_led3->odr &= (~(0x1 << 8));
break;
}
break;
}
return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __file__, __func__, __line__);
return 0;
}
// 定义操作方法结构体变量并赋值
struct file_operations fops = {
.open = mycdev_open,
.unlocked_ioctl = mycdev_ioctl,
.release = mycdev_close,
};
int all_led_init(void)
{
// 寄存器地址的映射
vir_led1 = ioremap(phy_led1_addr, sizeof(gpio_t));
if (vir_led1 == null)
{
printk("ioremap filed:%d\n", __line__);
return -enomem;
}
vir_led2 = ioremap(phy_led2_addr, sizeof(gpio_t));
if (vir_led2 == null)
{
printk("ioremap filed:%d\n", __line__);
return -enomem;
}
vir_led3 = vir_led1;
vir_rcc = ioremap(phy_rcc_addr, 4);
if (vir_rcc == null)
{
printk("ioremap filed:%d\n", __line__);
return -enomem;
}
printk("物理地址映射成功\n");
// 寄存器的初始化
// rcc
(*vir_rcc) |= (3 << 4);
// led1
vir_led1->moder &= (~(3 << 20));
vir_led1->moder |= (1 << 20);
vir_led1->odr &= (~(1 << 10));
// led2
vir_led2->moder &= (~(3 << 20));
vir_led2->moder |= (1 << 20);
vir_led2->odr &= (~(1 << 10));
// led3
vir_led3->moder &= (~(3 << 16));
vir_led1->moder |= (1 << 16);
vir_led1->odr &= (~(1 << 8));
printk("寄存器初始化成功\n");
return 0;
}
static int __init mycdev_init(void)
{
// 字符设备驱动注册
major = register_chrdev(0, "mychrdev", &fops);
if (major < 0)
{
printk("字符设备驱动注册失败\n");
return major;
}
printk("字符设备驱动注册成功:major=%d\n", major);
// 向上提交目录
cls = class_create(this_module, "mychrdev");
if (is_err(cls))
{
printk("向上提交目录失败\n");
return -ptr_err(cls);
}
printk("向上提交目录成功\n");
// 向上提交设备节点信息
int i; // 向上提交三次设备节点信息
for (i = 0; i < 3; i++)
{
dev = device_create(cls, null, mkdev(major, i), null, "myled%d", i);
if (is_err(dev))
{
printk("向上提交设备节点失败\n");
return -ptr_err(dev);
}
}
printk("向上提交设备节点成功\n");
// 寄存器映射以及初始化
all_led_init();
return 0;
}
static void __exit mycdev_exit(void)
{
// 取消地址映射
iounmap(vir_led1);
iounmap(vir_led2);
iounmap(vir_rcc);
// 销毁设备节点信息
int i;
for (i = 0; i < 3; i++)
{
device_destroy(cls, mkdev(major, i));
}
// 销毁目录
class_destroy(cls);
// 注销字符设备驱动
unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
module_license("gpl");
应用程序:
#include<stdlib.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include<unistd.h>
#include<string.h>
#include "head.h"
int main(int argc, char const *argv[])
{
char buf[128]={0};
int a,b;
int fd=open("/dev/myled0",o_rdwr);
if(fd<0)
{
printf("打开设备文件失败\n");
exit(-1);
}
while(1)
{
//从终端读取
printf("请输入要实现的功能 ");
printf("0(关灯) 1(开灯)\n");
printf("请输入>");
scanf("%d",&a);
printf("请选择要控制的灯:1(led1)2(led2) 3(led3)\n");
printf("请输入>");
scanf("%d",&b);
switch(a)
{
case 1:
ioctl(fd,led_on,&b);
break;
case 0:
ioctl(fd,led_off,&b);
break;
}
}
close(fd);
return 0;
}
发表评论