牛棚

--拥有的远比想象的多...
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

和菜鸟一起学linux: 第五篇:第一个字符型驱动

Posted on 2009-06-23 12:03  Moi  阅读(1364)  评论(1)    收藏  举报
第五篇:第一个字符型驱动
日期:2009-3-10

驱动板子上的LED,共四个,查原理图,发现教程上的四个GPIO与我板子的GPIO对应不上,教程上是用的GPIO_B7,8,9,10; 而
我的板子是 GPIO_F4,5,6,7
所以我只替换了管脚的说明,其他一致 呵呵

代码如下:

#ifndef _KERNEL_
#define _KERNEL_
#endif
#ifndef MODULE
#define MODULE
#endif
                                                                                               
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <asm-arm/arch-s3c2410/hardware.h>
#define DEVICE_NAME "leds"
#define LED_MAJOR 232
                                                                                               
static unsigned long led_table[]={GPIO_F4,GPIO_F5,GPIO_F6,GPIO_F7};
static int leds_ioctl(struct inode *inode,struct file *file,unsigned int cmd,
        unsigned long arg){
switch(cmd){
        case 0:
        case 1:
        if(arg>4){
            return -EINVAL;
        }
        write_gpio_bit(led_table[arg],!cmd);
        default:
                return -EINVAL;
        }
}
                                                                                               
static struct file_operations leds_fops={
        owner:THIS_MODULE,
        ioctl:leds_ioctl,
                                                                                               
};
                                                                                               
static int __init leds_init(void){
int ret;
int i;
ret = register_chrdev(LED_MAJOR,DEVICE_NAME,&leds_fops);
if(ret<0)
{
        printk(DEVICE_NAME"can't register major number");
        return ret;
}
for(i=0;i<4;i++)
{
        set_gpio_ctrl(led_table[i]|GPIO_PULLUP_EN|GPIO_MODE_OUT);
        write_gpio_bit(led_table[i],1);
}
printk(DEVICE_NAME "initialized\n");
return 0;
}
                                                                                               
static void __exit leds_exit(void){
        unregister_chrdev(LED_MAJOR,DEVICE_NAME);
}
                                                                                               
module_init(leds_init);
module_exit(leds_exit);       



按下列方式编译, 我的乖乖,这么多警告,错误啊。
[root@localhost sin]# arm-linux-gcc -O2 -c -I/armsys2410/kernel/include/ led.c
In file included from led.c:13:
/armsys2410/kernel/include/asm-arm/arch-s3c2410/hardware.h:70: warning: no semic
olon at end of struct or union
/armsys2410/kernel/include/asm-arm/arch-s3c2410/hardware.h:70: parse error befor
e `offset'
/armsys2410/kernel/include/asm-arm/arch-s3c2410/hardware.h:70: warning: data def
inition has no type or storage class
led.c:19: warning: `struct file' declared inside parameter list
led.c:19: warning: its scope is only this definition or declaration, which is pr
obably not what you want.
led.c:19: warning: `struct inode' declared inside parameter list
led.c: In function `leds_ioctl':
led.c:24: `EINVAL' undeclared (first use in this function)
led.c:24: (Each undeclared identifier is reported only once
led.c:24: for each function it appears in.)
led.c:26: parse error before `)'
led.c:26: parse error before `u32'
led.c:26: `u32' undeclared (first use in this function)
led.c:26: parse error before `)'
led.c:26: parse error before `)'
led.c:26: parse error before `u32'
led.c:26: parse error before `)'
led.c: At top level:
led.c:32: variable `leds_fops' has initializer but incomplete type
led.c:33: unknown field `owner' specified in initializer
led.c:33: warning: excess elements in struct initializer
led.c:33: warning: (near initialization for `leds_fops')
led.c:34: unknown field `ioctl' specified in initializer
led.c:34: warning: excess elements in struct initializer
led.c:34: warning: (near initialization for `leds_fops')
led.c: In function `leds_init':
led.c:41: `LEDMAJOR' undeclared (first use in this function)
led.c:49: parse error before `)'
led.c:49: parse error before `u32'
led.c:49: `u32' undeclared (first use in this function)
led.c:49: parse error before `)'
led.c:49: parse error before `)'
led.c:49: parse error before `u32'
led.c:49: parse error before `)'
led.c:49: parse error before `)'
led.c:49: parse error before `u32'
led.c:49: parse error before `)'
led.c:49: parse error before `)'
led.c:49: parse error before `u32'
led.c:49: parse error before `)'
led.c:50: parse error before `)'
led.c:50: parse error before `u32'
led.c:50: parse error before `)'
led.c:50: parse error before `)'
led.c:50: parse error before `u32'
led.c:50: parse error before `)'

