Note06_01_LED驱动_ioctl传输命令

Note06_01_LED驱动_ioctl传输命令驱动

目录

Led硬件电路图

Led GPIO引脚控制寄存器

Led内存映射函数

Led驱动程序框架

1)杂项设备驱动简介

2)杂项设备驱动注册函数

3)ioctl 控制命令实现

驱动实现流程梳理

1)驱动程序入口实现

2)驱动程序出口实现

3)驱动程序fops操作接口实现

Led驱动测试程序

Led驱动程序实现

Led 测试程序实现

总结

1)左移运算符 (<<)

2)Ioctl 系统调用实质:


        本文采用内存映射的方式,将LED的寄存器地址(物理内存)映射为虚拟内存,然后通过读写虚拟地址的形式,对LED 的寄存器进行端口初始化、和状态读取、设置;    然后通过注册 misc 杂项设备驱动的形式,将LED灯注册为杂项设备(主设备号为10,次设备号xx);再通过 ioctl 系统调用 由用户层发送控制命令到内存层,实现对LED的控制。

  • Led硬件电路图

  1. 如下图所示,当LED1——LED4引脚为低电平时,LED灯亮;否则熄灭;即低电平有效使能。

 

  1. 如下图所示,LED1——LED4 受控的GPIO引脚为GPM4_0——GPM4_4引脚。

 

  • Led GPIO引脚控制寄存器

因控制LED灯,当为低电平时灯亮,所以设置4个LED灯的GPIO引脚为输出模式,且初始化引脚电平为高电平,否则LED灯会点亮;

 10 #define GPM4BASE 0x11000000

 11 #define GPM4CON  0x2e0

 12 #define GPM4DAT  0x2e4

 

1)GPM4CON:

GPIO引脚功能控制寄存器:输入、输出、模块功能引脚

LED1——LED4对应的控制SFR引脚为GPM4CON[0——3],因需设置该引脚输出低电平时,灯才能点亮,故需配置为输出模式,即GPM4CON |= 0x00001111;

2)GPM4DAT:

当配置为输入时,则读取该位引脚的电平,反之设置引脚电平;

LED1——LED4对应的数据SFR引脚为GPM4DAT[0——3],因需设置该引脚输出低电平时,灯才能点亮,故需初始化led引脚电平为高电平,即GPM4DAT |= 0x0f;

 

3)GPM4PUD:

上下拉电平使能/禁止

从LED硬件电路图可知,已有1K的上拉电阻,且起到限流作用,故此SFR不设置

 

4)特别注意:

裸机操作寄存器,类似于直接操作内存,CPU访问的是物理地址;

但当开启了 mmc 之后,CPU访问的是虚拟地址,所以需要内存转换;详见下个章节。

 

  • Led内存映射函数

内核启动以后,CPU所访问的地址都是虚拟地址,而内核态的地址,不是平板映射,所以内核态将物理地址映射成和内核态对应的内核的虚拟地址;

1)ioremap(): 物理地址映射成内核态的虚拟地址

2)iounmap(): 移除内核态物理地址映射

3)读、写SFR 对应的虚拟地址的函数接口:

/arch/arm/include/asm/io.h文件中,ioread32()、ioread16()、ioread8(),iowrite32()、iowrite16()、iowrite8()等函数接口;注意,写操作时,必须先读取SFR中原有的数据,然后在设置所需控制的数据位后,再写入,避免直接写入影响其他功能。

设置LED1——LED4的GPIO引脚模式为输出模式

初始化LED1——LED4的初始电平为高电平

  • Led驱动程序框架

1)杂项设备驱动简介

Linux把无法归类的五花八门的设备定义为杂项设备,用miscdevice结构体进行描述;比如lcd的字符设备、内核共享内存(NVRAM?)、看门狗、LED等设备驱动;

杂项设备共享一个主设备号为10,而次设备号不同;所有的杂项设备形成一个链表,对设备访问时内核根据次设备号查找对应的杂项设备,然后调用其fops结构张哦功能注册的文件操作接口进行操作。

Cat /proc/devices:杂项设备驱动使用统一个主设备号10

Cat /proc/misc:杂项设备,一个主设备号对应一套驱动

2)杂项设备驱动注册函数

#define DEVNAME "leds"

//杂项设备注册
misc_register(&misc);

