LINUX驱动编程之LED驱动

/* gpio_drv.c */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h>	/* printk() */
#include <linux/slab.h>		/* kmalloc() */
#include <linux/fs.h>		/* everything... */
#include <linux/errno.h>	/* error codes */
#include <linux/types.h>	/* size_t */
#include <linux/mm.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/arch-s3c2410/regs-gpio.h>
//#include "gpio_drv.h"

/**********************************************/

#ifndef FS2410_GPIO_SET_H
#define FS2410_GPIO_SET_H
#include <linux/ioctl.h>
#define GPIO_DEVICE_NAME "gpio"
#define GPIO_DEVICE_FILENAME "/dev/gpio"
#define LED_NUM 4
#define GPIO_IOCTL_MAGIC 'G'
#define LED_D09_SWT _IOW(GPIO_IOCTL_MAGIC, 0, unsigned int)
#define LED_D10_SWT _IOW(GPIO_IOCTL_MAGIC, 1, unsigned int)
#define LED_D11_SWT _IOW(GPIO_IOCTL_MAGIC, 2, unsigned int)
#define LED_D12_SWT _IOW(GPIO_IOCTL_MAGIC, 3, unsigned int)
#define BEEP_SWT _IOW(GPIO_IOCTL_MAGIC, 4, unsigned int)
#define LED_SWT_ON 0
#define LED_SWT_OFF 1
#define BEEP_SWT_ON 1
#define BEEP_SWT_OFF 0
#endif /* FS2410_GPIO_SET_H */

/*********************************************/
static int major = 0; /* 采用字符设备号的动态分配*/
module_param(major, int, 0); /* 以参数的方式可以指定设备的主设备号*/

void s3c2410_gpio_cfgpin_led(unsigned int pin, unsigned int function)
{
    /* 对某个管脚进行配置(输入/输出/其他功能)*/
    unsigned long base = S3C2410_GPIO_BASE(pin); /* 获得端口的组基地址
    unsigned long shift = 1;
    unsigned long mask = 0x03; /* 通常用配置寄存器的两位表示一个端口*/
    unsigned long con;
    unsigned long flags;
    if (pin < S3C2410_GPIO_BANKB)
    {
        shift = 0;
        mask = 0x01; /* 在GPA端口中用配置寄存器的一位表示一个端口*/
    }
    mask <<= (S3C2410_GPIO_OFFSET(pin) << shift);
    local_irq_save(flags); /* 保存现场,保证下面一段是原子操作*/
    con = __raw_readl(base + 0x00);
    con &= ~mask;
    con |= function;
    __raw_writel(con, base + 0x00); /* 向配置寄存器写入新配置数据*/
    local_irq_restore(flags); /* 恢复现场*/
}

void s3c2410_gpio_pullup_led(unsigned int pin, unsigned int to)
{
    /* 配置上拉功能*/
    unsigned long base = S3C2410_GPIO_BASE(pin); /* 获得端口的组基地址*/
    unsigned long offs = S3C2410_GPIO_OFFSET(pin);/* 获得端口的组内偏移地址*/
    unsigned long flags;
    unsigned long up;
    if (pin < S3C2410_GPIO_BANKB)
    {
        return;
    }
    local_irq_save(flags);
    up = __raw_readl(base + 0x08);
    up &= ~(1 << offs);
    up |= to << offs;
    __raw_writel(up, base + 0x08); /* 向上拉功能寄存器写入新配置数据*/
    local_irq_restore(flags);
}

void s3c2410_gpio_setpin_led(unsigned int pin, unsigned int to)
{
    /* 向某个管脚进行输出*/
    unsigned long base = S3C2410_GPIO_BASE(pin);
    unsigned long offs = S3C2410_GPIO_OFFSET(pin);
    unsigned long flags;
    unsigned long dat;
    local_irq_save(flags);
    dat = __raw_readl(base + 0x04);
    dat &= ~(1 << offs);
    dat |= to << offs;
    __raw_writel(dat, base + 0x04); /* 向数据寄存器写入新数据*/
    local_irq_restore(flags);
}

int gpio_open (struct inode *inode, struct file *filp)
{
    /* open操作函数:进行寄存器配置*/
    s3c2410_gpio_pullup_led(S3C2410_GPB0, 1); /* BEEP*/
    s3c2410_gpio_pullup_led(S3C2410_GPF4, 1); /* LED D12 */
    s3c2410_gpio_pullup_led(S3C2410_GPF5, 1); /* LED D11 */
    s3c2410_gpio_pullup_led(S3C2410_GPF6, 1); /* LED D10 */
    s3c2410_gpio_pullup_led(S3C2410_GPF7, 1); /* LED D9 */
    s3c2410_gpio_cfgpin_led(S3C2410_GPB0, S3C2410_GPB0_OUTP);
    s3c2410_gpio_cfgpin_led(S3C2410_GPF4, S3C2410_GPF4_OUTP);
    s3c2410_gpio_cfgpin_led(S3C2410_GPF4, S3C2410_GPF5_OUTP);
    s3c2410_gpio_cfgpin_led(S3C2410_GPF4, S3C2410_GPF6_OUTP);
    s3c2410_gpio_cfgpin_led(S3C2410_GPF4, S3C2410_GPF7_OUTP);
    return 0;
}

ssize_t gpio_read(struct file *file, char __user *buff,size_t count, loff_t *offp)
{
    /* read操作函数:没有实际功能*/
    return 0;
}

