LED驱动_字符设备

  实现LED驱动测试案例及要求:

led_test on    /* 对应四个LED全亮           */
led_test off   /* 对应四个LED全灭           */
led_test run   /* 运行跑马灯实验            */
led_test shine /* 4个LED灯全灭、全亮交替闪烁 */
led_test 1 on  /* 对应LED1点亮              */
led_test 1 off /* 对应LED1熄灭              */
...
led_test 4 on  /* 对应LED4点亮              */
led_test 4 off /* 对应LED4熄灭              */

  设备驱动如下:

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <mach/regs-clock.h>
#include <plat/regs-timer.h>
#include <linux/cdev.h>
#include <mach/regs-gpio.h> /* 包含GPIO口的封装 */

static int led_major = 0; /* 主设备号 */
static struct cdev LedDevs;

/* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
#define LED_MAGIC 'k'
#define IOCTL_LED_ON     _IOW (LED_MAGIC, 1, int)
#define IOCTL_LED_OFF    _IOW (LED_MAGIC, 2, int)
#define IOCTL_LED_RUN    _IOW (LED_MAGIC, 3, int)
#define IOCTL_LED_SHINE  _IOW (LED_MAGIC, 4, int)
#define IOCTL_LED_ALLON  _IOW (LED_MAGIC, 5, int)
#define IOCTL_LED_ALLOFF _IOW (LED_MAGIC, 6, int)

static unsigned long led_table [] = { /* 用来指定LED所用的GPIO引脚 */
    S3C2410_GPB ( 5 ),
    S3C2410_GPB ( 6 ),
    S3C2410_GPB ( 7 ),
    S3C2410_GPB ( 8 ),
};

/* 应用程序对设备文件“/dev/led”执行open时,就会调用s3c24xx_leds_open函数 */
static int s3c2440_leds_open ( struct inode *inode, struct file *file ) {
    int i;

    for ( i = 0; i < 4; i++ ) {
        /* 设置GPIO引脚的功能:本驱动中LED所涉及的GPIO引脚设为输出功能 */
        s3c2410_gpio_cfgpin ( led_table[i], S3C2410_GPIO_OUTPUT );
    }

    return 0;
}

void leds_all_on() { /* LEDS all light on */
    int i;

    for ( i = 0; i < 4; i++ ) {
        s3c2410_gpio_setpin ( led_table[i], 0 );
    }
}

void leds_all_off() { /* LEDs all light off */
    int i;

    for ( i = 0; i < 4; i++ ) {
        s3c2410_gpio_setpin ( led_table[i], 1 );
    }
}

/* 应用程序对设备文件“/dev/leds”执行ioctl时,就会调用s3c24xx_leds_ioctl函数 */
static int s3c2440_leds_ioctl ( struct inode *inode, struct file *file,
                                unsigned int cmd, unsigned long arg ) {
    unsigned int data;

    /*
     * if ( copy_from_user ( &data, ( unsigned int __user * ) arg, sizeof ( int ) ) ) {
     *     return -EFAULT;
     * }
     *
     * data = ( unsigned int ) arg; // 方法一:数据传递
     */
    if ( __get_user ( data, ( unsigned int __user * ) arg ) ) { /* 方法二:指针参数传递 */
        return -EFAULT;
    }

    switch ( cmd ) {
        case IOCTL_LED_ON:
            s3c2410_gpio_setpin ( led_table[data], 0 ); /* 设置指定引脚的输出电平为0 */
            return 0;
        case IOCTL_LED_OFF:
            s3c2410_gpio_setpin ( led_table[data], 1 ); /* 设置指定引脚的输出电平为1 */
            return 0;
        case IOCTL_LED_RUN: { /* 跑马灯 */
            int i, j;
            leds_all_off();

            for ( i = 0; i < data; i++ ) /* 跑data次 */
                for ( j = 0; j < 4; j++ ) {
                    s3c2410_gpio_setpin ( led_table[j], 0 ); /* 亮 */
                    mdelay ( 400 ); /* mdelay一直等待,msleep休眠则执行别的进程 */
                    s3c2410_gpio_setpin ( led_table[j], 1 ); /* 灭 */
                    mdelay ( 400 );
                }

            return 0;
        }
        case IOCTL_LED_SHINE: { /* LED闪烁 */
            int i, j;
            leds_all_off();
            printk ( "IOCTL_LED_SHINE\n" );

            for ( i = 0; i < data; i++ ) {
                for ( j = 0; j < 4; j++ ) {
                    s3c2410_gpio_setpin ( led_table[j], 0 );
                }

                mdelay ( 400 ); /* 延时400ms */

                for ( j = 0; j < 4; j++ ) {
                    s3c2410_gpio_setpin ( led_table[j], 1 );
                }

                mdelay ( 400 );
            }

            return 0;
        }

        case IOCTL_LED_ALLON: /* 设置指定引脚的输出电平为0 */
            leds_all_on();
            return 0;

        case IOCTL_LED_ALLOFF: /* 设置指定引脚的输出电平为1 */
            leds_all_off();
            return 0;

        default:
            return -EINVAL;
    }
}

/* 这个结构是字符设备驱动程序的核心,当应用程序操作设备文件时所调用的
   open、read、write等函数,最终会调用这个结构中指定的对应函数 */