//杂项设备文件结构体
struct miscdevice
{
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
}


//初始化杂项设备结构体;
static struct miscdevice misc = { 
    .minor  = MISC_DYNAMIC_MINOR,
    .name   = DEVNAME,
    .fops   = &fops,    
};

3)ioctl 控制命令实现

Ioctl命令:命令码是一个 32位编码描述;

                                                                        Ioctl命令码

含义

位数

详细描述

设备类型(type)

8bit

一个“幻数”,可以是0~0xff之间,也可以市一个字母,区分大小写,但不能和其他设备已定义的幻数冲突

序列号(nr)

8bit

顺序编号

方向(隐含)

2bit

数据传送方向,是从应用程序角度来看的;无参数、读、写、读写双向

数据尺寸(size)

13/14bit

涉及的用户数据的大小

—————————————————————————————————————————————
bit31~bit30  2位为 “区别读写” 区,作用是区分是读取命令还是写入命令。
bit29~bit15  14位为 "数据大小" 区,表示 ioctl() 中的 arg 变量传送的内存大小。
bit20~bit08   8位为 “魔数"(也称为"幻数")区,这个值用以与其它设备驱动程序的 ioctl 命令进行区别。
bit07~bit00   8位为 "区别序号" 区,是区分命令的命令顺序序号。

_IOC_NR(), _IOC_TYPE(), _IOC_SIZE(), _IOC_DIR()  这几个宏用来取得 cmd 命令中的域,其作用如下:
_IOC_NR()  :  读取基数域值 (bit0~ bit7)
_IOC_TYPE :  读取魔数域值 (bit8 ~ bit15)
_IOC_SIZE  :  读取数据大小域值 (bit16 ~ bit29)
_IOC_DIR    :  获取读写属性域值 (bit30 ~ bit31)

_IOC_NR() 的定义为:

#define _IOC_NR(nr)        (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
_IOC_NRSHIFT 的值为 0 ;
_IOC_NRMASK 的定义为:

#define _IOC_NRMASK    ((1 << _IOC_NRBITS)-1)
_IOC_NRBITS 的值被定义为 8 ,所以 _IOC_NRMASK 的值为 0xFF 。
其余的宏分析类似。

【20181102 补充】

—————————————————————————————————————————————

内核定义了4个宏:_IO()、_IOR()、_IOW()、_IORW() 来辅助的生成ioctl 控制命令,宏名隐含方向字段、移位组合生成命令码;

#define _IO(type, nr)

_IOC(_IOC_NONE, (type), (nr), 0)

#define _IOR(type, nr, size)

_IOC(_IOC_READ, (type), (nr), (IOC_TYPECHECK(size)))

#define _IOR(type, nr, size)

_IOC(_IOC_WRITE, (type), (nr), (IOC_TYPECHECK(size)))

#define _IOR(type, nr, size)

_IOC(_IOC_READ | IOWRITE, (type), (nr), (IOC_TYPECHECK(size)))

 

其作用是根据传入的type、nr、size以及宏名隐含的方向字段移位组合生成命令码。

用法:

A. 在用户空间,使用ioctl系统调用来控制设备,原型如下:

int ioctl(int fd,unsigned long cmd,...);
fd:文件描述符
cmd:控制命令
...:可选参数:插入*argp,具体内容依赖于cmd,至少2个参数,最多3个参数

用户程序通过命令码告诉驱动程序它想做什么,至于如何进行真正的命令操作和怎么实现这些命令,都是驱动程序要做的事情。

B. 驱动ioctl方法:

int (*ioctl) (struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);
inode与filp对应于应用程序传递的文件描述符fd,这和传递open方法的参数一样。
cmd 由用户空间直接不经修改的传递给驱动程序
arg 可选。

在驱动程序中实现的ioctl函数体内,实际上是有一个switch {case}结构,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程序员自己的事情,因为设备都是特定的。关键在于怎么样组织命令码,因为在ioctl中命令码是唯一联系用户程序命令和驱动程序支持的途径。

Eg:

#define LEDTYPE ‘S‘ // 幻数定义

