让vdsp与uclinux共舞(14):驱动发布

快乐虾

http://blog.csdn.net/lights_joy/

[email protected]

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

本文适用于

ADSP-BF561

Visual DSP++ 5.0(update 6)

Bfin-uclinux-2009r1.6

 

 

欢迎转载,但请保留作者信息

 

在驱动开发完成之后,同样有一个发布的问题,原来的计划是在开发完成后将这个dxe文件链接到内核中,在初始化的时候解开这个DXE文件并执行其中的代码。现在我们已经使用appstub完成了dxe应用程序的加载,那么是否有可能借用这个程序呢?

当然可以,因为都需要分析dxe文件格式,并将其中的内容复制到正确的位置。但是它们有两点不同:

1、它们一个在内核态执行程序,一个在用户态执行程序。

2、应用程序可以反复运行,驱动则通常只初始化一次,如果要再次初始化通常需要先卸载。

1.1    驱动的驱动

为了解决上面的两个问题,我们开发一个驱动的驱动:

/*

 * arch/blackfin/mach-common/vdsp.c - Visual dsp driver

 *

 * Copyright 2009 lights joy.

 *

 * Licensed under the GPL-2 or later.

 *

 * [email protected]

 */

 

#include <linux/string.h>

#include <linux/kernel.h>

#include <linux/device.h>

#include <linux/miscdevice.h>

#include <linux/types.h>

#include <linux/sched.h>

#include <linux/fs.h>

#include <linux/kthread.h>

#include <linux/delay.h>

 

// max driver address to be monitored

#define MAX_DRIVER_ADDRESS  5

 

// fix symtab addr

#define FIX_SYMTAB_ADDR     0x1000

 

#define DRIVER_NAME "vdsp"

 

/*

 * Set up the driver adress to be monitored

 */

static unsigned int driver_addr[MAX_DRIVER_ADDRESS] = {0};

static int __init driver_addr_setup(char *str)

{

     char *s = str;

     int idx = 0;

 

     while(*s)

     {

         driver_addr[idx] = simple_strtoul(s, &s, 16);

         if(driver_addr[idx] >= 0x10000 && driver_addr[idx] < _text)

         {

              idx++;

              if(s[0] != ',' || idx >= MAX_DRIVER_ADDRESS) break;

              s++;

         }

         else

              break;

     }

 

     return 1;

}

__setup("driver_addr=", driver_addr_setup);

 

 

typedef void (*pfcallback)();

 

static int monitor_thread(void* arg)

{

    // our monitor thread

    int i;

    unsigned int* p;

    printk("driver monitor thread begin./n");

    while(1)

    {

        for(i = 0; i < MAX_DRIVER_ADDRESS; i++)

        {

            p = (unsigned int*)driver_addr[i];

            if(p < 0x10000 || p >= _stext) continue;

            if(p[0] != 0)

            {

                printk("driver reload detected: %08x/n", driver_addr[i]);

                p[0] = 0;

                if(p[1] == NULL)

                {

                    printk("no driver init function specified./n");

                }

                else

                {

                    printk("prepare to call driver init function at %08x./n", p[1]);

                    ((pfcallback)p[1])();

                    printk("driver init function execute over./n");

                }

            }

            if(p[2] != 0)

            {

                printk("driver exit detected: %08x/n", driver_addr[i]);

                p[2] = 0;

                if(p[3] == NULL)

                {

                    printk("no driver exit function specified./n");

                }

                else

                {

                    printk("prepare to call driver exit function at %08x./n", p[3]);

                    ((pfcallback)p[3])();

                    printk("driver exit function execute over./n");

                }

            }

        }

 

         msleep(100);

    }

    return 0;

}

 

#define CMD_LOAD        0

#define CMD_UNLOAD      1

 

/**

 *   vdsp_ioctl - handle commands from user space

 *

 * Every command needs a valid address as an argument. 

 */

