Linux驱动开发-字符设备控制技术笔记 3
Posted hntea-hong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux驱动开发-字符设备控制技术笔记 3相关的知识,希望对你有一定的参考价值。
字符设备控制技术
笔记要做的自己看起来舒服和有头绪,这不又折腾切换编辑器来从新排版,有强迫症啊!对于字符控制,很多时候编写上层应用程序时,使用ioctl系统调用来控制设备,原型如下:
/*
fd: 要控制的设备文件描述符
cmd: 发送给设备的控制命令
…: 第3个参数是可选的参数,存在与否是依赖于控制命令(第2 个参数)。
*/
int ioctl(int fd,unsigned long cmd,...);
但是在Linux2.6.36之后的
/*
c) 参数说明
fiel:打开的设备描述符
cmd:传递的命令
arg:命令的个数
*/
long (*unlocked_ioctl) (struct file *file, unsigned int cmd, unsigned long arg);
long (*compat_ioctl) (struct file *file, unsigned int cmd, unsigned long arg);
d) 注意:
- 为了防止对错误设备使用正确的命令,命令号应在系统范围内是唯一的
- 既每个命令号都应该由多个位段组成
- 定义号码的方法使用四个位段
位段详解
a) 头文件:<linux/ioctl.h>
b) type(类型)
i. 幻数(magic number):选择系统中没有使用的号码,并在整个驱动程序中使用这个号码。
ii. 8位宽度(_IOC_TYPEBITS)
iii. 源码的/Document目录下magic-num.txt 记录了当前已经使用的幻数
c) number(号码)
i. 序数:8位宽度(_IOC_NRBITS)
d) direct(方向)
i. 如果该命令有数据传输。则它定义数据传输方向
_IOC_NONE:没有传输方向
_IOC_READ:
_IOC_WRITE:
_IOC_READ|_IOC_WRITE:双向传输
e) size(尺寸)
i. 涉及数据的大小
ii. 如果想要驱动可移植,则只能认为最大尺寸可达255字节
iii. 如果驱动程序需要更大的尺度的数据传输,则可忽略这个字段
4.构造命令号码的宏
/*
type:幻数
nr:命令编号
*/
_IO(type,nr); //定义一个没有数据传输的命令号编号
/*
type:幻数
nr:命令编号
size:数据大小
*/
_IOR(type,nr,size):定义一个读数据的命令编号;从设备读取参数
_IOW(type,nr,size):定义一个写数据的命令编号;向设备写入参数
_IOWR(type,nr,size):定义一个可读可写的命名编号
_IOC_DIR(nr):获得命令编号中的数据传输方向
_IOC_TYPE(nr):获取命令编号中的幻数
_IOC_NR(nr):获取命令编号中的号码
_IOC_SIZE(nr):获取命令编号中的尺寸
5.例:
a) 新建控制文件:led_ioctl.h;编写一下代码
#define LED_MAGIC ‘L’
#define LED_ON _IOW(LED_MAGIC,1,int)
#define LED_OFF _IOW(LED_MAGIC,0,int)
b) 引用头文件“led_ioctl.h”
c) 驱动程序在相应的文件中可以使用这些数据
代码架构:
static int xxx_ioctl(struct file* filp,unsigned int cmd,unsignend long arg)
if(_IOC_TYPE(cmd) != xxx_IOC_MAGIC) /*驱动的幻数,我们在头文件中定义的*/
return -ENOTTY;
if(_IOC_NR(cmd) >= xxx_MAXNR)
return -ENOTTY;
switch(cmd)
case LED_ON:
处理;
break;
case LED_OFF:
处理;
break;
default:
return -ENOTTY;
break;
应用程序测试
代码架构:
#include<fcntl.h>
#include<sys/ioctl.h>
#include “led_ioctl.h”
/*
参数说明:
arg:参数个数(注意,在终端输入执行命令时已近占据0号参数,我们自己的 参数从1开始)
argv:参数内容
*/
int main(int arg,unsigned char* argv)
int fd=0;
int ret=0;
/*处理终端传递过来的参数*/
atio();//将终端输入的字符串转换成int型。
fd = open(“驱动设备文件”,打开模式);
判断是否成功;
/*更具需要调用读写,控制函数,这里调用ioctl()*/
ret = ioctl(fd,cmd,...);
close(fd);
LED 控制驱动程序设计
第一步:申明控制号,保存文件为 led_ioctl.h
#define LED_MAGIC 'L'
#define TURNON_LED _IOW(LED_MAGIC,1,int)
#define TURNOFF_LED _IOW(LED_MAGIC,0,int)
第二步:编写驱动
/**********************************************
作者:hntea
时间:2016/3/9
功能:led 字符设备驱动+ioctl
**********************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fcntl.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/ioctl.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include "led_ioctl.h"
#define DEV_NAME "led"
#define MAJOR_NR 10
#define MINIR_NR 1
#define LED_ON 0x00000018
#define LED_OFF 0X00000000
#define CON_ADDR 0xE0200060
#define DAT_ADDR 0xE0200064
struct cdev led_dev; /*静态分配设备号*/
dev_t dev_nm; /*设备号*/
/*************************************************
函数名: led_hardinit 实现
函数参数:
函数功能:led硬件初始化
*************************************************/
static void led_hardinit(void)
unsigned int tmp = 0;
unsigned int *led_config;
/*找出物理地址对应的虚拟地址*/
led_config = ioremap(CON_ADDR,4); /*该寄存器是32位数值*/
/*读取当前寄存器状态*/
#if 0
tmp = ioread32(led_config);
tmp &= (~(0x11 << 3));
tmp |= (0x11 << 3);
#endif
tmp = 0x11000;
/*寄存器设置写回*/
iowrite32(tmp,led_config);
static void led_on(void)
unsigned int *led_data;
led_data = ioremap(DAT_ADDR,4);
iowrite32(LED_ON,led_data);
printk("revice cmd:ledon\\n");
static void led_off(void)
unsigned int *led_data;
led_data = ioremap(DAT_ADDR,4);
iowrite32(LED_OFF,led_data);
printk("revice cmd:ledoff\\n");
/*************************************************
函数名: file_operations 实现
函数参数:
函数功能:
*************************************************/
static int led_open (struct inode *inode, struct file *fp)
int ret = 0;
/*led 打开时只需要初始化相关寄存器就够*/
led_hardinit();
return ret;
static int led_release (struct inode *inode, struct file *fp)
return 0;
static long led_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
/*这里可以使用命令解析宏解析幻数再判断,使代码逻辑更严谨*/
/*命令解析*/
switch(cmd)
case TURNON_LED:
led_on();
break;
case TURNOFF_LED:
led_off();
break;
default:
return -ENOTTY;
break;
return 0;
/*************************************************
函数名: led_write 实现
函数参数:
函数功能:向led设备文件写如数据
*************************************************/
static ssize_t led_write(struct file *fp, const char __user *buf, size_t count, loff_t *f_pos)
unsigned char count_tmp = count ; /*获取写入字节数*/
unsigned char led_sta[2]; /*用来存放两个led的状态*/
int i = 0;
memset(led_sta,0,2); /*数据清零*/
if( count > 2)
count_tmp = 2;
/*将数据从用户空间复制到内核空间*/
if(copy_from_user(led_sta,buf,count_tmp))
return -EFAULT;else
/*控制两个led的状态*/
for(i=0;i<2;i++)
/*读取当前led寄存器状态,严谨就要处理,防止数据破话而使系统奔溃*/
if( led_sta[i] == '1' )
led_on();
else
led_off();
return 0;
struct file_operations ledfp=
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
.unlocked_ioctl = led_ioctl,
.release = led_release,
;
struct cdev cdev=
.owner = THIS_MODULE,
.ops = &ledfp,
;
static int ledInit(void)
int ret = 0;
/*1.分配设备号,0是以该数字作为主设备号分配的起始,2是次设备号(用来标识设备的个数)*/
if((ret = alloc_chrdev_region(&dev_nm, 0, 1,DEV_NAME))<0)
printk("device num alloc err!\\n");
/*2.分配设备结构;静态分配一个全局变量*/
printk("led major number is %d\\n",MAJOR(dev_nm));
printk("led minor number is %d\\n",MINOR(dev_nm));
/*3.初始化设备结构*/
cdev_init(&cdev, &ledfp);
/*4.注册设备*/
if((ret = cdev_add(&cdev,dev_nm, 1)) < 0) /*最后一个参数说明设备数*/
printk("cdev add err!\\n");
/*5.创建设备文件,现在使用 手工创建mknod*/
//device_create();
printk("led.ko insmod success!\\n");
return 0;
static void ledexit(void)
/*2.注销设备号,注销设备*/
cdev_del(&cdev); /*注销设备*/
unregister_chrdev_region(dev_nm, 1); /*释放设备号*/
printk("led device rmmod success!\\n");
MODULE_AUTHOR("hntea");
MODULE_LICENSE("GPL");
module_init(ledInit);
module_exit(ledexit);
加载驱动:手动创建 /dev/led 节点文件
mknod /dev/led c 主设备号 次设备号
第三步:编译测试应用
#include<sys/fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/ioctl.h>
#include<stdio.h>
#include "led_ioctl.h"
#define DEVICE_FILE "/dev/led"
int main(int arg,char **argv)
int fd = 0 ;
int cmd = 0;
int ret = 0;
/*解析传递进来的参数*/
if(arg < 2)
printf("Please input : <cmd> <0/1>\\n");
return -1;
/*格式化*/
cmd = atoi((argv[1]));
/*设备操作*/
if((fd = open(DEVICE_FILE,O_RDWR))< 0)
printf("File open err!\\n");
switch(cmd)
case 0:
ret = ioctl(fd,TURNOFF_LED);
break;
case 1:
ret = ioctl(fd,TURNON_LED);
break;
default:
printf("Command er!\\n");
return -1;
close(fd);
以上是关于Linux驱动开发-字符设备控制技术笔记 3的主要内容,如果未能解决你的问题,请参考以下文章