字符設(shè)備驅(qū)動(dòng)-輪詢方式操控按鍵
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
②將open、read驅(qū)動(dòng)函數(shù)框架寫出
static int second_drv_open(struct inode *inode,struct file *file)
{
return 0;
}
③定義fileoperation
static struct file_operations second_drv_fops = {
.owner = THIS_MODULE, /* 這是一個(gè)宏,推向編譯模塊時(shí)自動(dòng)創(chuàng)建的__this_module變量 */
.open = second_drv_open,
.read = second_drv_read,
};
④
入口函數(shù)編寫(注冊驅(qū)動(dòng)程序)
int major;
static int second_drv_init(void)
{
major = register_chrdev(0,"second_drv",&second_drv_fops);
return 0;
}
出口函數(shù)編寫(卸載驅(qū)動(dòng)程序)
static int second_drv_exit(void)
{
unregister_chrdev(major,"second_drv");
return 0;
}
入口函數(shù)和出口函數(shù)也只是普通的函數(shù),何以達(dá)到入口出口的功能!需要通過module_來修飾。
module_init(second_drv_init);
module_exit(second_drv_exit);
⑤給sysfs提供更多信息,利用udev機(jī)制創(chuàng)建設(shè)備節(jié)點(diǎn)
Ⅰ定義兩個(gè)結(jié)構(gòu)體
static struct class *seconddrv_class;
static struct class_device *seconddrv_class_dev;
Ⅱ 入口函數(shù)中創(chuàng)建一個(gè)類=>類下面創(chuàng)建一個(gè)設(shè)備
int major;
static int second_drv_init(void)
{
major = register_chrdev(0,"second_drv",&second_drv_fops);
seconddrv_class = class_create(THIS_MODULE,"seconddrv");
seconddrv_class_dev = class_device_create(seconddrv_class,NULL,MKDEV(major,0),NULL,"buttons");
}
Ⅲ 出口函數(shù)中卸載類補(bǔ): mdev是udev的簡化版本,mdev應(yīng)用程序會(huì)被內(nèi)核來調(diào)用,根據(jù)⑤提供的信息來創(chuàng)建/dev/buttons 設(shè)備節(jié)點(diǎn)。
static int second_drv_exit(void)
{
unregister_chrdev(major,"second_drv");
class_device_unregister(seconddrv_class_dev);
class_destroy(seconddrv_class);
return 0;
}
⑥ MODULE_LICENSE("GPL");
測試:通過 “GPL” 指明 這是GNU General Public License的任意版本
修改Makefile
cp second_drv.ko /work/nfs_root/czg
insmod ./second_drv.ko
lsmod
cat /proc/devices
ls /dev/buttons -l
原理圖: 2440手冊:VA = ioremap(PA,size)
volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;
volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;
② 地址映射建立和解除
入口函數(shù)中建立:
static int second_drv_init(void)
{
major = register_chrdev(0,"second_drv",&second_drv_fops);
seconddrv_class = class_create(THIS_MODULE,"seconddrv");
seconddrv_class_dev = class_device_create(seconddrv_class,NULL,MKDEV(major,0),NULL,"buttons");
gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
gpfdat = gpfcon + 1;
gpgcon = (volatile unsigned long *)ioremap(0x56000060,16);
gpgdat = gpgcon + 1;
return 0;
}
出口函數(shù)中解除:
static int second_drv_exit(void)
{
unregister_chrdev(major,"second_drv");
class_device_unregister(seconddrv_class_dev);
class_destroy(seconddrv_class);
iounmap(gpfcon);
iounmap(gpgcon);
return 0;
}
③ open函數(shù)中配置引腳
static int second_drv_open(struct inode *inode,struct file *file)
{
/* 配置GPF0,2為輸入引腳 */
*gpfcon &= ~((0x3<<(0*2)) | (0x3<<(2*2)));
/* 配置GPG3為輸入引腳 */
*gpgcon &= ~((0x3<<(3*2)));
return 0;
}
④ read函數(shù)中返回引腳狀態(tài)
static ssize_t second_drv_read (struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
/* 返回4個(gè)引腳的電平狀態(tài) */
unsigned char key_vals[3];
int regval;
//看用戶需要讀取的空間,和這里的是否相同
if(count != sizeof(key_vals))
return -EINVAL;
/* 讀GPF0,2 */
regval = *gpfdat;
key_vals[0] = (regval & (1<<0)) ? 1 : 0;
key_vals[1] = (regval & (1<<2)) ? 1 : 0;
/* 讀GPF3 */
regval = *gpgdat;
key_vals[2] = (regval & (1<<3)) ? 1 : 0;
//將值返回給用戶程序
copy_to_user(buf,key_vals,sizeof(key_vals));
return sizeof(key_vals);
}
完整代碼:
Makefile
KERN_DIR = /work/system/linux-2.6.22.6
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += second_drv.o
驅(qū)動(dòng)程序:second_drv.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static struct class *seconddrv_class;
static struct class_device *seconddrv_class_dev;
volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;
volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;
static int second_drv_open(struct inode *inode,struct file *file)
{
/* 配置GPF0,2為輸入引腳 */
*gpfcon &= ~((0x3<<(0*2)) | (0x3<<(2*2)));
/* 配置GPG3為輸入引腳 */
*gpgcon &= ~((0x3<<(3*2)));
return 0;
}
static ssize_t second_drv_read (struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
/* 返回4個(gè)引腳的電平狀態(tài) */
unsigned char key_vals[3];
int regval;
//看用戶需要讀取的空間,和這里的是否相同
if(count != sizeof(key_vals))
return -EINVAL;
/* 讀GPF0,2 */
regval = *gpfdat;
key_vals[0] = (regval & (1<<0)) ? 1 : 0;
key_vals[1] = (regval & (1<<2)) ? 1 : 0;
/* 讀GPF3 */
regval = *gpgdat;
key_vals[2] = (regval & (1<<3)) ? 1 : 0;
//將值返回給用戶程序
copy_to_user(buf,key_vals,sizeof(key_vals));
return sizeof(key_vals);
}
static struct file_operations second_drv_fops = {
.owner = THIS_MODULE, /* 這是一個(gè)宏,推向編譯模塊時(shí)自動(dòng)創(chuàng)建的__this_module變量 */
.open = second_drv_open,
.read = second_drv_read,
};
int major;
static int second_drv_init(void)
{
major = register_chrdev(0,"second_drv",&second_drv_fops);
seconddrv_class = class_create(THIS_MODULE,"seconddrv");
seconddrv_class_dev = class_device_create(seconddrv_class,NULL,MKDEV(major,0),NULL,"buttons");
gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
gpfdat = gpfcon + 1;
gpgcon = (volatile unsigned long *)ioremap(0x56000060,16);
gpgdat = gpgcon + 1;
return 0;
}
static int second_drv_exit(void)
{
unregister_chrdev(major,"second_drv");
class_device_unregister(seconddrv_class_dev);
class_destroy(seconddrv_class);
iounmap(gpfcon);
iounmap(gpgcon);
return 0;
}
module_init(second_drv_init);
module_exit(second_drv_exit);
MODULE_LICENSE("GPL");
驅(qū)動(dòng)測試程序:seconddrvtest.c
#include
#include
#include
#include
/*
* firstdrvtest
*/
int main(int argc, char **argv)
{
int fd;
unsigned char key_vals[3];
int cnt = 0;
fd = open("/dev/buttons",O_RDWR);
if(fd < 0)
{
printf("can't open!n");
}
while(1)
{
read(fd,key_vals,sizeof(key_vals));
if(!key_vals[0] || !key_vals[1] || !key_vals[2])
{
printf("%04d key pressed: %d %d %d.n",cnt++,key_vals[0],key_vals[1],key_vals[2]);
}
}
return 0;
}
測試:
后臺(tái)運(yùn)行:
./seconddrvtest &
查看后臺(tái)程序:top
放在前臺(tái):fg
insmod second_drv.ko
lsmod
cat /proc/devices
ls /dev/buttons -l
./seconddrvtest