1. 程式人生 > >[嵌入式Linux驅動]S5PV210的蜂鳴器Linux驅動

[嵌入式Linux驅動]S5PV210的蜂鳴器Linux驅動

智慧家居報警器控制驅動程式: 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 <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/uaccess.h>

#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 <asm/io.h>
#include <asm/uaccess.h>

#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

#include <linux/platform_device.h>
#include <linux/miscdevice.h>

#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 <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#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`