linux2.6.32 led 设备驱动及应用程序

来源:互联网 发布:淘宝情趣内衣好评50字 编辑:程序博客网 时间:2024/05/17 01:35

沉迷于游戏有一段时间了,该学习了,上次写了helloword驱动,感觉要有点长进,今天就开始搞led驱动。

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/io.h>


#include <mach/hardware.h>
#include <mach/gpio-fns.h>
#include <asm/irq.h>


#include <mach/regs-gpio.h>


#define DEVICE_NAME     "leds"
#define LED_MAJOR       231
#define LED_ON          0
#define LED_OFF         1


static struct class *leds_class;
static unsigned long led_pin_table[] = {
    S3C2410_GPB(5),
    S3C2410_GPB(6),
    S3C2410_GPB(7),
    S3C2410_GPB(8),
};


static int s3c24xx_leds_open(struct inode *inode, struct file *file)
{
    int i;


    for(i = 0; i < 4; i++){
        s3c2410_gpio_cfgpin(led_pin_table[i], S3C2410_GPIO_OUTPUT);
    }


    return 0;
}


static int s3c24xx_leds_ioctl(
    struct inode *inode,
    struct file *file,
    unsigned int cmd,
    unsigned long arg)
{
    if(arg > 4)
        return -EINVAL;


    switch(cmd){
        case LED_ON: 
            s3c2410_gpio_setpin(led_pin_table[arg], 0);
            return 0;
        case LED_OFF:
            s3c2410_gpio_setpin(led_pin_table[arg], 1);
            return 0;
        default:
            return -EINVAL;
    }
}


static struct file_operations s3c24xx_leds_fops = {
    .owner = THIS_MODULE,
    .open = s3c24xx_leds_open,
    .ioctl = s3c24xx_leds_ioctl,
};


static int __init s3c24xx_leds_init(void)
{
    int ret;
    printk(KERN_INFO "Linux led V1.0\n");
    ret = register_chrdev(LED_MAJOR,DEVICE_NAME,&s3c24xx_leds_fops);
    if(ret < 0){
        printk(KERN_WARNING DEVICE_NAME"canot register major number!\n");
        return ret;
    }
    leds_class = class_create(THIS_MODULE,"leds");                      //创建类
    if(IS_ERR(leds_class))
    {
        unregister_chrdev(LED_MAJOR, DEVICE_NAME);
        return PTR_ERR(leds_class);
    }
    device_create(leds_class, NULL, MKDEV(LED_MAJOR,0), NULL, "leds");  //创建设备,在insmod后会自动创建leds设备,不用手动mknod创建节点了
    printk(KERN_INFO DEVICE_NAME" initialized!\n");
    return 0;
}


static void __exit s3c24xx_leds_exit(void)
{
    device_destroy(leds_class, MKDEV(LED_MAJOR, 0));
    class_destroy(leds_class);
    unregister_chrdev(LED_MAJOR, DEVICE_NAME);
    printk(KERN_INFO DEVICE_NAME" exited!\n");
}


module_init(s3c24xx_leds_init);
module_exit(s3c24xx_leds_exit);


MODULE_LICENSE("GPL");


/*
 * leds.c - The first kernel app programming
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>


void usage(char *exename)
{
    printf("Usage:\n");
    printf(" %s <led_no> <on/off>\n",exename);
    printf(" led_no = 1, 2, 3, 4.\n");
}
int main(int argc, char *argv[])
{
int led_no;
    int fd = -1;
if(argc != 3)
   goto err;
    
fd = open("/dev/leds", 0);                      //打开设备用open,注意open和fopen的区别
    if(fd < 0)
    {
        printf("Can't open /dev/leds\n");
        exit(1);                                                   //注意exit和return的区别
    }
    led_no = strtoul(argv[1], 0, 0)-1;
    if(led_no > 3 || led_no < 0)
        goto err;
    if(!strcmp(argv[2], "on")){
        ioctl(fd, 0, led_no);
    }else if(!strcmp(argv[2], "off")){
        ioctl(fd, 1, led_no);
    }else{
        goto err;
    }
    close(fd);
return 0;


    err:
        if(fd > 0)
            close(fd);
        usage(argv[0]);
        return -1;
}


#
# This file is write for the first drivers
# Copyright (c) 2013 sxbg


#obj-m := hello.o
#obj-m := led.o
#hello-objs := hello.o
obj-m += hello.o
obj-m += led.o
ARM_LINUX_KERNEL := /home/sxbg/Downloads/linux
CURRENT_PATH := $(shell pwd)


all:
$(MAKE) -C $(ARM_LINUX_KERNEL) M=$(CURRENT_PATH) modules


clean:
$(MAKE) -C $(ARM_LINUX_KERNEL) M=$(CURRENT_PATH) clean


网上大部分的驱动程序需要手动创建设备节点,此驱动程序的亮点在于自动创建设备节点,程序尽量的规范,请读者特别是和我一样的菜鸟认真体会,其实最好的驱动的代码范例还是内核,笔者的驱动也参见了内核相关代码:

/drivers/isdn/capi/capi.c中有如下代码

static int __init capi_init(void)
{
char *p;
char *compileinfo;
int major_ret;


if ((p = strchr(revision, ':')) != NULL && p[1]) {
strlcpy(rev, p + 2, sizeof(rev));
if ((p = strchr(rev, '$')) != NULL && p > rev)
  *(p-1) = 0;
} else
strcpy(rev, "1.0");


major_ret = register_chrdev(capi_major, "capi20", &capi_fops);
if (major_ret < 0) {
printk(KERN_ERR "capi20: unable to get major %d\n", capi_major);
return major_ret;
}
capi_class = class_create(THIS_MODULE, "capi");
if (IS_ERR(capi_class)) {
unregister_chrdev(capi_major, "capi20");
return PTR_ERR(capi_class);
}


device_create(capi_class, NULL, MKDEV(capi_major, 0), NULL, "capi");


#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
if (capinc_tty_init() < 0) {
device_destroy(capi_class, MKDEV(capi_major, 0));
class_destroy(capi_class);
unregister_chrdev(capi_major, "capi20");
return -ENOMEM;
}
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */


proc_init();


#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
        compileinfo = " (middleware+capifs)";
#else
        compileinfo = " (no capifs)";
#endif
#else
        compileinfo = " (no middleware)";
#endif
printk(KERN_NOTICE "capi20: Rev %s: started up with major %d%s\n",
rev, capi_major, compileinfo);


return 0;
}


static void __exit capi_exit(void)
{
proc_exit();


device_destroy(capi_class, MKDEV(capi_major, 0));
class_destroy(capi_class);
unregister_chrdev(capi_major, "capi20");


#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
capinc_tty_exit();
#endif
printk(KERN_NOTICE "capi: Rev %s: unloaded\n", rev);
}

where there is a will,there is a way.

只要我们正确的面对,linux驱动没有想象中的那么可怕。

有疑问的细节请参考 嵌入式Linux之我行——LED驱动在2440上的实例开发 

原创粉丝点击