学习完了字符驱动,是按照宋宝华的Linux设备驱动开发讲解学习的,代码练习敲了一遍,自己也理解了。
字符驱动主要的就是一些open,close,read,write等操作
通过上层调用到自己写的底层函数
这里写代码片
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#define GLOBALMEM_SIZE 0x1000 /*全局内存4kb*/
#define MEM_CLEAR 0x1 /*清除全局内存*/
#define GLOBALMEM_MAJOR 250 /*主设备号*/
static int globalmem_major = GLOBALMEM_MAJOR;
struct globalmem_dev
{
struct cdev cdev;
unsigned char mem[GLOBALMEM_SIZE];
};
struct globalmem_dev *globalmem_devp; /*全局结构体*/
int globalmem_open(struct inode* inode, struct file* filp)
{
filp->private_data = globalmem_devp;//将设备结构体指针值赋值给文件私有数据指针
return 0;
}
int globalmem_release(struct inode* inode, struct file* filp)
{
struct globalmem_dev* dev = filp->private_data;
return 0;
}
static int globalmem_ioctl(struct inode* inodep, struct file* filp,
unsigned int cmd,unsigned long arg)
{
struct globalmem_dev* dev = filp->private_data;
return 0;
}
static ssize_t globalmem_read(struct file* filp, char __user *buf,
size_t count, loff_t *ppos)
{
struct globalmem_dev* dev = filp->private_data;
unsigned long p = *ppos;
int ret = 0;
if (p >= GLOBALMEM_SIZE ) //偏移量超过数组范围
return 0;
if (count > GLOBALMEM_SIZE - p) //读的个数太大
count = GLOBALMEM_SIZE - p;
//从内核读到用户
if(copy_to_user(buf,dev->mem + p,count))
ret = - EFAULT; //EFAULT 参数buf指向无效内存地址
else {
*ppos += count;
ret = count;
printk(KERN_INFO "read %d bytes from %x\n",count,p);
}
return ret;
}
static ssize_t globalmem_write(struct file* filp, char __user *buf,
size_t count, loff_t *ppos)
{
struct globalmem_dev* dev = filp->private_data;
unsigned long p = *ppos;
int ret = 0;
if(p >= GLOBALMEM_SIZE) //偏移量超过内核空间大小
return 0;
if(count > GLOBALMEM_SIZE - p) //写的个数超过现有的
count = GLOBALMEM_SIZE - p;
//从用户写到内核
if(copy_from_user(dev->mem + p,(void*)buf,count))
ret = - EFAULT;
else
{
*ppos += count;
ret = count;
printk(KERN_INFO "write %d bytes from %x\n", count,dev->mem + p);
}
return ret;
}
//filp是内核文件结构体
static loff_t globalmem_llseek(struct file* filp, loff_t offset, int orig)
{
struct globalmem_dev* dev = filp->private_data;
int ret;
//SEEK_SET 0 文件开头
//SEEK_CUR 1 当前位置
//SEEK_END 2 文件尾
switch(orig){
case 0:
if(offset < 0){
ret = - EINVAL;
break;
}
if(offset > GLOBALMEM_SIZE){
ret = - EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case 1:
if(offset > GLOBALMEM_SIZE){
ret = - EINVAL;
break;
}
if(filp->f_pos + offset > GLOBALMEM_SIZE){
ret = - EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
case 2:
if(offset > 0){
ret = - EINVAL; //在文件末尾,只能偏移赋值
break;
}
if(offset + GLOBALMEM_SIZE < 0){
ret = - EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default:
;
}
return 0;
}
static const struct file_operations globalmem_fops = {
.owner = THIS_MODULE,
.open = globalmem_open,
.read = globalmem_read,
.write = globalmem_write,
.release = globalmem_release,
.llseek = globalmem_llseek,
.compat_ioctl = globalmem_ioctl
};
static void globalmem_setup_dev(struct globalmem_dev* globalmem_dev,int index)
{
int err,devno;
devno = MKDEV(globalmem_major,0);
cdev_init(&globalmem_dev->cdev,&globalmem_fops);
err = cdev_add(&globalmem_dev->cdev,devno,1);
if(err)
{
printk(KERN_NOTICE "ERROR %d adding globalmem %d",err,index);
}
}
static int __init globalmem_init(void)
{
printk(KERN_INFO "globalmem_init");
int result;
dev_t devno = MKDEV(globalmem_major,0);//根据主设备号,或得设备号,一般次设备号为0
if(globalmem_major != 0)
register_chrdev_region(devno,1,"globalmem_dev");
else
{
result = alloc_chrdev_region(&devno,0,1,"globalmem_dev");//自动分配一个设备号
globalmem_major = MAJOR(devno);
}
if(result < 0)
return result;
//动态申请结构体内存
globalmem_devp = kmalloc(sizeof(struct globalmem_dev),GFP_KERNEL);
if(!globalmem_devp)
goto fail_malloc;
memset(globalmem_devp,0,sizeof(struct globalmem_dev));
globalmem_setup_dev(globalmem_devp,0);
return 0;
fail_malloc:
unregister_chrdev_region(devno,1);
return result;
}
static int __exit globalmem_exit(void)
{
printk(KERN_INFO "globalmem_exit...");
dev_t devno;
devno = MKDEV(globalmem_major,0);
cdev_del(&globalmem_devp->cdev); // 删除cdev结构体
unregister_chrdev_region(devno,1);//注销设备
kfree(globalmem_devp);
return 0;
}
module_init(globalmem_init);
module_exit(globalmem_exit);
Makefile
KVERS = $(shell uname -r)
obj-m += globalmem_private.o
build:kernel_modules
kernel_modules:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
insmod:
insmod globalmem.ko
clean:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules clean
测试程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define path "/dev/globalmem_dev"
int main()
{
char buf[] = "Hello word!\n";
int fd = open(path,O_RDWR);
if(fd == -1){
perror("open");
exit(1);
}
write(fd,buf,sizeof(buf));
close(fd);
}
执行结果