hello_world-3.1之增加操作函数fops(二)源码解析
1.helloworldmem.c
#include<linux/module.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include<linux/errno.h> //-EFAULT
#include<linux/uaccess.h> //copy_to_user and copy_from_user
#include "helloworld_ioctl.h"
#define HELLOWORLDMEM_SIZE 0x1000
#define HELLOWORLDMEM_MAJOR 250
static int helloworldmem_major = HELLOWORLDMEM_MAJOR;
struct helloworldmem_dev {
struct cdev cdev;
unsigned char mem[HELLOWORLDMEM_SIZE];
};
struct helloworldmem_dev dev;
int helloworldmem_open(struct inode *inode,struct file *filp)
{
return 0;
}
int helloworldmem_release(struct inode *inode,struct file *flip)
{
return 0;
}
static ssize_t helloworldmem_read(struct file *filp,char __user *buf,size_t count,loff_t *ppos)
{
unsigned long p = *ppos;
int ret = 0;
if(p >= HELLOWORLDMEM_SIZE)
return 0;
if(count > HELLOWORLDMEM_SIZE - p)
count = HELLOWORLDMEM_SIZE - p;
if(copy_to_user(buf,(void*)(dev.mem + p),count))
ret = -EFAULT;
else{
*ppos += count;
ret = count;
printk(KERN_INFO "read %d bytes(s) from %d \n",count,p);
}
return ret;
}
static ssize_t helloworldmem_write(struct file *filp,const char __user *buf,size_t count,loff_t *ppos)
{
unsigned long p = *ppos;
int ret = 0;
if(p >= HELLOWORLDMEM_SIZE)
return 0;
if(count > HELLOWORLDMEM_SIZE - p)
count = HELLOWORLDMEM_SIZE - p ;
if(copy_from_user(dev.mem+p,buf,count))
ret = -EFAULT;
else{
*ppos += count;
ret = count;
printk(KERN_INFO "written %d bytes from %d \n",count,p);
}
return ret;
}
static int helloworldmem_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
switch(cmd){
case HELLOWORLD_IOCTL_SAY_BYE:
printk(KERN_INFO "HELLOWORLDMEM SAY BYE BYE to the world\n");
break;
/*case HELLOWORLD_IOCTL_SAY_HELLO:
printk(KERN_INFO "HELLOWORLDMEM SAY HELLO to the world\n");
break;*/
default:
return -EINVAL;
}
return 0;
}
static const struct file_operations helloworldmem_fops = {
.owner = THIS_MODULE,
.read = helloworldmem_read,
.write = helloworldmem_write,
.unlocked_ioctl = helloworldmem_ioctl,
.open = helloworldmem_open,
.release = helloworldmem_release,
};
static void helloworldmem_setup_cdev()
{
int err,devno = MKDEV(helloworldmem_major,0);
cdev_init(&dev.cdev,&helloworldmem_fops);
dev.cdev.owner = THIS_MODULE;
err = cdev_add(&dev.cdev,devno,1);
if(err)
printk(KERN_NOTICE "Error %d adding helloworldmem",err);
}
int __init helloworldmem_init(void)
{
int result;
dev_t devno = MKDEV(helloworldmem_major,0);
if(helloworldmem_major)
result = register_chrdev_region(devno,1,"helloworldmem");
else
{
result = alloc_chrdev_region(&devno,0,1,"helloworldmem");
helloworldmem_major = MAJOR(devno);
}
if(result < 0)
return result;
helloworldmem_setup_cdev();
return 0;
}
void __exit helloworldmem_exit(void)
{
cdev_del(&dev.cdev);
unregister_chrdev_region(MKDEV(helloworldmem_major,0),1);
}
module_init(helloworldmem_init);
module_exit(helloworldmem_exit);
MODULE_LICENSE("GPL");
2. helloworld_ioctl.h
#ifndef __HELLOWORLD_IOCTL_H_
#define __HELLOWORLD_IOCTL_H
#include<linux/ioctl.h>
#define HELLOWORLD_MAGIC 12
#define HELLOWORLD_IOCTL_SAY_BYE _IO(HELLOWORLD_MAGIC,0)
//#define HELLOWORLD_IOCTL_SAY_HELLO _IO(HELLOWORLD_IOCTL_SAY_HELLO,1)
#endif
这里我有话要说,我当时准备是用两个IOCTL无读写方向的如上面那个“HELLOWORLD_IOCTL_SAY_HELLO”,但是这个宏在编译的时候老是提示未定义,我想应该是
这种_IO()函数只能用一次吧
还有那个HELLOWORLD_MAGIC 为幻数,是8位的,也可以用单个字符去定义。我上面的这个是定义无读写方向的IOCTL命令码。
/* used to create numbers */
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
其中size,表示命令码的位数,一把为int;
3.用户空间的程序hello.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<linux/errno.h>
#include "helloworld_ioctl.h"
int main(void)
{
int fd = 0;
int cmd;
fd = open("/dev/helloworldmem",O_RDWR);
if(fd < 0)
{
printf(" open helloworldmem failed\n");
return -EFAULT;
}
cmd = HELLOWORLD_IOCTL_SAY_BYE;
if(ioctl(fd,cmd) < 0)
{
printf("Called ioctl failed!\n");
}
close(fd);
}
4.执行结果及分析
4.1 对ioctl的结果分析
1.创建设备节点
mknod /dev/helloworldmem c 250 0
2.执行./hello后
从dmesg 可以看出:
[ 3891.531814] HELLOWORLDMEM SAY BYE BYE to the world
4.2 对read and write的结果分析
其实这个比较简单,直接应用shell的命令
写命令
root@thiz-H61H-G11:~# echo "helloworld" > /dev/helloworldmem
root@thiz-H61H-G11:~# cat /dev/helloworldmem
helloworld