设计一个程序,在用户空间的用户应用程序中产生20个随机数,通过内核空间的设备驱动程序按五行四列的排列输出,并显示能被5整除的数。
分析:要实现这个功能需要做以下工作:
1.编写嵌入式设备驱动程序:实现按五行四列的排列输出,并显示能被5整除的数。
1设主设备号为100,即#define data_MAJOR 100
2在函数data_read()中将用户空间通过参数buf传递来的数据按5行4列的排列输出。在使用printk函数时,输出的格式用“%d”。
3在函数data_write()中找出用户空间通过参数buf传递来的能被5整除的数据输出到屏幕上。在使用printk函数时,输出的格式用“%d”。
4使用Vi编辑器编辑一个设备驱动程序data_drv.c。
/*************************************
*设备驱动程序data_drv.c
*由用户应用程序传递20个数值,在本设备驱动程序中进行排列输出,
*并打印出能被5整除的数
*************************************/
#include<linux/config.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/devfs_fs_kernel.h>
#include<linux/module.h>
#define data_MAJOR 100
ssize_t data_read(struct file * file,char *buf,size_tcount, loff_t* f_ops)
{//将用户空间通过参数buf传递来的数据按5行4列的排列输出
printk("data_read[--kernel--]\n");
for(count=0;count<20;count++){
printk("%d ",buf[count]);
if((count+1)%4==0)
printk("\n");
}
returncount;
}
ssize_t data_write(struct file * file, const char*buf, size_t count, loff_t * f_ops)
{//找出用户空间通过参数buf传递来的能被5整除的数据
printk("data_write[--kernel--]\n");
for(count=0;count<20;count++){
if(buf[count]%5==0)
printk("%d ",buf[count]);
}
return count;
}
int data_open(struct inode * inode, struct file *file)
{
printk("data_open[--kernel--]\n");
MOD_INC_USE_COUNT;
return 0;
}
int data_release(struct inode * inode, struct file *file)
{
printk("data_release[--kernel--]\n");
MOD_DEC_USE_COUNT;
return 0;
}
struct file_operations Test_ctl_ops=
{
open: data_open,
read: data_read,
write: data_write,
release: data_release,
};
static int __init HW_Test_CTL_init()
{
int ret =-1;
ret =devfs_register_chrdev(data_MAJOR," data_drv",&Test_ctl_ops);
if(ret <0){
printk("data_modulefailed with %d\n[--kernel--]",ret);
returnret;
}
else {
printk("data_driverregister success!!![--kernel--]\n");
}
printk("\n...\nret=%x\n...\n",ret);
return ret;
}
static int __init data_Test_CTL_init()
{
int ret =-1;
printk("data_Test_CTL_init[--kernel--]\n");
ret=HW_Test_CTL_init();
if(ret)
returnret;
return 0;
}
static void __exit cleanup_Test_ctl()
{
printk("cleanup_INT_ctl[--kernel--]\n");
devfs_unregister_chrdev(data_MAJOR,"data_drv");
}
module_init(data_Test_CTL_init);
module_exit(cleanup_Test_ctl);
2.编写用户测试程序:实现产生20个随机数并调用设备驱动程序硬件实现按五行四列的排列输出,并显示能被5整除的数。
根据题目要求,使用rand()函数产生20个随机数,存入到数组num[20]中(num定义成字符数组,如下:char num[20];),并通过调用设备驱动程序的read()、write()接口函数,将数据传入到内核空间的设备驱动程序中。把在/dev目录下建立的设备文件(设备进入点)设定为data_drv。
使用Vi编辑器编辑一个用户应用程序data_app.c。
用户应用程序中需要用到的函数(本题中不用ioctl函数):
在Linux系统中,I/O设备的存取是通过一组固定的入口点来进行,这组入口点是由每个设备的设备驱动程序提供的。一般来说,字符型设备驱动程序能够提供如下几个入口点。
(1)open——打开设备准备I/O操作,其调用格式为:
int open(char * filename, int access);
open()函数打开成功,返回值就是文件描述符(或文件描述字)的值(非负值),否则返回-1。该函数表示按access的要求打开名为filename的文件,返回值为文件描述符。access的取值请参看详细的open()函数说明,这里access的取值为O_RDWR,表示“读写”。
(2)close——关闭由open()函数打开的文件,其调用格式为:
int close(int handle);
该函数关闭文件描述符handle相连的文件。
(3)read——从设备上读数据,其调用格式为:
int read(int handle, void* buf, int count);
该函数从handle(文件描述符)相连的文件中,读取count个字节放到buf所指的缓冲区中,返回值为实际所读字节数,返回-1表示出错,返回0表示文件结束。
注意:在本程序中需要通过buf传递数组,因此read函数中的参数count为0。
(4)write——往设备上写数据,其调用格式为:
int write(int handle, void *buf, int count);
该函数把count个字节从buf指向的缓冲区中写入与handle相连的文件中,返回值为实际写入的字节数。
注意:在本程序中需要通过buf传递数组,因此write函数中的参数count为0。
(5)ioctl——执行读、写以外的操作,其调用格式为:
int ioctl(int fd, int cmd, …);
参数cmd不经修改地传递给驱动程序,可选的参数都以unsigned long的形式传递给驱动程序。
/************************************************************
* 用户程序data_app.c
************************************************************/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
#define DEVICE_NAME "/dev/data_drv"
int main()
{
char num[20];
int i=0;
for(i=0;i<20;i++)
num[i]=rand();
intfd; //声明文件描述符
intret; //记录关闭设备的返回值
char *i;
printf("\nstartdata_driver test\n\n");
fd =open(DEVICE_NAME,O_RDWR); //调用打开设备函数
printf("fd=%d\n",fd);
if(fd ==-1){
printf("opendevice %s error\n",DEVICE_NAME);
}
else {
write(fd,num,0); //调用驱动程序的写操作接口函数
read(fd,num,0); //调用驱动程序的读操作接口函数
ret=close(fd); //若ret的值为0,表示设备成功关闭
printf("ret= %d\n",ret);
printf("closedata_drive test\n");
}
return 0;
}
3.加载和卸载设备驱动程序模块:将设备驱动程序模块动态加载到内核中,输出完成后,从内核中卸载。
(1)为设备驱动程序编写Makefile文件,命名为Makefile,生成data_drv.o。
注意:在虚拟机上编写的Makefile文件要运行在虚拟机上,因此Makefile的前两行为:
INCLUDEDIR =/usr/src/linux-2.4.20-8/include
CC = gcc
(2)编译和运行程序
1。在虚拟机上编译和运行程序
1编译设备驱动程序
make
2编译用户应用程序
gcc–o data_app data_app.c
3在虚拟机的/dev目录下,建立设备入口点并查看:
mknod/dev/data_drv c 100 0
ls–l /dev |grep data_drv
4加载设备驱动程序
insmoddata_drv.o
5运行用户应用程序
./data_app
注意:因为使用printk函数将数据输出到/var/log/messages文件中,因此使用cat/var/log/messages查看按照5行4列输出的数据和被5整除的数据。
2。在开发板上编译和运行程序
1修改Makefile文件
因为下载到开发板上运行,需要使用armv4l-unknown-linux-gcc编译器,将Makefile的前两行修改为:
INCLUDEDIR= /opt/host/armv4l/include
CC= armv4l-unknown-linux-gcc
2编译设备驱动程序
make
3编译用户应用程序
armv4l-unknown-linux-gcc–o data_app data_app.c
4将上述编译后的用户应用程序data_app和设备驱动程序data_drv.o通过mount命令下载到嵌入式系统开发板上。
mount-t nfs –o nolock 192.168.221.140:/arm2410s/cwsaqdrv /mnt
5在开发板的/dev目录下,建立设备入口点:
mknod/dev/data_drv c 100 0
6加载设备驱动程序
insmoddata_drv.o
7运行用户应用程序
./data_app
注意:输出显示在超级终端中。