[国嵌攻略][116][字符设备控制技术]

设备控制理论

1.设备控制的作用

大部分驱动程序除了需要提供读写设备的能力外,还需要具备控制设备的能力。比如改变波特率。

2.设备控制的函数

在用户空间使用ioctl系统调用函数来控制设备。

int ioclt(int fd, unsigned long cmd, ...)

fd:要控制的设备文件描述符

cmd:发送给设备的命令

...:第三个参数是可选的参数,存在与否是依赖于控制命令

当应用程序使用ioclt系统调用时,驱动程序将由如下函数来响应:

1. 2.6.36以前的内核

long (*ioclt)(struct inode *node, struct file *filp, unsigned int cmd, unsigned long arg)

2. 2.6.36以后的内核

long (*unlocked_ioctl)(struct file *filp, unsigned int cmd, unsigned long arg)

参数cmd:通过应用程序ioctl传递下来的命令

设备控制的实现

1.定义命令

命令从其实质而言就是一个整数,但为了让这个整数具备更好的可读性,我们通常会把这个整数划分为几个段:类型(8位),序号,参数传递方向,参数长度

type(类型/幻数):表明这是属于哪个设备的命令。

number(序号):用来区分同一个设备的不同命令。

direction:参数传递的方向,可以是_IOC_NONE(没有数据传输),_IOC_READ(从设备读取数据),_IOC_WRITE(向设备中写入数据)

size:参数长度

Linux系统提供了下面的宏来帮助定义命令:

_IO(type, number):不带参数的命令

_IOR(type, number, datatype):从设备中读取参数的命令

_IOW(type, number, datatype):向设备中写入参数的命令

示例:

#define MEM_MAGIC ‘m’

#define MEM_SET _IOW(MEM_MAGIC, 0, int)

2.操作实现

unlocked_ioclt函数的现实通常是根据命令执行的一个switch语句。但是当命令不能匹配任何一个设备所支持的命令是,返回-EINVAL。

编程模型:

switch(cmd){

case A:

//执行A对应的操作

case B:

//执行B对应的操作

default:

//return -EINVAL

}

memdev.h

//定义命令
#define MEM_MAGIC ‘M‘                     //设备幻数
#define MEM_RST _IO(MEM_MAGIC, 0)         //重启命令
#define MEM_SET _IOW(MEM_MAGIC, 1, int)   //设置命令

memdev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

#include "memdev.h"

//全局变量
struct cdev memdev;   //字符设备
dev_t devnum;         //设备编号

int register0[5];   //设备0寄存器
int register1[5];   //设备1寄存器

//定位设备
loff_t mem_lseek(struct file *filp, loff_t offset, int whence){
    //设置位置
    loff_t newpos;

    switch(whence){
        case SEEK_SET:
            newpos = offset;
            break;

        case SEEK_CUR:
            newpos = filp->f_pos + offset;
            break;

        case SEEK_END:
            newpos = (5 *  sizeof(int)) + offset;
            break;

        default:
            newpos = 0;
            break;
    }

    //移动位置
    filp->f_pos = newpos;

    return newpos;
}

//读取设备
ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos){
    //获取地址
    int *base;

    base = filp->private_data + *ppos;

    //读取数据
    copy_to_user(buf, base, size);

    //移动位置
    filp->f_pos += size;

    return size;
}

//写入设备
ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos){
    //获取地址
    int *base;

    base = filp->private_data + *ppos;

    //写入数据
    copy_from_user(base, buf, size);

    //移动位置
    filp->f_pos += size;

    return size;
}
//控制设备
long mem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){
    //处理命令
    switch(cmd){
        case MEM_RST:   //重启命令
            printk("reset device\n");
            break;

        case MEM_SET:   //设置命令
            printk("set arg is %d\n", arg);
            break;

        default:        //错误命名
            return -EINVAL;
    }

    return 0;
}

//打开设备
int mem_open(struct inode *node, struct file *filp){
    //获取次设备号
    int secnum;

    secnum = MINOR(node->i_rdev);

    //设置设备地址
    if(secnum == 0){
        filp->private_data = register0;
    }

    if(secnum == 1){
        filp->private_data = register1;
    }

    return 0;
}

//关闭设备
int mem_colse(struct inode *node, struct file *filp){
    return 0;
}

//设备方法
struct file_operations memfops = {
    .llseek         = mem_lseek,
    .read           = mem_read,
    .write          = mem_write,
    .unlocked_ioctl = mem_ioctl,
    .open           = mem_open,
    .release        = mem_colse
};

//驱动注册
static int memdev_init(){
    //注册设备结构
    cdev_init(&memdev, &memfops);

    //注册主设备号
    alloc_chrdev_region(&devnum, 0, 2, "memdev");

    //添加设备结构
    cdev_add(&memdev, devnum, 2);

    return 0;
}

//驱动注销
static void memdev_exit(){
    //注销设备结构
    cdev_del(&memdev);

    //注销主设备号
    unregister_chrdev_region(devnum, 2);
}