// 控制码定义
#define LED_ON _IOW(LEDTYPE, 0, int)
#define LED_OFF _IOW(LEDTYPE, 1, int)
#define LED_ON_ALL _IO(LEDTYPE, 2)
#define  LED_OFF_ALL _IO(LEDTYPE, 3)
#define LEDGET_SATA _IOR(LEDTYPE, 4, int)

  4 /* ioctl command encoding: 32 bits total, command in lower 16 bits,
  5  * size of the parameter structure in the lower 14 bits of the
  6  * upper 16 bits.
  7  * Encoding the size of the parameter structure in the ioctl request
  8  * is useful for catching programs compiled with old versions
  9  * and to avoid overwriting user space outside the user buffer area.
 10  * The highest 2 bits are reserved for indicating the ``access mode''.
 11  * NOTE: This limits the max parameter size to 16kB -1 !
 12  */

If you are adding new ioctl's to the kernel, you should use the _IO
  7 macros defined in <linux/ioctl.h>:
  8 
  9     _IO    an ioctl with no parameters
 10     _IOW   an ioctl with write parameters (copy_from_user)
 11     _IOR   an ioctl with read parameters  (copy_to_user)
 12     _IOWR  an ioctl with both write and read parameters.
 13 
 14 'Write' and 'read' are from the user's point of view, just like the
 15 system calls 'write' and 'read'.  For example, a SET_FOO ioctl would
 16 be _IOW, although the kernel would actually read data from user space;
 17 a GET_FOO ioctl would be _IOR, although the kernel would actually write
 18 data to user space.
 19 
 20 The first argument to _IO, _IOW, _IOR, or _IOWR is an identifying letter
 21 or number from the table below.  Because of the large number of drivers,
 22 many drivers share a partial letter with other drivers.
 24 If you are writing a driver for a new device and need a letter, pick an
 25 unused block with enough room for expansion: 32 to 256 ioctl commands.
 26 You can register the block by patching this file and submitting the
 27 patch to Linus Torvalds.  Or you can e-mail me at <[email protected]> and
 28 I'll register one for you.
 29 
 30 The second argument to _IO, _IOW, _IOR, or _IOWR is a sequence number
 31 to distinguish ioctls from each other.  The third argument to _IOW,
 32 _IOR, or _IOWR is the type of the data going into the kernel or coming
 33 out of the kernel (e.g.  'int' or 'struct foo').  NOTE!  Do NOT use
 34 sizeof(arg) as the third argument as this results in your ioctl thinking
 35 it passes an argument of type size_t.
 36 
 37 Some devices use their major number as the identifier; this is OK, as
 38 long as it is unique.  Some devices are irregular and don't follow any
 39 convention at all.

 

  • 驱动实现流程梳理

驱动入口:

  1. LED物理地址映射
  2. 设置LED的GPIO口工作模式
  3. 注册杂项设备驱动

驱动出口:

  1. 映射的虚拟地址释放
  2. LED的状态复位
  3. 注销杂项设备驱动

驱动程序操作接口函数实现:fops

如:open,write,read,ioctl等;

1)驱动程序入口实现

133 static int __init demo_init(void)
134 {
135     int ret;
136 
137     unsigned int tmp;
138
139     gpm4base = ioremap(GPM4BASE, SZ_4K);
140     if (NULL == gpm4base) {
141         return -EINVAL;
142     }
143 
144     tmp = ioread32(gpm4base+GPM4CON);
145     tmp &= ~0xffff;
146     tmp |= 0x1111;
147     iowrite32(tmp, gpm4base+GPM4CON);
148
149     tmp = ioread8(gpm4base+GPM4DAT);
150     tmp |= 0xf;
151     iowrite8(tmp, gpm4base+GPM4DAT);
152
153     /*ioread32/ioread16/ioared8  iowrite32/iowrite16/iowrite8*/
154     ret = misc_register(&misc);
155     if (ret < 0) {
156         goto error0;
157     }
158
159     printk("KER-[%s] leave, line = %d.\n", __func__, __LINE__);
160     return 0;
161 
162 error0:
163     iounmap(gpm4base);
164
165     printk("KER-[%s] leave, return error0, line = %d.\n", __func__, __LINE__    );
166     return ret;
167 }
168 
169 module_init(demo_init);

 

2)驱动程序出口实现

171 static void __exit demo_exit(void)
172 {
173     u8 tmp;
174 
175     tmp = ioread8(gpm4base+GPM4DAT);
176     tmp |= 0xf;
177     iowrite8(tmp, gpm4base+GPM4DAT);
178 
179     iounmap(gpm4base);
180     misc_deregister(&misc);
181     printk("KER-[%s] leave, line = %d.\n", __func__, __LINE__);
182 }
183 
184 module_exit(demo_exit);

