1. 命令的格式: 命令的本质是一个32位的无符号数,其实,命令可以是任意的32位数,但是考虑到不同的设备如果支持相同的命令,如果发错了,也会执行的尴尬, linux提供了统一的命令格式。
| 设备类型 | 序列号 | 方向 | 数据尺寸 |
|---------- |-------- |------ | -------- |
| 8 bit | 8 bit | 2 bit | 8~14 bit |
设备类型:是哪个设备的命令
序列号 :是设备的哪一条命令
方向 :命令的参数的数据方向(从应用程序的角度说)
读写(_IOC_READ|_IOC_WRITE)不读不写/无数据传输(_IOC_NONE)只读(_IOC_READ)、只写(_IOC_WRITE)
数据尺寸:命令的参数的大小
2. 命令的构造:构造命令用到一个宏:
_IO:
库:
include/asm/ioctl.h
在linux源码中:
/* 构造无参数的命令编号 */
#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)
<img id="aimg_YNwxX" class="zoom" file="http://img.blog.csdn.net/20160829174711170" lazyloadthumb="1" border="0" alt="" />
参数:
type,魔术,范围是 0~255(0xff) 。通常,用英文字符 &quot;A&quot; ~ &quot;Z&quot; 或者 &quot;a&quot; ~ &quot;z&quot; 来表示。设备驱动程序从传递进来的命令获取魔数,然后与自身处理的魔数想比较,如果相同则处理,不同则不处理。魔数是拒绝误使用的初步辅助状态。不同的设备驱动程序最好设置不同的魔数,但并不是要求绝对,也是可以使用其他设备驱动程序已用过的魔数。 nr,基数
基数用于区别各种命令。通常,从 0开始递增,相同设备驱动程序上可以重复使用该值。例如,读取和写入命令中使用了相同的基数,设备驱动程序也能分辨出来,原因在于设备驱动程序区分命令时使用 switch ,且直接使用命令变量 cmd值。
size,数据长度
3. 避开linux系统中的预定义命令:
#define FIONCLEX 0x5450
#define FIOCLEX 0x5451
#define FIOQSIZE 0x5460
#define FIONBIO 0x5421
之所以避开 预定义命令是因为,如果某设备驱动中包含了与预定义命令一样的命令码,这些命令将被当作预定义命令而不是被设备驱动处理。
这些预定义命令的含义,这里不介绍。
4. 驱动中的实现:思路:主要是实现 struct file_operations 结构体里边的 unlocked_ioctl 方法
要实现的方法中有3个参数,举例如下:
static long led_ioctl (struct file *filep, unsigned int cmd, unsigned long argv)
{
switch(cmd)
{
case HELLO_ONE:
printk(&quot;HELLO_ONE\n&quot;);
break;
case HELLO_TWO:
printk(&quot;HELLO_TWO\n&quot;);
break;
default:
return -EINVAL;
}
return 0;
}
struct file_operations led_fops ={
.unlocked_ioctl =led_ioctl,
};
对应的用户空间(/测试程序):
ioctl(fd, HELLO_ONE)
其中的fd是打开的文件的操作符, HELLO_ONE 是命令码
5. 代码:
ioctrl.c
[cpp] view plain copy
print?<img id="aimg_W9JHy" class="zoom" width="12" height="12" file="https://code.csdn.net/assets/CODE_ico.png" border="0" alt="" /><img id="aimg_B45PE" class="zoom" width="12" height="12" file="https://code.csdn.net/assets/ico_fork.svg" border="0" alt="" />
#include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include &quot;test.h&quot; #include <linux/device.h> MODULE_LICENSE(&quot;GPL&quot;); #define LED_MA 250 #define LED_MI 0 struct cdev cdev; struct class *my_class; dev_t devno = MKDEV(LED_MA,LED_MI); static int led_open (struct inode * inodep, struct file *filep) { printk(&quot;led_open\n&quot;); return 0; } static int led_release (struct inode *inodep, struct file *filep) { printk(&quot;led_release\n&quot;); return 0; } static long led_ioctl (struct file *filep, unsigned int cmd, unsigned long argv) { switch(cmd) { case HELLO_ONE: printk(&quot;HELLO_ONE\n&quot;); break; case HELLO_TWO: printk(&quot;HELLO_TWO\n&quot;); break; default: return -EINVAL; } return 0; } struct file_operations led_fops ={ .owner=THIS_MODULE, .open = led_open, .release =led_release, .unlocked_ioctl =led_ioctl, }; static int char_dev_create (void) { my_class = class_create(THIS_MODULE,&quot;ioctrl_class&quot;); if(IS_ERR(my_class)) { printk(&quot;Err: failed in creating class.\n&quot;); return -1; } device_create(my_class,NULL,devno,NULL,&quot;ioctrl&quot;); return 0; } static int led_init(void) { unsigned int ret = 0; printk(&quot;led_init start \n&quot;); ret = register_chrdev_region(devno,1,&quot;fs4412_led_device&quot;); if(ret<0) { printk(&quot;register_chrdev_region error\n&quot;); return ret; } char_dev_create(); cdev.owner = THIS_MODULE; cdev_init(&cdev,&led_fops); ret = cdev_add(&cdev,devno,1); if(ret<0) { printk(&quot;cdev_add error\n&quot;); goto err1; } return 0; err1: unregister_chrdev_region(devno,1); device_destroy(my_class, devno); //delete device node under /dev//必须先删除设备,再删除class类 class_destroy(my_class); //delete class created by us return ret; } static void led_exit(void) { cdev_del(&cdev); device_destroy(my_class, devno); //delete device node under /dev//必须先删除设备,再删除class类 class_destroy(my_class); //delete class created by us unregister_chrdev_region(devno,1); printk(&quot;led_exit end \n&quot;); return ; } module_init(led_init); module_exit(led_exit);
Makefile
[cpp] view plain copy
print?<img id="aimg_SQL4O" class="zoom" width="12" height="12" file="https://code.csdn.net/assets/CODE_ico.png" border="0" alt="" /><img id="aimg_m44hq" class="zoom" width="12" height="12" file="https://code.csdn.net/assets/ico_fork.svg" border="0" alt="" />
ifeq ($(KERNELRELEASE),) KERNELDIR ?= /lib/modules/$(shell uname -r)/build #KERNELDIR ?= ~/wor_lip/linux-3.4.112 PWD := $(shell pwd) modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules modules_install: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules* Module* .PHONY: modules modules_install clean else obj-m := ioctrl.o endif
test.h
[cpp] view plain copy
print?<img id="aimg_FCPMR" class="zoom" width="12" height="12" file="https://code.csdn.net/assets/CODE_ico.png" border="0" alt="" /><img id="aimg_mpdJ2" class="zoom" width="12" height="12" file="https://code.csdn.net/assets/ico_fork.svg" border="0" alt="" />
#ifndef __TEST_H__ #define __TEST_H__ #define HELLO_MAGIC 'k' #define HELLO_ONE _IO (HELLO_MAGIC, 1) #define HELLO_TWO _IO (HELLO_MAGIC, 2) #endif
test.c
[cpp] view plain copy
print?<img id="aimg_g6Mch" class="zoom" width="12" height="12" file="https://code.csdn.net/assets/CODE_ico.png" border="0" alt="" /><img id="aimg_m89ep" class="zoom" width="12" height="12" file="https://code.csdn.net/assets/ico_fork.svg" border="0" alt="" />
#include <stdio.h> #include <sys/types.h> #include <sys/ioctl.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h> #include &quot;test.h&quot; int main (void) { int fd; fd = open (&quot;/dev/ioctrl&quot;,O_RDWR); if (fd < 0) { perror (&quot;fd open failed&quot;); exit(-1); } if (ioctl (fd, HELLO_ONE) < 0) { perror(&quot;fail to ioctl 1&quot;); } puts(&quot;ioctl HELLO_ONE is done&quot;) ; getchar(); if (ioctl (fd, HELLO_TWO) < 0) { perror(&quot;fail to ioctl 2&quot;); } puts(&quot;ioctl HELLO_TWO is done&quot;) ; getchar(); close (fd); return 0; }
测试:
> sudo ./a.out
ioctl HELLO_ONE is done
ioctl HELLO_TWO is done
> sudo rmmod ioctrl
> dmesg
[24811.453631] led_init start
[24857.563362] led_open
[24857.563370] HELLO_ONE
[24860.293633] HELLO_TWO
[24862.970848] led_release
[24902.467995] led_exit end |
|