花了一下午时间,在网上搜集资料,但始终也没有解决
在坛上经大家的指点,用kernel/drivers/example-leds.c (发现这个和我的板子的GPIO定义都一样)
但一编译发现错误更多。。。(错误更多,就不列出来了,省的打击信心。。。)
继续找资料。。。
继续搜索资料,发现基于2.6内核的好象多些,胡乱看了一通。。。
有点心恢意冷了
病急乱投医了,开始怀疑内核没有编译会不会影响呢?
(之前把内核一释放,啥操作都没有做)
于是把交叉编译工具,内核都删除,按教程重新弄一边,问题依旧。。。
始终不得法门,郁闷啊,
后来就怀疑编译时,参数错了,换个关键字继续搜索
有人提到编译驱动要加 “-DKERNEL -DMODULE ”
于是
编译写成如下格式
arm-linux-gcc -DKERNEL -DMODULE -I/armsys2410/kernel/include -c led.c
仍旧有问题
没办法,看代码再
发现下面的两句 KERNEL前后的 下划线似乎有点短了,如下:
#ifndef _KERNEL_
#define _KERNEL_

改为:
(下面是两个下划线是两横 嘿嘿)
#ifndef __KERNEL__
#define __KERNEL__
再编译
arm-linux-gcc -DKERNEL -DMODULE -I/armsys2410/kernel/include -c led.c
哈哈 通过了,没有警告也没有错误,兴奋了一把
当前的目录下也多了个 led.o文件 嘿嘿
上传到开发板
修改其权限:
# chmod 777 led.o
挂载:
# insmod ./led.o
查看:
# cat /proc/devices
Character devices:
  1 mem
  2 pty/m%d
  3 pty/s%d
  4 vc/0
  5 ptmx
  7 vcs
10 misc
29 fb
90 mtd
128 ptm
136 pts/%d
162 raw
204 ttyS%d
205 cua%d
232 leds
创建设备文件
# mknod /dev/leds c 243 0

查看
# ls /dev
bon          fb           misc         ptmx         shm          ttyS2
console      full         mtd          pts          touchscreen  urandom
cua0         kmem         mtdblock     pty          tty          vc
cua1         leds         null         random       ttyS0        vcc
cua2         mem          port         root         ttyS1        zero
#

测试程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>

int main(int argc,char **argv)
{
    int on;
    int led_no;
    int fd;
    if(argc!=3||sscanf(argv[1],"%d",&led_no)!=1|| sscanf(argv[2],"%d",&no)!=1 || on<0 || on>1 ||led_no<0 ||
led_no>3)
    {
        fprintf(stderr,"Usage:led led_no 1|0\n");
        exit(1);    
    }
    fd = open("/dev/leds", 0);
    if (fd < 0) {
        perror("open device leds");
        exit(1);
    }
    ioctl(fd, on, led_no);
    close(fd);    
    return 0;
}


[root@localhost sin]# arm-linux-gcc -o ledtest led-test.c
[root@localhost sin]# ls
example-leds.c  hh     led.o   ledss.c  led-test.c  test.c  tt
hello.c         led.c  leds.c  ledtest  Makefile    test.o
[root@localhost sin]# cp ./ledtest /mnt/hgfs/share

上传到 开发板的 tmp目录
# chmod 777 ledtest
# ls
erase    led.o    ledtest
# ./ledtest 1 1
open device leds: No such device
# ./ledtest 1 1
open device leds: No such device
# ./ledtest 0 1
open device leds: No such device

居然说没有??真是奇怪了,哪里又出错了呢?

建立接点时出错了,驱动中用的主设备号是:232
而建立时用的是:243, 所以提示“open device leds: No such device”
不知道教程上,怎么出现这么个意外?
我也是网上搜索后,才知道这么个情况(所以啊,基础的东西还是要好好学习学习的)
后修改如下:

# mknod /dev/leds c 232 0
# ls /dev
bon          fb           misc         ptmx         shm          ttyS2
console      full         mtd          pts          touchscreen  urandom
cua0         kmem         mtdblock     pty          tty          vc
cua1         leds         null         random       ttyS0        vcc
cua2         mem          port         root         ttyS1        zero
# ./ledtest 0 1
# ./ledtest 1 1
# ./ledtest 2 1
# ./ledtest 3 1

OK, 板子上的LED有反应了 呵呵