1. 程式人生 > >linux驅動開發之蜂鳴器驅動源碼分析(一)

linux驅動開發之蜂鳴器驅動源碼分析(一)

linux 蜂鳴器 驅動

蜂鳴器的驅動源碼在/driver/char/buzzer/x210-buzzer.c文件中,源碼如下

#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 <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <plat/regs-timer.h>
#include <mach/regs-irq.h>
#include <asm/mach/time.h>
#include <linux/clk.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/miscdevice.h>

#include <linux/gpio.h>

#include <plat/gpio-cfg.h>
//#include <plat/regs-clock.h>
//#include <plat/regs-gpio.h>

//#include <plat/gpio-bank-e.h>
//#include <plat/gpio-bank-f.h>
//#include <plat/gpio-bank-k.h>

#define DEVICE_NAME     "buzzer"

#define PWM_IOCTL_SET_FREQ		1
#define PWM_IOCTL_STOP			0

static struct semaphore lock;

// TCFG0在Uboot中設置,這裏不再重復設置
// Timer0輸入頻率Finput=pclk/(prescaler1+1)/MUX1
//                     =66M/16/16
// TCFG0 = tcnt = (pclk/16/16)/freq;
// PWM0輸出頻率Foutput =Finput/TCFG0= freq
static void PWM_Set_Freq( unsigned long freq )
{
	unsigned long tcon;
	unsigned long tcnt;
	unsigned long tcfg1;

	struct clk *clk_p;
	unsigned long pclk;

	//unsigned tmp;
	
	//設置GPD0_2為PWM輸出
	s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(2));

	tcon = __raw_readl(S3C2410_TCON);
	tcfg1 = __raw_readl(S3C2410_TCFG1);

	//mux = 1/16
	tcfg1 &= ~(0xf<<8);
	tcfg1 |= (0x4<<8);
	__raw_writel(tcfg1, S3C2410_TCFG1);
	
	clk_p = clk_get(NULL, "pclk");
	pclk  = clk_get_rate(clk_p);

	tcnt  = (pclk/16/16)/freq;
	
	__raw_writel(tcnt, S3C2410_TCNTB(2));
	__raw_writel(tcnt/2, S3C2410_TCMPB(2));//占空比為50%

	tcon &= ~(0xf<<12);
	tcon |= (0xb<<12);		//disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0
	__raw_writel(tcon, S3C2410_TCON);
	
	tcon &= ~(2<<12);			//clear manual update bit
	__raw_writel(tcon, S3C2410_TCON);
}

void PWM_Stop( void )
{
	//將GPD0_2設置為input
	s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(0));	
}

static int x210_pwm_open(struct inode *inode, struct file *file)
{
	if (!down_trylock(&lock))
		return 0;
	else
		return -EBUSY;
	
}


static int x210_pwm_close(struct inode *inode, struct file *file)
{
	up(&lock);
	return 0;
}

// PWM:GPF14->PWM0
static int x210_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
	switch (cmd) 
	{
		case PWM_IOCTL_SET_FREQ:
			printk("PWM_IOCTL_SET_FREQ:\r\n");
			if (arg == 0)
				return -EINVAL;
			PWM_Set_Freq(arg);
			break;

		case PWM_IOCTL_STOP:
		default:
			printk("PWM_IOCTL_STOP:\r\n");
			PWM_Stop();
			break;
	}

	return 0;
}


static struct file_operations dev_fops = {
    .owner   =   THIS_MODULE,
    .open    =   x210_pwm_open,
    .release =   x210_pwm_close, 
    .ioctl   =   x210_pwm_ioctl,
};

static struct miscdevice misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &dev_fops,
};

static int __init dev_init(void)
{
	int ret;

	init_MUTEX(&lock);
	ret = misc_register(&misc);
	
	/* GPD0_2 (PWMTOUT2) */
	ret = gpio_request(S5PV210_GPD0(2), "GPD0");
	if(ret)
		printk("buzzer-x210: request gpio GPD0(2) fail");
		
	s3c_gpio_setpull(S5PV210_GPD0(2), S3C_GPIO_PULL_UP);
	s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(1));
	gpio_set_value(S5PV210_GPD0(2), 0);

	printk ("x210 "DEVICE_NAME" initialized\n");
    	return ret;
}

static void __exit dev_exit(void)
{
	misc_deregister(&misc);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("www.9tripod.com");
MODULE_DESCRIPTION("x210 PWM Driver");

蜂鳴器的驅動模塊代碼如上。和其他驅動模塊代碼一樣,module_init中的函數為驅動模塊被加載的時候(insmd)時運行的函數,module_exit中的函數為驅動模塊被卸載時(rmmod)運行的函數,

首先看下module_Init驅動被加載時執行的函數dev_init函數,代碼分析如下

static int __init dev_init(void)
{
	int ret;

	init_MUTEX(&lock);
	ret = misc_register(&misc);
	
	/* GPD0_2 (PWMTOUT2) */
	ret = gpio_request(S5PV210_GPD0(2), "GPD0");
	if(ret)
		printk("buzzer-x210: request gpio GPD0(2) fail");
		
	s3c_gpio_setpull(S5PV210_GPD0(2), S3C_GPIO_PULL_UP);
	s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(1));
	gpio_set_value(S5PV210_GPD0(2), 0);

	printk ("x210 "DEVICE_NAME" initialized\n");
    	return ret;
}

