一、开发环境
软件开发环境:linux-3.0
硬件开发环境:fl2440开发板
日期:2013.12.28
二、驱动程序
1.注册主次设备号
2.注册驱动程序
3.led硬件初始化,配置相应寄存器
4.释放资源
/*********************************************************************************
* Copyright: (C) 2013 Yang zheng<yangzheng@gmail.com>
* All rights reserved.
*
* Filename: s3c_led.c
* Description: This file
*
* Version: 1.0.0(12/21/2013~)
* Author: yangzheng <yangzheng@mail.com>
* ChangeLog: 1, Release initial version on "12/21/2013 11:29:12 AM"
*
********************************************************************************/
#include <linux/module.h> /* Every Linux kernel module must include this head */
#include <linux/init.h> /* Every Linux kernel module must include this head */
#include <linux/kernel.h> /* printk() */
#include <linux/fs.h> /* struct fops */
#include <linux/errno.h> /* error codes */
#include <linux/cdev.h> /* cdev_alloc() */
#include <asm/io.h> /* ioremap() */
#include <linux/ioport.h> /* request_mem_region() */
#include <asm/ioctl.h> /* Linux kernel space head file for macro _IO() to generate ioctl command */
#ifndef __KERNEL__
#include <sys/ioctl.h> /* User space head file for macro _IO() to generate ioctl command */
#endif
#define DEV_NAME "fl2440_led"
#define DEV_MAJOR 0
#define LED_COUNT 4
#define DEV_GPB_BASE 0x56000010
#define GPBCON_OFFSET 0
#define GPBDAT_OFFSET 4
#define GPBUP_OFFSET 8
#define DEV_GPB_LEN 0x10 /* 0x56000010~0x56000020 */
#define GPIO_OUTPUT 0x01
#define GPIO_INPUT 0x00
#define PLATDRV_MAGIC 0x60
#define LED_ON _IO (PLATDRV_MAGIC, 0x18)
#define LED_OFF _IO (PLATDRV_MAGIC, 0x19)
//#define LED_ON 1
//#define LED_OFF 0
#define DISABLE 0
#define ENABLE 1
#define s3c_gpio_write(val, reg) __raw_writel((val), (reg)+s3c_gpb_membase)
#define s3c_gpio_read(reg) __raw_readl((reg)+s3c_gpb_membase)
int led[LED_COUNT] = {5, 6, 8, 10}; /* Four leds use GPB5,GPB6,GPB8,GPB10 */
static void __iomem *s3c_gpb_membase;
int led_count = ARRAY_SIZE(led);
int dev_major = DEV_MAJOR;
int dev_minor = 0;
int debug = DISABLE;
static struct cdev *led_cdev;
static int led_hw_init(void)
{
int i;
volatile unsigned long gpb_con;
volatile unsigned long gpb_dat;
volatile unsigned long gpb_up;
if (!request_mem_region(DEV_GPB_BASE, DEV_GPB_LEN, "DEV_NAME" ))
{
return -EBUSY;
}
if ( !(s3c_gpb_membase=ioremap( DEV_GPB_BASE, DEV_GPB_LEN)) )
{
release_mem_region(DEV_GPB_BASE, DEV_GPB_LEN);
return ENOMEM;
}
for (i=0; i<led_count; i++)
{
/* Set GPBCON register, set correspond GPIO port as input or output mode */
gpb_con = s3c_gpio_read(GPBCON_OFFSET);
gpb_con &= ~(0x3 << (2*led[i])); /* Clear the currespond LED GPIO configure register */
gpb_con |= GPIO_OUTPUT << (2*led[i]); /* Set the currespond LED GPIO as output mode */
s3c_gpio_write(gpb_con, GPBCON_OFFSET);
/* Set GPBUP register, set correspond GPIO port pull up resister as enable or disable */
gpb_up = s3c_gpio_read(GPBUP_OFFSET);
gpb_up |= (0x1 << led[i]); /* Disable pull up resister */
s3c_gpio_write(gpb_up, GPBUP_OFFSET);
/* Set GPBDAT register, set correspond GPIO port power level as high level or low level */
gpb_dat = s3c_gpio_read(GPBDAT_OFFSET);
gpb_dat |= (0x1 << led[i]); /* This port set to high level, then turn LED off */
s3c_gpio_write(gpb_dat, GPBDAT_OFFSET);
}
return 0;
}
static void turn_led(int which, unsigned int cmd)
{
volatile unsigned long gpb_dat;
gpb_dat = s3c_gpio_read(GPBDAT_OFFSET);
if ( LED_ON == cmd )
{
gpb_dat &= ~(0x1 << led[which]); /* Turn on led */
}
else if ( LED_OFF == cmd )
{
gpb_dat |= (0x1 << led[which]); /* Turn off led */
}
s3c_gpio_write(gpb_dat, GPBDAT_OFFSET);
}
static void led_hw_term(void)
{
int i;
volatile unsigned long gpb_dat;
for (i= 0; i<led_count; i++)
{
gpb_dat = s3c_gpio_read(GPBDAT_OFFSET);
gpb_dat |= (0x1 << led[i]); /* Turn off led */
s3c_gpio_write(gpb_dat, GPBDAT_OFFSET);
}
release_mem_region(DEV_GPB_BASE, DEV_GPB_LEN);
iounmap(s3c_gpb_membase);
}
static int led_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
//file->private_data = (void *)(minor);
printk(KERN_DEBUG "dev/led%d opened.\n", minor);
return 0;
}
static int led_release(struct inode *inode, struct file *file)
{
printk(KERN_DEBUG "dev/led%d closed.\n", iminor(inode));
return 0;
}
static void print_help(void)
{
printk("Follow is the ioctl() commands for %s driver:\n", DEV_NAME);
printk("Turn LED on command : %u\n", LED_ON);
printk("Turn LED off command : %u\n", LED_OFF);
return;
}
static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
//int which = (int)file->private_data;
switch (cmd)
{
case LED_ON:
turn_led(arg, LED_ON);
break;
case LED_OFF:
turn_led(arg, LED_OFF);
break;
default:
printk(KERN_ERR "%s driver don't support ioctl command = %d.\n", DEV_NAME, cmd);
print_help();
break;
}
return 0;
}
static struct file_operations led_fops=
{
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.unlocked_ioctl = led_ioctl,
};
static int __init s3c_led_init(void)
{
int result;
dev_t devno;
if ( 0 != led_hw_init() )
{
printk(KERN_ERR "s3c led hardware initialize failure\n");
return -ENODEV;
}
printk(KERN_ERR "s3c led hardware initialize seccessful\n");
if ( 0 != dev_major )
{
devno = MKDEV(dev_major, 0);
result = register_chrdev_region(devno, led_count, DEV_NAME);
}
else
{
result = alloc_chrdev_region(&devno, dev_minor, led_count, DEV_NAME);
dev_major = MAJOR(devno);
}
if ( result < 0 )
{
printk(KERN_ERR "s3c %s driver can't use major %d\n", DEV_NAME, result);
return -ENODEV;
}
printk(KERN_DEBUG "s3c %s driver can use major %d\n", DEV_NAME, result);
if ( NULL == (led_cdev= cdev_alloc()) )
{
printk(KERN_ERR "s3c %s driver can't alloc for the cdev.\n", DEV_NAME);
unregister_chrdev_region(devno, led_count);
return -ENODEV;
}
led_cdev->owner = THIS_MODULE;
cdev_init(led_cdev, &led_fops);
result = cdev_add(led_cdev, devno, led_count);
if ( 0 != result )
{
printk(KERN_ERR "s3c %s driver can't register cdev: result = %d\n.", DEV_NAME, result);
goto ERROR;
}
return 0;
ERROR:
printk(KERN_ERR "s3c %s driver installed failure.\n", DEV_NAME);
cdev_del(led_cdev);
unregister_chrdev_region(devno, dev_minor);
return result;
}
static void __exit s3c_led_exit(void)
{
dev_t devno = MKDEV(dev_major, dev_minor);
led_hw_term();
cdev_del(led_cdev);
unregister_chrdev_region(devno, led_count);
printk(KERN_ERR "s3c %s driver removed.\n", DEV_NAME);
return ;
}
module_init(s3c_led_init);
module_exit(s3c_led_exit);
module_param(debug,int, S_IRUGO);
module_param(dev_major, int, S_IRUGO);
MODULE_LICENSE("GPL");
三、测试程序
/*********************************************************************************
* Copyright: (C) 2013 fulinux<fulinux@sina.com>
* All rights reserved.
*
* Filename: l_test.c
* Description: This file
*
* Version: 1.0.0(12/24/2013~)
* Author: fulinux <fulinux@sina.com>
* ChangeLog: 1, Release initial version on "12/24/2013 05:21:55 PM"
*
********************************************************************************/
#include <stdio.h>
#include <string.h>
#include <linux/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <asm/ioctl.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#define PLATDRV_MAGIC 0x60
#define LED_ON _IO (PLATDRV_MAGIC, 0x18)
#define LED_OFF _IO (PLATDRV_MAGIC, 0x19)
//#define LED_ON 1
//#define LED_OFF 0
/********************************************************************************
* Description:
* Input Args:
* Output Args:
* Return Value:
********************************************************************************/
int main (int argc, char **argv)
{
int fd;
int led_no;
fd = open("/dev/fl2440_led", O_RDWR);
if ( fd < 0 )
{
printf("led opened failure.\n");
}
led_no = strtoul(argv[2], 0, 0);
printf("Command LED_ON: %d\n", LED_ON);
printf("Command LED_OFF: %d\n", LED_OFF);
while ( argc == 3 )
{
ioctl(fd, LED_ON, led_no);
sleep(1);
ioctl(fd, LED_OFF, led_no);
close(fd);
return 0;
}
printf("Useage:%s <LED_ON/LED_OFF> <led_no>\n", argv[0]);
close(fd);
return 0;
} /* ----- End of main() ----- */