设备驱动实现

1. 建立内核源码树:

1.1 下载相应的内核源代码

直接到www.kernel.org 官网中下载所需的版本,并且将其下载在桌面。然后执行命令 #mv /home/lzb/桌面/linux-2.6.32.69.tar.xz /home,将其解压。

1.2 安装编译内核时需要的工具

总共安装两个软件,都采用apt-get install 命令安装

build-essential(基本的编程库(gcc, make 等) libncurses5-dev (make menuconfig 要调用的)

1.3 编译相应的内核。

先使用 cd /home/linux-2.6.32.69,进入到内核源码之中。然后执行sudo make mrproper (清除以前曾经编译过的旧文件) ,接着将把正在使用中的内核配置文件/boot/config-3.19.0-25-generi

执行make menuconfig ,会弹出一个配置界面。这个配置界面就是关于内核的一些详细配置。

在这里可以看见一些条目,只关注两条:

load a kernel configuration   save a kernel configuration

扫描二维码关注公众号,回复: 1675919 查看本文章

 用刚刚中boot 中考过来的以及配好的文件进行配置,点击load a kernel configuration,然后

输入/boot/config-3.19.0-25-generic点击ok,退出这个界面进入终端,然后输入make 指令进行内核编译。

2 编写globalmem 设备程序
2.1 虚拟内存设备globalmem 驱动:
在Linux2.6 内核以前注册字符设备的函数接口是register_chrdev(),在2.6 中其可继续使用。
register_chrdev 大致作用:向内核注册cdev 结构体,当在用户空间打开设备文件时内核可以根据
设备号快速定位此设备文件的cdev->file_operations 结构体,从而调用驱动底层的open、close、
read 、write 、ioctl 等函数, 当我们在用户空间open 字符设备文件时, 首先调用
def_chr_fops->chrdev_open() 函数( 所有字符设备都调用此函数) , chrdev_open 会调用
kobj_lookup 函数找到设备的cdev->kobject,从而得到设备的cdev,进而获得file_operations。
要想通过kobj_lookup 找到相应的cdev,必需调用register_chrdev()函数。向内核注册。
globalmem 设备是我们构造的一个虚拟设备,globalmem 意味着“全局内存”,在globalmem 字符设
备驱动中会分配一片大小为GLOBALMEM_SIZE(4KB)的内存空间,并在驱动中提供针对该片内存的
读写、控制和定位函数,以供用户空间的进程能通过Linux 系统调用访问这片内存。
2.2 编写globalmem驱动程序
1、打开终端输入命令 mkdir /home/user/globalmem 创建globalmem 文件夹
2、然后再输入命令 cd /home/user/globalmem 进入globalmem 文件夹,
3、接着输入命令 gedit globalmem.c 编写globalmem.c程序
在/home/user/globalmem新建文件globalmem.c如下:
#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>
#define GLOBALMEM_SIZE 0x1000 /*全局内存最大4K 字节*/
#define MEM_CLEAR 0x1 /*清0 全局内存*/
#define GLOBALMEM_MAJOR 150 /*预设的globalmem 的主设备号*/
static int globalmem_major = GLOBALMEM_MAJOR;
/*globalmem 设备结构体*/
struct globalmem_dev
{
struct cdev 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)
{
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;/*获得设备结构体指针*/
switch (cmd)
{
case MEM_CLEAR:
memset(dev‐>mem, 0, GLOBALMEM_SIZE);
printk(KERN_INFO ʺglobalmem is set to zero\nʺ);
break;
default:
return ‐ EINVAL;
}
return 0;
}
static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
68
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct globalmem_dev *dev = filp‐>private_data; /*获得设备结构体指针*/
/*分析和获取有效的写长度*/
if (p >= GLOBALMEM_SIZE)
return count ? ‐ ENXIO: 0;
if (count > GLOBALMEM_SIZE ‐ p)
count = GLOBALMEM_SIZE ‐ p;
/*内核空间‐>用户空间*/
if (copy_to_user(buf, (void*)(dev‐>mem + p), count))
{
ret = ‐ EFAULT;
}
else
{
*ppos += count;
ret = count;
printk(KERN_INFO ʺread %d bytes(s) from %d\nʺ, count, p);
}
return ret;
}
static ssize_t globalmem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct globalmem_dev *dev = filp‐>private_data; /*获得设备结构体指针*/
/*分析和获取有效的写长度*/
if (p >= GLOBALMEM_SIZE)
return count ? ‐ ENXIO: 0;
if (count > GLOBALMEM_SIZE ‐ p)
count = GLOBALMEM_SIZE ‐ p;
/*用户空间‐>内核空间*/
if (copy_from_user(dev‐>mem + p, buf, count))
ret = ‐ EFAULT;
else
{
*ppos += count;
ret = count;
printk(KERN_INFO ʺwritten %d bytes(s) from %d\nʺ, count, p);
}
return ret;
}
static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{
loff_t ret = 0;
switch (orig)
{
case 0: /*相对文件开始位置偏移*/
if (offset < 0)
{
ret = ‐ EINVAL;
break;
}
if ((unsigned int)offset > GLOBALMEM_SIZE)
{
ret = ‐ EINVAL;
break;
}
filp‐>f_pos = (unsigned int)offset;
ret = filp‐>f_pos;
break;
case 1: /*相对文件当前位置偏移*/
if ((filp‐>f_pos + offset) > GLOBALMEM_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 globalmem_fops =
{
.owner = THIS_MODULE,
.llseek = globalmem_llseek,
.read = globalmem_read,
.write = globalmem_write,
.ioctl = globalmem_ioctl,
.open = globalmem_open,
.release = globalmem_release,
};
static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)
{
int err, devno = MKDEV(globalmem_major, index);
cdev_init(&dev‐>cdev, &globalmem_fops);
dev‐>cdev.owner = THIS_MODULE;
dev‐>cdev.ops = &globalmem_fops;
err = cdev_add(&dev‐>cdev, devno, 1);
if (err)
printk(KERN_NOTICE ʺError %d adding LED%dʺ, err, index);
}
int globalmem_init(void)
{
int result;
dev_t devno = MKDEV(globalmem_major, 0);
/* 申请设备号*/
if (globalmem_major)
result = register_chrdev_region(devno, 1, ʺglobalmemʺ);
else /* 动态申请设备号 */
{
result = alloc_chrdev_region(&devno, 0, 1, ʺglobalmemʺ);
globalmem_major = MAJOR(devno);
}
if (result < 0)
return result;
/* 动态申请设备结构体的内存*/
globalmem_devp = kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);
if (!globalmem_devp) /*申请失败*/
{
result = ‐ ENOMEM;
goto fail_malloc;
}
memset(globalmem_devp, 0, sizeof(struct globalmem_dev));
globalmem_setup_cdev(globalmem_devp, 0);
return 0;
fail_malloc: unregister_chrdev_region(devno, 1);
return result;
}
void globalmem_exit(void)
{
cdev_del(&globalmem_devp‐>cdev); /*注销cdev*/
kfree(globalmem_devp); /*释放设备结构体内存*/
unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); /*释放设备号*/
}
MODULE_AUTHOR(ʺSong Baohuaʺ);
MODULE_LICENSE(ʺDual BSD/GPLʺ);
module_param(globalmem_major, int, S_IRUGO);
module_init(globalmem_init);
module_exit(globalmem_exit);
在/home/user/globalmem 新建Makefile 文件如下:
ifneq ($(KERNELRELEASE),)
obj‐m := globalmem.o
else
KERNELDIR = /usr/src/linux‐headers‐2.6.31‐14‐generic/
PWD := $(shell pwd)
default:
$(MAKE) ‐C $(KERNELDIR) M=$(PWD) modules
clean:
rm ‐rf *.o *~ core .depend .*.cmd *.ko *.mod.c
endif
编译globalmem.c,得到myglobalmem.ko文件。
运行insmod globalmem.ko命令加载模块,通过lsmod命令,发现globalmem模块已被加载。再通
过cat /proc/devices命令查看,发现多出了主设备号为150的myglobalmem字符设备驱动,如下所示:
[root@EmbedSky /]# sudo insmod globalmem.ko
[root@EmbedSky /]# lsmod
Not tainted
myglobalmem 3396 0 ‐ Live 0xbf067000
ov9650 11088 0 ‐ Live 0xbf05c000
……
[root@EmbedSky /]# cat /proc/devices
Character devices:
……
150 globalmem
……
[root@EmbedSky /]# sudo mknod /dev/globalmem c 150 0
编写应用程序test.c用于测试:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
main()
{
char str[1024], str2[1024];
int fd, stop;
int ret;
fd = open(ʺ/dev/globalmemʺ, O_RDWR, S_IRUSR | S_IWUSR);
if (fd != ‐1 )
{
printf(ʺPlease input the string written to globalmem\nʺ);
scanf(ʺ%sʺ, str);
printf(ʺThe str is %s\nʺ, str);
ioctl(fd, 1, NULL);
ret = write(fd, str, strlen(str));
printf(ʺThe ret is %d\nʺ, ret);
lseek(fd, 0, SEEK_SET);
scanf(ʺ%dʺ, &stop);
read(fd, str2, 100);
printf(ʺThe globalmem is %s\nʺ, str2);
close(fd);
}
else
{
printf(ʺDevice open failure\nʺ);
}
}
编译上述文件:
gcc ‐o test test.c
运行
$ sudo ./test
Please input the string written to globalmem
qwertyuiop
The str is qwertyuiop
The ret is 10
0
The globalmem is qwertyuiop


猜你喜欢

转载自blog.csdn.net/sheloveme123/article/details/80738868