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]#