static int vdsp_ioctl(struct inode *inode, struct file *filp,

                          unsigned cmd, unsigned long arg)

{

    unsigned int* p;

    printk("/dev/vdsp: cmd=%d, arg=%08x/n", cmd, arg);

     switch (cmd)

     {

     case CMD_LOAD:

        if(arg < 0x10000 || arg >= _stext) break;

        printk("prepare to call driver init function at %08x./n", arg);

        ((pfcallback)arg)();

        printk("driver init function execute over./n");

         return 0;

     case CMD_UNLOAD:

        if(arg < 0x10000 || arg >= _stext) break;

        printk("prepare to call driver exit function at %08x./n", arg);

        ((pfcallback)arg)();

        printk("driver exit function execute over./n");

         return 0;

     }

 

     return -EINVAL;

}

 

 

static struct file_operations vdsp_fops = {

     .ioctl    = vdsp_ioctl,

};

 

static struct miscdevice vdsp_device = {

     .minor    = MISC_DYNAMIC_MINOR,

     .name     = DRIVER_NAME,

     .fops     = &vdsp_fops,

};

 

extern char __start___ksymtab[], __stop___ksymtab[];

 

static void vdsp_driver_init()

{

    int i, size, ret;

    printk(KERN_INFO "vdsp driver inited. /nfollow driver address monitored:/n");

    for(i = 0; i < MAX_DRIVER_ADDRESS; i++)

    {

        printk("%08x ", driver_addr[i]);

    }

    printk("/n");

 

    // 清空我们需要的区域

    memset((void*)FIX_SYMTAB_ADDR, 0, (unsigned long)_stext - FIX_SYMTAB_ADDR);

 

    // 复制一份符号表

    size = __stop___ksymtab - __start___ksymtab;

    memcpy((void*)FIX_SYMTAB_ADDR, __start___ksymtab, size);

    printk("total %d bytes of symtab copy to %08x/n", size, FIX_SYMTAB_ADDR);   

 

    // 启动监视线程

    kthread_run(monitor_thread, NULL, "vdsp");

   

    // 创建设备/dev/vdsp

     if (misc_register(&vdsp_device))

         printk("unable to register vdsp device/n");

        

}

 

module_init(vdsp_driver_init)

 

这个驱动和内核一起用gcc编译,它将创建一个叫/dev/vdsp的设备,用户程序可以通过它执行驱动的初始化代码和卸载代码。在上面的代码中,LOADUNLOAD两个功能完全一样,只不过考虑到后面的扩展将之分开。

此外,在此驱动初始化时会创建一个叫vdsp的线程,此线程检测指定位置的数据,当数据变化时自动执行指定位置的代码。以此实现对vdsp开发驱动的支持。

1.2    appstub

现在我们还需要修改appstub,以将内核或者用户程序加载到正确的位置,如果是内核驱动还必须调用/dev/vdsp提供的功能以进行驱动的初始化或者卸载。

#include <getopt.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <fcntl.h>

#include <signal.h>

#include <paths.h>

#include <sched.h>

#include <elf.h>

#include <errno.h>

#include <sys/stat.h>

#include <sys/mman.h>

 

typedef int (*pfcall)(int argc, char *argv[]);

typedef enum _load_type

{

    LT_LOAD,

    LT_UNLOAD

}load_type;

 

#define printf(fmt, ...) do {} while(0)

 

#ifndef EM_BLACKFIN

# define EM_BLACKFIN 106

#endif

 

#define IS_ELF(buff) /

     (buff[EI_MAG0] == ELFMAG0 && /

      buff[EI_MAG1] == ELFMAG1 && /

      buff[EI_MAG2] == ELFMAG2 && /

      buff[EI_MAG3] == ELFMAG3)

 

static void* find_entry(char* buf, load_type ltype)