3)驱动程序fops操作接口实现

120 static struct file_operations fops = {
121     .owner      = THIS_MODULE,
122     .open       = mill_open,
123     .unlocked_ioctl = mill_unlocked_ioctl,
124     .release    = mill_release,
125 };


78 static long
 79 mill_unlocked_ioctl (struct file *filp, unsigned int request, unsigned long     arg)
 80 {
 81     if (_IOC_TYPE(request) == LEDTYPE) {
 82         switch (_IOC_NR(request)) {
 83             case 0:
 84                 if (arg < 1 || arg > 4) {
 85                     return -EINVAL;
 86                 }
 87                 led_ctl(ON, arg);
 88                 break;
 89             case 1:
 90                 if (arg < 1 || arg > 4) {
 91                     return -EINVAL;
 92                 }
 93                 led_ctl(OFF, arg);
 94                 break;
 95             case 2:
 96                 led_ctl_all(ON);
 97                 break;
 98             case 3:
 99                 led_ctl_all(OFF);
100                 break;
101             case 4:
102                 led_get_stat((char *)arg);
103             default:
104                 printk("KER-[%s] leave,return -EINVAL,line = %d.\n", __func    __, __LINE__);
105                 return -EINVAL;
106        }
107     }
108 
109     printk("[%s] leave, line = %d.\n", __func__, __LINE__);
110     return 0;
111 }
  • Led驱动测试程序

Ioctl 命令定义的头文件:

读写LEDS 字符设备的测试程序:

Led测试结果

插入驱动模块

[root@millet0926_01_leds_note06]# insmod demo.ko                                       
[ 1086.250000] KER-[demo_init] leave, line = 159.   
[root@milletapp_0923]# lsmod                                                           
demo 2808 0 - Live 0xbf254000 (O)                                                      
libertas_sdio 8709 0 - Live 0xbf242000 (O)  

查看设备号:主设备号和次设备号

[root@milletapp_0923]# cat /proc/misc                                                  
 50 leds   // 次设备号为50,设备名为 leds, misc设备驱动主设备号为10


[root@milletapp_0923]# ls -l /dev/leds                                                 
crw-rw----    1 root     root       10,  50 Jan  1 12:18 /dev/leds                     
[root@milletapp_0923]#

控制效果:全部点亮

[root@milletapp_0923]# ./app onall                                                     
[ 2887.505000] KER-[mill_open] leave, line = 23.                                       
[ 2887.505000] KER-[led_ctl], line = 33, read tmp = 0x7, arg=1.                        
[ 2887.505000] KER-[led_ctl], line = 42, write tmp = 0x6.                              
[ 2887.505000] KER-[led_ctl] leave, line = 45.                                         
[ 2887.505000] KER-[led_ctl], line = 33, read tmp = 0x6, arg=2.                        
[ 2887.510000] KER-[led_ctl], line = 42, write tmp = 0x4.                              
[ 2887.515000] KER-[led_ctl] leave, line = 45.                                         
[ 2887.520000] KER-[led_ctl], line = 33, read tmp = 0x4, arg=3.                        
[ 2887.525000] KER-[led_ctl], line = 42, write tmp = 0x0.                              
[ 2887.530000] KER-[led_ctl] leave, line = 45.                                         
[ 2887.535000] KER-[led_ctl], line = 33, read tmp = 0x0, arg=4.                        
[ 2887.540000] KER-[led_ctl], line = 42, write tmp = 0x0.                              
[ 2887.545000] KER-[led_ctl] leave, line = 45.                                         
[ 2887.550000] KER-[led_ctl_all] leave, line = 60.                                     
[ 2887.555000] [mill_unlocked_ioctl] leave, line = 109.                                
[ 2887.560000] KER-[mill_release] leave, line = 116.                                   
[root@milletapp_0923]# 

控制效果:全部熄灭

