1、设备ioctl
2、内核等待队列
3、阻塞型字符设备
4、poll设备操作
5、自动创建设备文件

 

1、设备ioctl
作用:个人认为就是用来传递参数和命令到内核驱动,内核驱动程序接收到这个命令之后进行控制设备。
用户态函数原型:
int ioctl(int fd, unsigned long cmd, ...)
内核空间驱动原型:
int (*ioctl)(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg);
cmd参数就是用户传递下来的参数cmd,arg是一个整数或者一个指针,反正只能以整数的形式表示。

使用:如果你很懒的话,直接在用户态下int ioctl(fd, 0x1),内核态下直接读cmd的值

int chardev_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg)
{
    switch(cmd)
    {
        case 0x1;    
        。。。
    }
}    

 


注意:如果cmd是一个整数,可以直接使用。如果是指针,我们必须确保这个用户地址是有效的,因此使用前需要进行正确性检查。
如果arg是一个地址:有两种使用情况:
一种不需要检查:copy_from_user copy_to_user,get_user,put_user(因为函数本身已经帮你检查了)
另一种情况需要检查:__get_user,__put_user(此时函数未进行检查)
怎么检查呢??
int access_ok(int type,const void*addr,unsigned long size).第一个参数用宏VERIFY_READ或者VERIFY_WRITE,来检查这个用户空间是否能读或者写。所以说用户空间一般需要先malloc一个空间,然后在把地址下发进来。如果ioctl需要从用户空间读一个整数,那么size参数等于sizeof(int),就是检查一个整数长度的空间的有效性。注意acess_ok返回1是成功的,0是失败的。

但是一般不建议这样使用,这样是不合规的,我们需要符合规则的定义使用这些命令:
定义命令:就是定义一个稍微有意义的整数作为命令,比如说不同的位表示不同的意义:类型(幻数)8位,序号8位,传送方向,参数的大小。
内核提供了下列宏来帮助定义命令:
_IO(type,nr) 没有参数的命令
_IOR(type,nr,datatype) 从驱动中读是数据
_IOW(type,nr,datatype) 从驱动中写数据
_IOWR(type,nr,datatype) 双向传输

使用范例
#define MEM_IOC_MAGIC 'm'
#define MEM_IOCSET _IOW(MEM_IOC_MAGIC,0,int)
#define MEM_IOCGQSET _IOR(MEM_IOC_MAGIC,1,int)

实例代码memdev.c

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>  
#include <linux/slab.h>  
#include <linux/fs.h>    
#include <linux/errno.h> 
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <asm/system.h>  
#include <asm/uaccess.h>   /*使用copy_to_user,copy_from_user必须要包含该文件*/

#include "memdev.h"
MODULE_LICENSE("GPL");

Mem_Dev *mem_devices; 
int memdev_major = MEMDEV_MAJOR;

/*IO操作*/
int memdev_ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)
{
    int err = 0;
    int ret = 0;
    int ioarg = 0;
    
    /* 检测命令的有效性 */
    if (_IOC_TYPE(cmd) != MEMDEV_IOC_MAGIC) 
        return -ENOTTY;
    if (_IOC_NR(cmd) > MEMDEV_IOC_MAXNR) 
        return -ENOTTY;

    /* 根据命令类型,检测参数空间是否可以访问 */
    if (_IOC_DIR(cmd) & _IOC_READ)
        err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
    else if (_IOC_DIR(cmd) & _IOC_WRITE)
        err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
    if (err) 
        return -EFAULT;

    /* 根据命令,执行相应的操作 */
    switch(cmd) {

      /* 打印当前设备信息 */
      case MEMDEV_IOCPRINT:
          printk("<--- CMD MEMDEV_IOCPRINT Done--->\n\n");
        break;
      
      /* 获取参数 */
      case MEMDEV_IOCGETDATA: 
        ioarg = 1101;
        ret = __put_user(ioarg, (int *)arg);
        break;
      
      /* 设置参数 */
      case MEMDEV_IOCSETDATA: 
        ret = __get_user(ioarg, (int *)arg);
        printk("<--- In Kernel MEMDEV_IOCSETDATA ioarg = %d --->\n\n",ioarg);
        break;

      default:  
        return -ENOTTY;
    }
    return ret;
}

int memdev_open(struct inode *inode, struct file *filp)
{
    Mem_Dev *dev;
    
    /*获取次设备号*/
    int num = MINOR(inode->i_rdev);

    dev = (Mem_Dev *)filp->private_data;
    if (!dev) 
    {
        if (num >= MEMDEV_NR_DEVS) 
            return -ENODEV;
        dev = &mem_devices[num];
        filp->private_data = dev; 
    }
    /*增加模块引用计数*/
    MOD_INC_USE_COUNT;   
    return 0;          
}

