#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/printk.h>
#include <linux/err.h>
#include <linux/miscdevice.h>
#include <linux/sysfs.h>
#include "freg.h"
//#define FREG_DEV_T
#ifdef FREG_DEV_T
#define FREG_MAJOR 243
#define FREG_MINOR 0
#endif
// 主裝置号和從裝置号變量
static int freg_major = 0;
static int freg_minor = 0;
// 裝置類型和裝置變量
//static struct class* freg_class = NULL;
static struct fake_reg_dev* freg_dev = NULL;
//
static struct proc_dir_entry* freg_dir, *freg;
// 傳統的裝置檔案操作方法
static int freg_open(struct inode* inode, struct file* filp);
static int freg_release(struct inode* inode, struct file* filp);
static ssize_t freg_read(struct file * filp, char __user * buf, size_t size, loff_t * ppos);
static ssize_t freg_write(struct file * filp, const char __user * buf, size_t size, loff_t * ppos);
static int freg_open(struct inode* inode, struct file* filp)
{
filp->private_data = freg_dev;
pr_info("lxf %s\n",__func__);
return 0;
}
static int freg_release(struct inode* inode, struct file* filp)
{
pr_info("lxf %s\n",__func__);
return 0;
}
static ssize_t freg_read(struct file * filp, char __user * buf, size_t size, loff_t * ppos)
{
int cnt =0;
char val[4]={0};
pr_info("lxf %s bf lock\n",__func__);
mutex_lock(&freg_dev->freg_mutex);
cnt = sprintf(val,"%d\n",freg_dev->val);
cnt = simple_read_from_buffer(buf, size, ppos, val, cnt);
mutex_unlock(&freg_dev->freg_mutex);
pr_info("lxf %s af lock\n",__func__);
return cnt;
}
static ssize_t freg_write(struct file * filp, const char __user * buf, size_t size, loff_t * ppos)
{
char mem[10];
int cnt;
pr_info("lxf %s\n",__func__);
mutex_lock(&freg_dev->freg_mutex);
cnt = simple_write_to_buffer(mem, size, ppos, buf, sizeof(freg_dev->val));
freg_dev->val = (int)simple_strtoul(mem, NULL, 10);
mutex_unlock(&freg_dev->freg_mutex);
return cnt;
}
// 傳統的裝置檔案操作方法表
static struct file_operations freg_fops =
{
.owner = THIS_MODULE,
.open = freg_open,
.release = freg_release,
.read = freg_read,
.write = freg_write,
};
ssize_t freg_proc_read(struct file *file, char __user * buf, size_t size, loff_t * ppos)
{
int cnt =0;
char* page ;
page = kzalloc(128,GFP_KERNEL);
cnt = sprintf(page,"%d\n",freg_dev->val);
pr_info("lxf %d\n", freg_dev->val);
cnt = simple_read_from_buffer(buf, size, ppos, page, cnt);
kfree(page);
return cnt;
}
ssize_t freg_proc_write(struct file *file, const char __user * buf, size_t count, loff_t * ppos)
{
pr_info("lxf %s\n",__func__);
freg_dev->val = (int)simple_strtoul(buf, NULL, 10);
pr_info("lxf %d\n", freg_dev->val);
return count;
}
static struct file_operations proc_fops =
{
.owner = THIS_MODULE,
.read = freg_proc_read,
.write = freg_proc_write,
};
int freg_creat_proc(void)
{
freg_dir = proc_mkdir("freg_dir", NULL);
freg = proc_create_data("freg", 0666, freg_dir, &proc_fops, freg_dev);
if (!freg)
{
return -1;
}
return 0;
}
static struct kobject* freg_device; //sysfs kobject
//static DEVICE_ATTR(tp_info, 0444, msm_tp_module_id_show, NULL);
static ssize_t freg_sys_show(struct device *dev,
struct device_attribute * attr, char *buf)
{
pr_info("lxf %s\n",__func__);
return sprintf(buf,"%d\n",freg_dev->val);
}
ssize_t freg_sys_store (struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
pr_info("lxf %s\n",__func__);
freg_dev->val = (int)simple_strtoul(buf, NULL, 10);
return count;
}
static DEVICE_ATTR(freg_info, 0666, freg_sys_show, freg_sys_store);
static int freg_creat_sysfs(void)
{
int ret = 0;
freg_device = kobject_create_and_add("freg_sys",NULL);
if (!freg_device)
{
pr_info("lxf subsystem_register failed\n");
ret = -ENOMEM;
return ret;
}
ret = sysfs_create_file(freg_device, &dev_attr_freg_info.attr);
if (ret)
{
pr_info("lxf sysfs_create_file fail\n");
kobject_del(freg_device);
}
return 0;
}
static int __init freg_init(void)
{
int ret;
dev_t devno;
struct class *myclass;
//建立字元裝置
#ifdef FREG_DEV_T
freg_major = FREG_MAJOR;
freg_minor = FREG_MAJOR;
#endif
devno = MKDEV(freg_major, freg_minor);
if (freg_major)
{
ret = register_chrdev_region(devno, 1, "freg_cdev");
if (ret)
{
printk(KERN_INFO "lxf register_chrdev_region fail\n");
return ret;
}
}
else
{
ret = alloc_chrdev_region(&devno, 0, 1, "freg_cdev");
if (ret)
{
printk(KERN_INFO "lxf alloc_chrdev_region fail\n");
return ret;
}
freg_major = MAJOR(devno);
freg_minor = MINOR(devno);
}
freg_dev = kzalloc(sizeof(struct fake_reg_dev), GFP_KERNEL);
if (IS_ERR_OR_NULL(freg_dev))
{
printk(KERN_INFO "lxf kzalloc fail\n");
ret = -ENOMEM;
goto fail_malloc;
}
cdev_init(&freg_dev->dev,&freg_fops);
freg_dev->dev.owner = THIS_MODULE;
//freg_dev->val = 521;
mutex_init(&freg_dev->freg_mutex);
// 添加字元裝置
ret = cdev_add(&freg_dev->dev, devno, 1);
if (ret)
{
printk(KERN_INFO "lxf register_chrdev_region fail\n");
goto cdev_del;
}
//建立相關節點
myclass = class_create(THIS_MODULE, "freg_cdev_driver");
device_create(myclass, NULL, devno, NULL,"freg_cdev");
freg_creat_proc();
freg_creat_sysfs();
pr_info("lxf freg_init sucessfully\n");
return ret;
cdev_del:
cdev_del(&freg_dev->dev);
fail_malloc:
unregister_chrdev_region(devno, 1);
return ret;
}
static void __exit freg_exit(void)
{
//登出裝置
cdev_del(&freg_dev->dev);
kfree(freg_dev);
unregister_chrdev_region(freg_dev->dev.dev, 1);
//删除節點
remove_proc_entry("freg", freg_dir);
remove_proc_entry("freg_dir", NULL);
sysfs_remove_file(freg_device, &dev_attr_freg_info.attr);
kobject_del(freg_device);
}
module_init(freg_init);
module_exit(freg_exit);
MODULE_AUTHOR("lxf");
MODULE_DESCRIPTION("this driver just for test");
MODULE_LICENSE("GPL v2");