Linux 27 字符设备驱动二(ioctl)

file_operations

在Linux字符设备驱动入门(一)中,我们实现了字符设备的简单读写字符功能,接下来我们要在这个基础上加入ioctl功能。首先,我们先来看看3.0内核下…/include/linux/fs.h中file_operations结构体的定义:

struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    int (*readdir) (struct file *, void *, filldir_t);
    unsigned int (*poll) (struct file *, struct poll_table_struct *);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, int datasync);
    int (*aio_fsync) (struct kiocb *, int datasync);
    int (*fasync) (int, struct file *, int);
    int (*lock) (struct file *, int, struct file_lock *);
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
    int (*check_flags)(int);
    int (*flock) (struct file *, int, struct file_lock *);
    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
    int (*setlease)(struct file *, long, struct file_lock **);
    long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len);
};

kernel 3.0中已经完全删除了struct file_operations 中的ioctl 函数指针,剩下unlocked_ioctl和compat_ioctl,取而代之的是unlocked_ioctl,主要改进就是不再需要上大内核锁 (调用之前不再先调用lock_kernel()然后再unlock_kernel())。
所以,在hellow.c中,我们在file_operations中加入成员函数hello_ioctl。

static struct file_operations hello_ops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = hello_ioctl,
    .open = hello_open,
    .read = hello_read,
    .write = hello_write,
    .release = hello_release,

};

hello_ioctl实现如下:

static long hello_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{    
    int temp = 0;
    
    switch(cmd)
    {
        case HELLO_CMD1:
        {
            temp = 1;
            if(copy_to_user( (int *)arg, &temp, sizeof(int))) 
                return -EFAULT;
            break;
        }
        case HELLO_CMD2:
        {
            temp = 2;
            if(copy_to_user( (int *)arg, &temp, sizeof(int))) 
                return -EFAULT;
            break;
        }
    }
    printk(KERN_NOTICE"ioctl CMD%d done!\n",temp);    

    return 0;
}
CMD解释
#define HELLO_MAGIC 'k'
#define HELLO_CMD1    _IO(HELLO_MAGIC,0x1a)
#define HELLO_CMD2    _IO(HELLO_MAGIC,0x1b)

其中'k'为幻数,要按照Linux内核的约定方法为驱动程序选择ioctl编号,应该首先看看include/asm/ioctl.h和Documentation/ioctl-number.txt这两个文件,下面是ioctl.h的部分内容,也是比较重要的:

_IO(type, nr)
    User for construct CMD
_IOR(type, nr, datatype)
    User for reading data from driver
_IOW(type, nr, datatype)
    User for writing data
_IOWR(type, nr, datatype)
    Used for two way transmit

注意对幻数的编号千万不能重复定义,如ioctl-number.txt已经说明‘k’的编号已经被占用的范围为:
‘k’ 00-0F linux/spi/spidev.h conflict!
‘k’ 00-05 video/kyro.h conflict!

用户测试函数:

/* ioctl_test.c */
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define HELLO_MAGIC 'k' //当然我们也可以定义一个相应的头文件,把ioctl的cmd放进里面,然后再include进来
#define HELLO_CMD1    _IO(HELLO_MAGIC,0x1a)
#define HELLO_CMD2    _IO(HELLO_MAGIC,0x1b)

int main(void)
{
    int ioctl_rdata;
    int fd, ret;

    fd = open ( "/dev/hellow" , O_RDWR);
    if ( fd == -1 )
    {
        perror("open");
        exit(0);
    }

    ret = ioctl( fd, HELLO_CMD2,&ioctl_rdata);
    if ( ret == -1)
    {
        perror("ioctl");
        exit(0);
    }
    printf("ioctl_rdata= %d \n",ioctl_rdata);

    close(fd);
	
    return 0;
}

内核完整代码:


#include <linux/proc_fs.h> 
#include <linux/module.h>  
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/cdev.h>

static int hello_major = 0;  /* major device number */
static struct cdev hellow;   /* hello device structure */

#define HELLO_MAGIC 'k'
#define HELLO_CMD1    _IO(HELLO_MAGIC,0x1a)
#define HELLO_CMD2    _IO(HELLO_MAGIC,0x1b)

/* Open the device */
static int hello_open(struct inode *inode, struct file *filp)
{
    printk(KERN_NOTICE"Hello device open!\n");
    return 0;
}

/* Close hello_device */
static int hello_release(struct inode *inode, struct file *filp)
{
    printk(KERN_NOTICE"Hello device close!\n");
    return 0;
}

/* user read from hello device*/
ssize_t hello_read(struct file *flip, char __user *buf, size_t count, loff_t *f_pos)
{
    ssize_t retval = 0;
    char *bank;

    bank = kmalloc(count+1, GFP_KERNEL);
    if(bank == NULL)
        return -1;
    memset(bank, 'A',count);
    if(copy_to_user(buf, bank, count))
    {
        retval = -EFAULT;
        goto out;
    }
    retval += count;
    *(bank + count) = 0;
    printk( KERN_NOTICE"hello: user read %d bytes from me. %s\n",count,bank );

out:
    kfree(bank);
    return retval;
}

