下面是用了自定義char_dev結構體,利用container_of來指向。內存空間用kmalloc來申請。練手用的。
#include<linux/module.h>
#include<linux/init.h>
#include<linux/errno.h>
#include<linux/fs.h>
#include<asm/uaccess.h>
#include<linux/kernel.h>//container_of
#include<linux/device.h>
#include<linux/cdev.h>
#include<linux/types.h>
#include<linux/slab.h>//kmalloc
//#include<asm/device.h>
MODULE_LICENSE("Dual BSD/GPL");
#define SIZE 1024
dev_t devno;//設備號
struct cdev cdev;
struct class *char_class;
struct char_dev *dev;
/*char_dev結構體中定義了cdev,方便多設備時cdev指定;內核空間data;空間大小size*/
struct char_dev//dev結構,未包含量子qset什麼的。
{
struct cdev cdev;
char *data;//定義data內存空間然後kmalloc分配
unsigned long size;
/*struct char_qset *data;
unsigned long size;*/
//unsigned char data[size];//這個還是靜態分配,和上次一樣
};
/*struct char_qset//量子集qset結構
{
void **data;
struct char_qset *next;
}*/
//open
int char_open(struct inode *inode,struct file *filp)
{
dev=container_of(inode->i_cdev,struct char_dev,cdev);
filp->private_data=dev;
//dev->size=SIZE;
return 0;
}
//release
int char_release(struct inode *inode,struct file *filp)
{
return 0;
}
//read
ssize_t char_read(struct file *filp,char *buf,size_t count,loff_t *f_pos)
{
dev=filp->private_data;
if(*f_pos>=dev->size){
*f_pos=dev->size;
return 0;
}
if(*f_pos+count>dev->size)
count=dev->size-*f_pos;
if(copy_to_user(buf,dev->data+*f_pos,count)){
printk(KERN_WARNING"copy_to_user error!\n");
return -EFAULT;
}else{
*f_pos+=count;
}
return count;
}
//write
ssize_t char_write(struct file *filp,const char*buf,size_t count,loff_t *f_pos)
{
dev=filp->private_data;
if(*f_pos>=dev->size){
*f_pos=dev->size;
return 0;
}
if(*f_pos+count>dev->size)
count=dev->size-*f_pos;
if(copy_from_user(dev->data+*f_pos,buf,count)){
printk(KERN_WARNING"copy_from_user error!\n");
return -EFAULT;
}else{
*f_pos+=count;
}
return count;
}
//llseek,比上一篇多實現了lseek,來控制偏移量
loff_t char_llseek(struct file *filp,loff_t offset,int whence)
{
loff_t newpos;
switch(whence){
case 0://SEEK_SET
newpos=offset;
break;
case 1://SEEK_CUR
newpos=filp->f_pos+offset;
break;
case 2://SEEK_END
newpos=dev->size-1+offset;
break;
default://can't happen
return -EINVAL;
}
filp->f_pos=newpos;
return newpos;
}
//file_operations,如果不聲明上面的open等,必須這個放它們後面
struct file_operations char_fops=
{
.owner=THIS_MODULE,
.open=char_open,
.release=char_release,
.read=char_read,
.write=char_write,
.llseek=char_llseek,//多了這個
};
//加載
static int char_init(void)
{
int result;
/*alloc_chrdev_region(dev_t *dev,第一個次設備號,設備個數,名字)*/
result=alloc_chrdev_region(&devno,0,1,"char_module");
if(result<0){
printk(KERN_WARNING"alloc_chrdev_region error!\n");
return -ENODEV;
}
printk(KERN_INFO"major:%d,minor:%d",MAJOR(devno),MINOR(devno));
/*下面來分配內核空間,這個必須在cdev_init之前申請空間,會用到,不然雖然編譯沒錯誤,測試時會出錯
可以用kzalloc來代替下面的kmalloc和memset,申請後初始化。
dev=kzalloc(sizeof(struct char_dev),GFP_KERNEL)
*/
/*因爲dev是結構體指針,所以要申請空間,如果是struct char_dev dev,則不必*/
dev=kmalloc(sizeof(struct char_dev),GFP_KERNEL);//申請dev結構體空間
if(dev==NULL){
printk(KERN_WARNING"kmalloc error!\n");
return -ENOMEM;
}
memset(dev,0,sizeof(struct char_dev));//初始化空間
/*dev->data申請空間*/
dev->size=SIZE;
dev->data=kmalloc(SIZE,GFP_KERNEL);
if(dev->data==NULL){
printk(KERN_WARNING"kzalloc error!\n");
return -ENOMEM;
}
memset(dev->data,0,SIZE);//初始化
/*cdev_init(struct cdev *cdev,struct file_oppations *fops)*/
cdev_init(&dev->cdev,&char_fops);
dev->cdev.owner=THIS_MODULE;
/*cdev_add(struct cdev *dev,dev_t num,unsigned int count一般是1)*/
cdev_add(&dev->cdev,devno,1);
if(result){
/*unregister_chrdev_region(dev_t first,unsigned int count)*/
unregister_chrdev_region(devno,1);
return -EINVAL;
}
/*class_creat(owner,name)*/
char_class=class_create(THIS_MODULE,"char_module");
if(char_class==0){
printk(KERN_WARNING"class_create error\n");
unregister_chrdev_region(devno,1);
/*cdev_dev(struct cdev *dev)*/
cdev_del(&dev->cdev);
return -EINVAL;
}
/*device_create(上面的class,NULL,dev_t,NULL,device name)*/
device_create(char_class,NULL,devno,NULL,"char_module");
return 0;
}
//卸載
static void char_exit(void)
{
/*unregister_chrdev_region(dev_t first,unsigned int count)*/
unregister_chrdev_region(devno,1);
/*device_destroy(myclass,dev_t)*/
device_destroy(char_class,devno);
/*class_destroy(myclass)*/
class_destroy(char_class);
/*cdev_dev(struct cdev *dev)*/
cdev_del(&dev->cdev);
/*釋放空間*/
kfree(dev->data);
kfree(dev);
dev->data=NULL;
dev=NULL;
}
//聲明module
module_init(char_init);
module_exit(char_exit);
------------------------------------------------------------------------------------------
紅色的就是和上篇不太一樣的東西。這個順序也有調整。修改了printk的輸出級別等。
下面是測試程序
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
int main()
{
int fd,size;
char buff_write[]="test data";
char buff_read[128];
if((fd=open("/dev/char_module",2))==-1){//open
printf("open error!\n");
exit(1);
}
if((size=read(fd,buff_read,strlen(buff_read)))<0){//read第一次
printf("read error!\n");
exit(1);
}
printf("read:%dbytes\n",size);
printf("R:%s\n",buff_read);
if((size=write(fd,buff_write,strlen(buff_write)))!=strlen(buff_write)){//write
printf("write error!\n");
exit(1);
}
printf("W:%s\n",buff_write);
if(lseek(fd,0,SEEK_SET)==-1){//重置偏移量
printf("lseek error\n");
exit(1);
}
if((size=read(fd,buff_read,strlen(buff_write)))<0){//read第二次
printf("read error!\n");
exit(1);
}
printf("read:%dbytes\n",size);
printf("R:%s\n",buff_read);
close(fd);
exit(0);
}
---------------------------------------------------------------------------------------
這裏的測試程序比上一篇的測試程序要好點。