static struct file_operations s3c2440_leds_fops = {
    .owner = THIS_MODULE, /* 这是一个宏,指向编译模块时自动创建的__this_module变量 */
    .open  = s3c2440_leds_open,
    .ioctl = s3c2440_leds_ioctl,
};

/* Set up the cdev structure for a device */
static void led_setup_cdev ( struct cdev *dev, int minor, struct file_operations *fops ) {
    int err, devno = MKDEV ( led_major, minor );
    cdev_init ( dev, fops ); /* 设备的注册 */
    dev->owner = THIS_MODULE;
    dev->ops = fops;
    err = cdev_add ( dev, devno, 1 );

    if ( err ) { /* Fail gracefully if need be */
        printk ( KERN_NOTICE "Error %d adding Led%d", err, minor );
    }
}

/* 执行“insmod s3c24xx_leds.ko”命令时就会调用这个函数 */
static int __init s3c2440_leds_init ( void ) {
    int result;
    dev_t dev = MKDEV ( led_major, 0 );
    char dev_name[] = "led"; /* 加载模式后,执行“cat /proc/devices”命令看到的设备名称 */

    if ( led_major ) { /* Figure out our device number */
        result = register_chrdev_region ( dev, 1, dev_name );
    } else {
        result = alloc_chrdev_region ( &dev, 0, 1, dev_name );
        led_major = MAJOR ( dev );
    }

    if ( result < 0 ) {
        printk ( KERN_WARNING "leds: unable to get major %d\n", led_major );
        return result;
    }

    if ( led_major == 0 ) {
        led_major = result;
    }

    led_setup_cdev ( &LedDevs, 0, &s3c2440_leds_fops ); /* Now set up cdev */
    printk ( "Led device installed, with major %d\n", led_major );
    printk ( "The device name is: %s\n", dev_name );
    return 0;
}

/* 执行“rmmod s3c24xx_leds”命令时就会调用这个函数 */
static void __exit s3c2440_leds_exit ( void ) {
    cdev_del ( &LedDevs ); /* 卸载驱动程序 */
    unregister_chrdev_region ( MKDEV ( led_major, 0 ), 1 );
    printk ( "Led device uninstalled\n" );
}

/* 这两行指定驱动程序的初始化函数和卸载函数 */
module_init ( s3c2440_leds_init );
module_exit ( s3c2440_leds_exit );

/* 描述驱动程序的一些信息 */
MODULE_AUTHOR ( "http://embedclub.taobao.com" ); /* 驱动程序的作者 */
MODULE_DESCRIPTION ( "s3c2440 LED Driver" ); /* 一些描述信息 */
MODULE_LICENSE ( "Dual BSD/GPL" ); /* 遵循的协议 */

  测试程序如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define LED_MAGIC 'k'
#define IOCTL_LED_ON     _IOW (LED_MAGIC, 1, int)
#define IOCTL_LED_OFF    _IOW (LED_MAGIC, 2, int)
#define IOCTL_LED_RUN    _IOW (LED_MAGIC, 3, int)
#define IOCTL_LED_SHINE  _IOW (LED_MAGIC, 4, int)
#define IOCTL_LED_ALLON  _IOW (LED_MAGIC, 5, int)
#define IOCTL_LED_ALLOFF _IOW (LED_MAGIC, 6, int)

void usage ( char *exename ) {
    printf ( "Usage:\n" );
    printf ( "    %s <led_no> <on/off>\n", exename );
    printf ( "    led_no = 1, 2, 3 or 4\n" );
}

int main ( int argc, char **argv ) {
    unsigned int led_no;
    int fd = -1;
    unsigned int count = 10;

    if ( argc > 3 || argc == 1 ) {
        goto err;
    }

    fd = open ( "/dev/led", 0 ); /* 打开设备 */

    if ( fd < 0 ) {
        printf ( "Can't open /dev/leds\n" );
        return -1;
    }

    if ( argc == 2 ) {
        if ( !strcmp ( argv[1], "on" ) ) {
            ioctl ( fd, IOCTL_LED_ALLON, &count ); /* 点亮它 */
        } else if ( !strcmp ( argv[1], "off" ) ) {
            ioctl ( fd, IOCTL_LED_ALLOFF, &count ); /* 熄灭它 */
        } else if ( !strcmp ( argv[1], "run" ) ) {
            ioctl ( fd, IOCTL_LED_RUN, &count ); /* 运行跑马灯 */
        } else if ( !strcmp ( argv[1], "shine" ) ) {
            ioctl ( fd, IOCTL_LED_SHINE, &count ); /* 闪烁 */
        } else {
            goto err;
        }
    }

    if ( argc == 3 ) {
        led_no = strtoul ( argv[1], NULL, 0 ) - 1; /* 操作哪个LED */

        if ( led_no > 3 ) {
            goto err;
        }

        if ( !strcmp ( argv[2], "on" ) ) {
            ioctl ( fd, IOCTL_LED_ON, &led_no ); /* 点亮 */
        } else if ( !strcmp ( argv[2], "off" ) ) {
            ioctl ( fd, IOCTL_LED_OFF, &led_no ); /* 熄灭 */
        } else {
            goto err;
        }
    }

    close ( fd );
    return 0;

err:
    if ( fd > 0 ) {
        close ( fd );
    }

    usage ( argv[0] );
    return -1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值