{

    // 读取函数入口

    Elf32_Ehdr* ehdr = (Elf32_Ehdr*)buf;

    Elf32_Shdr* shdr_str, *shdr;

    char* str_head;

    int i;

    pfcall entry = NULL;

   

    shdr_str = buf + ehdr->e_shoff + ehdr->e_shstrndx * ehdr->e_shentsize;

    str_head = buf + shdr_str->sh_offset;

   

    shdr = buf + ehdr->e_shoff;

    for(i=0; i < ehdr->e_shnum; i++, shdr++)

    {

        printf("section found: %s/n", str_head + shdr->sh_name);

        if(strcmp(str_head + shdr->sh_name, ".vdsp_comm") == 0)

        {

            printf("entry found./n");

            if(ltype == LT_LOAD)

                memcpy(&entry, buf+shdr->sh_offset+4, 4);

            else

                memcpy(&entry, buf+shdr->sh_offset+12, 4);

            return entry;

        }

    }

   

    return NULL;

}

 

static void* load_dxe_file(const char* fname, load_type ltype)

{

    // 读取dxe文件中的所有section,并返回入口地址

     int i, fd;

     struct stat stat;

     void *entry = NULL;

     char* buf;

    Elf32_Ehdr* ehdr;

    Elf32_Phdr* phdr;

 

    // 打开映射文件

     fd = open(fname, O_RDONLY);

     if (fd < 0) {

         fprintf(stderr, "Unable to load %s: %s/n", fname, strerror(errno));

         return NULL;

     }

 

     if (fstat(fd, &stat) < 0) {

         fprintf(stderr, "Unable to stat %s: %s/n", fname, strerror(errno));

         return NULL;

     }

 

     if (stat.st_size < EI_NIDENT) {

         fprintf(stderr, "File is too small to be an ELF/n");

         return NULL;

     }

 

     buf = mmap(0, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);

     if (buf == MAP_FAILED) {

         fprintf(stderr, "Unable to mmap %s: %s/n", fname, strerror(errno));

         return NULL;

     }

 

    // 判断内容是否合法

     /* make sure we have a valid ELF */

     ehdr = buf;

     if (!IS_ELF(ehdr->e_ident) || ehdr->e_machine != EM_BLACKFIN)

     {

         fprintf(stderr, "file is not a Blackfin ELF file/n");

         return NULL;

     }

 

     /* make sure we have no unhandled program headers */

     if(ltype == LT_LOAD)

     {

         phdr = (Elf32_Phdr *)(buf + ehdr->e_phoff);

         for (i = 0; i < ehdr->e_phnum; ++i)

         {

             printf("program head %d: /ntype=%d offset=%d vaddr=%08x filesz=%d memsz=%d flags=%x/n",

                 i, phdr->p_type, phdr->p_offset, phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz, phdr->p_flags);

             if(phdr->p_filesz > 0)

                 memcpy((void*)phdr->p_vaddr, buf + phdr->p_offset, phdr->p_filesz);

             else

                 memset((void*)phdr->p_vaddr, 0, phdr->p_memsz);

             printf("load OK!/n");

             ++phdr;

         }

     }

   

     entry = find_entry(buf, ltype);

 

     munmap(buf, stat.st_size);

     close(fd);

    

     return entry;

}

 

/*

 * The main program.

 */

int main(int argc, char *argv[])