/* write to hello device */
ssize_t hello_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
    ssize_t retval = 0;
    char *bank = kmalloc(count ,GFP_KERNEL);

    if(bank == NULL)
    return retval;
    if(copy_from_user(bank, buf, count))
    {
        retval = -EFAULT;
        printk(KERN_NOTICE"hello: write error\n");
        goto out;
    }
    retval += count;
    printk(KERN_NOTICE"hello: user has written %d bytes to me: %s\n",count, bank);

out:
    kfree(bank );
    return retval;
}

/* ioclt callback */
static long hello_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{    
    int temp = 0;
    
    switch(cmd)
    {
        case HELLO_CMD1:
        {
            temp = 1;
            if(copy_to_user( (int *)arg, &temp, sizeof(int))) 
                return -EFAULT;
            break;
        }
        case HELLO_CMD2:
        {
            temp = 2;
            if(copy_to_user( (int *)arg, &temp, sizeof(int))) 
                return -EFAULT;
            break;
        }
    }
    printk(KERN_NOTICE"ioctl CMD%d done!\n",temp);    

    return 0;
}

static struct file_operations hello_ops = {
    .owner = THIS_MODULE, /*owner为所有者字段,防止在使用时模块被卸载。一边都设为THIS_MODULE*/
	.unlocked_ioctl = hello_ioctl,
    .open = hello_open,
    .read = hello_read,
    .write = hello_write,
    .release = hello_release,

};

/* set up the cdev stucture for a device */
static void hello_setup_cdev(struct cdev *dev, int minor, struct file_operations *fops)
{
    int err;
    int devno = MKDEV(hello_major, minor);

    /* initialize the cdev struct */
    cdev_init(dev,fops);
    dev->owner = THIS_MODULE;
    err = cdev_add(dev, devno, 1); /* register the cdev in the kernel */
    if(err)
    printk( KERN_NOTICE"Error %d adding hello%d\n",err ,minor );
}

/* Module housekeeping */
static int hello_init(void)
{
    int result;
    dev_t dev = MKDEV(hello_major, 0);  /*to transfer major as dev_t type ,将主设备号转换为设备类型 */

    /* alloc the major device number dynamicly */
    result = alloc_chrdev_region(&dev, 0 ,1, "hello");	/* 向内核申请动态主设备号 */
    if(result < 0)
    {
    printk(KERN_NOTICE"Hello: unable to get major %d\n",hello_major);
    return result;
    }

    hello_major = MAJOR(dev);	/* 获取主设备号 */

    /* set up devices, in this case, there is only one device */
    printk( KERN_NOTICE"hello init. major:%d, minor:%d\n",hello_major,0 );
    //printk( KERN_ALERT"hello init: %d, %d\n",hello_major,0 );

    hello_setup_cdev(&hellow, 0 , &hello_ops );	/* 设置字符设备的结构,主要是一些函数指针 */
    
    return 0;
    
}

/* Exit routine */
static void hello_exit(void)
{
    /* remove the cdev from kernel */
    cdev_del(&hellow );

    /* release the device numble alloced earlier */
    unregister_chrdev_region(MKDEV( hello_major, 0 ), 1);
    printk(KERN_NOTICE"hello exit. major:%d,minor %d\n",hello_major,0);
}

/* register the init and exit routine of the module */
module_init(hello_init);
module_exit(hello_exit);

MODULE_AUTHOR("root");
MODULE_LICENSE("Dual BSD/GPL");

Makefile:

obj-m += hellow.o
KER_APP = hellow
USER_APP = ioctl_test
USER_OBJ = ioctl_test.o

KERNEL_PATH = /lib/modules/$(shell uname -r)/build/
  
all: $(KER_APP) $(USER_APP)

$(USER_APP): $(USER_OBJ)
	gcc $(USER_OBJ) -o $(USER_APP)
	
$(KER_APP):
	make -C $(KERNEL_PATH) M=$(PWD) modules
	
clean:  
	make -C $(KERNEL_PATH) M=$(PWD) clean
	rm -f $(USER_APP) $(USER_OBJ)

运行结果:

[root@ charDeviceIoctl]# insmod hellow.ko 
[root@ charDeviceIoctl]# dmesg | tail -10

[244803.435151] hello init. major:246, minor:0
[root@ charDeviceIoctl]# ./ioctl_test
ioctl_rdata= 2 
ioctl_rdata= 1 
[root@ charDeviceIoctl]# dmesg | tail -10
[244803.435151] hello init. major:246, minor:0
[244821.176210] Hello device open!
[244821.176214] ioctl CMD2 done!
[244821.176307] ioctl CMD1 done!
[244821.176310] Hello device close!
[root@ charDeviceIoctl]# lsmod | grep hello
hellow                 16384  0
[root@ charDeviceIoctl]# rmmod hellow 
[root@ charDeviceIoctl]# dmesg | tail -10
[244803.435151] hello init. major:246, minor:0
[244821.176210] Hello device open!
[244821.176214] ioctl CMD2 done!
[244821.176307] ioctl CMD1 done!
[244821.176310] Hello device close!
[244866.920559] hello exit. major:246,minor 0
[root@ charDeviceIoctl]#  
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值