嵌入式Linux驱动笔记(一)------第一个LED驱动程序

来源:互联网 发布:黑暗之光披风升阶数据 编辑:程序博客网 时间:2024/05/21 18:40

你好!这里是风筝的博客,

欢迎和我一起交流。



//应用程序:#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;elseval = 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 就会看到刚刚加载上的驱动了。
在应用程序里,输入./leddrvtest on后,就会调用write函数,操作led灯了。 








阅读全文
0 0
原创粉丝点击