Linux LED字符设备驱动



// 申请IO资源
int gpio_request(unsigned gpio, const char *label);
// 释放IO资源
void gpio_free(unsigned gpio);
// 将IO引脚配置为输入
int gpio_direction_input(unsigned gpio);
// 将IO引脚配置为输出并设置引脚电平
int gpio_direction_output(unsigned gpio, int value);
// 读取引脚电平
int gpio_get_value(unsigned gpio);
// 设置引脚电平
void gpio_set_value(unsigned gpio, int value);


// 应用层的open/close/read/write/ioctl/llseek会调用以下对应的函数
struct file_operations {
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
loff_t (*llseek) (struct file *, loff_t, int);
};

//此命令用于应用和驱动之间的ioctl通信

// 参数1--表示一个魔幻数--用一个字符表示
 //参数2--区分不同命令的整数
 //参数3--传递的数据的类型

// 自动生成一个普通的命令号
_IO(type,nr)
// 自动生成一个带可读参数的命令号
_IOR(type,nr,size)
// 自动生成一个带可写参数的命令号
_IOW(type,nr,size)
// 自动生成一个带可读写参数的命令号
_IOWR(type,nr,size)
// 例:
#define LED_ON_IOW('L', 0x90, int)
#define LED_OFF_IOW('L', 0x91, int)
#define LED_ON_ALL_IO('L', 0x92)
#define LED_OFF_ALL_IO('L', 0x93)


// 1,向系统申请设备号
//参数1:需要申请的主设备号,次设备号默认为0---如果参数1大于0表示静态指定,等于由系统分配
//参数2:用于描述设备信息--自定义--会显示在/proc/devices
//参数3:文件操作对象,为用户提供文件IO操作集合
//返回值:错误返回负数,成功返回0
static inline int register_chrdev(unsigned int major, const char *name,
  const struct file_operations *fops);
// 释放设备号
static inline void unregister_chrdev(unsigned int major, const char *name);


// 在/sys/class目录下创建一个目录,目录名是name指定的
// 参数1:THIS_MODULE
// 参数2:设备名
// 返回值:class指针
struct class *class_create(struct module *owner, const char *name);


// 删除class指针指向的目录
// 参数:class指针
void class_destroy(struct class *cls);


// 在class指针指向的目录下再创建一个目录
// 参数1:class指针
// 参数2:父对象,一般写NULL
// 参数3:设备号
// 参数4:私有数据,一般写NULL
// 参数5:/dev下的设备名,可变参数
// 返回值:device指针
struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);


// 删除device_create创建的目录
// 参数:class指针

void device_destroy(struct class *cls, dev_t devt);


led_drv.c

#include <linux/module.h>
#include <linux/init.h>

#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/slab.h>

#include <asm/ioctl.h>

//根据参数由内核生成一个唯一的命令号供ioctl使用
#define LED_ON            _IOW('L', 0x90, int)
#define LED_OFF            _IOW('L', 0x91, int)
#define LED_ON_ALL        _IO('L', 0x92)
#define LED_OFF_ALL        _IO('L', 0x93)


struct samsung
{
    int major;
    struct class *cls;
    struct device *dev;
};

static struct samsung *led_dev;

