第一个简单的驱动程序——LED开关驱动程序

5 篇文章 0 订阅
平台:FriendARM —— mini2440
内核:2.6.32.2
交叉编译工具:arm-linux-gcc-4.4.3

CPU芯片:S3C2440 A


驱动程序代码如下:

#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>

/* 2.6.3x.x内核较2.6.2x.x内核驱动的架构有所改变,
 * 所以这里所包含的头文件写法和代码的写法有所改变 
 */
/* 2.6.3x.x 内核需要包含的头完文件 */
#include <linux/device.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>

static struct class *firstdrv_class;
static struct device	*firstdrv_class_dev;

volatile unsigned long* gpbcon = NULL;
volatile unsigned long* gpbdat = NULL;

static int first_drv_open(struct inode *inode, struct file *file)
{
    //printk("first_drv_open\n");
    /*
     * LED1、LED2、LED3、LED4分别对应引脚GPB5、GPB6、GPB7、GPB8
     */

    // 清零全部引脚GPB5、GPB6、GPB7、GPB8
    *gpbcon &= ~((0x3<<(5*2)) | (0x3<<(6*2)) | (0x3<<(7*2)) | (0x3<<(8*2)));

    // 设置GPB5、GPB6、GPB7、GPB8为输出引脚(低电平)
    *gpbcon |= ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(7*2)) | (0x1<<(8*2)));
    
    return 0;
}

static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    int val;
    //printk("first_drv_write\n");
    copy_from_user(&val, buf, count); // 从用户空间拷贝值到内核空间,与只相反的函数是澹?copy_to_user(); 

    if (val == 1) {
       // LED ON,全部清零
       *gpbdat &= ~(1<<5 | 1<<6 | 1<<7 | 1<<8);
    } else {
       // LED OFF
       *gpbdat |= (1<<5 | 1<<6 | 1<<7 | 1<<8);
    }

    return 0;
}

static struct file_operations first_drv_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   first_drv_open,     
	.write	=	first_drv_write,	   
};

int major;
static int first_drv_init(void)
{
    // 参数为0表示然系统自动分配设备号  
    major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册驱动程序, 告诉内核
    firstdrv_class = class_create(THIS_MODULE, "firstdrv");
    // 自动创建设备节点  
    /*  2.6.3x.x 内核不能用class_device_create,要用device_create*/
	firstdrv_class_dev = device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */

    /* 建立物理地址和虚拟地址的映射 */
    gpbcon = (volatile unsigned long*)ioremap(0x56000010,16);
    gpbdat = gpbcon + 1;

    return 0;
}

static void first_drv_exit(void)
{
    unregister_chrdev(major, "first_drv"); // 卸载驱动程序
    
    /* 新版的内核不能用class_device_unregister 要用device_unregister*/
    device_unregister(firstdrv_class_dev);
	class_destroy(firstdrv_class);

    /* 取消(卸载)映射 */
    iounmap(gpbcon);
}

module_init(first_drv_init);    /* 想上层VFS告知此设备的初始化函数 */
module_exit(first_drv_exit);    /* 想上层VFS告知此设备的卸载函数 */
MODULE_LICENSE("GPL");  /* 遵循模块的的GPL协议,可以解决一些警告提示和一些代码的识别 */

驱动测试程序如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/* 
 * ./first_drv_test on
 * ./first_drv_test off
 */
int main(int argc, char** argv)
{
	int fd;
	int val = 1;
	fd = open("/dev/xyz", O_RDWR);
    
	if (fd < 0)
	{
		printf("can not open!\n");
	} else {
        if (argc != 2) {
            printf("Usage :\n");
		    printf("%s <on|off>\n", argv[0]);
		    return 0;
        } else {
           if(strcmp(argv[1], "on") == 0) {
                val = 1;
           } else if(strcmp(argv[1], "off") == 0) {
                val = 0;
           } else {
                printf("Usage :\n");
		        printf("%s <on|off>\n", argv[0]);
		        return 0;
           }
        }
    }
    
    write(fd, &val, 4);
    return 0;
}

测试过程如下:
1. 先将驱动程序first_drv.ko和驱动测试程序拷贝到板子中,或者拷贝到挂载的nfs网络文件系统
2. 使用insmod first_drv.ko命令加载驱动
3. 使用lsmod命令查看当前已经加载的驱动,若查询结果中有first_drv出现,说明该驱动程序已经加载成功。
我的板子的查询结果是:

[root@FriendlyARM /]# lsmod
first_drv 1267 0 - Live 0xbf006000
也可以用cat /proc/device命令显示当前板子中有哪些设备以及对应的设备号,在显示这结果中的字符设备中出现first_drv设备也表示该驱动程序已经加载成功。
我的板子执行这条命令结果是:


[root@FriendlyARM /]# cat /proc/devices 
Character devices:
  1 mem
  4 /dev/vc/0
  4 tty
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 10 misc
 13 input
 14 sound
 21 sg
 29 fb
 81 video4linux
 89 i2c
 90 mtd
116 alsa
128 ptm
136 pts
180 usb
188 ttyUSB
189 usb_device
204 s3c2410_serial
253 first_drv
254 rtc

Block devices:
259 blkext
  7 loop
  8 sd
 31 mtdblock
 65 sd
 66 sd
 67 sd
 68 sd
 69 sd
 70 sd
 71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
4. 使用./fisrt_drv_test 或者 ./fisrt_drv_test 后跟除on或者off外的任意字符会出现如下结果:
[root@FriendlyARM /]# ./first_drv_test.o 
Usage :
./first_drv_test.o <on|off>
[root@FriendlyARM /]# ./first_drv_test.o abc
Usage :
./first_drv_test.o <on|off>
[root@FriendlyARM /]# 

5.使用./fisrt_drv_test on命令,板子上的灯会全部亮
6.使用./fisrt_drv_test off命令,板子上的灯会全部灭


总结:

写驱动的过程和要点为:

1.搭建驱动程序的基本框架

2.注意在不同内核版本之上写驱动程序的代码差别,否则报错都很难找出来

3.操作硬件时要使用ioremap函数来设置物理地址和虚拟地址的隐身,最后在消除这个映射

3.注意编译驱动程序时一定要用跟板子一样的内核,和板子契合的交叉编译链接器


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值