/*设备关闭*/
int memdev_release(struct inode *inode, struct file *filp)
{
    MOD_DEC_USE_COUNT;
    return 0;
}
/*
 * The following wrappers are meant to make things work with 2.0 kernels
 */
struct file_operations memdev_fops = {
    ioctl:      memdev_ioctl,
    open:       memdev_open,
    release:    memdev_release,
};

/*卸载函数*/
void memdev_cleanup_module(void)
{
    int i;
    
    /*注销字符设备*/
    unregister_chrdev(memdev_major, "memdev");

    /*释放内存*/
    if (mem_devices) 
    {
        for (i=0; i<MEMDEV_NR_DEVS; i++)
        kfree(mem_devices[i].data);
        kfree(mem_devices);
    }
}

/*加载函数*/
int memdev_init_module(void)
{
    int result, i;
    
    /*设置模块owner*/
    SET_MODULE_OWNER(&memdev_fops);
 
    /*注册字符设备*/
    result = register_chrdev(memdev_major, "memdev", &memdev_fops);
    if (result < 0) 
    {
        printk(KERN_WARNING "mem: can't get major %d\n",memdev_major);
        return result;
    }
    if (memdev_major == 0) 
        memdev_major = result; 

    /*为设备描述结构分配内存*/
    mem_devices = kmalloc(MEMDEV_NR_DEVS * sizeof(Mem_Dev), GFP_KERNEL);
    if (!mem_devices) 
    {
        result = -ENOMEM;
        goto fail;
    }
    memset(mem_devices, 0, MEMDEV_NR_DEVS * sizeof(Mem_Dev));
    
    /*为设备分配内存*/
    for (i=0; i < MEMDEV_NR_DEVS; i++) 
    {
        mem_devices[i].size = MEMDEV_SIZE;
        mem_devices[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);
        memset(mem_devices[i].data, 0, MEMDEV_SIZE);
        sema_init(&mem_devices[i].sem, 1);
    }
    
    return 0;
  fail:
    mem_cleanup_module();
    return result;
}

module_init(memdev_init_module);
module_exit(memdev_cleanup_module);

 

memdev.h

#ifndef _MEMDEV_H_
#define _MEMDEV_H_

#include <linux/ioctl.h>
#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 0   
#endif

#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 3    
#endif

#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif

typedef struct Mem_Dev {
   char *data;
   struct Mem_Dev *next;   /* next listitem */
   unsigned long size;
   struct semaphore sem;     /* 定义信号量 */
} Mem_Dev;

/* 定义幻数 */
#define MEMDEV_IOC_MAGIC  'k'
/* 定义命令 */
#define MEMDEV_IOCPRINT   _IO(MEMDEV_IOC_MAGIC, 1)
#define MEMDEV_IOCGETDATA _IOR(MEMDEV_IOC_MAGIC, 2, int)
#define MEMDEV_IOCSETDATA _IOW(MEMDEV_IOC_MAGIC, 3, int)


#define MEMDEV_IOC_MAXNR 3

#endif /* _MEMDEV_H_ */

 

ioctl_app.c

#include <stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

#include "memdev.h"  /* 包含命令定义 */

int main()
{
    int fd = 0;
    int cmd;
    int arg = 0;
    char Buf[4096];
    
    
    /*打开设备文件*/
    fd = open("/dev/memdev0",O_RDWR);
    if (fd < 0)
    {
        printf("Open Dev Mem0 Error!\n");
        return -1;
    }
    
    /* 调用命令MEM_IOCPRINT */
    printf("<--- Call MEM_IOCPRINT --->\n");
    cmd = MEM_IOCPRINT;
    if (ioctl(fd, cmd, &arg) < 0)
        {
            printf("Call cmd MEM_IOCPRINT fail\n");
            return -1;
    }
    
    /* 调用命令MEM_IOCSETDATA */
    printf("<--- Call MEM_IOCSETDATA --->\n");
    cmd = MEM_IOCSETDATA;
    arg = 2007;
    if (ioctl(fd, cmd, &arg) < 0)
        {
            printf("Call cmd MEM_IOCSETDATA fail\n");
            return -1;
    }

    /* 调用命令MEM_IOCGETDATA */
    printf("<--- Call MEM_IOCGETDATA --->\n");
    cmd = MEM_IOCGETDATA;
    if (ioctl(fd, cmd, &arg) < 0)
        {
            printf("Call cmd MEM_IOCGETDATA fail\n");
            return -1;
    }
    printf("<--- In User Space MEM_IOCGETDATA Get Data is %d --->\n\n",arg);
    
    close(fd);
    return 0;    
}