上面代碼中的init_MUTEX(&lock),這個lock是在本文件全局定義的,定義如下,init_MUTEXH函數是初始化信號量的,將信號量的計數值初始化為1。計數值初始化為1的信號量其實就是互斥鎖,由這個代碼可以知道,編寫者的本意是想用的互斥鎖的,但可能那個時候並沒有互斥鎖,只有信號量,所以用信號量來實現了互斥鎖。因為信號量的計數值為1,其實跟互斥鎖就是類似的了。因為信號量沒有互斥鎖優化的好,所以這裏使用信號量其實並不是現在主流的。

static struct semaphore lock;

struct semaphore是一個信號量結構體,信號量和互斥鎖的不同前面已經講過。結構體內容如下

struct semaphore {
	spinlock_t		lock;
	unsigned int		count;
	struct list_head	wait_list;    //信號量的等待隊列
};

在dev_init函數中,接著代碼是

ret = misc_register(&misc);

misc_register是註冊misc雜散類設備的函數,參數misc是被定義在全局並且填充好的結構體變量,內容如下

static struct miscdevice misc = {    //這裏定義miscdevice結構體並填充了三個成員,根據實際情況進行填充
	.minor = MISC_DYNAMIC_MINOR,    //次設備號minor成員被賦值為MISC_DYNAMIC_MINOR宏的值(255),前面說過該值表示讓內核為我們自動分配次設備                                        //號。
	.name = DEVICE_NAME,    //該設備的名字,這個設備的名字並不像platform平臺總線機制中的名字那麽重要(因為是用來macth函數匹配設備和驅動的//)。這裏這個name作用只是用來記錄的。
	.fops = &dev_fops,    //該設備的操作方法
};

上面定義的strcut miscdevice結構體中的fops成員被綁定成dev_fops,dev_fops也是被定義在本文件的全局的,該結構體包含的是操作這個驅動的操作而方法,內容如下

static struct file_operations dev_fops = {
    .owner   =   THIS_MODULE,
    .open    =   x210_pwm_open,    //應用層open操作該設備文件時執行的函數
    .release =   x210_pwm_close,     //應用層close操作該設備文件時執行的函數
    .ioctl   =   x210_pwm_ioctl,    //應用層ioctl操作該設備文件時執行的函數
};

經過misc_register(&misc)後,就會將該misc設備和其驅動的操作方法進行註冊,完成雜散類設備的註冊。

在dev_init函數中接下來的代碼就是對蜂鳴器硬件相關的設置,根據硬件原理圖來看蜂鳴器對應的GPIO,操作設置如下

	/* GPD0_2 (PWMTOUT2) */
	ret = gpio_request(S5PV210_GPD0(2), "GPD0");    //向內核申請GPIOD0_2引腳資源
	if(ret)
		printk("buzzer-x210: request gpio GPD0(2) fail");
		
	s3c_gpio_setpull(S5PV210_GPD0(2), S3C_GPIO_PULL_UP);    //向內核申請後,設置GPIOD0_2引腳為上拉
	s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(1));    //將該GPIOD0_2引腳的模式設置成S3C_GPIO_SFN(1)模式(特殊模式1,根據手冊是輸出
	                                                      //模式,詳細情況看數據手冊)
	gpio_set_value(S5PV210_GPD0(2), 0);    //設置該引腳初始輸出0低電平,因為是初始化為0低電平,所以蜂鳴器開始是不會響的。

	printk ("x210 "DEVICE_NAME" initialized\n");    //DEVICE_NAME宏是設備名字,這種方式的打印信息,編譯會將宏代表的字符串一起打印出來。
    	return ret;

上面的分析已經將蜂鳴器驅動模塊被裝載時發生的事情(dev_init)分析完了。dev_init函數執行成功表示驅動已經安裝好了,此時驅動代碼為待命狀態。等待應用層去操作。


應用層用open代開蜂鳴器設備文件時,對應的驅動執行函數是x210_pwm_open,代碼如下

static int x210_pwm_open(struct inode *inode, struct file *file)
{
	if (!down_trylock(&lock))    //因為蜂鳴器這種設備很簡單,所以open函數是空的,這裏只是執行了這麽一個上鎖操作,防止蜂鳴器設備被打開多次
		return 0;            //使用非阻塞的方式去嘗試上鎖
	else
		return -EBUSY;
	
}


本文出自 “whylinux” 博客,謝絕轉載!

linux驅動開發之蜂鳴器驅動源碼分析(一)