你好!这里是风筝的博客,
欢迎和我一起交流。
//应用程序: #include <syspes.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> /* leddrvtest on * leddrvtest off */ int main(int argc, char **argv) { int fd; int val = 1; fd = open("/dev/led", O_RDWR); if (fd < 0) { printf("can't open!\n"); } if (argc != 2) { printf("Usage :\n"); printf("%s <on|off>\n", argv[0]); return 0; } /*如果输入了leddrvtest on*/ if (strcmp(argv[1], "on") == 0) val = 1; else val = 0; /*写val*/ write(fd, &val, 4); return 0; }
当我们打开一个文件的时候,需要获得文件的文件描述符,其实就是文件数组下标,一般是通过函数open函数来完成.
fd=open(文件名,打开方式);
例如:fd = open("/dev/led", O_RDWR | O_NONBLOCK);
用来打开一个设备,他返回的是一个整型变量,如果这个值等于-1,说明打开文件出现错误,如果为大于0的值,那么这个值代表的就是文件描述符。
其中:
O_RDONLY 以只读方式打开文件。
O_WRONLY 以只读方式打开文件。
O_RDWR 以只读方式打开文件。
上面三个参数只能选择一个,下面的可以合理的任意组合:
O_APPEND 强制每次写(write)时都加到文件的尾端。
O_CREAT 打开文件,若此文件不存在则创建它。使用此选择项时,需同时说明第三个参数mode,用其说明该新文件的存取许可权位。
O_EXCL 如果同时指定了O_CREAT,而且文件已经存在,则强制open()失败。这可测试一个文件是否存在,如果不存在则创建此文件成为一个原子操作。
O_TRUNC 如果此文件存在,而且为只读或只写成功打开,则将其长度截短为0。
O_NOCTTY 如果p a t h n a m e指的是终端设备,则不将此设备分配作为此进程的控制终端。
O_NONBLOCK 如果p a t h n a m e指的是一个F I F O、一个块特殊文件或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续的I / O操作(open\read\write)设置非阻塞方式。
O_SYNC 使每次w r i t e都等到物理I / O操作完成。
//驱动程序: #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 <linux/of.h> #include <linux/of_address.h> #include <linux/of_device.h> #include <linux/of_platform.h> static struct class *leddrv_class; static struct class_device *leddrv_class_dev; volatile unsigned long *gpfcon = NULL; volatile unsigned long *gpfdat = NULL; static int led_drv_open(struct inode *inode, struct file *file) { /* 配置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 led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { int val; 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 led_drv_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = led_drv_open, .write = led_drv_write, }; int major; /*insmod 时调用*/ static int led_drv_init(void) { major = register_chrdev(0, "led_drv", &led_drv_fops); // 注册, 告诉内核 leddrv_class = class_create(THIS_MODULE, "leddrv"); leddrv_class_dev = device_create(leddrv_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */ gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); gpfdat = gpfcon + 1; return 0; } /*rmmod 时调用*/ static void led_drv_exit(void) { unregister_chrdev(major, "led_drv"); // 卸载 device_destroy(leddrv_class,MKDEV(major, 0)); class_destroy(leddrv_class); iounmap(gpfcon); } module_init(led_drv_init); module_exit(led_drv_exit); MODULE_LICENSE("GPL");
其中:
major = register_chrdev(0, "led_drv", &led_drv_fops);
向内核注册了一个字符设备。
第一个参数是主设备号,0代表内核会动态分配。第二个参数是设备的名字,第三个参数是文件操作指针。
完成注册后,在/proc/devices中可以看到我们的设备。
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
将一个IO地址空间映射到内核的虚拟地址空间上去,便于访问。
Ioremap函数中,0x56000050为要映射的起始的IO地址;16为要映射空间的大小;这些都视自己芯片手册而定。
MODULE_LICENSE("GPL");
模块的许可证声明。
从2.4.10版本内核开始,模块必须通过MODULE_LICENSE宏声明此模块的许可证,否则在加载此模块时,会收到内核被污染 “kernel tainted” 的警告。
copy_from_user(&val, buf, count);
从用户空间拷贝数据到内核空间,失败返回没有被拷贝的字节数,成功返回0。
由于内核空间与用户空间的内存不能直接互访,因此借助函数copy_to_user()完成用户空间到内核空间的复制,函数copy_from_user()完成内核空间到用户空间的复制。
makefile:
KERN_DIR = /work/system/linux-4.8.17
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += led_drv.o
make编译后即可生成.ko文件.
这样,insmod led.ko驱动后,输入 lsmod 就会看到刚刚加载上的驱动了。
major = register_chrdev(0, "led_drv", &led_drv_fops);
向内核注册了一个字符设备。
第一个参数是主设备号,0代表内核会动态分配。第二个参数是设备的名字,第三个参数是文件操作指针。
完成注册后,在/proc/devices中可以看到我们的设备。
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
将一个IO地址空间映射到内核的虚拟地址空间上去,便于访问。
Ioremap函数中,0x56000050为要映射的起始的IO地址;16为要映射空间的大小;这些都视自己芯片手册而定。
MODULE_LICENSE("GPL");
模块的许可证声明。
从2.4.10版本内核开始,模块必须通过MODULE_LICENSE宏声明此模块的许可证,否则在加载此模块时,会收到内核被污染 “kernel tainted” 的警告。
copy_from_user(&val, buf, count);
从用户空间拷贝数据到内核空间,失败返回没有被拷贝的字节数,成功返回0。
由于内核空间与用户空间的内存不能直接互访,因此借助函数copy_to_user()完成用户空间到内核空间的复制,函数copy_from_user()完成内核空间到用户空间的复制。
makefile:
KERN_DIR = /work/system/linux-4.8.17
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += led_drv.o
make编译后即可生成.ko文件.
这样,insmod led.ko驱动后,输入 lsmod 就会看到刚刚加载上的驱动了。
在应用程序里,输入./leddrvtest on后,就会调用write函数,操作led灯了。