天天看點

【LINUX驅動】第二篇—基于MIPS裝置字元驅動之LED驅動控制實作

鋪墊:

這倆天寫了一個控制LED燈的驅動,真的是哭死我這個小白了,因為之前ARM上弄過相似的,以為很簡單,哪知道,還是有點麻煩的,現在的的程式是基于MIPS平台編寫的。什麼都得純手工。

正題:

LED燈驅動

在mips平台上,制作一個簡易的LED驅動來練習char字元驅動

  • Step1:檢視對應硬體寄存器配置需求

首先檢視,目前LED對應的端口是PB03,查找資料手冊,GPIO部分,看看需要如何設定,可以看到下面2個主要的寄存器,一個控制方向(輸入or輸出),一個資料寄存器(寫入0 or 1–對應拉高或拉低)

【LINUX驅動】第二篇—基于MIPS裝置字元驅動之LED驅動控制實作
【LINUX驅動】第二篇—基于MIPS裝置字元驅動之LED驅動控制實作
【LINUX驅動】第二篇—基于MIPS裝置字元驅動之LED驅動控制實作
【LINUX驅動】第二篇—基于MIPS裝置字元驅動之LED驅動控制實作

知道了LED對應的端口寄存器的配置需求,接下來,就可以編寫驅動源檔案了

  • Step2:myled.c編寫
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/fs.h>
#include<linux/init.h>
#include<linux/delay.h>
#include<asm/uaccess.h>
#include<asm/irq.h>
#include<asm/io.h>
#include<linux/device.h>


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);

           

- Step3: Makefile的編寫

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檔案

【LINUX驅動】第二篇—基于MIPS裝置字元驅動之LED驅動控制實作

編寫測試檔案來測試目前驅動的功能

  • Step4:test.c編寫
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>



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

對應的LED燈亮滅(0 亮,1滅)

【LINUX驅動】第二篇—基于MIPS裝置字元驅動之LED驅動控制實作
【LINUX驅動】第二篇—基于MIPS裝置字元驅動之LED驅動控制實作

繼續閱讀