铺垫:
这俩天写了一个控制LED灯的驱动,真的是哭死我这个小白了,因为之前ARM上弄过相似的,以为很简单,哪知道,还是有点麻烦的,现在的的程序是基于MIPS平台编写的。什么都得纯手工。
正题:
在mips平台上,制作一个简易的LED驱动来练习char字符驱动
首先查看,当前LED对应的端口是PB03,查找数据手册,GPIO部分,看看需要如何设置,可以看到下面2个主要的寄存器,一个控制方向(输入or输出),一个数据寄存器(写入0 or 1–对应拉高或拉低)
知道了LED对应的端口寄存器的配置需求,接下来,就可以编写驱动源文件了
#include
#include
#include
#include
#include
#include
#include
#include
#include
MODULE_LICENSE("GPL");
static struct class *my_led_class;
static struct device *my_led_device;
volatile unsigned long *my_led_pbpat=NULL;
volatile unsigned long *my_led_pbdata=NULL;
static int led_open(struct inode *inode,struct file *file);
static int led_read(struct file *filp,char __user *buff,size_t count,loff_t *offp);
static int led_write(struct file *file,const char __user*buf,size_t count,loff_t *ppos);
struct file_operations led_drv_ops={
.open=led_open,
.read=led_read,
.write=led_write,
.owner=THIS_MODULE,
};
int dev_num=0;
static int led_open(struct inode *inode,struct file *file)
{
printk("led open\n");
*my_led_pbpat &=~(0x1<<3);//PBPAT103 0: out,1: input
// *my_led_pbpat |= (0x1<<3);
*my_led_pbdata |=(1<<3);
return 0;
}
static int led_write(struct file *file,const char __user *buf,size_t count,loff_t *ppos)
{
int val;
printk("led write\n");
copy_from_user(&val,buf,count);
if(val == '0')
{
//to set 0
*my_led_pbdata &=~(1<<3);
}
else
{
//to set 1
*my_led_pbdata |=(1<<3);
}
return 0;
}
static int led_read(struct file *filp,char __user *buff,size_t cunt,loff_t *offp)
{
printk("led read\n");
return 0;
}
static int led_init(void)
{
dev_num=register_chrdev(0,"led_drv",&led_drv_ops);
if(dev_num<0)
{
printk("sorry,register char device failed!\n");
return -1;
}
my_led_class=class_create(THIS_MODULE,"leddrv");
my_led_device=device_create(my_led_class,NULL,MKDEV(dev_num,0),NULL,"shlled");
printk("init ok\n");
my_led_pbpat=(volatile unsigned long *)ioremap(0x10010130,16);
my_led_pbdata=(volatile unsigned long *)ioremap(0x10010140,16);//0x10010140
return 0;
}
static void led_exit(void)
{
unregister_chrdev(dev_num,"led_drv");
device_unregister(my_led_device);
class_destroy(my_led_class);
iounmap(my_led_pbpat);
iounmap(my_led_pbdata);
printk("led driver exit\n");
}
module_init(led_init);
module_exit(led_exit);
obj-m:=myled.o
KERNELDIR :=/home/shl/work/x1500/kernel/
#KERNELDIR :=/usr/src/linux-headers-4.15.0-47-generic/
PWD :=$(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
.PHONEY:clean
clean:
rm -f *.o *.ko *.order *.symvers *.mod*
当然,在编译makefile需要注意的是,当前编译需要使用交叉编译器
因为linux下默认的是gcc,编译出来的文件在x1500核心板上是无法运行的,(我因为这个问题困住了2天),将环境变量中的ARCH和CROSS_COMPILE都修改一下
命令行输入:
export ARCH=mips
export CROSS_COMPILE=mips-linux-gnu-
执行make ,生成myled.ko文件
编写测试文件来测试当前驱动的功能
#include
#include
#include
int main(int argc,char *argv[])
{
char buf[2]={'0','1'};
int cmd;
int fd=open("/dev/shlled",O_RDWR);
if(fd<0)
{
printf("open dev/shlled fail!\n");
return -1;
}
cmd=atoi(argv[1]);
if(cmd==0)
{
write(fd,&buf[0],sizeof(char));
}
else if(cmd==1)
{
write(fd,&buf[1],sizeof(char));
}
else
{
printf("please input 0 or 1");
}
printf("\n");
return 0;
}
编译生成可执行文件
mips-linux-gnu-gcc test.c -muclibc -o test
(为何多了一个 -muclibc,这是因为系统的依赖库和板子上的依赖库的版本不一样,板子是老板给的,系统是我自己装的,老板又不让我重新刷系统,我能咋办)
详情可以参照动态库不匹配(超链接日后再补)
step5: 加载驱动,并测试
(执行代码摘录,test,myled.ko当前存放在/tmp目录下)
/tmp # chmod 777 test
/tmp # insmod myled.ko
[ 736.732691] init ok
/tmp # ls /dev/shlled
/dev/shlled (这里可以看到,我自己编写的shlled已经成功显示在dev下面了)
/tmp # ./test 0
[ 813.653481] led open
[ 813.655778] led write
/tmp # ./test 1
[ 817.871106] led open
[ 817.873552] led write
/tmp # ./test 0
[ 893.370064] led open
[ 893.372463] led write