led驅動_自動建立裝置節點
阿新 • • 發佈:2019-01-04
原文連結: http://liu1227787871.blog.163.com/blog/static/20536319720128901736417/
上篇文章大概說明了驅動編寫的格式, 但是我們每次都要去先insmod驅動程式, 然後cat /proc/devices 來檢視主裝置號, 然後 mknod 建立裝置節點. 這一篇實現自動建立裝置節點功能.
Q: struct class *class_create(struct module *owner, const char *name)
A: owner 表示類的所有者是誰, 一般都是 THIS_MODULE, 即當前模組. name表示類名
Q: class_device *class_device_create(struct class *cls, struct class_device *parent, dev_t devt, struct device *device, const char *fmt, ...);
Q: MKDEV(MAJOR, MINOR);
A: 獲取主/次裝置號
注意在exit時要取消註冊 / 銷燬類 / 取消記憶體對映
class_device_unregister();
class_destroy();
unioremap();
驅動程式碼:
本程式使用copy_from_user來獲取應用層的資料, 通過該資料來開啟/關閉 led
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> static struct class *firstdrv_class; static struct class_device *firstdrv_class_dev; volatile unsigned long *gpfcon = NULL; volatile unsigned long *gpfdat = NULL; static int first_drv_open(struct inode *inode, struct file *file) { //printk("first_drv_open\n"); /* 配置GPF4,5,6為輸出 */ *gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2))); *gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2))); return 0; } static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { int val; //printk("first_drv_write\n"); copy_from_user(&val, buf, count); // copy_to_user(); if (val == 1) { // 點燈 *gpfdat &= ~((1<<4) | (1<<5) | (1<<6)); } else { // 滅燈 *gpfdat |= (1<<4) | (1<<5) | (1<<6); } return 0; } static struct file_operations first_drv_fops = { .owner = THIS_MODULE, /* 這是一個巨集,推向編譯模組時自動建立的__this_module變數 */ .open = first_drv_open, .write = first_drv_write, }; int major; static int first_drv_init(void) { major = register_chrdev(0, "first_drv", &first_drv_fops); // 註冊, 告訴核心 <span style="white-space:pre"> </span>firstdrv_class = class_create(THIS_MODULE, "firstdrv");//建立類 firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */ gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); gpfdat = gpfcon + 1; return 0; } static void first_drv_exit(void) { unregister_chrdev(major, "first_drv"); // 解除安裝 <span style="white-space:pre"> </span>class_device_unregister(firstdrv_class_dev); class_destroy(firstdrv_class); iounmap(gpfcon); } module_init(first_drv_init); module_exit(first_drv_exit); MODULE_LICENSE("GPL");
驅動程式的makefile:
KERN_DIR = /home/cxh/arm/kernel/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 += first_drv.o
測試程式 test.c
#include <stdio.h>
#include <fcntl.h>
#include <sys/type.h>
#include <sys/stat.h>
int main(int argc, char **argv)
{
int fd, val = 0;
fd = open("dev/xyz", O_RDWR);
if (fd < 0)
{
printf ("can't open /dev/xyz\n");
return fd;
}
if (argc != 2)
{
printf ("usage: led <off | on>\n");
}
if (strcmp(argc[1], "on") == 0)
{
val = 1;
}
else
{
val = 0;
}
write(fd, &val, 4);
return 0;
}
測試方法及現象:
將驅動make之後的 first.ko拷貝到 /usr/src/rootnfs/ 下, 鍵入 insmod, 將test.c 編譯之後拷貝到 /usr/src/rootnfs/ 下執行. led on 或者 led off 可以開啟或者關閉led