经过几个下班业余时间,终于完成了一个比较全面的字符设备了,(注释掉的为阻塞read方法)该字符设备结合了s3c6410的led和key,key为非中断驱动,接下来可以研究异步通知,中断和内核定时器了(这个在工作实际应用中比较常用,比如一个i2c设备如gsensor,不需要中断的时候(节约gpio口),可以结合内核定时器和工作队列来实现固定时间读取)。期待下期吧·······
要应用测试程序的请留言。
参考:linux设备驱动程序(第三版)
linux设备驱动开发详解
file:gpio.h#ifndef _GPIO_H_
#define _GPIO_H_
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/semaphore.h>
#define LED_MAJOR 0
#define DEVICE_NAME &quot;leds&quot;
#define GPKCON0 0
#define GPKCON1 1
#define GPKDAT 2
#define GPKPUD 3
#define MAX_NUM 6
struct led_dev{
struct cdev cdev;
struct semaphore sem;
wait_queue_head_t w_wait;
wait_queue_head_t r_wait;
unsigned int* base;
unsigned int key;
unsigned char rflag;
unsigned char wflag;
unsigned int val;
};
#define BITSET(name, val) do{name |= val;}while(0)
#define BITCSET(name, mask, val) do{ name &= ~mask; \
name |= val }while(0)
#define BITCLR(name, mask) do{ name &= ~mask;}while(0)
#endif复制代码
file:gpio.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank-k.h>
#include <mach/gpio-bank-n.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include &quot;gpio.h&quot;
#define GPIO_DEBUG
#ifdef GPIO_DEBUG
#define dprintk(fmt, arg...) printk(&quot;###GPIO DEBUG###%s():&quot;fmt, __func__, ##arg)
#else
#define dprintk(fmt, arg...)
#endif
static int led_major = LED_MAJOR;
struct led_dev* led;
static int led_open(struct inode* inode, struct file* filp)
{
dprintk(&quot;open!\n&quot;);
filp->private_data = led;
return 0;
}
static int led_close(struct inode* inode, struct file* filp)
{
dprintk(&quot;close!\n&quot;);
return 0;
}
static ssize_t led_read(struct file* filp, char __user* buff, size_t count, loff_t* offp)
{
struct led_dev* led = filp->private_data;
if(down_interruptible(&led->sem))
return -ERESTARTSYS;
#if 0
dprintk(&quot;led->rflag=%d, led->wflag=%d\n&quot;, led->rflag, led->wflag);
while(!led->rflag){
DEFINE_WAIT(wait);
up(&led->sem);
if(filp->f_flags & O_NONBLOCK)
return -EAGAIN;
dprintk(&quot;prepare to wait!\n&quot;);
prepare_to_wait(&led->r_wait, &wait, TASK_INTERRUPTIBLE);
if(led->rflag == 0)
schedule();
dprintk(&quot;finish wait!\n&quot;);
finish_wait(&led->r_wait, &wait);
if(signal_pending(current))
return -ERESTARTSYS;
if(down_interruptible(&led->sem))
return -ERESTARTSYS;
}
#endif
if(count < 0 || count > 2)
count = 1;
led->key = readl(S3C64XX_GPNDAT);
dprintk(&quot;led->key is 0x%x\n&quot;, led->key); //led->key & 0x3f
if(copy_to_user(buff, &led->key, count))
return -EFAULT;
led->rflag = 0;
if((led->key & 0x3f) != 0x3f){
led->wflag = 1;
wake_up_interruptible(&led->w_wait);
}
up(&led->sem);
return count;
}
static ssize_t led_write(struct file* filp, const char __user* buff, size_t count, loff_t* offp)
{
struct led_dev* led = filp->private_data;
DECLARE_WAITQUEUE(wait, current);
if(down_interruptible(&led->sem))
return -ERESTARTSYS;
add_wait_queue(&led->w_wait, &wait);
dprintk(&quot;led->rflag=%d, led->wflag=%d\n&quot;, led->rflag, led->wflag);
while(!led->wflag){
if(filp->f_flags & O_NONBLOCK){
up(&led->sem);
remove_wait_queue(&led->w_wait, &wait);
return -EAGAIN;
}
__set_current_state(TASK_INTERRUPTIBLE);
up(&led->sem);
dprintk(&quot;into schedule!\n&quot;);
schedule();
dprintk(&quot;out schedule!\n&quot;);
if(signal_pending(current)){
up(&led->sem);
remove_wait_queue(&led->w_wait, &wait);
set_current_state(TASK_RUNNING);
return -ERESTARTSYS;
}
if(down_interruptible(&led->sem))
return -ERESTARTSYS;
}
if(count < 0 || count > 2)
count = 1;
if(copy_from_user(&led->val, buff, count))
return -EFAULT;
dprintk(&quot;led->val=0x%x\n&quot;, led->val);
writel(led->val << 4, led->base + GPKDAT);
led->rflag = 1;
led->wflag = 0;
wake_up_interruptible(&led->r_wait);
up(&led->sem);
remove_wait_queue(&led->w_wait, &wait);
set_current_state(TASK_RUNNING);
return count;
}
static long led_ioctl(struct file* filp, unsigned int cmd, unsigned long arg)
{
struct led_dev* led = filp->private_data;
unsigned int tmp;
switch(cmd){
case 0:
case 1:
dprintk(&quot;cmd=%d, arg=%d\n&quot;, cmd, arg);
if(down_interruptible(&led->sem))
return -ERESTARTSYS;
tmp = readl(led->base + GPKDAT);
tmp &= ~(1 << (4 + arg)); //clear
tmp |= ( (!cmd) << (4 + arg) ); //set
writel(tmp, led->base + GPKDAT);
up(&led->sem);
break;
default:
break;
}
return 0;
}
static unsigned int led_poll(struct file* filp, poll_table* wait)
{
unsigned int mask = 0;
struct led_dev* led = filp->private_data;
down(&led->sem);
poll_wait(filp, &led->w_wait, wait);
poll_wait(filp, &led->r_wait, wait);
if(led->rflag)
mask |= POLLIN | POLLRDNORM;
if(led->wflag)
mask |= POLLOUT | POLLWRNORM;
up(&led->sem);
return mask;
}
static const struct file_operations led_fops = {
.open = led_open,
.release = led_close,
.read = led_read,
.write = led_write,
.unlocked_ioctl = led_ioctl,
.poll = led_poll,
};
static void led_setup_cdev(struct led_dev* dev, int index)
{
int err, devno = MKDEV(led_major, 0);
cdev_init(&dev->cdev, &led_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&dev->cdev, devno, 1);
if(err)
printk(&quot;error add cdev!\n&quot;);
}
struct class* led_class;
int led_init(void)
{
int result;
unsigned tmp;
dev_t devno = MKDEV(led_major, 0);
if(led_major)
result = register_chrdev_region(devno, 1, DEVICE_NAME);
else{
dprintk(&quot;alloc_chrdev_region\n&quot;);
result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
led_major = MAJOR(devno);
}
if(result < 0)
return result;
led = kmalloc(sizeof(struct led_dev), GFP_KERNEL);
if(!led){
result = -ENOMEM;
goto malloc_error;
}
memset(led, 0, sizeof(struct led_dev));
led_setup_cdev(led, 0);
init_MUTEX(&led->sem);
init_waitqueue_head(&led->w_wait);
init_waitqueue_head(&led->r_wait);
led->rflag = 0;
led->wflag = 0;
dprintk(&quot;S3C64XX_GPK_BASE=0x%x\n&quot;, (unsigned int)S3C64XX_GPK_BASE);
led->base = S3C64XX_GPK_BASE;
led->val = 0xf << 4;
dprintk(&quot;led->base=0x%x\n&quot;, (unsigned int)led->base);
tmp = readl(led->base + GPKCON0);
tmp = (tmp & ~(0xffffU<<16))|(0x1111U<<16);
writel(tmp, led->base + GPKCON0);
dprintk(&quot;led->base=0x%x\n&quot;, (unsigned int)(led->base + GPKDAT));
dprintk(&quot;S3C64XX_GPKDAT=0x%x\n&quot;, (unsigned int)S3C64XX_GPKDAT);
writel(led->val, led->base + GPKDAT);
tmp = readl(S3C64XX_GPNCON);
tmp &= ~0xfffU;
writel(tmp, S3C64XX_GPNCON);
// dprintk(&quot;key val is 0x%x\n&quot;, readl(S3C64XX_GPNDAT));
led_class = class_create(THIS_MODULE, &quot;led_class&quot;);
device_create(led_class, NULL, MKDEV(led_major, 0), NULL, &quot;%s&quot;, DEVICE_NAME);
return 0;
malloc_error:
unregister_chrdev_region(devno, 1);
return result;
}
void led_exit(void)
{
cdev_del(&led->cdev);
device_destroy(led_class, MKDEV(led_major, 0));
class_destroy(led_class);
kfree(led);
unregister_chrdev_region(MKDEV(led_major, 0), 1);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE(&quot;GPL&quot;);
MODULE_AUTHOR(&quot;jf.s <jingfeng.shi@emdoor.com>&quot;);复制代码 |