[root@milletapp_0923]# ./app offall                                                    
[ 2909.155000] KER-[mill_open] leave, line = 23.                                      
[ 2909.155000] KER-[led_ctl], line = 33, read tmp = 0x0, arg=1.                        
[ 2909.155000] KER-[led_ctl], line = 42, write tmp = 0x1.                              
[ 2909.155000] KER-[led_ctl] leave, line = 45.                                         
[ 2909.155000] KER-[led_ctl], line = 33, read tmp = 0x1, arg=2.                        
[ 2909.155000] KER-[led_ctl], line = 42, write tmp = 0x3.                              
[ 2909.160000] KER-[led_ctl] leave, line = 45.                                         
[ 2909.165000] KER-[led_ctl], line = 33, read tmp = 0x3, arg=3.                        
[ 2909.170000] KER-[led_ctl], line = 42, write tmp = 0x7.                              
[ 2909.175000] KER-[led_ctl] leave, line = 45.                                         
[ 2909.180000] KER-[led_ctl], line = 33, read tmp = 0x7, arg=4.                        
[ 2909.185000] KER-[led_ctl], line = 42, write tmp = 0xf.                              
[ 2909.190000] KER-[led_ctl] leave, line = 45.                                         
[ 2909.195000] KER-[led_ctl_all] leave, line = 60.                                     
[ 2909.200000] [mill_unlocked_ioctl] leave, line = 109.                                
[ 2909.205000] KER-[mill_release] leave, line = 116.                                   
[root@milletapp_0923]#

控制效果:点亮4

[root@milletapp_0923]# ./app on 4                                                      
[ 2944.605000] KER-[mill_open] leave, line = 23.                                       
[ 2944.605000] KER-[led_ctl], line = 33, read tmp = 0xf, arg=4.                        
[ 2944.605000] KER-[led_ctl], line = 42, write tmp = 0x7.                              
[ 2944.605000] KER-[led_ctl] leave, line = 45.                                         
[ 2944.605000] [mill_unlocked_ioctl] leave, line = 109.                                
[ 2944.610000] KER-[mill_release] leave, line = 116.                                   
[root@milletapp_0923]# 

查询状态:

[root@milletapp_0923]# ./app stat                                                      
[ 2961.140000] KER-[mill_open] leave, line = 23.                                       
[ 2961.140000] KER-[led_get_stat], line = 69, tmp = 7.                                 
[ 2961.140000] KER-[led_get_stat] leave, line = 75.                                    
[ 2961.140000] KER-[mill_unlocked_ioctl] leave,return -EINVAL,line = 104.             
[ 2961.140000] KER-[mill_release] leave, line = 116.                                   
led 1 is off                                                                           
led 2 is off                                                                           
led 3 is off                                                                           
led 4 is on 
  • Led驱动程序实现

 #include <linux/init.h>
  2 #include <linux/module.h>
  3 #include <linux/fs.h>
  4 #include <linux/io.h>
  5 #include <linux/miscdevice.h>
  6 #include "ioctl.h"
  7 
  8 #define DEVNAME "leds"
  9 
 10 #define GPM4BASE 0x11000000
 11 #define GPM4CON  0x2e0
 12 #define GPM4DAT  0x2e4
 13 
 14 enum led{
 15     OFF, ON
 16 };
 17 
 18 static void *gpm4base = NULL;
 19 
 20 static int
 21 mill_open (struct inode *inodp, struct file *filp)
 22 {
 23     printk("KER-[%s] leave, line = %d.\n", __func__, __LINE__);
 24     return 0; 
 25 }
 26 
 27 void led_ctl(enum led cmd, int arg)
 28 {
 29     u8 tmp; 
 30 
 31     // 获取LEDs状态 
 32     tmp = ioread8(gpm4base+GPM4DAT);
 33     printk("KER-[%s], line = %d, read tmp = 0x%x, arg=%d.\n", __func__, __LINE__,tmp,arg);
 34     
 35     if (cmd == ON) {
 36         /*** 将1向左移动(arg-1)位 ***/
 37         tmp &= ~(1 << (arg - 1)); 
 38     } 
 39     else if (cmd == OFF) {
 40         tmp |= (1 << (arg - 1));
41     }
 42     printk("KER-[%s], line = %d, write tmp = 0x%x.\n", __func__, __LINE__,tmp);
 43     // 设置LEDs状态
 44     iowrite8(tmp, gpm4base+GPM4DAT);
 45     printk("KER-[%s] leave, line = %d.\n", __func__, __LINE__);
 46 }
 47 
 48 void led_ctl_all(enum led cmd)
 49 {
 50     int i;
 51 
 52     for (i = 0; i < 4; i++) {
 53         if (cmd == ON) {
 54             led_ctl(ON, i+1);
 55         }
 56         else {
 57             led_ctl(OFF, i+1);
 58         }
 59     }
 60     printk("KER-[%s] leave, line = %d.\n", __func__, __LINE__);
 61 }
 62 
 63 static void led_get_stat(char *buf)
 64 {
 65     int i;
 66     u8 tmp;
 67 
 68     tmp = ioread8(gpm4base+GPM4DAT);
 69     printk("KER-[%s], line = %d, tmp = %x.\n", __func__, __LINE__,tmp);
 70     // 一次性获取4个led灯的状态
 71     for (i = 0; i < 4; i++) {
 72         buf[i] = (tmp&(1<<i))?'0':'1';
 73     }
 74 
 75     printk("KER-[%s] leave, line = %d.\n", __func__, __LINE__);
 76 }
 77 
 78 static long
 79 mill_unlocked_ioctl (struct file *filp, unsigned int request, unsigned long arg)
 80 {
81     if (_IOC_TYPE(request) == LEDTYPE) {
 82         switch (_IOC_NR(request)) {
 83             case 0:
 84                 if (arg < 1 || arg > 4) {
 85                     return -EINVAL;
 86                 }
 87                 led_ctl(ON, arg);
 88                 break;
 89             case 1:
 90                 if (arg < 1 || arg > 4) {
 91                     return -EINVAL;
 92                 }
 93                 led_ctl(OFF, arg);
 94                 break;
 95             case 2:
 96                 led_ctl_all(ON);
 97                 break;
 98             case 3:
 99                 led_ctl_all(OFF);
100                 break;
101             case 4:
102                 led_get_stat((char *)arg);
103             default:
104                 printk("KER-[%s] leave,return -EINVAL,line = %d.\n", __func__, __LINE__);
105                 return -EINVAL;
106         }
107     }
108 
109     printk("[%s] leave, line = %d.\n", __func__, __LINE__);
110     return 0;
111 }
112 
113 static int
114 mill_release (struct inode *inodp, struct file *filp)
115 {
116     printk("KER-[%s] leave, line = %d.\n", __func__, __LINE__);
117     return 0;
118 }
119 
120 static struct file_operations fops = {
121     .owner      = THIS_MODULE,
122     .open       = mill_open,
123     .unlocked_ioctl = mill_unlocked_ioctl,
124     .release    = mill_release,
125 };
126 
127 static struct miscdevice misc = {
128     .minor  = MISC_DYNAMIC_MINOR,
129     .name   = DEVNAME,
130     .fops   = &fops,
131 };
132 
133 static int __init demo_init(void)
134 {
135     int ret;
136 
137     unsigned int tmp;
138 
139     gpm4base = ioremap(GPM4BASE, SZ_4K);
140     if (NULL == gpm4base) {
141         return -EINVAL;
142     }
143 
144     tmp = ioread32(gpm4base+GPM4CON);
145     tmp &= ~0xffff;
146     tmp |= 0x1111;
147     iowrite32(tmp, gpm4base+GPM4CON);
148 
149     tmp = ioread8(gpm4base+GPM4DAT);
150     tmp |= 0xf;
151     iowrite8(tmp, gpm4base+GPM4DAT);
152 
153     /*ioread32/ioread16/ioared8  iowrite32/iowrite16/iowrite8*/
154     ret = misc_register(&misc);
155     if (ret < 0) {
156         goto error0;
157     }
158 
159     printk("KER-[%s] leave, line = %d.\n", __func__, __LINE__);
160     return 0;
161 
162 error0:
163     iounmap(gpm4base);
164 
165     printk("KER-[%s] leave, return error0, line = %d.\n", __func__, __LINE__);
166     return ret;
167 }
168 
169 module_init(demo_init);
170 
171 static void __exit demo_exit(void)
172 {
173     u8 tmp;
174 
175     tmp = ioread8(gpm4base+GPM4DAT);
176     tmp |= 0xf;
177     iowrite8(tmp, gpm4base+GPM4DAT);
178 
179     iounmap(gpm4base);
180     misc_deregister(&misc);
181     printk("KER-[%s] leave, line = %d.\n", __func__, __LINE__);
182 }
183 
184 module_exit(demo_exit);
185 
186 MODULE_LICENSE("GPL");
187 
188 MODULE_AUTHOR("zhang li lin");
189 MODULE_VERSION("zhang plus 10");
190 MODULE_DESCRIPTION("It is a simple example for LEDS module.");

  • Led 测试程序实现