//驱动声明
MODULE_LICENSE("GPL");
MODULE_AUTHOR("D");
MODULE_DESCRIPTION("memdev");
MODULE_VERSION("v1.0");

module_init(memdev_init);
module_exit(memdev_exit);

contorl.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#include "memdev.h"

int main(int argc, char **argv){
    //打开设备
    int fd;

    fd = open("/dev/memdev0", O_RDWR);

    //设置设备
    ioctl(fd, MEM_SET, 115200);

    //重启设备
    ioctl(fd, MEM_RST);

    //关闭设备
    close(fd);

    return 0;
}
时间: 2024-08-24 19:46:36

[国嵌攻略][116][字符设备控制技术]的相关文章

[国嵌攻略][112][使用字符设备驱动]

编译/安装驱动程序 在Linux系统中,驱动程序通常采用内核模块的程序结构来进行编码.因此编译/安装一个驱动程序,其实就是编译/安装一个内核模块. 示例: make insmod memdev.ko 创建设备文件 应用程序->设备文件->驱动程序 通过字符设备文件,应用程序可以使用相应的字符设备驱动程序来控制字符设备.创建字符设备文件的方法一般有两种: 1.使用mknod命令 mknod /dev/文件名 c 主设备号 次设备号 驱动程序通过主设备号与字符设备文件一一对应,驱动程序的主设备号可

[国嵌攻略][117][LED驱动程序设计]

LED程序设计 1.编写内核模块 2.搭建字符驱动框架 3.实现设备方法 头文件 <linux/io.h> writel() leddev.h //设备命令 #define LED_MAGIC 'L' //LED幻数 #define LED_ON _IO(LED_MAGIC, 0) //打开LED #define LED_OFF _IO(LED_MAGIC, 1) //关闭LED leddev.c /***********************************************

[国嵌攻略][133][网卡驱动架构分析]

Linux网络子系统 1.系统调用接口:提供系统调用 2.协议无关接口:统一网络协议给系统调用接口使用 3.网络协议栈  :实现网络协议 4.设备无关接口:统一设备驱动程序给网络协议使用 5.设备驱动程序:实现网卡驱动 Linux驱动在内核中都有一个结构来描述,首先找到设备描述结构,然后找到设备如何注册和初始化. 网卡描述结构 在Linux内核中,每个网卡都由一个net_device结构来描述,其中一些重要成员: char name[IFNAMSIZ]   设备名,如:eth%d unsigne

[国嵌攻略][147][简单块设备驱动设计]

头文件 #include <linux/blkdev.h> #include <linux/bio.h> blkdev.c /******************************************************************** *头文件 *********************************************************************/ #include <linux/init.h> #incl

[国嵌攻略][156][I2C自编设备驱动设计]

AT24C08的驱动在Linux内核中已经提供,在/drivers/misc/eeprom/at24.c文件中.在对应的probe函数中有一个创建/sys/.../eeprom文件的函数,应用程序通过调用/sys/.../eeprom设备文件来对设备进行读写操作. 移植AT24C08驱动 1.修改内核注册eeprom设备 在kernel/linux-mini2440/arch/arm/mach-s3c2440.c中的machine_init下注册IIC设备. mach-mini2440.c st

[国嵌攻略][164][USB驱动程序设计]

USB驱动模型 1.USB host controller driver(主控器驱动):为USB主控制器提供驱动程序 2.USB core(USB核心):连接USB主控制器驱动和USB设备驱动 3.USB client driver:为USB外部设备提供驱动程序 USB设备模型 device(设备)->config(配置)->interface(接口)->endpoint(端点),构成了USB设备的四个层次.一个usb driver(驱动)对应的是一个interface(接口).一个接口

[国嵌攻略][155][I2C用户态驱动设计]

用户态驱动模型 用户态驱动模型首先是一个应用程序,其次是在这个用户程序中通过内核调用来驱动设备. IIC通用驱动代码 IIC通用驱动程序的代码在/drivers/i2c/i2c-dev.c中.一次读操作或者一次写操作就是一条消息. EEPROM用户态驱动 IIC通用设备对应/dev/i2c-0设备文件. 1.打开通用设备驱动 2.构造写数据到eeprom的消息 3.使用ioctl写入数据 4.构造从eeprom读数据的消息 5.使用ioctl读出数据 6.关闭设备 配置IIC驱动 make me

[国嵌攻略][057][串口控制台建立]

控制台分类 1.菜单型控制台,通过选择菜单来执行命令 2.解析型控制台,通过输入命令来执行命令 printf().scanf()函数移植 1.函数采用变参 2.打印信息到串口 3.关键在于把变参转换成字符串,相关函数需要从Linux内核或标准C库中移植 va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); /***********************************************

[国嵌攻略][102][内核驱动开发环境搭建]

服务器环境搭建 1.配置tftp服务器 1.1.设置tftp目录 vim /etc/xinetd.d/tftp server_args             = -s .../tftp disable                 = no 1.2.启动tftp服务 /etc/init.d/xinetd restart 2.配置nfs服务器 2.1.设置nfs目录 vim /etc/exports .../nfs *(rw,sync,no_root_squash) 2.2.启动nfs服务 /