目的是实现,通过板子上一按钮来控制系统恢复初始配置。 其实也就是在应用层将备份文件 覆盖 配置文件这一个操作而已。
按键恢复默认配置,其基本的思想是 中断+ 应用阻塞。
前前后后花了挺多的时间,看了中断的理论,看了寄存器的配置,还去看输入子系统,有些步骤是不必要的,但是做的时候并不知道不需要。
最后的实验,代码的大部分都是借用人家的。自己仅看的懂代码而已,如果自己去写一份,估计写不出来。
连接如下:http://wenku.baidu.com/view/c82df93283c4bb4cf7ecd181.html
自己在看对方代码的时候,有许多地方不太明白,因此把查阅的一些信息也都写进代码的注释中了。
另外poll 是一种监听机制,对自己方案的实现没有任何作用(参阅部分资料后得出的结论)。所以就没有注册这个方法。
关于寄存器的操作时看 datasheet里的,不过操作系统做了这部分的工作,自己画蛇添足了。 而关于清中断这一步,都说是要手动进行的,查了半天的资料都没有看到相关的配置,于是也就放弃了。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <mach/regs-irq.h>
#define DEVICE_NAME "RESTORE_BUTTON"
//#define S3C2410_GPF2 162
struct button_irq_desc
{
int irq;
int pin;
int pin_setting;
char * name;
};
// 这一部分还得看看具体情形才好。2不是被占用了么?被加密芯片
//IRQ_EINT2 = 16+2 =18
//S3C2410_GPF2 = 32x5+2 =162
//S3C2410_GPF2_EINT2 = 0x02 << 4
//触发设置为上升沿触发,因此
static struct button_irq_desc button=
{
IRQ_EINT2,S3C2410_GPF2,S3C2410_GPF2_EINT2,"RESTORE_BUTTON"
};
//设置等待队列
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
//按键进行记录
static volatile int ev_press = 0;
//触发设置为下降沿触发
//中断处理函数,由中断号和中断结构体组成。
static irqreturn_t irq_interrupt(int irq,void *dev_id)
{
printk("%s",dev_id); // 原先中断有问题的,就是kernel panic,加了这句竟然莫名好了,摸不着头脑。 没有再试验了
struct button_irq_desc * button_irq = (struct button_irq_desc*) dev_id;
int down;
// 这一部分的工作 为清理中断的工作,是否也被操作系统做完了?
//unsigned long srcpnd;
// 进入中断处理函数,现在可以清中断了。
//srcpnd = __raw_readl(S3C2410_SRCPND);
// 取反之后 再与操作
//srcpnd &=(~(1<<2));
//__raw_writel(srcpnd,S3C2410_SRCPND);
// 取值,值并不一定为1,可能为其他数值,但是不影响为真。
down=s3c2410_gpio_getpin(button_irq->pin);
if(!down)
{
// 记录按键被按下
ev_press=1;//;
//唤醒指定的注册在等待队列button_waitq 上的进程。该函数不能直接的立即唤醒进程,
//而是由调度程序转换上下文,调整为可运行状态。
wake_up_interruptible(&button_waitq);
}
// 表示中断处理结束
return IRQ_RETVAL(IRQ_HANDLED);
}
//register the interrupt,when failed ,quit;
static int irq_open(int irq,void *dev_id)
{
int err=0;
s3c2410_gpio_cfgpin(S3C2410_GPF2,S3C2410_GPF2_EINT2);
err = request_irq(button.irq,irq_interrupt,IRQ_TYPE_EDGE_FALLING,button.name,(void*)&button);
//对应中断号,中断处理函数,中断触发,中断名,设备结构体
// 正常时返回0 ,负责返回对应错误的负值。
if(err)
return -EBUSY;
return 0;
}
//释放已经注册的中断
static int irq_close(struct inode* inode,struct file *file)
{
free_irq(button.irq,(void*)&button);
return 0;
};
static volatile char key_values[]={'0','0','0'}
static int irq_read(struct file* file,char __user *buff,size_t count)//)
{
//3unsigned long err;
//ev_press为0,表明按钮未被按下
if (!ev_press)
{
// nonblock 为非阻塞的意思
// 即先判断文件描述符是否支持阻塞
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
// 支持阻塞,则进入阻塞队列,等待被唤醒
// 若ev_press 为1的话,则被唤醒
else
//将当前进程加入到等待队列button_waitq中,并在内部访问ev_press看值是否成立。
// 将标志位设置为TASK_INTTUPTABLE 并从runqueue 中删除,同时调用onshedule
// 当前进程则进入等待队列,不被执行,即睡眠了。
wait_event_interruptible(button_waitq, ev_press);
}
// 将ev_press 重置为0
ev_press = 0;
// 将值传递到用户空间,数据已经被读取。
// 需要改动
err = copy_to_user(buff, (const void *)key_values,count));
// 即完成读取这个过程即可,并不需要真正的数据传输
// 这里返回的是什么?
//return err ? -EFAULT : min(sizeof(key_values), count);
// 直接返回0 ,表示程序运行完了。即可。
return 0;
};
//监听函数,即监听当前进程,这里没有放入操作列表中。
//这里没有使用监听函数实现
static unsigned int irq_poll(struct file *file,struct poll_table_struct * wait)
{
unsigned int mask = 0;
// 将当前进程放入等待队列中
// poll_wait()函数会监测进程队列button_waitq里的进程
// 它的作用就是把当前进程添加到wait参数指定的等待列表(poll_table)中
// 首先调用poll_wait将等待队列添加到wait结构中
poll_wait(file,&button_waitq,wait);
if(ev_press)
mask |=POLLIN|POLLRDNORM; // 有数据可读
return mask; // 返回事件记录,若是0表示等待事件超时。
};
static struct file_operations dev_fops =
{
.owner = THIS_MODULE,
.open = irq_open,
.release = irq_close,
.read = irq_read,
// .poll = irq_poll;
};
static struct miscdevice misc=
{
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
static int __init dev_init(void)
{
int ret;
//unsigned long intmod;
//unsigned long intmask;
ret = misc_register(&misc);
printk(DEVICE_NAME" initialized\n");
if(ret<0){
printk("Add cdev failed!\n");
}
// 查到资料说寄存器的设置,操作系统已经做好了,自己这一部分的工作就有点画蛇添足了。
// 对中断寄存器的设置,初始化部分
// INTMOD 中断模式寄存器,设置为快中断,为1
// INTMASK 中断屏蔽寄存器,设置为0,表示响应中断
// 其中还要对寄存器SRCPEND进行中断清除,具体在代码那个位置还需要商榷
// 要定位这些寄存器的位置在哪里,并且进行设置。
//#define S3C2410_SRCPND S3C2410_IRQREG(0x000)
//#define S3C2410_INTMOD S3C2410_IRQREG(0x004)
//#define S3C2410_INTMSK S3C2410_IRQREG(0x008)
// 寄存器设置使用方法。
// 设置管脚为中断模式
//设置中断模式和中断屏蔽
//intmod = __raw_readl(S3C2410_INTMOD);
//将INTMOD 相应位 设置为1,即为快中断,同时写回寄存器中。
//具体哪一位,看 S3C2440 datasheet
//intmod = intmod|(1<<2);
//__raw_writel(intmod,S3C2410_INTMOD);
//将中断服务设置为可服务
//其他位置保持不变。
//intmask = __raw_readl(S3C2410_INTMSK);
//intmask = intmask&(~(1<<2));
//__raw_writel(intmask,S3C2410_INTMSK);
return ret;
}
static int __exit dev_exit(void)
{
misc_deregister(&misc);
return 0;
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("csw");
MODULE_DESCRIPTION("IRQ for restore IP");
/* 如果当前不可读,那么在sys_poll->do_poll中当前进程就会睡眠在等待队列上,
* 这个等待队列是由驱动程序提供的(就是poll_wait中传入的那个)。当可读的时候,
* 驱动程序可能有一部分代码运行了(比如驱动的中断服务程序),那么在这部分代码中,
* 就会唤醒等待队列上的进程,也就是之前睡眠的那个,当那个进程被唤醒后do_poll会再一次的
* 调用驱动程序的poll函数,这个时候应用程序就知道是可读的了。
* POOL方法就是用来支持非阻塞式的访问,当然是立即返回,但是它会把这次请求放入一个等待
* 队列中,当某个条件满足时,内核会通知应用程序(应用程序的select函数会感知),然后就
* 会接着select操作
*poll_wait不会挂起当前进程,而是把自己注册到某个事件等待队列中.
*poll_wait()是用在select系统调用中的.
*/
应用程序部分就没贴了,因为忘记从linux 的 home 路径下拷贝过来了。
应用程序主要实现的就是去读取对应的设备文件,当读到字符的时候,就实现拷贝工作,读取不到的时候就阻塞睡眠了。