??xml version="1.0" encoding="utf-8" standalone="yes"?>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <mach/irqs.h>
#include <mach/regs-gpio.h>
#include "../ioctl_new.h"
static spinlock_t wiegand_lock;
static int wiegand_in = 0;
static int wiegand_in_cur = 0;
static int wiegand_in_stime = 0;
static int wiegand_in1_stime = 0;
static int wiegand_in2_stime = 0;
static unsigned char wiegand_data[40];
static WIEGAND_TYPE wiegand_mode;
inline void wiegand_delay(int microseconds)
{
int i;
while (microseconds--)
{
for (i=0; i<65; i++) __asm("NOP");
}
}
static int wiegand_gettickcount(void)
{
static int __start_time = 0; //second
struct timeval tv;
do_gettimeofday(&tv);
if (__start_time == 0)
__start_time = tv.tv_sec;
return ((tv.tv_sec - __start_time) * 1000 + tv.tv_usec / 1000);
}
inline void wiegand_minus_interrupt(void)
{
if (wiegand_in != WGNSTAT_RECVFULL)
{
if (wiegand_gettickcount() - wiegand_in_stime >= 100)
wiegand_in_cur = 0;
wiegand_in_stime = wiegand_gettickcount();
wiegand_data[wiegand_in_cur++] = 0;
if (wiegand_in_cur >= 34)
wiegand_in = WGNSTAT_RECVFULL;
else if (wiegand_in_cur >= 26)
wiegand_in = WGNSTAT_RECV26;
}
}
inline void wiegand_plus_interrupt(void)
{
if (wiegand_in != WGNSTAT_RECVFULL)
{
if (wiegand_gettickcount() - wiegand_in_stime >= 100)
wiegand_in_cur = 0;
wiegand_in_stime = wiegand_gettickcount();
wiegand_data[wiegand_in_cur++] = 1;
if (wiegand_in_cur >= 34)
wiegand_in = WGNSTAT_RECVFULL;
else if (wiegand_in_cur >= 26)
wiegand_in = WGNSTAT_RECV26;
}
}
static irqreturn_t wiegand_minus1_interrupt(int irq, void *dev_id)
{
spin_lock(&wiegand_lock);
// printk("wiegand_minus1_interrupt\n");
if (wiegand_gettickcount() - wiegand_in2_stime > 200)
{
wiegand_minus_interrupt();
wiegand_in1_stime = wiegand_in_stime;
}
spin_unlock(&wiegand_lock);
return IRQ_HANDLED;
}
static irqreturn_t wiegand_plus1_interrupt(int irq, void *dev_id)
{
spin_lock(&wiegand_lock);
// printk("wiegand_plus1_interrupt\n");
if (wiegand_gettickcount() - wiegand_in2_stime > 200)
{
wiegand_plus_interrupt();
wiegand_in1_stime = wiegand_in_stime;
}
spin_unlock(&wiegand_lock);
return IRQ_HANDLED;
}
static int wiegand_open(struct inode * inode, struct file * filp);
static int wiegand_release(struct inode * inode,struct file * filp);
static ssize_t wiegand_read(struct file *flip, char *buf, size_t len, loff_t *pos);
static ssize_t wiegand_write(struct file *flip, const char *buf, size_t len, loff_t *pos);
static long wiegand_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
static struct file_operations wiegand_fops = {
owner: THIS_MODULE,
open: wiegand_open,
release: wiegand_release,
read: wiegand_read,
write: wiegand_write,
unlocked_ioctl: wiegand_ioctl,
};
static struct miscdevice wiegand_dev = {
WIEGAND_MINOR,
WIEGAND_MODULE_NAME,
&wiegand_fops
};
static void config_wiegand_pins(void)
{
s3c2410_gpio_cfgpin(WGNIN_0, WGNIN_0_CON);
s3c2410_gpio_cfgpin(WGNIN_1, WGNIN_1_CON);
s3c2410_gpio_cfgpin(WGNOUT0, WGNOUT0_CON);
s3c2410_gpio_cfgpin(WGNOUT1, WGNOUT1_CON);
#if 0
if (request_irq(WGNIN_0_IRQ_NUM, &wiegand_minus1_interrupt,
IORESOURCE_IRQ | IRQF_TRIGGER_RISING,
WIEGAND_IRQ_MINUS,
&wiegand_dev)
|| request_irq(WGNIN_1_IRQ_NUM, &wiegand_plus1_interrupt,
IORESOURCE_IRQ | IRQF_TRIGGER_RISING,
WIEGAND_IRQ_PLUS,
&wiegand_dev))
{
printk("WIEGAND request_irq Fail\n");
}
#endif
}
static int wiegand_open(struct inode * inode, struct file * filp)
{
config_wiegand_pins();
return 0;
}
static int wiegand_release(struct inode * inode,struct file * filp)
{
#if 0
free_irq(WGNIN_0_IRQ_NUM, &wiegand_dev);
free_irq(WGNIN_1_IRQ_NUM, &wiegand_dev);
#endif
return 0;
}
static ssize_t wiegand_read(struct file *flip, char *buf, size_t len, loff_t *pos)
{
int result;
spin_lock(&wiegand_lock);
if (wiegand_in == WGNSTAT_NONE || len > sizeof(wiegand_data) || (wiegand_gettickcount() - wiegand_in_stime > 1800)) //500
{
wiegand_in = WGNSTAT_NONE;
spin_unlock(&wiegand_lock);
return 0;
}
if (len > wiegand_in_cur)
len = wiegand_in_cur;
result = copy_to_user(buf, wiegand_data, len);
wiegand_in = WGNSTAT_NONE;
wiegand_in_cur = 0;
spin_unlock(&wiegand_lock);
return len;
}
static ssize_t wiegand_write(struct file *flip, const char *buf, size_t len, loff_t *pos)
{
int i;
char data[40];
if (len > sizeof(data))
return 0;
i = copy_from_user(data, buf, len);
for (i=0; i<len; i++)
{
if (data[i] == 0)
{
WGNOUT0_1; wiegand_delay(wiegand_mode.dwTpw); WGNOUT0_0;
}
else
{
WGNOUT1_1; wiegand_delay(wiegand_mode.dwTpw); WGNOUT1_0;
}
wiegand_delay(wiegand_mode.dwTpi);
}
return len;
}
static long wiegand_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int result = 0;
spin_lock(&wiegand_lock);
switch(cmd)
{
case WIEGAND_MODE_SET:
result = copy_from_user(&wiegand_mode, (void*)arg, sizeof(WIEGAND_TYPE));
break;
}
spin_unlock(&wiegand_lock);
return (result == sizeof(WIEGAND_TYPE) ? 0 : -1);
}
static int __init s3c2416_wiegand_init(void)
{
int ret = 0;
ret = misc_register(&wiegand_dev);
if (ret)
{
printk("probuck s3c2416 WIEGAND Driver" WIEGAND_DRIVER_VERSION "Fail\n");
goto exit_sb3kt_wiegand_init;
}
spin_lock_init(&wiegand_lock);
printk("probuck s3c2416 WIEGAND Driver" WIEGAND_DRIVER_VERSION "\n");
exit_sb3kt_wiegand_init:
return ret;
}
static void __exit s3c2416_wiegand_cleanup(void)
{
misc_deregister(&wiegand_dev);
}
module_init(s3c2416_wiegand_init);
module_exit(s3c2416_wiegand_cleanup);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("s3c2416 WIEGAND Driver");
MODULE_SUPPORTED_DEVICE("s3c2416 WIEGAND");
]]>
信号量在创徏旉要设|一个初始|表示同时可以有几个Q务可以访问该信号量保护的׃n资源Q初始gؓ1变成互斥锁QMutexQ,卛_时只能有一个Q务可以访问信号量保护的共享资源?br />
一个Q务要惌问共享资源,首先必须得到信号量,获取信号量的操作把信号量的值减1Q若当前信号量的gؓ负数Q表明无法获得信号量Q该d必须挂v在该信号量的{待队列{待该信号量可用Q若当前信号量的gؓ非负敎ͼ表示可以获得信号量,因而可以立刻访问被该信号量保护的共享资源?br />
当Q务访问完被信号量保护的共享资源后Q必释放信号量Q释放信号量通过把信号量的值加1实现Q如果信号量的gؓ非正敎ͼ表明有Q务等待当前信号量Q因此它也唤醒所有等待该信号量的d?br />
信号量的API有:
DECLARE_MUTEX(name)
该宏声明一个信号量nameq初始化它的gؓ1Q即声明一个互斥锁?br />
//在Linux2.6.26中没扑ֈ该宏
DECLARE_MUTEX_LOCKED(name)
该宏声明一个互斥锁nameQ但把它的初始D|ؓ0Q即锁在创徏时就处在已锁状态。因此对于这U锁Q一般是先释攑获得?br />
void sema_init (struct semaphore *sem, int val);
该函用于数初始化讄信号量的初|它设|信号量sem的gؓval?br />
void init_MUTEX (struct semaphore *sem);
该函数用于初始化一个互斥锁Q即它把信号量sem的D|ؓ1?br />
void init_MUTEX_LOCKED (struct semaphore *sem);
该函C用于初始化一个互斥锁Q但它把信号量sem的D|ؓ0Q即一开始就处在已锁状态?br />
void down(struct semaphore * sem);
该函数用于获得信号量semQ它会导致睡眠,因此不能在中断上下文Q包括IRQ上下文和softirq上下文)使用该函数。该函数把sem的值减1Q如果信号量sem的值非负,q接返回,否则调用者将被挂P直到别的d释放该信号量才能l箋q行?br />
int down_interruptible(struct semaphore * sem);
该函数功能与downcMQ不同之处ؓQdown不会被信PsignalQ打断,但down_interruptible能被信号打断Q因此该函数有返回值来区分是正常返回还是被信号中断Q如果返?Q表C得信号量正常q回Q如果被信号打断Q返?EINTR?br />
int down_trylock(struct semaphore * sem);
该函数试着获得信号量semQ如果能够立刻获得,它就获得该信号量q返?Q否则,表示不能获得信号量semQ返回gؓ?倹{因此,它不会导致调用者睡眠,可以在中断上下文使用?br />
void up(struct semaphore * sem);
该函数释放信号量semQ即把sem的值加1Q如果sem的gؓ非正敎ͼ表明有Q务等待该信号量,因此唤醒q些{待者?nbsp;
头文?
#include <linux/semaphore.h>
/*
* Copyright (c) 2008 Intel Corporation
* Author: Matthew Wilcox <willy@linux.intel.com>
*
* Distributed under the terms of the GNU GPL, version 2
*
* Please see kernel/semaphore.c for documentation of these functions
*/
#ifndef __LINUX_SEMAPHORE_H
#define __LINUX_SEMAPHORE_H
#include <linux/list.h>
#include <linux/spinlock.h>
/* Please don't access any members of this structure directly */
struct semaphore {
spinlock_t lock;
unsigned int count;
struct list_head wait_list;
};
#define __SEMAPHORE_INITIALIZER(name, n) \
{ \
.lock = __SPIN_LOCK_UNLOCKED((name).lock), \
.count = n, \
.wait_list = LIST_HEAD_INIT((name).wait_list), \
}
#define __DECLARE_SEMAPHORE_GENERIC(name, count) \
struct semaphore name = __SEMAPHORE_INITIALIZER(name, count)
#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name, 1)
static inline void sema_init(struct semaphore *sem, int val)
{
static struct lock_class_key __key;
*sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);
}
#define init_MUTEX(sem) sema_init(sem, 1)
#define init_MUTEX_LOCKED(sem) sema_init(sem, 0)
extern void down(struct semaphore *sem);
extern int __must_check down_interruptible(struct semaphore *sem);
extern int __must_check down_killable(struct semaphore *sem);
extern int __must_check down_trylock(struct semaphore *sem);
extern int __must_check down_timeout(struct semaphore *sem, long jiffies);
extern void up(struct semaphore *sem);
#endif /* __LINUX_SEMAPHORE_H */
]]>
自旋锁与互斥锁有点类|只是自旋锁不会引赯用者睡眠,如果自旋锁已l被别的执行单元保持Q调用者就一直@环在那里看是否该自旋锁的保持者已l释放了锁,"自旋"一词就是因此而得名?br />
׃自旋锁用者一般保持锁旉非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率q高于互斥锁?br />
信号量和d信号量适合于保持时间较长的情况Q它们会D调用者睡眠,因此只能在进E上下文使用Q_trylock的变U能够在中断上下文用)Q而自旋锁适合于保持时间非常短的情况,它可以在M上下文用?br />
如果被保护的׃n资源只在q程上下文访问,使用信号量保护该׃n资源非常合适,如果对共巯源的讉K旉非常短,自旋锁也可以。但是如果被保护的共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部卌Y中断Q,必M用自旋锁?br />
自旋锁保持期间是抢占失效的,而信号量和读写信号量保持期间是可以被抢占的。自旋锁只有在内核可抢占或SMP的情况下才真正需要,在单CPU且不可抢占的内核下,自旋锁的所有操作都是空操作?br />
跟互斥锁一P一个执行单元要惌问被自旋锁保护的׃n资源Q必d得到锁,在访问完׃n资源后,必须释放锁。如果在获取自旋锁时Q没有Q何执行单元保持该锁,那么立卛_到锁Q如果在获取自旋锁时锁已l有保持者,那么获取锁操作将自旋在那里,直到该自旋锁的保持者释放了锁?br />
无论是互斥锁Q还是自旋锁Q在M时刻Q最多只能有一个保持者,也就_在Q何时L多只能有一个执行单元获得锁?br />
自旋锁的API有:
spin_lock_init(x)
该宏用于初始化自旋锁x。自旋锁在真正用前必须先初始化。该宏用于动态初始化?br />
DEFINE_SPINLOCK(x)
该宏声明一个自旋锁xq初始化它。该宏在2.6.11中第一ơ被定义Q在先前的内怸q没有该宏?br />
SPIN_LOCK_UNLOCKED
该宏用于静态初始化一个自旋锁?br />
DEFINE_SPINLOCK(x){同于spinlock_t x = SPIN_LOCK_UNLOCKED
spin_is_locked(x)
该宏用于判断自旋锁x是否已经被某执行单元保持Q即被锁Q,如果是,q回真,否则q回假?br />
spin_unlock_wait(x)
该宏用于{待自旋锁x变得没有被Q何执行单元保持,如果没有M执行单元保持该自旋锁Q该宏立卌回,否则@环在那里Q直到该自旋锁被保持者释放?br />
spin_trylock(lock)
该宏力获得自旋锁lockQ如果能立即获得锁,它获得锁q返回真Q否则不能立卌得锁Q立卌回假。它不会自旋{待lock被释放?br />
spin_lock(lock)
该宏用于获得自旋锁lockQ如果能够立卌得锁Q它马上返回,否则Q它自旋在那里Q直到该自旋锁的保持者释放,q时Q它获得锁ƈq回。MQ只有它获得锁才q回?br />
spin_lock_irqsave(lock, flags)
该宏获得自旋锁的同时把标志寄存器的g存到变量flags中ƈ失效本地中断?br />
spin_lock_irq(lock)
该宏cM于spin_lock_irqsaveQ只是该宏不保存标志寄存器的倹{?br />
spin_lock_bh(lock)
该宏在得到自旋锁的同时失效本地Y中断?br />
spin_unlock(lock)
该宏释放自旋锁lockQ它与spin_trylock或spin_lock配对使用。如果spin_trylockq回假,表明没有获得自旋锁,因此不必使用spin_unlock释放?br />
spin_unlock_irqrestore(lock, flags)
该宏释放自旋锁lock的同Ӟ也恢复标志寄存器的gؓ变量flags保存的倹{它与spin_lock_irqsave配对使用?br />
spin_unlock_irq(lock)
该宏释放自旋锁lock的同Ӟ也能本C断。它与spin_lock_irq配对应用?br />
spin_unlock_bh(lock)
该宏释放自旋锁lock的同Ӟ也能本地的软中断。它与spin_lock_bh配对使用?br />
spin_trylock_irqsave(lock, flags)
该宏如果获得自旋锁lockQ它也将保存标志寄存器的值到变量flags中,q且失效本地中断Q如果没有获得锁Q它什么也不做?br /> 因此如果能够立即获得锁,它等同于spin_lock_irqsaveQ如果不能获得锁Q它{同于spin_trylock。如果该宏获得自旋锁lockQ那需要用spin_unlock_irqrestore来释放?br />
spin_trylock_irq(lock)
该宏cM于spin_trylock_irqsaveQ只是该宏不保存标志寄存器。如果该宏获得自旋锁lockQ需要用spin_unlock_irq来释放?br />
spin_trylock_bh(lock)
该宏如果获得了自旋锁Q它也将失效本地软中断。如果得不到锁,它什么也不做。因此,如果得到了锁Q它{同于spin_lock_bhQ如果得不到锁,它等同于spin_trylock。如果该宏得C自旋锁,需要用spin_unlock_bh来释放?br />
spin_can_lock(lock)
该宏用于判断自旋锁lock是否能够被锁Q它实际是spin_is_locked取反。如果lock没有被锁Q它q回真,否则Q返回假。该宏在2.6.11中第一ơ被定义Q在先前的内怸q没有该宏?nbsp;
获得自旋锁和释放自旋锁有好几个版本,因此让读者知道在什么样的情况下使用什么版本的获得和释N的宏是非常必要的?nbsp;
如果被保护的׃n资源只在q程上下文访问和软中断上下文讉KQ那么当在进E上下文讉K׃n资源Ӟ可能被Y中断打断Q从而可能进入Y中断上下文来对被保护的共享资源访问,因此对于q种情况Q对׃n资源的访问必M用spin_lock_bh和spin_unlock_bh来保护?nbsp;
当然使用spin_lock_irq和spin_unlock_irq以及spin_lock_irqsave和spin_unlock_irqrestore也可以,它们失效了本地硬中断Q失效硬中断隐式C失效了Y中断。但是用spin_lock_bh和spin_unlock_bh是最恰当的,它比其他两个快?nbsp;
如果被保护的׃n资源只在q程上下文和tasklet或timer上下文访问,那么应该使用与上面情늛同的获得和释N的宏Q因为tasklet和timer是用软中断实现的?nbsp;
如果被保护的׃n资源只在一个tasklet或timer上下文访问,那么不需要Q何自旋锁保护Q因为同一个tasklet或timer只能在一个CPU上运行,即是在SMP环境下也是如此。实际上tasklet在调用tasklet_schedule标记光要被调度时已l把该taskletl定到当前CPUQ因此同一个tasklet决不可能同时在其他CPU上运行?nbsp;
timer也是在其被用add_timerd到timer队列中时已经被帮定到当前CPUQ所以同一个timerl不可能q行在其他CPU上。当然同一个tasklet有两个实例同时运行在同一个CPU更不可能了?nbsp;
如果被保护的׃n资源只在两个或多个tasklet或timer上下文访问,那么对共享资源的讉K仅需要用spin_lock和spin_unlock来保护,不必使用_bh版本Q因为当tasklet或timerq行Ӟ不可能有其他tasklet或timer在当前CPU上运行?nbsp;
如果被保护的׃n资源只在一个Y中断Qtasklet和timer除外Q上下文讉KQ那么这个共享资源需要用spin_lock和spin_unlock来保护,因ؓ同样的Y中断可以同时在不同的CPU上运行?nbsp;
如果被保护的׃n资源在两个或多个软中断上下文讉KQ那么这个共享资源当然更需要用spin_lock和spin_unlock来保护,不同的Y中断能够同时在不同的CPU上运行?nbsp;
如果被保护的׃n资源在Y中断Q包括tasklet和timerQ或q程上下文和中断上下文讉KQ那么在软中断或q程上下文访问期_可能被硬中断打断Q从而进入硬中断上下文对׃n资源q行讉KQ因此,在进E或软中断上下文需要用spin_lock_irq和spin_unlock_irq来保护对׃n资源的访问?nbsp;
而在中断处理句柄中用什么版本,需依情况而定Q如果只有一个中断处理句柄访问该׃n资源Q那么在中断处理句柄中仅需要spin_lock和spin_unlock来保护对׃n资源的访问就可以了?nbsp;
因ؓ在执行中断处理句柄期_不可能被同一CPU上的软中断或q程打断。但是如果有不同的中断处理句柄访问该׃n资源Q那么需要在中断处理句柄中用spin_lock_irq和spin_unlock_irq来保护对׃n资源的访问?nbsp;
在用spin_lock_irq和spin_unlock_irq的情况下Q完全可以用spin_lock_irqsave和spin_unlock_irqrestore取代Q那具体应该使用哪一个也需要依情况而定Q如果可以确信在对共享资源访问前中断是能的Q那么用spin_lock_irq更好一些?nbsp;
因ؓ它比spin_lock_irqsave要快一些,但是如果你不能确定是否中断能,那么使用spin_lock_irqsave和spin_unlock_irqrestore更好Q因为它恢复访问共享资源前的中断标志而不是直接能中断?nbsp;
当然Q有些情况下需要在讉K׃n资源时必M断失效,而访问完后必M断能,q样的情形用spin_lock_irq和spin_unlock_irq最好?nbsp;
需要特别提醒读者,spin_lock用于L在不同CPU上的执行单元对共享资源的同时讉K以及不同q程上下文互相抢占导致的对共享资源的非同步访问,而中断失效和软中断失效却是ؓ了阻止在同一CPU上Y中断或中断对׃n资源的非同步讉K?nbsp;
#include <linux/spinlock.h>
头文?
#include<linux/spinlock_types.h>
#ifndef __LINUX_SPINLOCK_TYPES_H
#define __LINUX_SPINLOCK_TYPES_H
/*
* include/linux/spinlock_types.h - generic spinlock type definitions
* and initializers
*
* portions Copyright 2005, Red Hat, Inc., Ingo Molnar
* Released under the General Public License (GPL).
*/
//对称多处理器
#if defined(CONFIG_SMP)
#include <asm/spinlock_types.h>
#else
#include <linux/spinlock_types_up.h>
#endif
#include <linux/lockdep.h>
typedef struct {
raw_spinlock_t raw_lock;
#ifdef CONFIG_GENERIC_LOCKBREAK
unsigned int break_lock;
#endif
} spinlock_t;
#define SPINLOCK_MAGIC 0xdead4ead
typedef struct {
raw_rwlock_t raw_lock;
#ifdef CONFIG_GENERIC_LOCKBREAK
unsigned int break_lock;
#endif
} rwlock_t;
#define RWLOCK_MAGIC 0xdeaf1eed
#define SPINLOCK_OWNER_INIT ((void *)-1L)
# define __SPIN_LOCK_UNLOCKED(lockname) \
(spinlock_t) { .raw_lock = __RAW_SPIN_LOCK_UNLOCKED, \
SPIN_DEP_MAP_INIT(lockname) }
#define __RW_LOCK_UNLOCKED(lockname) \
(rwlock_t) { .raw_lock = __RAW_RW_LOCK_UNLOCKED, \
RW_DEP_MAP_INIT(lockname) }
/*
* SPIN_LOCK_UNLOCKED and RW_LOCK_UNLOCKED defeat lockdep state tracking and
* are hence deprecated.
* Please use DEFINE_SPINLOCK()/DEFINE_RWLOCK() or
* __SPIN_LOCK_UNLOCKED()/__RW_LOCK_UNLOCKED() as appropriate.
*/
#define SPIN_LOCK_UNLOCKED __SPIN_LOCK_UNLOCKED(old_style_spin_init)
#define RW_LOCK_UNLOCKED __RW_LOCK_UNLOCKED(old_style_rw_init)
#define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x)
#define DEFINE_RWLOCK(x) rwlock_t x = __RW_LOCK_UNLOCKED(x)
#endif /* __LINUX_SPINLOCK_TYPES_H */
]]>
1.softirq
适用于性能敏感的子pȝ
2.tasklet
建立在softirq之上,使用更简?br />softirq的不同实例可q行在不同的处理器上,而tasklet则不允许
必须使用define DECLARE_TASKLET 声明tasklet
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
void short_do_tasklet(unsigned long);
DECLARE_TASKLET(short_do_tasklet,short_tasklet,0);
3. 工作队列(work queue)
struct work_struct my_work; //定义一个工作队?/span>
void my_work_func(unsigned long); //定义一个处理函?/span>
头文?br />#include <linux/interrupt.h>
/* Tasklets --- multithreaded analogue of BHs.
Main feature differing them of generic softirqs: tasklet
is running only on one CPU simultaneously.
Main feature differing them of BHs: different tasklets
may be run simultaneously on different CPUs.
Properties:
* If tasklet_schedule() is called, then tasklet is guaranteed
to be executed on some cpu at least once after this.
* If the tasklet is already scheduled, but its excecution is still not
started, it will be executed only once.
* If this tasklet is already running on another CPU (or schedule is called
from tasklet itself), it is rescheduled for later.
* Tasklet is strictly serialized wrt itself, but not
wrt another tasklets. If client needs some intertask synchronization,
he makes it with spinlocks.
*/
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
使用CZ:
static atomic_t fpstatues;
static atomic_t has_image = {0};
static void gc0303_vsync_do_tasklet(ulong data)
{
ulong curr = gettickcount();
debug("curr:%lu",curr);
if (atomic_read(&fpstatues)==0)
{
s3c2410_dma_ctrl(DMACH_XD0, S3C2410_DMAOP_FLUSH);
gc0303_dma_buf.dma_addr = img_phys;
gc0303_dma_buf.size = bmp_w*bmp_h;
s3c2410_dma_enqueue(DMACH_XD0,
(void *)&gc0303_dma_buf,
gc0303_dma_buf.dma_addr,
gc0303_dma_buf.size);
atomic_set(&fpstatues,1);
}
}
DECLARE_TASKLET(gc0303_vsync_tasklet,gc0303_vsync_do_tasklet,0);
static irqreturn_t gc0303_vsync_handler(int irq,void* dev_id)
{
if (atomic_read(&fpstatues)==1)
{
s3c2410_dma_ctrl(DMACH_XD0, S3C2410_DMAOP_START);
}
//调度gc0303_vsync_tasklet
tasklet_schedule(&gc0303_vsync_tasklet);
return IRQ_HANDLED;
}
tasklet是作Z断下半部的一个很好的选择Q它在性能和易用性之间有着很好的^衡。较之于softirqQtasklet不需要考虑SMP下的q行问题Q而又比workqueues有着更好的性能?br />tasklet通常作ؓ中断的下半部来使用Q在中断中调用tasklet_schedule(t)。每ơ硬中断都会触发一ơtasklet_schedule(t)Q但是每ơ中断它只会向其中的一个CPU注册Q而不是所有的CPU。完成注册后的tasklet由tasklet_action()来执行,在SMP环境下,它保证同一时刻Q同一个tasklet只有一个副本在q行Q这样就避免了用softirq所要考虑的互斥的问题。再者,tasklet在执行tasklet->func()前,再一ơ允许tasklet可调度(注册Q,但是在该tasklet已有一个副本在其他CPU上运行的情况下,它只能退后执行。MQ同一个硬中断引v的一个tasklet_schedule()动作只会使一个tasklet被注册,而不同中断引Ltasklet则可能在不同的时刻被注册而多ơ被执行?br />
tasklet的互斥。由于同一个tasklet不能有多个副本同时运行,所以不需要在多CPU之间互斥。在taskletq行的过E中Q它会被中断打断(q也是Y中断的优点)Q所以如果tasklet和其他中断之间的互斥有可能存在?br />中断服务E序一般都是在中断h关闭的条件下执行?以避免嵌套而中断控制复杂化。但是,中断是一个随ZӞ它随时会到来Q如果关中断的时间太长,CPU׃能及时响应其他的中断hQ从而造成中断的丢失。因此,Linux内核的目标就是尽可能快的处理完中断请求,其所能把更多的处理向后推q。例如,假设一个数据块已经辑ֈ了网U,当中断控制器接受到这个中断请求信hQLinux内核只是单地标志数据到来了,然后让处理器恢复到它以前q行的状态,其余的处理稍后再q行Q如把数据移入一个缓冲区Q接受数据的q程可以在~冲区找到数据)。因此,内核把中断处理分Z部分Q上半部QtophalfQ和下半部(bottomhalfQ,上半部(是中断服务E序Q内核立x行,而下半部Q就是一些内核函敎ͼ留着E后处理Q?br />
首先Q一个快速的“上半?#8221;来处理硬件发出的hQ它必须在一个新的中断生之前终止。通常Q除了在讑֤和一些内存缓冲区Q如果你的设备用CDMAQ就不止q些Q之间移动或传送数据,定g是否处于健全的状态之外,q一部分做的工作很少?br />
下半部运行时是允怸断请求的Q而上半部q行时是关中断的Q这是二者之间的主要区别?br />
但是Q内核到底什时候执行下半部Q以何种方式l织下半部?q就是我们要讨论的下半部实现机制Q这U机制在内核的演变过E中不断得到改进Q在以前的内怸Q这个机制叫做bottomhalf(Ubh)Q在2.4以后的版本中有了新的发展和改q,改进的目标下半部可以在多处理机上ƈ行执行,q有助于驱动E序的开发者进行驱动程序的开发。下面主要介l常用的Q?Tasklet)机制?.6内核中的工作队列机制?br />
q里的小d是指对要推迟执行的函数进行组l的一U机制。其数据l构为tasklet_structQ每个结构代表一个独立的Q务,其定义如下:
structtasklet_struct {
structtasklet_struct *next; /*指向链表中的下一个结?/span>*/
unsignedlong state; /* Q务的状?/span>*/
atomic_t count; /* 引用计数?/span>*/
void(*func) (unsigned long); /* 要调用的函数*/
unsignedlong data; /* 传递给函数的参?/span>*/
}Q?br />l构中的func域就是下半部中要推迟执行的函敎ͼdata是它唯一的参数?br />state域的取gؓTASKLET_STATE_SCHED或TASKLET_STATE_RUN。TASKLET_STATE_SCHED表示Q务已被调度,正准备投入运行,TASKLET_STATE_RUN表示Q务正在运行。TASKLET_STATE_RUN只有在多处理器系l上才用,单处理器pȝ什么时候都清楚一个小d是不是正在运行(它要么就是当前正在执行的代码Q要么不是)?br />count 域是Q务的引用计数器。如果它不ؓ0Q则Q务被止Q不允许执行Q只有当它ؓӞQ务才被激z,q且在被讄为挂hQ小d才能够执行?br />1. 声明和用小d大多数情况下Qؓ了控制一个寻常的g讑֤Q小d机制是实C半部的最佳选择。小d可以动态创建,使用方便Q执行v来也比较快?br />我们既可以静态地创徏Q务,也可以动态地创徏它。选择那种方式取决于到底是惌对小dq行直接引用q是一个间接引用。如果准备静态地创徏一个小dQ也是对它直接引用Q,使用下面两个宏中的一个:
DECLARE_TASKLET(name,func, data)
DECLARE_TASKLET_DISABLED(name,func, data)
q两个宏都能Ҏl定的名字静态地创徏一个tasklet_structl构。当该小d被调度以后,l定的函数func会被执行Q它的参数由datal出。这两个宏之间的区别在于引用计数器的初始D|不同。第一个宏把创建的Q务的引用计数器设|ؓ0Q因此,该小d处于Ȁzȝ态。另一个把引用计数器设|ؓ1Q所以该Q务处于禁止状态。例如:
DECLARE_TASKLET(my_tasklet,my_tasklet_handler, dev);
q行代码其实{h?br />struct tasklet_struct my_tasklet = { NULL, 0, ATOMIC_INIT(0),tasklet_handler,dev};
q样创Z一个名为my_tasklet的小dQ其处理E序为tasklet_handlerQƈ且已被激zR当处理E序被调用的时候,dev׃被传递给它?br />2. ~写自己的小d处理E序Q务处理程序必ȝ合如下的函数cdQ?br />void tasklet_handler(unsigned long data)
׃Q务不能睡眠,因此不能在小d中用信号量或者其它生阻塞的函数。但是小dq行时可以响应中断?br />3. 调度自己的小d通过调用tasklet_schedule()函数q传递给它相应的tasklt_struct指针Q该Q务就会被调度以便适当的时候执行:
tasklet_schedule(&my_tasklet); /*把my_tasklet标记为挂?nbsp;*/
在小d被调度以后,只要有机会它׃可能早的运行。在它还没有得到q行Z之前Q如果一个相同的Q务又被调度了Q那么它仍然只会q行一ơ?br />可以调用tasklet_disable()函数来禁止某个指定的Q务。如果该Q务当前正在执行,q个函数会等到它执行完毕再返回。调用tasklet_enable()函数可以ȀzM个小dQ如果希望把以DECLARE_TASKLET_DISABLEDQ)创徏的小dȀz,也得调用q个函数Q如Q?br />tasklet_disable(&my_tasklet); /*Q务现在被止,q个Q务不能运?/span>*/
tasklet_enable(&my_tasklet); /* Q务现在被Ȁz?/span>*/
也可以调用tasklet_kill()函数从挂L队列中去掉一个小d。该函数的参数是一个指向某个小d的tasklet_struct的长指针。在Q务重新调度它自n的时候,从挂L队列中移d调度的小d会很有用。这个函数首先等待该Q务执行完毕,然后再将它移厅R?br />4.tasklet的简单用?br />下面是tasklet的一个简单应?以模块的形成加蝲?br />
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
static struct tasklet_struct my_tasklet;
static void tasklet_handler (unsigned long d ata)
{
printk("tasklet_handler is running.\n");
}
static int __init test_init(void)
{
tasklet_init(&my_tasklet,tasklet_handler,0);
tasklet_schedule(&my_tasklet);
return0;
}
static void __exit test_exit(void)
{
tasklet_kill(&tasklet);
printk("test_exit is running.\n");
}
MODULE_LICENSE("GPL");
module_init(test_init);
module_exit(test_exit);
从这个例子可以看出,所谓的Q务机制是Z半部函数的执行提供了一U执行机Ӟ也就是说Q推q处理的事情是由tasklet_handler实现Q何时执行,l由Q务机制封装后交给内核d理?/div>
]]>
查看中断:
cat /proc/interrupts
查看中断l计:
cat /proc/stat
1、头文g
#include <linux/interrupt.h>
#include <asm/arch/irqs.h>
2、申明中断处理程?nbsp;
static spinlock_t wiegand_lock; // 使用之前必须初始?spin_lock_init(&wiegand_lock);
static irqreturn_t wiegand_minus1_interrupt(int irq, void *dev_id)
{
spin_lock(&wiegand_lock);
//do something
//wiegand_minus_interrupt();
spin_unlock(&wiegand_lock);
return IRQ_HANDLED;
}
3.注册中断 request_irq()
2.6 内核 函数原型如下Q?br />extern int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev_id);
W?个参?中断?是要甌的中断号
W?个参?中断处理函数
W?个参?中断触发标志
IRQF_DISABLED 快中?止其他中断)
IRQF_TRIGGER_RISING 讑֤在中断线上生一个上升沿?发出中断.(说明在中断之?讑֤一直将中断U保持在一个电q上)
IRQF_TRIGGER_FALLING 下降沿触?br /> IRQF_TRIGGER_HIGH 高电q发中?br /> IRQF_TRIGGER_LOW 低电q发中?br /> IRQF_TRIGGER_RANDOM 为系l随机发生器提供支持
IRQF_TRIGGER_SHARED (表示׃n相同的中断号,多个讑֤׃n)
/*
* linux/interrupt.h
* These correspond to the IORESOURCE_IRQ_* defines in
* linux/ioport.h to select the interrupt line behaviour. When
* requesting an interrupt without specifying a IRQF_TRIGGER, the
* setting should be assumed to be "as already configured", which
* may be as per machine or firmware initialisation.
*/
#define IRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001
#define IRQF_TRIGGER_FALLING 0x00000002
#define IRQF_TRIGGER_HIGH 0x00000004
#define IRQF_TRIGGER_LOW 0x00000008
#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE 0x00000010
typedef irqreturn_t (*irq_handler_t)(int, void *);
W?个参?中断的标?中断的标识与/proc/interrupts对应
W?个参?传入中断处理E序的参敎ͼ可以为NULLQ在注册׃n中断Ӟ此参C能ؓNULLQ作为共享中断时的中断区别参数?br />
q回?如果中断处理成功,q回IREQ_HANDLED,否则,q回IRQ_NONE
/* irqreturn.h */
typedef int irqreturn_t;
#define IRQ_NONE (0)
#define IRQ_HANDLED (1)
#define IRQ_RETVAL(x) ((x) != 0)
中断上下?br />
中断上下文注意事?
1)中断上下文代码绝不可以停止运行。不能做M可能发生休眠的操?在从中断处理函数中调用一个内核API之前,应该仔细分手?以确保其内部不会触发d{待!
2)Z在中断处理函C保护临界?不能使用互斥?因ؓ它们也许D睡眠,应该使用自旋锁代替互斥体.
3)中断处理函数不能与用L间直接交互数?
调度器工作于q程之间,如果中断处理函数睡眠q被调度出去.无法调度回来!
4)中断处理函数一斚w需要快速地为其他进E让出处理器,另一斚w又需要完成它的工?Z规避q种冲突,中断处理函数通常工作分成两个部?半部设一个标志以宣称它已l服务了该中?而重大的工作负蝲都被丢给了底半部,底半部的执行被g?在其执行环境?所有的中断都是使能?
4.释放中断
extern void free_irq(int irq,void *dev_id)
5、启动用用中断
extern void disable_irq_nosync(unsigned int irq);
extern void disable_irq(unsigned int irq);
extern void enable_irq(unsigned int irq);
]]>
内核W号列表:
cat /proc/kallsyms
cat /proc/iomem
导出内核W号:
EXPORT_SYMBOL(sym)
1、缺省情况下QLinux2.6内核中默认不导出所有的W号,卛_2.6的内怸没有q个宏EXPORT_NO_SYMBOLS
2、如果想要自己指定导出哪些变量或函数Q先在Makefile中?/span>-DEXPORT_SYMTABQ然后在源文件中使用EXPORT_SYMBOL(xxx);导出?br />
echo 8 > /proc/sys/kernel/printk
查看中断:
cat /proc/interrupts
下半部机?br />1.tasklet
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
2. 工作队列
struct work_struct my_work; //定义一个工作队?/span>
void my_work_func(unsigned long); //定义一个处理函?/span>
在中断处理函?tasklet和内核定时器{非q程上下文不能阻?
]]>
]]>
M不是一个编译选项Q而是一个变量,从顶层makefile文g里可以找到它的定?
文g?/span>
/usr/src/linux-headers-2.6.32-5-common/Makefile
69 ifeq ("$(origin M)", "command line")
70 KBUILD_EXTMOD := $(M)
71 endif
-C 用于指定内核源代码的目录
-M 用于module所在的目录