{

    unsigned int addr = 0;

    unsigned int *p;

    pfcall entry = NULL;

    int fd;

   

    if(argc < 2)

    {

        printf("not enough param.");

        return 1;

    }

    if(argv[1][0] == '-' && argv[1][1] == 'L')

    {

        // 加载dxe应用程序

        entry = load_dxe_file(argv[1] + 2, LT_LOAD);

        if(entry)

        {

            printf("dxe entry found at 0x%08x/n", entry);

            argv[1] = argv[0];

            return entry(argc-1, argv+1);

        }

        else

            fprintf(stderr, "no dxe entry found/n");

    }

    else if(argv[1][0] == '-' && argv[1][1] == 'K')

    {

        // 加载dxe内核驱动

        entry = load_dxe_file(argv[1] + 2, LT_LOAD);

        if(entry)

        {

            printf("dxe entry found at 0x%08x/n", entry);

            fd = open("/dev/vdsp", O_RDONLY);

            if(fd <= 0)

                fprintf(stderr, "open /dev/vdsp failed./n");

            else

            {

                printf("/dev/vdsp open success./n");

                ioctl(fd, 0, entry);

                close(fd);

            }   

        }

        else

            fprintf(stderr, "no dxe entry found/n");

    }

    else if(argv[1][0] == '-' && argv[1][1] == 'U')

    {

        // 缷载dxe内核驱动

        entry = load_dxe_file(argv[1] + 2, LT_UNLOAD);

        if(entry)

        {

            printf("dxe unload entry found at 0x%08x/n", entry);

            fd = open("/dev/vdsp", O_RDONLY);

            if(fd <= 0)

                fprintf(stderr, "open /dev/vdsp failed./n");

            else

            {

                printf("/dev/vdsp open success./n");

                ioctl(fd, 1, entry);

                close(fd);

            }   

        }

        else

            fprintf(stderr, "no dxe entry found/n");

    }

    else if(argv[1][0] == '-' && argv[1][1] == 'E')

    {

        // 直接跳转到指定位置

        addr = strtoul(argv[1]+2, NULL, 16);

        p = (unsigned int*)addr;

        if(p[0])

        {

             printf("detect new app at %08x/n", p);

             p[0] = 0;

             if(p[1])

             {

                 printf("prepare call %08x/n", p[1]);

                 argv[1] = argv[0];

                 return ((pfcall)p[1])(argc-1, argv+1);

             }

         else

             printf("no function entry found./n");

        }

        else

             printf("old app exist./n");

    }

    else

    {

        // 显示帮助信息

        fprintf(stdout, "appstub usage:/n");

        fprintf(stdout, "appstub [-L | -E | -K | -U] parameter/n");

        fprintf(stdout, "/t-Ldxe load an dxe file as user app./n");

        fprintf(stdout, "/t-Eaddr check memory address as user app./n/t/taddr must be hex and no prefix./n");

        fprintf(stdout, "/t-Kdxe load an dxe file as kernel driver./n");

        fprintf(stdout, "/t-Udxe unload kernel driver./n");

    }

    return 0;

}

 

就这样,我们为其加上LEKU四个选项,用以完成驱动和程序的加载和调试。

 

 

近日,我家6岁的小姑娘参加了第六届POP全国少儿英语风采大赛,拉票进行中(2011-6-15前)。

请帮忙点击新东方网站的链接:

http://popdasai.xdf.cn/toupiao.php?do=space&uid=4237

投她一票,谢谢!

1       参考资料

vdspuclinux共舞(13):应用程序加载(2009-11-10)

vdspuclinux共舞(12):应用程序开发2009-11-9

vdspuclinux共舞(11):方案改进(2009-11-6)

vdspuclinux共舞(10):加载SMP内核(2009-11-4)

vdspuclinux共舞(9):查找内核函数(2009-11-3)

vdspuclinux共舞(8):vdsp驱动框架(2009-11-3)

vdspuclinux共舞(7):在内核为驱动预留空间(2009-11-2)

vdspuclinux共舞(6):用vdsp开发驱动的设想(2009-11-2)

vdspuclinux共舞(5):加入dwarf调试信息(2009-11-2)

vdspuclinux共舞(4):加载uclinux(2009-11-2)

vdspuclinux共舞(3):boot kernel(2009-10-31)

vdspuclinux共舞(2):vdsp的影响(2009-10-31)

VDSPuclinux共舞(1):开篇(2009-10-30)

 

 

你可能感兴趣的:(让vdsp与uclinux共舞(14):驱动发布)