文章目录
实验环境准备
实验材料:GEC6818实验箱、电脑、
实验环境:VMware下Linux系统、arm-linux-交叉编译环境,windows系统、secureCRT平台
第一步:测试三个系统是否连通
-
使用串口线、以太网线连接pc机与实验箱
-
禁用pc机的防火墙,并禁用无线网卡
-
设置pc机上以太网网卡的ip地址、子网掩码、网关(注意,不可设为主动获取,需要手动设置,因为目前只连接pc机与实验箱)
-
在secureCRT下设置实验箱Linux系统的ip地址,使其与pc机处于同一网段下(此处注意,开启实验箱时,直接等待其进入Linux系统,而不是u-boot模式,接着按CTRL+c即可进入Linux系统命令行)
-
在pc机上的vmware虚拟机的Linux系统下设置ip地址(此处注意要先设置好与pc机的网络连接模式,这里选择桥接模式桥接到外部机的一张网卡(该网卡可能是虚拟出来的)
-
前面三个ip地址一定要保证在同一网段下,例如pc机设ip为192.168.1.160、子网掩码为255.255.255.0、网关为192.168.1.1
实验箱ip地址设为192.168.1.180,虚拟机上Linux系统ip地址设为192.168.1.170 -
然后确保两两相互能ping得通
第二步:编写并编译驱动与测试程序
1.编写LED灯驱动程序led_old.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/ioctl.h>
#define old_way 1
#define GPIOEOUT_PA 0xC001E000 //外设的物理地址
#define BUF_SIZE 256
struct cdev chrdev_test;
dev_t ndev;
int TestMajor = 0; //主设备号
int TestMinor = 0; //此设备号
char drv_name[]="chrtest" ;
char kbuf[BUF_SIZE] ;
char wbuf[BUF_SIZE] ;
unsigned int *GPIOEOUT_VA; //0xC001E000
unsigned int *GPIOEOUTENB_VA; //0xC001E004
struct resource *GPIOE13_LED;
static int test_opne(struct inode *inode, struct file *file)
{
printk("<0>""chrdev is openning!\n");
*GPIOEOUTENB_VA = (1 << 13); //设置为输出模式
*GPIOEOUT_VA = (0 << 13); //输出为0
return 0;
}
static int test_release(struct inode *inode, struct file *file)
{
printk("<0>""chrdev is closeing\n");
return 0;
}
static ssize_t test_read(struct file *file, char __user *buf,\
size_t const count, loff_t *offset)
{
if(count < BUF_SIZE)
{
if(copy_to_user(buf, wbuf, count))//执行完成后返回还需要拷贝的字节数,成功为0
{
printk("<0>""copy to user filed\n");
return -EFAULT;
}
}
else
{
printk("<0>""read size must be less than %d\n", BUF_SIZE);
return -EINVAL;
}
*offset +=count; //文件位置指针更新
return count;
}
static ssize_t test_write(struct file *file, char __user *buf,\
size_t const count, loff_t *offset)
{
if(count < BUF_SIZE)//读写大小检查
{
if(copy_from_user(wbuf, buf, count))//执行完成后返回还需要拷贝的字节数,成功为0
{
printk("<0>""copy from user filed\n");
return -EFAULT;
}
}
else
{
printk("<0>""size must be less than %d\n", BUF_SIZE);
return -EINVAL;
}
*offset +=count; //文件位置指针更新
return count;
}
//cmd 设置灯的状态 0 ----off 1 ----on
//args 设置哪一盏灯的:
static long test_ioctl(struct file* file, unsigned int cmd, unsigned int args)
{
switch(cmd)
{
case 0:
*GPIOEOUT_VA = (0 << 13);
printk("<0>""cmd =0, args is not quest\n");
break;
case 1:
*GPIOEOUT_VA = (1 << 13);
printk("<0>""cmd =1, args is not quest\n");
break;
default:
printk("<0>""command is error!\n");
break;
}
return 0;
}
static const struct file_operations chrdev_fops = {
.owner = THIS_MODULE,
.open = test_opne,
.read = test_read,
.write = test_write,
.unlocked_ioctl = test_ioctl,
.release = test_release,
};
static int __init test_init(void)
{
int ret;
#if old_way
ret = register_chrdev(TestMajor, drv_name, &chrdev_fops);
if(ret < 0)
{
printk("<0>""can not register majior\n");
return ret;
}
else if(ret > 0)
{
printk("<0>""allocating majior successful \n");
TestMajor = ret;
ndev = MKDEV(TestMajor, TestMinor);
}
else
{
ndev = MKDEV(TestMajor, TestMinor);
}
#else
if(TestMajor)
{
ndev = MKDEV(TestMajor, TestMinor);//由主次设备号,得到设备号
ret = register_chrdev_region(ndev, 1, drv_name);//注册字符设备编号
}
else
{
ret = alloc_chrdev_region(&ndev, TestMinor, 1, drv_name);//动态分配一个字符设备号,&ndev存放返回的值
TestMajor = MAJOR(ndev);
}
if(ret < 0)
{
printk(KERN_WARNING "cannot get major %d \n", TestMajor);
goto fail_chrdev_region;//出错处理
}
cdev_init(&chrdev_test, &chrdev_fops);
chrdev_test.owner = THIS_MODULE;
ret = cdev_add(&chrdev_test, ndev, 1);
if(ret < 0)
{
printk("<0>""failed to cdev add\n");
goto fail_chrdev_add;//出错处理goto
}
#endif
printk("<0>""this is the first demo of drvier, inserting successful !\n");//通过console输出一个字符串
GPIOE13_LED = request_mem_region(GPIOEOUT_PA, 8, "LED_IO");
if(GPIOE13_LED == NULL)
{
ret = -EFAULT;
printk("<0>""can not request memory GPIOE13_LED");
goto fail_request_mem; //出错处理
}
GPIOEOUT_VA = (unsigned int *) ioremap(GPIOEOUT_PA, 8);
if(GPIOEOUT_VA == NULL)
{
ret = -EFAULT;
printk("<0>""can not ioremap GPIOEOUT_VA");
goto fail_ioremap; //出错处理
}
GPIOEOUTENB_VA = GPIOEOUT_VA + 1;
return 0;
fail_ioremap:
release_mem_region(GPIOEOUT_PA, 8);
fail_request_mem:
cdev_del(&chrdev_test);
fail_chrdev_add:
unregister_chrdev_region(ndev, 1);
fail_chrdev_region:
return ret;
}
static void __exit test_exit(void)
{
iounmap(GPIOEOUT_PA);
release_mem_region(GPIOEOUT_PA, 8);
unregister_chrdev_region(ndev, 1);
cdev_del(&chrdev_test);
printk("<0>""the driver is exiting!\n");
}
module_init(test_init);//驱动的入口函数会调用一个用户的初始化函数
module_exit(test_exit);//驱动的出口也函数会调用一个用户的退出函数
//驱动的描述信息: #modinfo *.ko ,驱动的描述信息并不是必需的。
MODULE_AUTHOR("kaoyangou");
MODULE_DESCRIPTION("the first demo of driver");
MODULE_LICENSE("GPL"); //遵循的协议
2. 编写驱动程序的Makefile文件
INSTALLDIR = /home/Hello
ifneq ($(KERNELRELEASE),)
obj-m:=led_old.o
else
KERNELDIR:=/home/Hello/demo/6818GEC/kernel
CROSS_COMPILE:=/home/Hello/demo/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-
PWD:=$(shell pwd)
default:
mkdir -p $(INSTALLDIR)
$(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNELDIR) M=$(PWD) modules
cp --target-dir=$(INSTALLDIR) led_old.ko
clean:
rm -rf *.o *.order .*.cmd *.mod.c *.symvers
endif
编写LED灯测试程序led_oldtest.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
int fd, ret;
fd = open("/dev/gec_led",O_RDWR);
if(fd < 0)
{
perror("open:");
return -1;
}
while(1)
{
ret = ioctl(fd, 1, 1);//LED OFF
if (ret < 0)
{
perror("ioctl:");
return -1;
}
sleep(1);
ret = ioctl(fd, 0, 1);//LED OFF
if (ret < 0)
{
perror("ioctl:");
return -1;
}
sleep(1);
}
close(fd);
return 0;
}
编写测试程序的Makefile文件
#
# Makefile for Linux application test example.
#
#---------------------------------------------------------------
# Linux application Makefile sample
# make -- the optical output without debug info
# make clean -- clean the object file(s)
# make install -- install the execute file(s) to INSTALLDIR
#---------------------------------------------------------------
# History:
#
INSTALLDIR = /home/Hello/nfs_share
#--------------------------------- /* execute file(s) */
TESTFILE = led_oldtest
#--------------------------------- /* object file(s) */
SRCFILE = led_oldtest.c
#--------------------------------- /* header file(s) */
TESTFILE_H =
CROSS = arm-none-linux-gnueabi-
CC = $(CROSS)gcc
AS = $(CROSS)as
LD = $(CROSS)ld
CFLAGS += -O2 -Wall
all: $(TESTFILE)
$(TESTFILE): $(SRCFILE) $(TESTFILE_H) Makefile
mkdir -p $(INSTALLDIR)
$(CC) $(CFLAGS) -o $@ $@.c
cp --target-dir=$(INSTALLDIR) $(TESTFILE)
clean:
rm -f $(TESTFILE)
第三步:编译生成led_old.ko与led_oldtest二进制执行文件
- 将驱动程序led_old.c与其对应Makefile文件放在同一目录下,然后在该目录下执行命令make,生成目标文件led_old.ko
- 将驱动程序led_oldtest.c与其对应Makefile文件放在同一目录下,然后在该目录下执行命令make,生成目标文件led_oldtest
注意,以上的编译属于交叉编译,Makefile文件中已指定相应的交叉编译器
第四步:将led_old.ko文件与led_oldtest文件挂载到实验箱板子
- 将led_old.ko文件与led_oldtest文件统一放在虚拟机的nfs_share目录上
- 在secureCRT命令框使用mount命令进行挂载,命令中的ip地址是虚拟机的ip地址
mount -o nolock,tcp 192.168.0.190:/home/Hello/nfs_share /mnt
第五步:在实验箱装载驱动并使用程序进行测试
- 在secureCRT中输入命令lsmod,然后回车,在返回的消息中如果找到有led字眼的一行,则使用rmmod led进行卸载原有驱动,确保实验箱系统不存在led灯的驱动程序
lsmod //查看已存在驱动
rmmod led //卸载led驱动
- 进入到目录 /mnt,然后插入驱动led_old.ko,因为刚才把自己制作的驱动挂载到了/mnt目录下
insmod led_old.ko
- 在/proc/devices 找到我们申请的主设备号和设备名
cat /proc/devices
在结果在找到的243 chrtest即为我们要的结果,注意chrtest设备名在驱动程序中已编写,当插入该驱动进实验箱时自动分配了这个243的主设备号
- 在 实验箱板子中创建设备文件的节点
命令如下:
mknod /dev/gec_led c 243 0
命令解释:
mknod //创建
/dev/gec_led //设备的名字
c //表示字符设备
243 //刚刚我们查看到的主设备号
0 //次设备号,可以任意給值,但是不要大于 255
- 在实验箱板子中执行程序
./led_oldtest
观察结果,实验箱上其中一个led灯在闪烁