Linux驱动入门 ioctl

本文详细介绍了Linux中的ioctl协议,包括命令码的构成(direction、devicetype、命令编号和参数大小),以及如何通过宏定义创建和解析ioctl命令。还展示了如何在驱动程序和用户程序间使用这些命令进行数据传输和操作。
摘要由CSDN通过智能技术生成

用户与驱动之间的ioctl协议构成
也就是request或cmd,本质上就是一个32位数字,理论上可以是任何一个数,但为了保证命令码的唯一性,linux定义了一套严格的规定,通过计算得到这个命令吗数字。linux将32位划分为四段。

含义如下。

1)dir,即direction,表示ioctl命令的访问模式,分为无数据(_IO)、读数据(_IOR)、写数据(_IOW)、读写数据(_IOWR)四种模式。

2)type,即device type,表示设备类型,也可翻译成“幻数”或“魔数”,可以是任意一个char型字符,如’a’、‘b’、‘c’等,其主要作用是使ioctl命令具有唯一的设备标识。不过在内核中’w’、‘y’、'z’三个字符已经被使用了。

3)nr,即number,命令编号/序数,取值范围0~255,在定义了多个ioctl命令的时候,通常从0开始顺次往下编号。

4)size,涉及到ioctl的参数arg,占据13bit或14bit,这个与体系有关,arm使用14bit。用来传递arg的数据类型的长度,比如如果arg是int型,我们就将这个参数填入int,系统会检查数据类型和长度的正确性。

在上面的四个参数都需要用户自己定义,linux系统提供了宏可以使程序员方便的定义ioctl命令码。

include/uapi/asm-generic/ioctl.h
--------------------------------------------
/* used to create numbers */
#define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))


分别对应了四个dir:
_IO(type, nr):用来定义不带参数的ioctl命令。
_IOR(type,nr,size):用来定义用户程序向驱动程序写参数的ioctl命令。
_IOW(type,nr,size):用来定义用户程序从驱动程序读参数的ioctl命令。
_IOWR(type,nr,size):用来定义带读写参数的驱动命令。

当然了,系统也定义反向解析ioctl命令的宏。

include/uapi/asm-generic/ioctl.h
--------------------------------------------
/* used to decode ioctl numbers */
#define _IOC_DIR(nr)        (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr)       (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr)     (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr)       (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK

led_drv.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/pwm.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/proc_fs.h>
#include <linux/ioctl.h>

#define LED_MAGIC 		'l'
#define LEDREAD    		_IOR(LED_MAGIC, 0, int)
#define LEDWRITE    	_IOW(LED_MAGIC, 1, int)


static int major;
static struct class* dh_led_class;

static int led_drv_open(struct inode *node, struct file *file)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

	return 0;
}

static int led_drv_close(struct inode *node, struct file *file)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

	return 0;
}

static long led_drv_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	int led_read_val;
	int ret;

	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

	if (_IOC_TYPE(cmd) != LED_MAGIC) 
	{
        printk("[%s] command type [%c] error!\n", __FUNCTION__, _IOC_TYPE(cmd));
        return -ENOTTY; 
    }

    switch (cmd)
    {
    	case LEDWRITE:
    		{
    			//memcpy((&led_read_val, (int*)(arg), sizeof(&arg)));
    			ret = copy_from_user(&led_read_val, (int*)arg, sizeof(int));
    			printk("write: led = %d\n", led_read_val);
    			break;
    		}
    	case LEDREAD:
    		{
    			ret = copy_to_user((int*)arg, &led_read_val, sizeof(int));
    			break;
    		}
    	default:
    		{
    			printk("error\n");
    			break;
    		}
    }
	return 0;
}

static struct file_operations led_drv = 
{
	.owner = THIS_MODULE,
	.open = led_drv_open,
	.release = led_drv_close,
	.unlocked_ioctl = led_drv_ioctl,
};

static int __init led_init(void)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

	major = register_chrdev(0, "dh_led", &led_drv);

	dh_led_class = class_create(THIS_MODULE, "dh_led_class");
	device_create(dh_led_class, NULL, MKDEV(major, 0), NULL, "dh_led_device_0");

	return 0;
}

static void __exit led_exit(void)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

	device_destroy(dh_led_class, MKDEV(major, 0));
	class_destroy(dh_led_class);

	unregister_chrdev(major, "dh_led");
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");

led_test.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <assert.h>
#include <sys/ioctl.h>
// #include "dh_led_drv.h"
#define LED_MAGIC 		'l'
#define LEDREAD    		_IOR(LED_MAGIC, 0, int)
#define LEDWRITE    	_IOW(LED_MAGIC, 1, int)



int main(int argc, char const *argv[])
{
	int fd;
	char status;
	int ret;

	assert(argc == 3);

	fd = open(argv[1], O_RDWR);
	assert(fd != -1);

	if (0 == strcmp(argv[2], "on"))
	{
		status = 1;
		ret = ioctl(fd, LEDWRITE, &status);
	}
	else if (0 == strcmp(argv[2], "off"))
	{
		status = 0;
		ret = ioctl(fd, LEDWRITE, &status);
	}
	else if (0 == strcmp(argv[2], "read"))
	{
		ret = ioctl(fd, LEDREAD, &status);
		printf("read led status: %d\n", status);
	}
	else
	{
		printf("error\n");
	}

	close(fd);

	return 0;
}

Makefile

KERNEL_HEAD = ../kernel-head
CUR_DIR := $(shell pwd)

all:
	make ARCH=arm CROSS_COMPILE=arm-sigmastar-linux-uclibcgnueabihf- -C $(KERNEL_HEAD) M=$(CUR_DIR) modules
	arm-sigmastar-linux-uclibcgnueabihf-gcc -o dh_led_test dh_led_test.c

clean:
	make ARCH=arm CROSS_COMPILE=arm-sigmastar-linux-uclibcgnueabihf- -C $(KERNEL_HEAD) M=$(CUR_DIR) clean
	rm -rf modules.order
	rm -f dh_led_test

obj-m += dh_led_drv.o

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值