#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/fcntl.h> #include <linux/cdev.h> #include <linux/version.h> #include <linux/vmalloc.h> #include <linux/ctype.h> #include <linux/pagemap.h>
#include "demo.h"
MODULE_AUTHOR("demo"); MODULE_LICENSE("GPL");
struct DEMO_dev *DEMO_devices; //申明自己定义的设备结构的指针
static unsigned char demo_inc=0; //计数 记录此设备被打开的次数
static u8 demoBuffer[256]; //8位无符号整型,相当于unsigned char
int DEMO_open(struct inode *inode, struct file *filp) { struct DEMO_dev *dev; //只允许打开设备一次
if(demo_inc>0)return -ERESTARTSYS; demo_inc++;
dev = container_of(inode->i_cdev, struct DEMO_dev, cdev); filp->private_data = dev;
return 0; }
int DEMO_release(struct inode *inode, struct file *filp) { demo_inc--; return 0; }
ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos) { int result; loff_t pos= *f_pos; /* 文件的读写位置 */ if(pos>=256) { result=0; goto out; } if(count>(256-pos)) { count=256-pos; } pos += count; if (copy_to_user(buf,demoBuffer+*f_pos,count)) { count=-EFAULT; /* 把数据写到应用程序空间 */ goto out; } *f_pos = pos; /* 改变文件的读写位置 */ out: return count; }
ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos) { ssize_t retval = -ENOMEM; /* value used in "goto out" statements */ loff_t pos= *f_pos; if(pos>=256) { goto out; } //如果要写入的输入大于剩下的内存空间 就只写入剩下的空间数量 以防溢出
if(count>(256-pos)) { count=256-pos; }
pos += count; //copy_from_user 将数据从用户空间复制到内核空间
if (copy_from_user(demoBuffer+*f_pos, buf, count)) { retval = -EFAULT; goto out; }
*f_pos = pos; return count; out: return retval; }
/*ioctl的作用 ioctl是用来控制设备的 ,unsigned int cmd就是发给设备的命令,ioctl或许是Linux下最庞杂的函数*/ int DEMO_ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg) { if(cmd==COMMAND1) { printk("ioctl command1 successfully\n"); return 0; } if(cmd==COMMAND2) { printk("ioctl command2 successfully\n"); return 0; } printk("ioctl error\n"); return -EFAULT; }
//llseek 实现随即存取 ,loff_t应该是一个无符号整型 记录文件指针偏移量的 这段代码参考用户态的seek()就不难理解
loff_t DEMO_llseek(struct file *filp, loff_t off, int whence) { loff_t pos; pos = filp->f_pos; switch (whence) { case 0: pos = off; break; case 1: pos += off; break; case 2: default: return -EINVAL; } if ((pos>256) || (pos<0)) { return -EINVAL; } return filp->f_pos=pos; }
//file_operations这个结构体真是相当重要 需要搞清楚它的作用
struct file_operations DEMO_fops = { .owner = THIS_MODULE, .llseek = DEMO_llseek, .read = DEMO_read, .write = DEMO_write, .ioctl = DEMO_ioctl, .open = DEMO_open, .release = DEMO_release, };
/******************************************************* MODULE ROUTINE *******************************************************/
void DEMO_cleanup_module(void) { dev_t devno = MKDEV(DEMO_MAJOR, DEMO_MINOR);
if (DEMO_devices) { cdev_del(&DEMO_devices->cdev); //内核下的内存操作函数还真怪异 习惯就好
kfree(DEMO_devices); } //调用unregister_chrdev_region()函数释放分配的一系列设备号
unregister_chrdev_region(devno,1); }
int DEMO_init_module(void) { /* 在内核中,dev_t类型(定义在<linux/types.h>中)用来保存设备编号——包括主设备号和次设备号。 在内核2.6.0中,dev_t是一个32位的数,其中12位表示主设备号,20位表示次设备号。 */
int result; dev_t dev = 0;
dev = MKDEV(DEMO_MAJOR, DEMO_MINOR);
/*获取一个或多个设备编号来使用 如果分配成功进行, register_chrdev_region 的返回值是 0. 出错的情况下, 返回一个负的错误码, 你不能存取请求的区域. */
result = register_chrdev_region(dev, 1, "DEMO"); if (result < 0) { printk(KERN_WARNING "DEMO: can't get major %d\n", DEMO_MAJOR); return result; } //为自定义的设备结构申请空间
DEMO_devices = kmalloc(sizeof(struct DEMO_dev), GFP_KERNEL); if (!DEMO_devices) { result = -ENOMEM; goto fail; } //将新申请的空间清零
memset(DEMO_devices, 0, sizeof(struct DEMO_dev)); //初始化一个字符驱动 结构
cdev_init(&DEMO_devices->cdev, &DEMO_fops); DEMO_devices->cdev.owner = THIS_MODULE; DEMO_devices->cdev.ops = &DEMO_fops; //在内核中添加字符驱动
result = cdev_add (&DEMO_devices->cdev, dev, 1); if(result) { printk(KERN_NOTICE "Error %d adding DEMO\n", result); goto fail; }
return 0;
fail: //失败了 DEMO_cleanup_module(); return result; } //模块入口和出口 module_init(DEMO_init_module); module_exit(DEMO_cleanup_module);
|