智能家居报警器控制驱动程序:
1. 本驱动使用platform模型进行设计,分为Alarm_Beeper_device和Alarm_Beeper_driver两个文件
2. 注册杂项设备(misc),主设备号固定是10(misc),从设备号由系统自动分配,加载成功后使用lsmod可以看到:
Alarm_Beeper_device
Alarm_Beeper_driver
3. 加载driver驱动模块之后自动对gpio进行初始化,初始化成功会输出:
[ 22.179372] Alarm_Beeper_dirver_probe: gpio init finished!!! //GPIO初始化成功
[ 22.191932] Alarm_Beeper_dirver_probe: timer0 init finished!!! //定时器初始化成功
初始化完成后会默认将蜂鸣器关掉。
4. 本驱动注册成功后生成 /dev/smarthome_alarm_beeper 节点
5. 对 smarthome_alarm_beeper 设备节点的操作主要有:
1)打开操作open
2)关闭操作close
3)发送命令ioctl:这里使用了_IO的办法对命令进行加密
#define MAGIC_WORD 'w'
#define ALARM_BEEPER_OFF _IO(MAGIC_WORD,10) //关闭蜂鸣器
#define ALARM_BEEPER_ON _IO(MAGIC_WORD,11) //打开蜂鸣器
#define ALARM_BEEPER_SPEED_UP _IO(MAGIC_WORD,12) //提高蜂鸣器频率(大约提升1倍,最多提升7次)
#define ALARM_BEEPER_SPEED_DOWN _IO(MAGIC_WORD,13) //降低蜂鸣器频率(大约降低1半,最多降低7次)
#define ALARM_BEEPER_SPEED_DEFAULT _IO(MAGIC_WORD,20) //恢复蜂鸣器默认频率
注意:这里仅仅是对蜂鸣器的频率进行操作,对蜂鸣器鸣响的持续时间控制在用户程序完成。
Alarm_Beeper_device.c
#include
#include
#include
#include
#define S5PV210_GPD_BASE 0xe02000a0 //read the s5pv210 datasheet!
#define S5PV210_TC_BASE 0xe2500000
#define GPD_SIZE 0x34
#define TC0_SIZE 0x14
void Alarm_Beeper_device_release(struct device * pdev);
static struct resource Alarm_Beeper_resource[]={
[0] = {
.start = S5PV210_GPD_BASE,
.end = S5PV210_GPD_BASE + GPD_SIZE,
.name = "GPD_BASE",
.flags = IORESOURCE_MEM,
},
[1] = {
.start = S5PV210_TC_BASE,
.end = S5PV210_TC_BASE + TC0_SIZE,
.name = "TC0_BASE",
.flags = IORESOURCE_MEM,
},
};
struct platform_device Alarm_Beeper_device={
.name = "Alarm_Beeper_drv",
.id = -1,
.dev={
.release=Alarm_Beeper_device_release,
},
.num_resources = ARRAY_SIZE(Alarm_Beeper_resource),
.resource = Alarm_Beeper_resource,
};
void Alarm_Beeper_device_release(struct device * pdev)
{
printk("entering %s\n",__FUNCTION__);
}
static int __init Alarm_Beeper_device_init(void)
{
printk("entering %s\n",__FUNCTION__);
if(platform_device_register(&Alarm_Beeper_device)){
printk("%s: platform_device_register failed! \n",__FUNCTION__);
return -EBUSY;
}
return 0;
}
static void __exit Alarm_Beeper_device_exit(void)
{
printk("entering %s\n",__FUNCTION__);
platform_device_unregister(&Alarm_Beeper_device);
}
module_init(Alarm_Beeper_device_init);
module_exit(Alarm_Beeper_device_exit);
MODULE_AUTHOR("kinyanderson");
MODULE_DESCRIPTION("Alarm_Beeper_device,use for controlling the beeper");
MODULE_LICENSE("GPL");
Alarm_Beeper_driver.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEBUG 1
#define SINGLE_MODULE 1
#define MAGIC_WORD 'w'
#define ALARM_BEEPER_OFF _IO(MAGIC_WORD,10)
#define ALARM_BEEPER_ON _IO(MAGIC_WORD,11)
#define ALARM_BEEPER_SPEED_UP _IO(MAGIC_WORD,12)
#define ALARM_BEEPER_SPEED_DOWN _IO(MAGIC_WORD,13)
#define ALARM_BEEPER_SPEED_DEFAULT _IO(MAGIC_WORD,20)
struct Alarm_Beeper_Info{
unsigned int pclk_freq;
unsigned int input_freq;
unsigned int prescale_value;
unsigned int mux_divide_value;
unsigned int duty_cycle_numerator;
unsigned int duty_cycle_denominator;
unsigned int tcntb_value;
unsigned short ALARM_BEEPER_status;
};
#define ALARM_BEEPER_RUNNING 1
#define ALARM_BEEPER_STOPPED 0
/* 1-255 */
#define DEFAULT_PRESCALE_DIVIDE_VALUE 250
/* 1/1 1/2 1/4 1/8 1/16 */
#define DEFAULT_MUX_DIVIDE_VALUE 16
#define PCLK_FREQ 66000000
#define DEFAULT_INPUT_FREQ (66000000/DEFAULT_PRESCALE_DIVIDE_VALUE/DEFAULT_MUX_DIVIDE_VALUE)
#define DEFAULT_TCNTB_VALUE DEFAULT_INPUT_FREQ
#define DEFAULT_DUTY_CYCLE_NUMERATOR 1
#define DEFAULT_DUTY_CYCLE_DENOMINATOR 2
static struct Alarm_Beeper_Info alarm_beeper_info={
.pclk_freq = PCLK_FREQ,
.input_freq = DEFAULT_INPUT_FREQ,
.prescale_value = DEFAULT_PRESCALE_DIVIDE_VALUE,
.mux_divide_value = DEFAULT_MUX_DIVIDE_VALUE,
.tcntb_value = DEFAULT_TCNTB_VALUE,
.duty_cycle_numerator = DEFAULT_DUTY_CYCLE_NUMERATOR,
.duty_cycle_denominator = DEFAULT_DUTY_CYCLE_DENOMINATOR,
.ALARM_BEEPER_status = ALARM_BEEPER_STOPPED,
};
struct resource * platform_resource[2];
static volatile unsigned long * GPD0_BASE;
static volatile unsigned long * TC0_BASE;
#define TCFG0 (TC0_BASE + 0)
#define TCFG1 (TC0_BASE + 1)
#define TCON (TC0_BASE + 2)
#define TCNTB0 (TC0_BASE + 3)
#define TCMPB0 (TC0_BASE + 4)
#define TCNTB_CURRENT 16500
#define TCNTB_MAX (TCNTB_CURRENT * 128)
#define TCNTB_MIN (TCNTB_CURRENT / 128)
//GPD0CON[3:0] = 0000 default input 0010 is TOUT0
#define GPD0_SET_INPUT(tmp) do{ \
tmp =ioread32(GPD0_BASE); \
tmp &= ~(0xf<<0); \
iowrite32(tmp,GPD0_BASE); \
}while(0)
#define GPD0_SET_TOUT0(tmp) do{ \
tmp =ioread32(GPD0_BASE); \
tmp &= ~(0xf<<0); \
tmp |= (0x2<<0); \
iowrite32(tmp,GPD0_BASE); \
}while(0)
#define TCNTB0_SET_VALUE(tmp,value) do{ \
tmp =ioread32(TCNTB0); \
tmp &= ~(0xffffffff<<0); \
tmp |= (value << 0); \
iowrite32(tmp,TCNTB0); \
}while(0)
#define TIMER0_TCON_CLEAR(tmp) do{ \
tmp =ioread32(TCON); \
tmp &= ~(0xf<<0); \
iowrite32(tmp,TCON); \
}while(0)
#define TIMER0_TCON_SET(tmp) do{ \
tmp =ioread32(TCON); \
tmp |= (0x1<<3)|(0x1<<2); \
iowrite32(tmp,TCON); \
}while(0)
#define TIMER0_TCON_UPDATE(tmp) do{ \
tmp =ioread32(TCON); \
tmp |= (0x1<<1); \
iowrite32(tmp,TCON); \
udelay(5); \
tmp &= ~(0x1<<1); \
iowrite32(tmp,TCON); \
}while(0)
#define TIMER0_START(tmp) do{ \
tmp =ioread32(TCON); \
tmp |= (0x1<<0); \
iowrite32(tmp,TCON); \
}while(0)
#define TIMER0_STOP(tmp) do{ \
tmp =ioread32(TCON); \
tmp &= ~(0x1<<0); \
iowrite32(tmp,TCON); \
}while(0)
/*** file_operation_function declare ****/
int Alarm_Beeper_driver_open (struct inode * inode_p, struct file *file_p);
long Alarm_Beeper_driver_ioctl (struct file *file_p, unsigned int cmd, unsigned long arg);
int Alarm_Beeper_driver_close (struct inode *inode_p, struct file *file_p);
static int __devexit Alarm_Beeper_driver_remove(struct platform_device * pdev);
static int __devinit Alarm_Beeper_dirver_probe(struct platform_device * pdev);
/*** Struct declare ****/
static struct platform_driver Alarm_Beeper_driver={
.probe = Alarm_Beeper_dirver_probe,
.remove = Alarm_Beeper_driver_remove,
.driver = {
.name = "Alarm_Beeper_drv",
.owner = THIS_MODULE,
},
};
static struct file_operations Alarm_Beeper_fop={
.open = Alarm_Beeper_driver_open,
.unlocked_ioctl = Alarm_Beeper_driver_ioctl,
.release = Alarm_Beeper_driver_close,
};
static struct miscdevice Alarm_Beeper_miscdev = {
.minor = MISC_DYNAMIC_MINOR, //dynamic
.name = "smarthome_alarm_beeper",
.fops = &Alarm_Beeper_fop,
};
/*** file_operation_function implement ****/
int Alarm_Beeper_driver_open (struct inode * inode_p, struct file *file_p)
{
printk("entering %s\n",__FUNCTION__);
unsigned int tmp;
/* initing the gpio */
GPD0_SET_INPUT(tmp);
printk("%s: gpio init finished!!!\n",__FUNCTION__);
/* initing the timer0 */
//Timer Input Frequency = PCLK / ( {prescaler value + 1} ) / {divider value}
//TCFG0[7:0] =249 (250) PCLK=66MHz
tmp =ioread32(TCFG0);
tmp &= ~(0xff<<0);
tmp |= ((alarm_beeper_info.prescale_value- 1)<<0);
//TCFG1[3:0] =0100 (1/16) 66M/250/16 = 16500
tmp =ioread32(TCFG1);
tmp &= ~(0xf<<0);
switch(alarm_beeper_info.mux_divide_value){
case 1:
tmp |= (0x0<<0);
break;
case 2:
tmp |= (0x1<<0);
break;
case 4:
tmp |= (0x2<<0);
break;
case 8:
tmp |= (0x3<<0);
break;
case 16:
tmp |= (0x4<<0);
break;
default:
tmp |= (0x5<<0);
break;
}
iowrite32(tmp,TCFG1);
TCNTB0_SET_VALUE(tmp,alarm_beeper_info.tcntb_value);
tmp =ioread32(TCMPB0);
tmp &= ~(0xffffffff<<0);
tmp |=(alarm_beeper_info.input_freq - alarm_beeper_info.input_freq *
alarm_beeper_info.duty_cycle_numerator / alarm_beeper_info.duty_cycle_denominator);
iowrite32(tmp,TCMPB0);
//TCON[3][2][1][0]=1110 [0]=0 default stop
TIMER0_TCON_CLEAR(tmp);
TIMER0_TCON_UPDATE(tmp);
TIMER0_TCON_SET(tmp);
alarm_beeper_info.ALARM_BEEPER_status = ALARM_BEEPER_STOPPED;
printk("%s: timer0 init finished!!!\n",__FUNCTION__);
return 0;
}
long Alarm_Beeper_driver_ioctl (struct file *file_p, unsigned int cmd, unsigned long arg)
{
printk("entering %s\n",__FUNCTION__);
unsigned int tmp;
if(_IOC_TYPE(cmd) != MAGIC_WORD)
return -EINVAL;
switch(cmd)
{
case ALARM_BEEPER_ON:
if(alarm_beeper_info.ALARM_BEEPER_status == ALARM_BEEPER_RUNNING){
return 0;
}
GPD0_SET_TOUT0(tmp);
TIMER0_START(tmp);
alarm_beeper_info.ALARM_BEEPER_status=ALARM_BEEPER_RUNNING;
break;
case ALARM_BEEPER_SPEED_UP :
if(alarm_beeper_info.tcntb_value<=TCNTB_MIN){
return 0;
}
alarm_beeper_info.tcntb_value /= 2;
TIMER0_TCON_CLEAR(tmp);
TCNTB0_SET_VALUE(tmp,alarm_beeper_info.tcntb_value);
TIMER0_TCON_UPDATE(tmp);
TIMER0_TCON_SET(tmp);
TIMER0_START(tmp);
alarm_beeper_info.ALARM_BEEPER_status=ALARM_BEEPER_RUNNING;
break;
case ALARM_BEEPER_SPEED_DOWN:
if(alarm_beeper_info.tcntb_value>=TCNTB_MAX){
return 0;
}
alarm_beeper_info.tcntb_value *= 2;
TIMER0_TCON_CLEAR(tmp);
TCNTB0_SET_VALUE(tmp,alarm_beeper_info.tcntb_value);
TIMER0_TCON_UPDATE(tmp);
TIMER0_TCON_SET(tmp);
TIMER0_START(tmp);
alarm_beeper_info.ALARM_BEEPER_status=ALARM_BEEPER_RUNNING;
break;
case ALARM_BEEPER_SPEED_DEFAULT:
alarm_beeper_info.tcntb_value = DEFAULT_TCNTB_VALUE;
TIMER0_TCON_CLEAR(tmp);
TCNTB0_SET_VALUE(tmp,alarm_beeper_info.tcntb_value);
TIMER0_TCON_UPDATE(tmp);
TIMER0_TCON_SET(tmp);
TIMER0_START(tmp);
alarm_beeper_info.ALARM_BEEPER_status=ALARM_BEEPER_RUNNING;
break;
case ALARM_BEEPER_OFF:
if(alarm_beeper_info.ALARM_BEEPER_status == ALARM_BEEPER_STOPPED){
return 0;
}
TIMER0_STOP(tmp);
GPD0_SET_INPUT(tmp);
alarm_beeper_info.ALARM_BEEPER_status=ALARM_BEEPER_STOPPED;
break;
default:
printk("%s: invalid command !!!\n",__FUNCTION__);
break;
}
return 0;
}
int Alarm_Beeper_driver_close (struct inode *inode_p, struct file *file_p)
{
printk("entering %s\n",__FUNCTION__);
unsigned int tmp;
TIMER0_STOP(tmp);
GPD0_SET_INPUT(tmp);
alarm_beeper_info.ALARM_BEEPER_status=ALARM_BEEPER_STOPPED;
return 0;
}
/*** driver_operation ****/
static int __devinit Alarm_Beeper_dirver_probe(struct platform_device * pdev)
{
printk("entering %s\n",__FUNCTION__);
struct resource * pcheck;
platform_resource[0]=platform_get_resource(pdev,IORESOURCE_MEM,0);
if(NULL==platform_resource[0]){
printk("%s: platform_get_resource[0] failed!\n",__FUNCTION__);
goto err1;
}
#if SINGLE_MODULE
pcheck=request_mem_region(platform_resource[0]->start,
platform_resource[0]->end - platform_resource[0]->start + 1,
platform_resource[0]->name);
if(NULL == pcheck){
printk("%s: request_mem_region failed!\n",__FUNCTION__);
goto err1; //return device busy!
}
#endif
GPD0_BASE=(unsigned long *)ioremap(platform_resource[0]->start,
platform_resource[0]->end - platform_resource[0]->start + 1);
platform_resource[1]=platform_get_resource(pdev,IORESOURCE_MEM,1);
if(NULL == platform_resource[1]){
printk("%s: platform_get_resource[1] failed!\n",__FUNCTION__);
goto err2;
}
#if SINGLE_MODULE
pcheck=request_mem_region(platform_resource[1]->start,
platform_resource[1]->end - platform_resource[1]->start + 1,
platform_resource[1]->name);
if(NULL == pcheck){
printk("%s: request_mem_region failed!\n",__FUNCTION__);
goto err2; //return device busy!
}
#endif
TC0_BASE=(unsigned long *)ioremap(platform_resource[1]->start,
platform_resource[1]->end - platform_resource[1]->start + 1);
#if DEBUG
printk("%s: GPD0_BASE is %p \n",__FUNCTION__,GPD0_BASE);
printk("%s: TC0_BASE is %p \n",__FUNCTION__,TC0_BASE);
printk("%s: TCFG0 is %p \n",__FUNCTION__,TCFG0);
printk("%s: TCFG1 is %p \n",__FUNCTION__,TCFG1);
printk("%s: TCON is %p \n",__FUNCTION__,TCON);
printk("%s: TCNTB0 is %p \n",__FUNCTION__,TCNTB0);
printk("%s: TCMPB0 is %p \n",__FUNCTION__,TCMPB0);
#endif
if(misc_register(&Alarm_Beeper_miscdev)){
printk("%s: misc_register failed!\n",__FUNCTION__);
goto err3; //release the memory!
}
return 0;
err3:
iounmap(TC0_BASE);
#if SINGLE_MODULE
release_mem_region(platform_resource[1]->start,
platform_resource[1]->end - platform_resource[1]->start + 1);
#endif
err2:
iounmap(GPD0_BASE);
#if SINGLE_MODULE
release_mem_region(platform_resource[0]->start,
platform_resource[0]->end - platform_resource[0]->start + 1);
#endif
err1:
return -EBUSY;
}
static int __devexit Alarm_Beeper_driver_remove(struct platform_device * pdev)
{
printk("entering %s\n",__FUNCTION__);
iounmap(TC0_BASE);
iounmap(GPD0_BASE);
#if SINGLE_MODULE
release_mem_region(platform_resource[1]->start,
platform_resource[1]->end - platform_resource[1]->start + 1);
release_mem_region(platform_resource[0]->start,
platform_resource[0]->end - platform_resource[0]->start + 1);
#endif
if(misc_deregister(&Alarm_Beeper_miscdev)){
printk("%s:misc_deregister failed!\n",__FUNCTION__);
return -EPERM;
}
return 0;
}
static int __init Alarm_Beeper_driver_init(void)
{
printk("entering %s\n",__FUNCTION__);
if(platform_driver_register(&Alarm_Beeper_driver)){
printk("%s: driver_register failed!\n",__FUNCTION__);
return -EBUSY;
}
return 0;
}
static void __exit Alarm_Beeper_driver_exit(void)
{
printk("entering %s\n",__FUNCTION__);
platform_driver_unregister(&Alarm_Beeper_driver);
}
module_init(Alarm_Beeper_driver_init);
module_exit(Alarm_Beeper_driver_exit);
MODULE_AUTHOR("kinyanderson");
MODULE_DESCRIPTION("Alarm_Beeper_driver,use for controlling the beeper");
MODULE_LICENSE("GPL");
app.c
#include
#include
#include
#define MAGIC_WORD 'w'
#define ALARM_OFF _IO(MAGIC_WORD,10)
#define ALARM_ON _IO(MAGIC_WORD,11)
#define ALARM_SPEED_UP _IO(MAGIC_WORD,12)
#define ALARM_SPEED_DOWN _IO(MAGIC_WORD,13)
#define ALARM_SPEED_DEFAULT _IO(MAGIC_WORD,20)
int main(int argc,char **argv)
{
int fd;
int i;
fd = open("/dev/smarthome_alarm_beeper",O_RDWR);
if(fd < 0){
printf("can not open smarthome_alarm_beeper!!!\n");
return -1;
}
while(1)
{
ioctl(fd,ALARM_ON);
sleep(5);
ioctl(fd,ALARM_OFF);
sleep(5);
ioctl(fd,ALARM_ON);
sleep(5);
for(i=0;i<3;i++){
ioctl(fd,ALARM_SPEED_DOWN);
sleep(5);
}
ioctl(fd,ALARM_SPEED_DEFAULT);
sleep(5);
for(i=0;i<3;i++){
ioctl(fd,ALARM_SPEED_UP);
sleep(5);
}
ioctl(fd,ALARM_SPEED_DEFAULT);
sleep(5);
ioctl(fd,ALARM_OFF);
sleep(10);
}
close(fd);
printf("the app finished!!!\n");
return 0;
}
Makefile
obj-m += Alarm_Beeper_device.o Alarm_Beeper_driver.o
KDIR := /home/kinyanderson/final_design/android-kernel-samsung-dev
modules:
make modules -C $(KDIR) M=`pwd`
arm-linux-gcc app.c -o app
clean:
make modules clean -C $(KDIR) M=`pwd`