.h 文件:

  1 #ifndef MILLET_LED_H_
  2 #define MILLET_LED_H_
  3 
  4 #include <linux/ioctl.h>
  5 
  6 #define LEDTYPE 'L'
  7 
  8 #define LED_ON          _IOW(LEDTYPE, 0, int)
  9 #define LED_OFF         _IOW(LEDTYPE, 1, int)
 10 #define LED_ON_ALL      _IO(LEDTYPE, 2)
 11 #define LED_OFF_ALL     _IO(LEDTYPE, 3)
 12 #define LED_GET_STAT    _IOR(LEDTYPE, 4, int)
 13 
 14 #endif

.c 文件:

1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <fcntl.h>
  5 #include <unistd.h>
  6 #include <assert.h>
  7 #include <stdlib.h>
  8 #include <string.h>
  9 
 10 #include "ioctl.h"
 11 
 12 void usage(const char *str)
 13 {
 14     fprintf(stderr, "Usage:\n");
 15     fprintf(stderr, "      %s on/off num(1~4)\n", str);
 16     fprintf(stderr, "      %s onall/offall\n", str);
 17     fprintf(stderr, "      %s stat\n", str);
 18     exit(1);
 19 }
 20 
 21 int main(int argc, char **argv)
 22 {
 23     int fd;
 24     int num;
 25     int request;
 26     int ret;
 27     char buf[4] = {0};
 28     int i;
 29 
 30     if ((argc != 2)&&(argc != 3)) {
 31         usage(argv[0]);
 32     }
 33     // 打开leds 杂项设备驱动,试想,若不注册杂项设备驱动,则操作led灯
 34     // 时,每个led灯都要有可操作的设备节点;
 35     fd = open("/dev/leds", O_RDWR | O_NDELAY);
 36     assert(fd > 0);
 37 
 38     if (argc == 2)
 39     {   // 全部打开
 40         if (!strncmp("onall", argv[1], 5))
 41         {
 42             ret = ioctl(fd, LED_ON_ALL);
 43             assert(ret == 0);
 44         }// 全部关闭
 45         else if (!strncmp("offall", argv[1], 5))
 46         {
 47             ret = ioctl(fd, LED_OFF_ALL);
 48             assert(ret == 0);
 49         }// 获取当前状态
 50         else if (!strncmp("stat", argv[1], 4))
 51         {
 52             ret = ioctl(fd, LED_GET_STAT, buf);
 53             for (i = 0; i < 4; i++) {
 54                 if (buf[i] == '1')
 55                 {
 56                     printf("led %d is on\n", i+1);
 57                 } else if (buf[i] == '0')
 58                 {
 59                     printf("led %d is off\n", i+1);
 60                 } else {
 61                     exit(1);
 62                 }
 63             }
 64         } else {
 65             usage(argv[0]);
 66         }
 67     }
 68     else
 69     {   // 打开指定的 led灯
 70         if (!strncmp("on", argv[1], 2))
 71         {
 72             request = LED_ON;
 73         }// 关闭指定的 led灯
 74         else if (!strncmp("off", argv[1], 3))
 75         {
 76             request = LED_OFF;
 77         }
 78         else
 79         {
 80             usage(argv[0]);
 81         }
 82         // 通过可执行文件传入的'第几个led灯'参数,是字符型,需转换为整形
 83         num = atoi(argv[2]);
 84         // 通过ioctl 系统调用,下发控制命令
 85         ret = ioctl(fd, request, num);
 86         assert(ret == 0);
 87     }
 88 
 89         return 0;
 90 }
                                               
  • 总结

1)左移运算符 (<<)

用来将一个数的各二进制位全部左移若干位;比如: 

a = a << 2; // 将a 的二进制数左移2位,右补0,高位左移后溢出舍弃

 

2)Ioctl 系统调用实质:

  1. Linux内核分析(六)----字符设备控制方法实现|揭秘系统调用本质

https://www.cnblogs.com/wrjvszq/p/4276567.html

猜你喜欢

转载自blog.csdn.net/llzhang_fly/article/details/83687391
今日推荐