这是一个关于按键点亮led灯的综合例子,没什么用处,但是综合度还是挺大的,主要是用来学习和巩固linux驱动的只是,适合学习linux驱动初级者,该程序以平台总线的方式注册led device 和 led driver, 当需要修改一个驱动的时候,之需要修改 led device 就可以,无需修改 led driver, 即 体现了平台设备总线分层分类的思想,该例子除了设计平台设备知识外,还设计中断 , 定时器, 轮询方式等和一些字符设备相关的知识,请阅读,如需修改的地方,请网友指出,互相学习。
LCplatform.h
#ifndef LCPLATFORM_H
#define LCPLATFORM_H
#define PLATFORM_NAME "LCPlatform"
#define PLATFORM_MAJOR 0
#define PLATFORM_MINOR 0
#define MASK 0
#define GPBCON 0x56000010
#define GPBDAT 0x56000014
#define GPGCON 0x56000060
#define GPGDAT 0x56000064
#define GPGUP 0x56000068
#define GPG0 (1<<0)
#define GPG3 (1<<3)
#define GPG5 (1<<5)
#define GPG6 (1<<6)
#define GPG7 (1<<7)
#define GPG11 (1<<11)
#define IRQNUM 0
#define K1 GPG0
#define K2 GPG3
#define K3 GPG5
#define K4 GPG6
#define K5 GPG7
#define K6 GPG11
#define LED1 5
#define LED2 6
#define LED3 7
#define LED4 8
enum
{
key_up = 0,
key_down ,
};
#endif
led_devie.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "LCplatform.h"
static struct resource LCPlatform_resource[] =
{
[0] = {
.start = GPGCON,
.end = GPGCON + 8 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = GPBCON,
.end = GPBCON + 8 - 1,
.flags = IORESOURCE_MEM,
},
[2] = {
.start = IRQNUM,
.end = IRQNUM,
.flags = IORESOURCE_IRQ,
},
};
static void LCRelease(struct inode * inode,struct file * file)
{
printk("%s: release platform device\n", __func__);
}
static struct platform_device LCPlatform_device =
{
.resource = LCPlatform_resource,
.num_resources = ARRAY_SIZE(LCPlatform_resource),
.id = -1,
.name = PLATFORM_NAME,
.dev = {
.release = LCRelease,
}
};
static int LCPlatform_init_module(void)
{
int ret;
ret = platform_device_register(&LCPlatform_device);
if(ret<0)
{
printk("%s: register platform device fail\n",__func__);
return ret;
}
printk("%s: register platform device success\n",__func__);
return 0;
}
static void LCPlatform_exit_module(void)
{
platform_device_unregister(&LCPlatform_device);
}
module_init(LCPlatform_init_module);
module_exit(LCPlatform_exit_module);
MODULE_LICENSE("GPL");
led_driver.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
#include <mach/regs-gpio.h>
#include <linux/poll.h>
#include "LCplatform.h"
#ifdef SHOW_LOG
#define print_log(args, ...) printk(args, ##__VA_ARGS__)
#endif
unsigned int *LEDDRESS;
static struct class *LCPlatform_class;
static unsigned long timer_arg = 1;
static struct timer_list LCPlatform_timer;
static volatile unsigned long * gpio_b_con;
static volatile unsigned long * gpio_b_dat;
static volatile unsigned long * gpio_g_con;
static volatile unsigned long * gpio_g_dat;
static unsigned char keyStatus = key_up;
static unsigned char isPressed = 0;
static unsigned char flag_irq = 0;
static DECLARE_MUTEX(button_lock);
static DECLARE_WAIT_QUEUE_HEAD(button_wait_queue);
void LCPlatform_do_timer(unsigned long data)
{
if( !(*gpio_g_dat & 0x01))
keyStatus = key_down;
else
keyStatus = key_up;
print_log("%s: keyStatus = %d", __FUNCTION__, keyStatus);
if(keyStatus)
{ /* exchange to turn on and off*/
if(!(*gpio_b_dat & ((1<<LED1) | (1<<LED2) | (1<<LED3) | (1<<LED4))))
*gpio_b_dat |= (1<<LED1) | (1<<LED2) | (1<<LED3) | (1<<LED4); // all light off
else
*gpio_b_dat &= ~((1<<LED1) | (1<<LED2) | (1<<LED3) | (1<<LED4)); //all light on
isPressed = 1;
wake_up_interruptible(&button_wait_queue);
}
else
*gpio_b_dat |= (1<<LED1) | (1<<LED2) | (1<<LED3) | (1<<LED4); // all light off
flag_irq = 0;
}
static irqreturn_t LCPlatform_Handle_irq(int irqNO, struct resource *LCplatform_resource)
{
if(!flag_irq)
{
LCPlatform_timer.expires = jiffies + HZ/10; //100ms
LCPlatform_timer.function = LCPlatform_do_timer;
add_timer(&LCPlatform_timer);
flag_irq = 1;
}
return IRQ_RETVAL(IRQ_HANDLED);
}
static int LCPlatform_open(struct inode* inode, struct file *file)
{
int ret;
if(file->f_flags & O_NONBLOCK)
{
if(down_trylock(&button_lock))
return -EBUSY;
}
else
{
down(&button_lock);
}
ret = request_irq(IRQ_EINT8,LCPlatform_Handle_irq,IRQ_TYPE_EDGE_FALLING,"S3C_IRQTYPE_EINT",NULL);
if(ret)
{
print_log("%s: request irq fail\n",__FUNCTION__);
return -EIO;
}
init_timer(&LCPlatform_timer);
return ret;
}
static int LCPlatform_release(struct inode* inode, struct file *file)
{
int ret = 0;
free_irq(IRQ_EINT8, NULL);
ret = del_timer(&LCPlatform_timer);
up(&button_lock);
return ret;
}
static int LCPlatform_read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off)
{
int ret;
if(file->f_flags & O_NONBLOCK)
{
if(!isPressed)
return -EAGAIN;
}
print_log("%s: do read\n",__FUNCTION__);
if(bytes> sizeof(char))
bytes = sizeof(char);
wait_event_interruptible(button_wait_queue,isPressed);
ret = copy_to_user(userbuf, &isPressed, sizeof(char));
if(ret)
{
print_log("%s: copy to user fail\n", __FUNCTION__);
return -EFAULT;
}
isPressed = 0;
return ret;
}
unsigned int LCPlatform_poll(struct file *file, struct poll_table_struct *poll_table)
{
unsigned int mask;
poll_wait(file, &button_wait_queue, poll_table);
if(isPressed)
{
mask |= POLLIN | POLLRDNORM;
}
return mask;
}
static struct file_operations LCPlatform_ops =
{
.open = LCPlatform_open,
.release = LCPlatform_release,
.read = LCPlatform_read,
.poll = LCPlatform_poll,
.owner = THIS_MODULE
};
/* config gpio */
static int LCPlatform_set_gpio(struct platform_device *LCPlatform_device)
{
struct resource *LCplatform_resource;
unsigned char key;
/* config key */
LCplatform_resource = platform_get_resource(LCPlatform_device, IORESOURCE_MEM, 0);
if(!LCplatform_resource)
{
print_log("%s: get resource IORESOURCE_MEM fail\n", __FUNCTION__);
return -EFAULT;
}
gpio_g_con = (volatile unsigned long *)ioremap(LCplatform_resource->start,LCplatform_resource->end - LCplatform_resource->start + 1);
gpio_g_dat = gpio_g_con + 1;
LCplatform_resource = platform_get_resource(LCPlatform_device, IORESOURCE_IRQ, 0);
if(!LCplatform_resource)
{
print_log("%s: get resource IORESOURCE_IRQ fail\n", __FUNCTION__);
goto fail;
}
key = LCplatform_resource->start;
/* config EINT irq*/
*gpio_g_con |= (0x02 << key);
/* config led */
LCplatform_resource = platform_get_resource(LCPlatform_device, IORESOURCE_MEM, 1);
if(!LCplatform_resource)
{
print_log("%s: get resource IORESOURCE_IRQ fail\n", __FUNCTION__);
goto fail;
}
gpio_b_con = (volatile unsigned long *)ioremap(LCplatform_resource->start,LCplatform_resource->end - LCplatform_resource->start + 1);
gpio_b_dat = gpio_b_con + 1;
*gpio_b_con |= (1<<LED1*2) | (1<<LED2*2) | (1<<LED3*2) | (1<<LED4*2); //config out
return 0;
fail:
iounmap(gpio_g_con);
return EFAULT;
}
int LCPlatform_probe(struct platform_device *LCPlatform_device)
{
int ret = 0;
int major;
ret = LCPlatform_set_gpio(LCPlatform_device);
if(ret)
{
print_log("%s: set gpio fail\n",__FUNCTION__);
return EAGAIN;
}
major = register_chrdev(0, PLATFORM_NAME, &LCPlatform_ops);
if(!major)
{
print_log("%s: use defalut major\n", __FUNCTION__);
major = PLATFORM_MAJOR;
ret = register_chrdev(major,PLATFORM_NAME,&LCPlatform_ops);
if(!ret)
{
print_log("%s: register char device fail\n",__FUNCTION__);
return ret;
}
}
LCPlatform_class = class_create(THIS_MODULE, "LCPlatform");
if(!LCPlatform_class)
{
print_log("%s: class create fial\n", __FUNCTION__);
unregister_chrdev(major, PLATFORM_NAME);
}
if(!device_create(LCPlatform_class, NULL, MKDEV(major,PLATFORM_MINOR),NULL,PLATFORM_NAME))
{
print_log("%s: device create fail\n", __FUNCTION__);
goto fail_1;
}
return ret;
fail_1:
class_destroy(LCPlatform_class);
unregister_chrdev(major, PLATFORM_NAME);
return -EAGAIN;
}
static struct platform_driver LCPlatform_driver = {
.probe = LCPlatform_probe,
.driver = {
.name = PLATFORM_NAME,
.owner = THIS_MODULE,
}
};
static int LCPlatform_init_module()
{
int ret;
ret = platform_driver_register(&LCPlatform_driver);
if(ret<0)
{
print_log("%s: init platform module fail\n",__func__);
return ret;
}
print_log("%s: init platform module success\n",__func__);
return ret;
}
static void LCPlatform_exit_module()
{
device_destroy(LCPlatform_class, MKDEV(PLATFORM_MAJOR,PLATFORM_MINOR));
class_destroy(LCPlatform_class);
platform_driver_unregister(&LCPlatform_driver);
del_timer(&LCPlatform_timer);
}
module_init(LCPlatform_init_module);
module_exit(LCPlatform_exit_module);
MODULE_AUTHOR("junzhang");
MODULE_LICENSE("GPL");
Makefile
KDIR = /home/share/linux-2.6/linux-2.6.32.2/
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
obj-m += platform_device.o
obj-m += platform_driver.o
EXTRA_CFLAGS += -DSHOW_LOG
#CFLAGS_platform_device.o += -DSHOW_LOG
clean:
rm *.o platform_device.mod.c platform_driver.mod.c modules.order Module.symvers
app
test.c
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define filePath "/dev/LCPlatform"
int main()
{
int fd;
int ret;
unsigned char data;
fd_set FdTable;
struct timeval pollTimer;
fd = open(filePath, O_RDWR, S_IRUSR | S_IWUSR);
if(fd<0)
{
printf("%s: open %s fail", __FUNCTION__, filePath);
return fd;
}
printf("open success\n");
while(1)
{
FD_ZERO(&FdTable);
FD_SET(fd, &FdTable);
pollTimer.tv_sec = 5;
pollTimer.tv_usec = 0;
ret = select(fd+1, &FdTable, NULL, NULL, &pollTimer);
if(FD_ISSET(fd, &FdTable))
{
ret = read(fd, &data, sizeof(char));
if(ret != sizeof(char))
printf("%s: read error \n", __FUNCTION__);
printf("%s: data = %d", __FUNCTION__, data);
}
else
printf("%s: poll time out\n", __FUNCTION__);
}
}