ssize_t gpio_write(struct file *file, const char __user *buff,size_t count, loff_t *offp)
{
    /* write操作函数:没有实际功能*/
    return 0;
}

int switch_gpio(unsigned int pin, unsigned int swt)
{
    /* 向5个端口中的一个输出ON/OFF值*/
    if (!((pin <= S3C2410_GPF7) && (pin >= S3C2410_GPF4))
    && (pin != S3C2410_GPB0))
    {
        printk("Unsupported pin");
        return 1;
    }
    s3c2410_gpio_setpin_led(pin, swt);
    return 0;
}

static int gpio_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg)
{
    /* ioctl函数接口:主要接口的实现。对5个GPIO设备进行控制(发亮或发声) */
    unsigned int swt = (unsigned int)arg;
    switch (cmd)
    {
        case LED_D09_SWT:
        {
            switch_gpio(S3C2410_GPF7, swt);
            break;
        }
        case LED_D10_SWT:
        {
            switch_gpio(S3C2410_GPF6, swt);
            break;
        }
        case LED_D11_SWT:
        {
            switch_gpio(S3C2410_GPF5, swt);
            break;
        }
        case LED_D12_SWT:
        {
            switch_gpio(S3C2410_GPF4, swt);
            break;
        }
        case BEEP_SWT:
        {
            switch_gpio(S3C2410_GPB0, swt);
            break;
        }
        default:
        {
            printk("Unsupported command\n");
            break;
        }
    }
    return 0;
}

static int gpio_release(struct inode *node, struct file *file)
{
    /* release操作函数,熄灭所有灯和关闭蜂鸣器*/
    switch_gpio(S3C2410_GPB0, BEEP_SWT_OFF);
    switch_gpio(S3C2410_GPF4, LED_SWT_OFF);
    switch_gpio(S3C2410_GPF5, LED_SWT_OFF);
    switch_gpio(S3C2410_GPF6, LED_SWT_OFF);
    switch_gpio(S3C2410_GPF7, LED_SWT_OFF);
    return 0;
}

static void gpio_setup_cdev(struct cdev *dev, int minor,struct file_operations *fops)
{
    /* 字符设备的创建和注册*/
    int err, devno = MKDEV(major, minor);
    cdev_init(dev, fops);
    dev->owner = THIS_MODULE;
    dev->ops = fops;
    err = cdev_add (dev, devno, 1);
    if (err)
    {
        printk (KERN_NOTICE "Error %d adding gpio %d", err, minor);
    }
}

static struct file_operations gpio_fops =
{
    /* gpio设备的file_operations结构定义*/
    .owner = THIS_MODULE,
    .open = gpio_open, /* 进行初始化配置*/
    .release = gpio_release, /* 关闭设备*/
    .read = gpio_read,
    .write = gpio_write,
    .ioctl = gpio_ioctl, /* 实现主要控制功能*/
};

static struct cdev gpio_devs;

static int gpio_init(void)
{
    int result;
    dev_t dev = MKDEV(major, 0);
    if (major)
    {
        /* 设备号的动态分配*/
        result = register_chrdev_region(dev, 1, GPIO_DEVICE_NAME);
    }
    else
    {
        /* 设备号的动态分配*/
        result = alloc_chrdev_region(&dev, 0, 1, GPIO_DEVICE_NAME);
        major = MAJOR(dev);
    }
    if (result < 0)
    {
        printk(KERN_WARNING "Gpio: unable to get major %d\n", major);
        return result;
    }
    gpio_setup_cdev(&gpio_devs, 0, &gpio_fops);
    printk("The major of the gpio device is %d\n", major);
    return 0;
}

static void gpio_cleanup(void)
{
    cdev_del(&gpio_devs); /* 字符设备的注销*/
    unregister_chrdev_region(MKDEV(major, 0), 1); /* 设备号的注销*/
    printk("Gpio device uninstalled\n");
}
module_init(gpio_init);
module_exit(gpio_cleanup);

测试程序:
/* gpio_test.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "gpio_drv.h"

int led_timer(int dev_fd, int led_no, unsigned int time)
{ /*指定LED发亮一段时间之后熄灭它*/
	led_no %= 4;
	ioctl(dev_fd, LED_D09_SWT + led_no, LED_SWT_ON); /* 发亮*/
	sleep(time);
	ioctl(dev_fd, LED_D09_SWT + led_no, LED_SWT_OFF); /* 熄灭*/
}

int beep_timer(int dev_fd, unsigned int time)
{/* 开蜂鸣器一段时间之后关闭*/
	ioctl(dev_fd, BEEP_SWT, BEEP_SWT_ON); /* 发声*/
	sleep(time);
	ioctl(dev_fd, BEEP_SWT, BEEP_SWT_OFF); /* 关闭*/
}
int main()
{
	int i = 0;
	int dev_fd;
	
	/* 打开gpio设备*/
	dev_fd = open(GPIO_DEVICE_FILENAME, O_RDWR | O_NONBLOCK);
	if ( dev_fd == -1 )
	{
		printf("Cann't open gpio device file\n");
		exit(1);
	}
	
	while(1)
	{
		i = (i + 1) % 4;
		led_timer(dev_fd, i, 1);
		beep_timer(dev_fd, 1);
	}
	close(dev_fd);
	return 0;
}




你可能感兴趣的:(LINUX驱动编程之LED驱动)