1.驅動程序結構
module_init(drv_init);//定義驅動初始化函數爲 static int __init drv_init(void){}(__init 表示註冊後該函數丟失)
module_exit(drv_exit);//定義驅動卸載函數爲 static void __exit third_drv_exit(void){}(__exit 表示驅動不可以卸載時函數丟失)
MODULE_LICENSE("GPL");
2.註冊驅動時使用函數
/*
major 爲返回的主設備號
第一個參數爲0時自動分配主設備號,不爲0時註冊的字符驅動爲該參數
第二個參數爲設備名稱
第三個參數爲結構體file_operations在頭文件 linux/fs.h中定義,用來存儲驅動內核模塊提供的對設備進行各種操作的函數的指針
mi = MINOR(Dev_id)用宏從設備號得出次設備號
ma = MAJOR(Dev_id)用宏重設備號得出主設備號
Dev_id = MKDEV(ma,mi)用宏重主次設備號得出設備號
static struct file_operations drv_fops = {
.owner = THIS_MODULE,
.open = drv_open,
.read =drv_read,
.release
= drv_close,
};
*/
major = register_chrdev(0, "drv", &drv_fops);
/*
創建一個設備類,需要包含頭文件 linux/device.h
static struct class *drv_class;
*/
drv_class = class_create(THIS_MODULE, "drv");
/*
創建設備
第一個參數爲設備類
第三個參數爲設備號
第五個參數爲設備名,/dev/buttons
static struct device *drv_class_dev;
*/
drv_class_dev = device_create(drv_class, NULL, MKDEV(major, 0), NULL, "buttons");
3.註冊卸載是使用函數
unregister_chrdev(major, "drv");
device_unregister(drv_class_dev);
class_destroy(drv_class);
4.常用頭文件
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <mach/irqs.h>
#include <asm/io.h>
#include <mach/regs-gpio.h>
#include <mach/gpio.h>
#include <mach/hardware.h>
#include <linux/wait.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
5.信號量
static DEFINE_SEMAPHORE(lock_r); // 定義賦值
down(&lock_r); //信號量減1
up(&lock_r); //信號量加1
6.管腳操作
/*需要包含頭文件 mach/regs-gpio.h 和 mach/gpio.h*/
s3c_gpio_cfgpin(S5PV210_GPJ2(0),S3C_GPIO_OUTPUT);//設置GPJ2_0爲輸出管腳
s3c_gpio_cfgpin(S5PV210_GPJ2(1),S3C_GPIO_INPUT);//設置GPJ2_1爲輸入管腳
gpio_set_value(S5PV210_GPJ2(0),0);//設置GPJ2_0輸出電平爲低電平
val = gpio_get_value(S5PV210_GPJ2(1));//讀取GPJ2_1電平,爲返回值
7.等待隊列
/*等待隊列需要包含頭文件 linux/wait.h */
static DECLARE_WAIT_QUEUE_HEAD(waitq_r);
//定義等待隊列
wait_event_interruptible(waitq_r, flag_wait);
//休眠等待喚醒,應該同時滿足兩個條件
//1.喚醒休眠的進程2.標誌flag_wait 爲真
wake_up_interruptible(&waitq_r); //喚醒休眠的進程
8.中斷的使用
/*
中斷註冊使用函數,需要包含頭文件 linux/interrupt.h linux/sched.h
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev_id)
irq 是申請的硬件中斷號 ,IRQ_EINT(16)表示中斷16,需要包含頭文件 linux/irq.h
handler 是一個回調函數,發生中斷時使用該函數。將參數dev_id傳入。static irqreturn_t func_irq(int irq, void *dev_id)
flags 爲中斷參數 IRQF_TRIGGER_FALLING下降沿有效 IRQF_TRIGGER_RISING上升沿有效 IRQF_TRIGGER_HIGH高電平有效 IRQF_TRIGGER_LOW低電平有效
name 名稱
*/
request_irq(IRQ_EINT(16), func_irq, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "S1", &pins_desc[0]);
/*
釋放中斷
void free_irq(unsigned int irq, void *dev_id)
irq 是申請的硬件中斷號 ,IRQ_EINT(16)表示中斷16,需要包含頭文件 linux/irq.h
dev_id 傳給free_irq的參數
*/
free_irq(IRQ_EINT(16), &pins_desc[0]);
9.poll機制和使用
/*
包含頭文件linux/poll.h
在驅動程序的poll要實現兩個功能1.調用poll_wait將進程加入某個休眠隊列(button_waitq),但不會立刻休眠。2.返回掩碼,確定有沒數據可讀寫
驅動程序可有使用 wake_up_interruptible(&button_waitq); 喚醒休眠進程,不然應用程序要等待相應時間後才喚醒。
應用程序調用int poll(struct pollfd fd[], nfds_t nfds, int timeout) -->>sys_poll -->>...-->>驅動程序poll
struct pollfd{
int fd; //文件描述符
short events; //請求的事件
short revents; //返回的事件
};
nfds:要監視的描述符的數目。
timeout:是一個用毫秒錶示的時間,是指定poll在返回前沒有接收事件時應該等待的時間。如果 它的值爲-1,poll就永遠都不會超時。如果整數值爲32個比特,那麼最大的超時週期大約是30分鐘。
*/
static unsigned drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_waitq, wait); // 不會立即休眠
if (ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
}
static struct file_operations poll_fops = {
.owner = THIS_MODULE;
.open =
.release =
.poll = drv_poll;
};
10.異步通信,信號
/*
在應用程序需要做以下事情
signal(SIGIO, my_signal_fun); //定義接受信號類型和處理函數 void my_signal_fun(int signum)
fcntl(fd, F_SETOWN, getpid()); //告訴驅動程序進程的PID
Oflags = fcntl(fd, F_GETFL); //先讀取標誌位
fcntl(fd, F_SETFL, Oflags | FASYNC);//在原先標誌位的基礎上增加FASYNC,這個時候驅動程序調用file_operations中的.fasync函數
在驅動程序中需要定義file_operations結構體中的.fasync函數以及發送信號
*/
/*
調用該函數時初始化button_async結構體(static struct fasync_struct *button_async;)
*/
static int drv_fasync (int fd, struct file *filp, int on)
{
return fasync_helper (fd, filp, on, &button_async);
}
static struct file_operations poll_fops = {
.owner = THIS_MODULE;
.open =
.release =
.fasync = drv_fasync ;
};
/*
發送信號給應用程序
void kill_fasync(struct fasync_struct **fp, int sig, int band)
*/
kill_fasync (&button_async, SIGIO, POLL_IN);
//發信號給應用程序
11.防止驅動程序重複加載
static DEFINE_SEMAPHORE(button_lock); //定義信號量
if (file->f_flags & O_NONBLOCK)
{
if (down_trylock(&button_lock))
return -EBUSY;
}
else
{
down(&button_lock);
}
在卸載驅動時釋放信號量
up(&button_lock);