static int led_open(struct inode *inode, struct file *filp)
{
    int ret;
    
    printk(KERN_INFO "^_^ %s\n", __FUNCTION__);

    //申请IO资源,并起名为led0
    ret = gpio_request(EXYNOS4_GPL2(0), "led0");
    if (ret < 0)
    {
        printk("gpio_request fail!\n");
        return -EBUSY;
    }

    //将IO口配置为输出并设置为低电平
    gpio_direction_output(EXYNOS4_GPL2(0), 0);

    ret = gpio_request(EXYNOS4_GPK1(1), "led1");
    if (ret < 0)
    {
        printk("gpio_request fail!\n");
        return -EBUSY;
    }
    gpio_direction_output(EXYNOS4_GPK1(1), 0);

    return 0;
}
static int led_close(struct inode *inode, struct file *filp)
{
    printk(KERN_INFO "^_^ %s\n", __FUNCTION__);

    gpio_direction_output(EXYNOS4_GPL2(0), 0);
    //释放IO口资源
    gpio_free(EXYNOS4_GPL2(0));

    gpio_direction_output(EXYNOS4_GPK1(1), 0);
    gpio_free(EXYNOS4_GPK1(1));

    return 0;
}
static ssize_t led_read(struct file *filp, char __user *buf, size_t len, loff_t *pos)
{
    printk(KERN_INFO "^_^ %s\n", __FUNCTION__);

    return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t len, loff_t *pos)
{
    printk(KERN_INFO "^_^ %s\n", __FUNCTION__);

    return 0;
}
static long led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    printk(KERN_INFO "^_^ %s\n", __FUNCTION__);

    switch (cmd)
    {
        case LED_ON:
            if (arg == 0)
                gpio_direction_output(EXYNOS4_GPL2(0), 1);
            else
                gpio_direction_output(EXYNOS4_GPK1(1), 1);
            break;
            
        case LED_OFF:
            if (arg == 0)
                gpio_direction_output(EXYNOS4_GPL2(0), 0);
            else
                gpio_direction_output(EXYNOS4_GPK1(1), 0);
            break;
            
        case LED_ON_ALL:
            gpio_direction_output(EXYNOS4_GPL2(0), 1);
            gpio_direction_output(EXYNOS4_GPK1(1), 1);
            break;
            
        case LED_OFF_ALL:
            gpio_direction_output(EXYNOS4_GPL2(0), 0);
            gpio_direction_output(EXYNOS4_GPK1(1), 0);
            break;
            
        default:
            printk(KERN_ERR "cmd is error!\n");
            break;
    }

    return 0;
}

struct file_operations led_fops = 
{
    .open = led_open,
    .release = led_close,
    .read = led_read,
    .write = led_write,
    .unlocked_ioctl = led_ioctl,
};


static int __init led_init(void)
{
    int ret;

    printk(KERN_INFO "^_^ %s\n", __FUNCTION__);

    //向内核申请内存
    led_dev = kmalloc(sizeof(struct samsung), GFP_KERNEL);
    if (led_dev == NULL)
    {
        printk("kmalloc err!\n");
        return -ENOMEM;
    }

    //申请设备号
    ret = register_chrdev(0, "led", &led_fops);
    if (ret < 0)
    {
        printk(KERN_ERR "register_chrdev error!\n");
        goto major_err;;
    }
    led_dev->major = ret;
    printk(KERN_INFO "major = %d\n", led_dev->major);

    //在/sys/class目录下创建一个名为"led"的目录
    led_dev->cls = class_create(THIS_MODULE, "led");
    if (led_dev->cls == NULL)
    {
        printk(KERN_ERR "class_create error!\n");
        ret = -EEXIST;
        goto class_err;
    }

    //在class指针指向的目录下再创建一个名为"led0"的目录
    led_dev->dev= device_create(led_dev->cls, NULL, MKDEV(led_dev->major, 0), NULL, "led0");
    if (led_dev->dev == NULL)
    {
        printk(KERN_ERR "device_create error!\n");
        ret = -EEXIST;
        goto device_err;
    }

    return 0;

device_err:
    class_destroy(led_dev->cls);
class_err:
    unregister_chrdev(led_dev->major, "led");
major_err:
    kfree(led_dev);

    return ret;
}

static void __exit led_exit(void)
{
    printk(KERN_INFO "^_^ %s\n", __FUNCTION__);

    //删除device_create创建的目录
    device_destroy(led_dev->cls, MKDEV(led_dev->major, 0));
    //删除class指针指向的目录
    class_destroy(led_dev->cls);
    //释放设备号
    unregister_chrdev(led_dev->major, "led");
    //释放内存
    kfree(led_dev);
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Aaron Lee");

 

led_app.c

#include <stdio.h>

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



#define LED_ON            _IOW('L', 0x90, int)
#define LED_OFF            _IOW('L', 0x91, int)
#define LED_ON_ALL        _IO('L', 0x92)
#define LED_OFF_ALL        _IO('L', 0x93)

int main(void)
{
    int fd;

    fd = open("/dev/led0", O_RDWR);

    while(1)
    {
        ioctl(fd,LED_ON,0);
        sleep(1);
        
        ioctl(fd,LED_OFF,0);
        sleep(1);

        ioctl(fd,LED_ON,1);
        sleep(1);

        ioctl(fd,LED_OFF,1);
        sleep(1);

        
        ioctl(fd,LED_ON_ALL,0);
        sleep(1);

        ioctl(fd,LED_OFF_ALL,0);
        sleep(1);
    }

    return 0;
}

 

Makefile文件请参照:http://www.cnblogs.com/lialong1st/p/7756677.html

 


posted @ 2017-10-23 15:49  LeeAaron  阅读(644)  评论(0编辑  收藏  举报