#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
//#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#define homework_MAJOR 248 //设备号
#define HOMEWORK_SIZE 128 //cmos最大内存
static int homework_major=homework_MAJOR;
struct homework_dev {
struct cdev cdev; /*一个cdev结构体代表一个字符设备,与字符设备一一对应*/
struct semaphore sem; //信号量
};
struct homework_dev *homework_devp;
int homework_open(struct inode *inode, struct file *filp)
{
printk("open success\n");
filp->private_data = homework_devp;
return 0;
}
int homework_release(struct inode *inode, struct file *filp)
{
printk("release success\n");
return 0;
}
static ssize_t homework_read(struct file *filp, char __user *buf, size_t count,loff_t *ppos)
{
char data;
int i=0;
printk("read ppos=%d\n",(unsigned int)*ppos);
if(down_interruptible(&homework_devp->sem)) //获得信号量
{
return -ERESTARTSYS;
}
for(i=0;i<count;i++){
outb(*(ppos+i),0x70); //将地址写入70H
data=inb(0x71); //将数据从71H拿出到data
printk("read data=%c\n",data);
if(copy_to_user(buf+i,&data,sizeof(data))) //将data写到用户空间buf
{
return -EFAULT;
}
}
*ppos+=count;
printk("read buf=%c\n",*buf);
up(&homework_devp->sem); //释放信号量
return 0;
}
static ssize_t homework_write(struct file *filp, const char __user *buf, size_t count,loff_t *ppos)
{
char data;
int i;
if(down_interruptible(&homework_devp->sem))
{
return -ERESTARTSYS;
}
printk("in the write..\n");
printk("write ppos=%d\n",(unsigned int)*ppos);
for(i=0;i<count;i++){
// put *ppos to 70H
outb(*(ppos+i),0x70); //将地址写入70H
//read 71H to buf
printk("buf=%c\n",*buf);
if(copy_from_user(&data,buf+i,sizeof(data))) //将buf中数据拿到data
{
return -EFAULT;
}
printk("data=%c\n",data);
outb(data,0x71); //将data写入71H
}
*ppos+=count;
up(&homework_devp->sem); //释放信号量
return 0;
}
static loff_t homework_llseek(struct file *filp, loff_t offset, int orig)
{
loff_t ret = 0;
//printk("orig=%d\n",orig);
switch (orig)
{
case 0: //相对文件开始位置偏移
//printk("case0\n");
if (offset < 0)
{
ret = -EINVAL;
break;
}
if ((unsigned int)offset > HOMEWORK_SIZE)
{
ret = -EINVAL;
break;
}
//printk("offset=%d\n",(unsigned int)offset);
filp->f_pos = (unsigned int)offset; //修改filp->f_pos值偏移当前指针位置
ret = filp->f_pos;
break;
case 1: //相对文件当前位置偏移
if ((filp->f_pos + offset) > HOMEWORK_SIZE)
{
ret = -EINVAL;
break;
}
if ((filp->f_pos + offset) < 0)
{
ret = -EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
//文件操作结构体
static const struct file_operations homework_fops = {
.owner = THIS_MODULE,
.llseek = homework_llseek,
.open = homework_open,
.release = homework_release,
.read = homework_read,
.write = homework_write,
};
//初始化设备cdev结构体
static void homework_setup_cdev(struct homework_dev *dev, int index)
{
int err, devno = MKDEV(homework_major, index);
cdev_init(&dev->cdev, &homework_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&dev->cdev, devno, 1);//向操作系统注册cdev结构体
if (err)
printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}
//模块加载函数
int homework_init(void)
{
int ret=0;
dev_t devno = MKDEV(homework_major, 0); //将主设备号和次设备号转换成dev_t类型
if (homework_major)
ret = register_chrdev_region(devno, 1, "homework");//手工分配主设备号
else {
ret = alloc_chrdev_region(&devno, 0, 1, "homework");//动态分配主设备号
homework_major = MAJOR(devno);
}
if (ret < 0)
return ret;
homework_devp = kmalloc(sizeof(struct homework_dev), GFP_KERNEL);
if (!homework_devp) {
ret = - ENOMEM;
goto fail_malloc;
}
memset(homework_devp, 0, sizeof(struct homework_dev));
homework_setup_cdev(homework_devp, 0);
//init_MUTEX(&homework_devp->sem);
sema_init(&homework_devp->sem,1); //初始化信号量结构体
return 0;
fail_malloc:
unregister_chrdev_region(devno, 1);//释放设备号
return ret;
}
//模块卸载函数
void homework_exit(void)
{
cdev_del(&homework_devp->cdev); //注销字符设备
kfree(homework_devp); //释放设备结构体占用内存
unregister_chrdev_region(MKDEV(homework_major, 0), 1); //释放设备号
//printk("exit success\n");
}
MODULE_AUTHOR("liushan");
MODULE_LICENSE("Dual BSD/GPL");
module_param(homework_major, int, S_IRUGO);
module_init(homework_init);
module_exit(homework_exit);