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驅動開發之蜂鳴器驅動源碼分析(一)