3242|4

74

帖子

0

TA的资源

一粒金砂(初级)

楼主
 

最简单的驱动 [复制链接]

写了一个最简单的LINUX,键盘驱动发现不能读硬件,不知道怎么在LINUX系统下获取读硬件的权限
char button_statusFetch(void)
{
        int i = 0;
        int k = 0;       
        (*((volatile unsigned int *)(0x41500000)))|=0x40000000;
        while ( (*((volatile unsigned int *)(0x41500020)))  & 0x80000000 );
        i = (*((volatile unsigned int *)(0x41500020))) & 0xff;
        if (i==0xff)
        return i;
        else
        k = (i>>4)&0xf;
        i &= 0xf;
        i += k * 3;
        return i;
}
这段代码在没有操作系统的ARM开发板上可以读硬件,但是有操作系统就不行


现在写的驱动是有一个FILE_OPERATION的结构,自己去看LINUX内核里面的驱动程序竟然没有FILE_OPERATION这个结构

麻烦各位大哥指点一下,小弟是新手,会写一个空壳子的驱动,维持缓存区,维持进程等待队列,就是不知道怎么获取读硬件的权限

提点一下,或者推荐一下图书,网站都可以,谢谢

最新回复

学习学习,大家新年快乐  详情 回复 发表于 2009-1-1 00:59
点赞 关注

回复
举报

83

帖子

0

TA的资源

一粒金砂(初级)

沙发
 
这是别人的一个例子,我在我的板子上试过是可以用的,其实只是用它的一个简单驱动的架构而已

C/C++ code#include
#include
#include
#include
#include
#include
#include
#include

#include
#include


#define LEDnKEY_MAJOR     251
#define KEYPAD_NAME     "X-Hyper250 Keypad"
#define KEYPAD_VERSION  "Version 0.1"

#define EXT_KEY_CS  EXT_PORT2
#define EXT_LED_CS  EXT_PORT3
#define LED_SHOW    10


void led_off_on()
{
    int i;
    EXT_LED_CS = 0xff;
   
    for(i =0 ; i<8;++i)
    {
        EXT_LED_CS = ~((1 << i) & 0xff);        
        udelay(30000);
    }
    EXT_LED_CS = 0xff;
}

int lednkey_open(struct inode *inode, struct file *filp)
{
    MOD_INC_USE_COUNT;
    return (0);          /* success */         
}

int lednkey_release(struct inode *inode, struct file *filp)
{
    led_off_on();
    MOD_DEC_USE_COUNT;
    return (0);
}

ssize_t lednkey_read(struct file *filp, char *Putbuf, size_t length, loff_t *f_pos)
{
    unsigned short BottonStatus;
       unsigned char Bottontmp = 0;
    int i;

    BottonStatus = ( EXT_KEY_CS & 0xff );

    for(i = 0 ; i < 8; ++i)
    {
        if( ((BottonStatus >> i) & 1) == 0 )
            Bottontmp = (i+1);
    }

    copy_to_user( Putbuf, &Bottontmp, length);
   
    return length;
}

ssize_t lednkey_write(struct file *filp, const char *Getbuf, size_t length, loff_t *f_pos)
{
    int num;
    unsigned char UsrWantLed;
      
      copy_from_user( &UsrWantLed, Getbuf, length);   
   
    num =  ( (UsrWantLed) & 0xff );

    EXT_LED_CS = ~(1 << (num-1));

    return (0);   
}

int lednkey_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,unsigned long arg)
{
    switch(cmd)
    {
        case LED_SHOW:
            {
                if(arg)
                    led_off_on();
                break;
            }
    }
    return 0;
}

struct file_operations lednkey_fops = {
    open:       lednkey_open,
    read:       lednkey_read,
    write:      lednkey_write,
    ioctl:      lednkey_ioctl,
    release:    lednkey_release,
};


static int __init xhyper250_keypad_init(void)
{
    int result;
    result = register_chrdev(LEDnKEY_MAJOR, "lednkey", &lednkey_fops);

    printk("%s %s initialized.\n",KEYPAD_NAME, KEYPAD_VERSION);
    led_off_on();

    return 0;
}

static void __exit xhyper250_keypad_exit(void)
{  
    unregister_chrdev( LEDnKEY_MAJOR, "lednkey" );           
    led_off_on();
}

module_init(xhyper250_keypad_init);
module_exit(xhyper250_keypad_exit);



