关闭

字符驱动之:控制

标签: ioctrl驱动linux
308人阅读 评论(0) 收藏 举报
分类:

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)



参数:
    type,魔术,范围是 0~255(0xff) 。通常,用英文字符 "A" ~ "Z" 或者 "a" ~ "z" 来表示。设备驱动程序从传递进来的命令获取魔数,然后与自身处理的魔数想比较,如果相同则处理,不同则不处理。魔数是拒绝误使用的初步辅助状态。不同的设备驱动程序最好设置不同的魔数,但并不是要求绝对,也是可以使用其他设备驱动程序已用过的魔数。
    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("HELLO_ONE\n");
        break;
    case HELLO_TWO:
        printk("HELLO_TWO\n");
        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 
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include "test.h"
#include <linux/device.h>
MODULE_LICENSE("GPL");

#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("led_open\n");
    return 0;
}

static int led_release (struct inode *inodep, struct file *filep)
{
    printk("led_release\n");
    return 0;
}

static long led_ioctl (struct file *filep, unsigned int cmd, unsigned long argv)
{
    switch(cmd)
    {
    case HELLO_ONE:
        printk("HELLO_ONE\n");
        break;
    case HELLO_TWO:
        printk("HELLO_TWO\n");
        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,"ioctrl_class");
    if(IS_ERR(my_class)) 
    {
        printk("Err: failed in creating class.\n");
        return -1; 
    }
    device_create(my_class,NULL,devno,NULL,"ioctrl");

    return 0;
}

static int led_init(void)
{
    unsigned int ret = 0;
    printk("led_init start \n");

    ret = register_chrdev_region(devno,1,"fs4412_led_device");
    if(ret<0)
    {
        printk("register_chrdev_region error\n");
        return ret;
    }
    char_dev_create();

    cdev.owner = THIS_MODULE;
    cdev_init(&cdev,&led_fops);

    ret = cdev_add(&cdev,devno,1);
    if(ret<0)
    {
        printk("cdev_add error\n");
        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("led_exit end \n");
    return ;

}
module_init(led_init);
module_exit(led_exit);


Makefile
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
#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
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>

#include "test.h"

int main (void) 
{
    int fd;

    fd = open ("/dev/ioctrl",O_RDWR);
    if (fd < 0) {
        perror ("fd open failed");
        exit(-1);

    }

    if (ioctl (fd, HELLO_ONE) < 0)
    {
        perror("fail to ioctl 1");

    }
    puts("ioctl HELLO_ONE is done") ;
    getchar();

    if (ioctl (fd, HELLO_TWO) < 0)
    {
        perror("fail to ioctl 2");

    }
    puts("ioctl HELLO_TWO is done") ;
    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 





0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:188703次
    • 积分:4385
    • 等级:
    • 排名:第6810名
    • 原创:235篇
    • 转载:13篇
    • 译文:3篇
    • 评论:12条
    博客专栏