将该驱动编译到内核中
  • 时自动调用 xhyper250_keypad_init 函数注册设备,或者 insmod xxx.o 时调用
    在应用层面调用 open("/dev/xxx", xxx) 是自动调用注册的 lednkey_fops 指向的 open 函数,也就是 lednkey_open 函数了

    一般驱动编写习惯:
    xxx_init 注册设备,申请内存(如果需要)、DMA(如果需要),做必要的初始化工作
    xxx_open 函数一般打开中断以及注册中断函数
    xxx_release 函数被应用层面的 close(xx) 调用
    xxx_ioctl 函数是改变设备设置调用的函数(应用层面 ioctl 调用)
  •  
     

    回复

    60

    帖子

    0

    TA的资源

    一粒金砂(初级)

    板凳
     
    另外在Linux下面访问硬件的地址最好是先map一下,然后再访问
    Linux内核里提供的/dev/mem驱动,为我们读写内存物理地址,提供了一个渠道。下面讲述2种利用mem设备文件进行物理地址读写的方法,一种是设备驱动的方法,另一种是系统调用的方法。
    首先我们看下mem这个设备文件,/dev/mem是linux下的一个字符设备,源文件是~/drivers/char/mem.c,这个设备文件是专门用来读写物理地址用的。里面的内容是所有物理内存的地址以及内容信息。通常只有root用户对其有读写权限。

        1.设备驱动的方法

        下面是mem.c文件里定义的file_operations结构,提供了llseek,read,write,mmap以及open等方法。
        static struct file_operations mem_fops =
        {
            .llseek  = memory_lseek,
            .read  = read_mem,
            .write  = write_mem,
            .mmap  = mmap_mem,
            .open  = open_mem,
        };
        因此我们可以通过一般驱动的使用方法,将内存完全当作一个设备来对对待。应用程序如下:
        #include
        #include
        int main(void)
        {
            int fd;
            char *rdbuf;
            char *wrbuf = "butterfly";
            int i;
            fd = open("/dev/mem",O_RDWR);
            if(fd < 0)
            {
                printf("open /dev/mem failed.");
            }
            read(fd,rdbuf,10);

            for(i = 0;i < 10;i++)
            {
                printf("old mem[%d]:%c\n",i,*(rdbuf + i));
            }
            lseek(fd,5,0);
            write(fd,wrbuf,10);
            lseek(fd,0,0);//move f_ops to the front
            read(fd,rdbuf,10);
            for(i = 0;i < 10;i++)
            {
                printf("new mem[%d]:%c\n",i,*(rdbuf + i));
            }

            return 0;
        }

        执行结果如下:将内存最开始10个字节的内容进行替换。
        [root@VOIP-IPCAM app]# ./memtest
        old mem[0]:b
        old mem[1]:u
        old mem[2]:t
        old mem[3]:t
        old mem[4]:e
        old mem[5]:r
        old mem[6]:f
        old mem[7]:l
        old mem[8]:y
        old mem[9]:!
        new mem[0]:b
        new mem[1]:u
        new mem[2]:t
        new mem[3]:t
        new mem[4]:e
        new mem[5]:b
        new mem[6]:u
        new mem[7]:t
        new mem[8]:t
        new mem[9]:e

        2.系统调用的方法

        细心的你可能会发现,既然你前面说了这个文件里存放的就是内存的地址及内容信息,那我可不可以直接查看到呢,答案是:可以的。linux内核的开发者为我们提供了一个命令hexedit,通过它就可以将/dev/mem的内容显示出来(如果你使用cat /dev/mem将会看到乱码),执行hexedit /dev/mem的结果如下:
        00000000   62 75 74 74  65 62 75 74  74 65 72 66  6C 79 21 20  butterfly!
        00000010   20 20 20 20  20 20 20 20  20 20 20 20  20 20 20 20
        00000020   20 20 20 20  20 20 20 20  20 20 20 20  20 20 20 20
        00000030   6F EF 00 F0  6F EF 00 F0  57 EF 00 F0  6F EF 00 F0  o...o...W...o...
        00000040   02 11 00 C0  4D F8 00 F0  41 F8 00 F0  34 85 00 F0  ....M...A...4...
        00000050   39 E7 00 F0  59 F8 00 F0  2E E8 00 F0  D2 EF 00 F0  9...Y...........
        00000060   A4 E7 00 F0  F2 E6 00 F0  6E FE 00 F0  53 FF 00 F0  ........n...S...
        00000070   53 FF 00 F0  A4 F0 00 F0  C7 EF 00 F0  1C 42 00 C0  S............B..
        从上图可见,最左边显示的是地址,接下来24列显示的是各内存字节单元内容的ASCII码信息,最右边显示的是对应的字符信息。让人欣慰的是,这个文件可以直接修改,按下tab键进入修改模式,修改过程中修改内容会以粗体显示,按下F2保存后粗体消失。上面的butterfly就是通过这种方式修改的。
        既然内存的地址以及内容信息全部被保存在mem这个设备文件里,那么我们可以想到通过另外一种方式来实现对物理地址的读写了。那就是将mem设备文件和mmap系统调用结合起来使用,将文件里的物理内存地址映射到进程的地址空间,从而实现对内存物理地址的读写。下面谈一下mmap系统调用。
        mmap的函数原型为:void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offset),该函数定义在/usr/include/sys/mman.h中,使用时要包含:#include,mmap()用来将某个文件中的内容映射到进程的地址空间,对该空间的存取即是对该文件内容的读写。参数说明如下:
        start:指向欲映射到的地址空间的起始地址,通常设为null或者0.表示让系统融自动选定地址,映射成功后该地址会返回。
        length:表示映射的文件内容的大小,以字节为单位。
        prot:表示映射区域的保护方式,有如下四种组合:
          --PROT_EXEC 映射区域可执行 ,
          --PROT_READ 映射区域可读 ,
          --PROT_WRITE 映射区域可写,
          --PROT_NONE 映射区域不能被访问
        flags:映射区域的一些特性,主要有:
          --MAP_FIXED 如果映射不成功则出错返回,
          --MAP_SHARED 对映射区域的写入数据会写回到原来的文件
          --MAP_PRIVATE 对映射区域的写入数据不会写回原来的文件
          --MAP_ANONYMOUS
          --MAP_DENYWRITE 只允许对映射区域的写入操作,其他对文件直接写入的操作将被拒绝
          --MAP_LOCKED 锁定映射区域
        在调用mmap()时,必须要指定MAP_SHARED或MAP_PRIVATE。
        fd:open()返回的文件描述符。
        offset:为被映射文件的偏移量,表示从文件的哪个地方开始映射,一般设置为0,表示从文件的最开始位置开始映射。offset必须是分页大小(4096字节)的整数倍。
        应用程序如下:
        #include
        #include
        #include //mmap head file
        int main (void)
        {
           int i;
           int fd;
           char *start;
           char *buf = "butterfly!";

           //open /dev/mem with read and write mode
           fd = open ("/dev/mem", O_RDWR);
           if (fd < 0)
           {
               printf("cannot open /dev/mem.");
               return -1;
           }

           //map physical memory 0-10 bytes
           start = (char *)mmap(0, 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
           if(start < 0)
           {
              printf("mmap failed.");
              return -1;
           }
           //Read old value
           for (i = 0; i < 10; i++)
           {
               printf("old mem[%d]:%c\n", i, *(start + i));
           }
           //write memory
           memcpy(start, buf, 10);
           //Read new value
           for (i = 0;i < 10;i++)
           {
              printf("new mem[%d]:%c\n", i,*(start + i));
           }
           munmap(start, 10); //destroy map memory
           close(fd);  //close file
           return 0;
        }
        程序执行结果如下:
        [root@VOIP-IPCAM app]# ./rwphy
        old mem[0]:b
        old mem[1]:u
        old mem[2]:t
        old mem[3]:t
        old mem[4]:e
        old mem[5]:b
        old mem[6]:u
        old mem[7]:t
        old mem[8]:t
        old mem[9]:e
        new mem[0]:b
        new mem[1]:u
        new mem[2]:t
        new mem[3]:t
        new mem[4]:e
        new mem[5]:r
        new mem[6]:f
        new mem[7]:l
        new mem[8]:y
        new mem[9]:!
        借用别人的一句话作为结束语:
        “/dev/mem是个很好玩的东西,你竟然可以直接访问物理内存。这在LINUX下简直是太神奇了,这种感觉象一个小偷打算偷一个银行,可是这个银行戒备森严,正当这个小偷苦无对策时,突然发现在一个不起眼的地方有个后门,这个后门可以直接到银行的金库.”

     
     
     

    回复

    86

    帖子

    0

    TA的资源

    一粒金砂(初级)

    4
     
    以上的文字是我这几天正在学习的时候看别人的博客里写的,现引用给LZ参考
     
     
     

    回复

    68

    帖子

    0

    TA的资源

    一粒金砂(初级)

    5
     
    学习学习,大家新年快乐
     
     
     

    回复
    您需要登录后才可以回帖 登录 | 注册

    随便看看
    查找数据手册?

    EEWorld Datasheet 技术支持

    相关文章 更多>>
    关闭
    站长推荐上一条 1/10 下一条

     
    EEWorld订阅号

     
    EEWorld服务号

     
    汽车开发圈

    About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

    站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

    北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

    电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
    快速回复 返回顶部 返回列表