Linux 文件系统
根目录(/)是 Linux 文件系统的顶层目录。所有的文件和目录都从这里开始,形成一个树形结构。根目录下的每个子目录都有其特定的功能和用途。
根目录(/)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| /bin:存放基本用户命令。 ls、cp、mv、rm、cat、echo /sbin:存放系统管理命令。 ifconfig:配置网络接口 reboot:重启 shutdown:关闭系统 fdisk:磁盘分区工具 mkfs:创建文件系统 /etc:存放系统配置文件。 /etc/passwd:用户账户信息文件 /etc/fstab:文件系统挂载表 /etc/hosts:主机名和IP地址对应表 /etc/hostname:定义系统的主机名 /etc/network/interfaces:网络接口配置文件 /dev:存放设备文件。 /sda:第一个SCSI硬盘 /tty:终端设备 /null:空设备 /random:随机数生成器 /tmp:存放临时文件。 /home:存放用户主目录。 /Documents:用户的文档目录。 ~/Downloads:用户的下载目录。 ~/Pictures:用户的图片目录。 ~/.bashrc:Bash Shell 配置文件。 ~/.profile:用户的环境设置文件。 /var:存放可变数据文件。 /var/log:系统日志文件目录。 常见的日志文件有 /var/log/syslog(系统日志)、/var/log/auth.log(认证日志)、/var/log/kern.log(内核日志)等。 /var/mail:用户邮件存放目录。 /var/spool:队列目录,用于存放打印任务、邮件队列等。 /var/cache:应用程序缓存文件。 /var/www:Web 服务器的根目录,存放网站文件 /usr:存放用户级应用程序和文件。 /usr/bin:用户级命令的二进制文件。常见的命令有 gcc(GNU 编译器)、perl(Perl 解释器)等。 /usr/sbin:系统管理命令的二进制文件。与 /sbin 类似,但这些命令不是启动时必须的。 /usr/lib:库文件目录,存放应用程序和系统所需的共享库。 /usr/share:共享数据目录,存放不特定于某个用户或系统的共享数据,如文档、图标、声音等。 /usr/local:本地安装的软件和文件。用户可以在不影响系统其他部分的情况下安装和管理软件。 /opt:存放附加软件包。 第三方软件或自定义应用程序会安装在此目录下。每个软件通常会在 /opt 下有一个独立的子目录 /mnt:用于临时挂载文件系统。 系统管理员可以将外部存储设备(如 USB 驱动器、网络文件系统等)挂载到 /mnt 下的某个子目录中 /media:用于自动挂载的可移动设备。 如光盘、U 盘等。当这些设备插入时,系统会自动将其挂载到 /media 下的一个子目录中 /boot:存放启动加载程序和内核文件。 系统启动时,启动加载程序(如 GRUB)会从这里加载内核和其他必要文件 vmlinuz:压缩的 Linux 内核镜像文件。 initrd.img:初始 RAM 盘,用于启动时加载必要的驱动程序和文件系统。 grub:GRUB 启动加载程序的配置文件和模块。 /lib:存放系统库文件和内核模块。 /proc:存放系统内核和进程信息。 /proc/cpuinfo:显示 CPU 的信息,包括型号、速度和核心数。 /proc/meminfo:显示内存使用情况,包括总内存、可用内存和缓存。 /proc/uptime:显示系统的运行时间和空闲时间。 /proc/[pid]/:每个运行中的进程都有一个以其 PID(进程标识符)命名的子目录,包含该进程的详细 信息,如状态、内存映射、打开的文件等 /sys:存放系统设备和内核信息。 /sys 目录主要用于提供内核与用户空间之间的接口,允许用户查看和配置硬件设备 /sys/class/:分类显示不同类型的设备,如网络设备(/sys/class/net)、块设备 (/sys/class/block)等。 /sys/devices/:显示系统中的所有设备,以设备树的形式组织。 /sys/module/:显示已加载的内核模块及其参数。 /root:超级用户的主目录。
|
//
字符设备模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| static ssize_t test_open(struct inode *inode,struct file *filp) { return 0; } static ssize_t test_read(struct file *filp, const char __user *buf, size_t cnt, loff_t *oftt) { return 0; } static ssize_t test_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt { return 0; } static int test_release(struct inode *inode,struct file *filp) { return 0; } static struct file_operations test_fops = { .owner = THIS_MODULE, .open = test_open, .read = test_read, .write = test_write, .release = test_release, };
static int __int test_init(void) { int dev=0; dev = register_chrdev(200,"test",&test_fops); return 0; }
static void __exit test_exit(void) { unregister_chrdev(200,"test"); } module_init(test_init); module_exit(test_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("draon");
|
命令
1 2 3 4
| insmod insmod drv.ko rmmod rmmod drv.ko
|
字符设备的注册与注销
1 2 3 4 5 6 7
| static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops) static inline void unregister_chrdev(unsigned int major, const char *name)
|
查看当前已经使用的设备号:cat /proc/devices su权限
实验程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
| #include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h>
#define CHRDEVBASE_MAJOR 200 #define CHRDEVBASE_NAME "chrdevbase"
static char readbuf[100]; static char writebuf[100]; static char kerneldata[] = {"kernel data!"};
static int chrdevbase_open(struct inode *inode, struct file *filp) {
return 0; }
static ssize_t chrdevbase_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt) { int retvalue = 0;
memcpy(readbuf, kerneldata, sizeof(kerneldata)); retvalue = copy_to_user(buf, readbuf, cnt); if(retvalue == 0){ printk("kernel senddata ok!\r\n"); }else{ printk("kernel senddata failed!\r\n"); }
return 0; }
static ssize_t chrdevbase_write(struct file *filp,const char __user *buf,size_t cnt, loff_t *offt) { int retvalue = 0;
retvalue = copy_from_user(writebuf, buf, cnt); if(retvalue == 0){ printk("kernel recevdata:%s\r\n", writebuf); }else{ printk("kernel recevdata failed!\r\n"); }
return 0; }
static int chrdevbase_release(struct inode *inode,struct file *filp) {
return 0; }
static struct file_operations chrdevbase_fops = { .owner = THIS_MODULE, .open = chrdevbase_open, .read = chrdevbase_read, .write = chrdevbase_write, .release = chrdevbase_release, };
static int __init chrdevbase_init(void) { int retvalue = 0;
retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME,&chrdevbase_fops); if(retvalue < 0){ printk("chrdevbase driver register failed\r\n"); } printk("chrdevbase_init()\r\n"); return 0; }
static void __exit chrdevbase_exit(void) {
unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME); printk("chrdevbase_exit()\r\n"); }
module_init(chrdevbase_init); module_exit(chrdevbase_exit);
MODULE_LICENSE("GPL"); MODULE_AUTHOR("Dragon");
|
prink函数:内核态运行,8个级别,0最高,7最低
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| #include "stdio.h" #include "unistd.h" #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "stdlib.h" #include "string.h"
static char usrdata[] = {"usr data!"};
int main(int argc, char *argv[]) { int fd, retvalue; char *filename; char readbuf[100], writebuf[100];
if(argc != 3){ printf("Error Usage!\r\n"); return -1; }
filename = argv[1];
fd = open(filename, O_RDWR); if(fd < 0){ printf("Can't open file %s\r\n", filename); return -1; }
if(atoi(argv[2]) == 1){ retvalue = read(fd, readbuf, 50); if(retvalue < 0){ printf("read file %s failed!\r\n", filename); }else{
printf("read data:%s\r\n",readbuf); } } if(atoi(argv[2]) == 2){
memcpy(writebuf, usrdata, sizeof(usrdata)); retvalue = write(fd, writebuf, 50); if(retvalue < 0){ printf("write file %s failed!\r\n", filename); } }
retvalue = close(fd); if(retvalue < 0){ printf("Can't close file %s\r\n", filename); return -1; }
return 0; }
|
Makefile
1 2 3 4 5 6 7 8 9 10
| KERNELDIR := CURRENT_PATH := $(shell pwd) obj-m :=
build:kernel_modules
kernel_modules: $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules clean: $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
|
make
测试程序需要交叉编译链
LED驱动
配置硬件寄存器
LED———GPIO1_IO03引脚 对应的地址:0x020E0068
地址映射
MMU:内存管理单元
1.完成虚拟空间到物理空间的映射
2.内存保护,设计存储器的访问权限,设置虚拟空间的缓冲特性 虚拟地址和物理地址
1 2 3 4 5 6 7
| void __iomem * __arm_ioremap(phys_addr_t phys_addr, size_t size,unsigned int mtype) { return arch_ioremap_caller(phys_addr, size, mtype,__builtin_return_address(0)); }
|
ioremap函数:将物理地址映射到内存空间,以指针形式直接访问这些地址
iounmap函数:
!!!Linux 内核不建议 这么做,而是推荐使用一组操作函数来对映射后的内存进行读写操作
1 2 3 4
| //读操作函数 readb(8bit)、readw(16bit)、readl(32bit) //写操作函数 writeb,writew、writel
|
1.使能时钟,2.多路复用为GPIO_IO03,3.设置IO属性,4.设置输出/输入功能
设备树
字符串、32位无符号整数、字符串列表
属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 属性: compatible 兼容性属性,将设备和驱动绑定起来----必须 厂商,模块对应的驱动名字 model 描述设备模块信息 status:与设备状态有关 okay:可操作 disabaled:不可操作,但是未来可以变为可操作的,比如热插拔 fail:不可操作,检测到错误 fail-sss:与fail相同,后面sss表示检测到的错误内容 #address-cells、#size-cells:可以用在任何拥有子节点的设备中,用于描述子节点地址信息 address-cells决定reg 地址占用字长 size-cells:子节reg点长度信息所占字长 reg:描述设备地址空间资源信息,键值对 ranges:地址映射 转换表 child-bus-address:子总线地址空间的物理地址 parent-bus-address:父总线地址空间物理地址 length:子地址空间长度 name:记录节点名字,弃用 device_type:只用于cpu节点或者memory节点
|
一般驱动程序文件都会有一个 OF 匹配表,此 OF 匹配表保存着一些 compatible 值,如果设 备节点的 compatible 属性值和 OF 匹配表中的任何一个值相等,那么就表示设备可以使用这个 驱动
设备树下的LED驱动
cat /proc/device-tree/ 查看设备树节点
pinctrl和gpio子系统
princtrl子系统:用于引脚的配置;复用为那种引脚(gpio,i2c模式等)
gpio子系统:用于引脚的控制。配置输出,输出高低电平
当princtrl子系统配置为gpio模式后,才能用gpio子系统控制引脚
pinctrl子系统
pinctrl子系统源码:drivers/pinctrl
主要工作内容如下:
1.获取设备树中pin信息
2.根据获取的pin信息来设置pin的复用功能
3.根据获取的pin信息设置电气特性,上/下拉、速度、驱动能力
对于开发者而言,著需要设备树里面设置好某个pin的相关属性,其他初始化由pinctrl完成。
一个设备树里配置基础信息
另一个设备树配置pinctrl具体信息
1 2 3 4
| pinctrl_test: testgrp { fsl,pins = < MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config /*config 是具体设置值*/ >;
|
gpio子系统
gpio子系统:初始化gpio并且提供相应的API函数,比如设置GPIO为输入输出,读取GPIO的值
gpio子系统API函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 1.gpio_request函数 原型:int gpio_request(unsigned gpio, const char *label) 申请一个GPIO管脚 2.gpio_free函数 原型:void gpio_free(unsigned gpio) 不用了,就释放 3.gpio_direction_input函数 原型:int gpio_direction_input(unsigned gpio) 设置GPIO为输入 4.gpio_direction_output函数 原型:int gpio_direction_output(unsigned gpio, int value) 设置GPIO为输出 5.gpio_get_value函数 原型:#define gpio_get_value __gpio_get_value int __gpio_get_value(unsigned gpio) 获取GPIO的值 6.gpio_set_value函数 原型:#define gpio_set_value __gpio_set_value void __gpio_set_value(unsigned gpio, int value) 设置GPIO的值
|
添加子节点 gpio模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| test{
};
test{ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_test>; };
test{ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_test>; gpio = <&gpio1 0 GPIO_ACTIVE_LOW> };
|
与gpio相关的OF函数
1 2 3 4 5 6
| 1.of_gpio_named_count() 获取设备树某个属性里面定义了几个GPIO信息 2.of_gpio_count() 3.of_get_named_gpio() 获取GPIO编号
|

platform思想:
驱动的分层
总线bus,驱动driver,设备device
文件位置:include/linux/device.h
match函数完成设备与驱动之间如何匹配
参数1:dev设备
参数2:drv驱动
1 2 3 4 5
| 驱动和设备的四种匹配方式 1.OF类型匹配 2.ACPI匹配方式 3.id_table匹配 4.如果第三种id_table匹配不上,直接比较驱动和设备的name字段,判断是否相等
|
1 2 3 4 5 6 7 8 9 10
| struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t state); int (*resume)(struct platform_device *); struct device_driver driver; const struct platform_device_id *id_table; bool prevent_deferred_probe; };
|
注:设备树通过设备节点的compatible属性值和of_match_table中每个项目的compatible成员进行比较,如有相等表示设备和驱动匹配成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| struct test_dev{ struct cdev cdev; };
struct test_dev testdev;
static int test_open(struct inode *inode,struct file *filp) { return 0; }
static ssize_t test_write(struct file *filp,const char __user *buf,size_t cnt,loff_t *offt) { return 0; }
static struct file_operations test_fops = { .owner = THIS_MODULE, .open = test_open, .write = test_write, };
static int test_probe(struct platform_device *dev) { cdev_init(&testdev.cdev,&test_fops); return 0; }
static int test_rmove(struct platform_device *dev) { cdev_del(&testdev.cdev); return 0; }
staic const struct of_device_id test_of_match[] = { {.compatible = "test-gpio",.data = "data1"}, {}, }
static struct platform_driver test_driver = { .driver = { .name = "test", .of_match_table = test_of_match, }, .probe = test_probe, .remove = test_remove, };
static int __init testdriver_init(void) { retunrn platform_driver_register(&test_driver); }
static void __exit testdriver_exit(void) { platform_driver_unregister(&test_driver); }
module_init(testdriver_init); module_exit(testdriver_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("XXX");
|
platform设备
1 2 3 4 5 6 7 8 9 10 11
| struct paltform_device { const char *name; int id; bool id_auto; struct device dev; u32 num_resources; struct resource *resource; const struct platform_device_id *id_entry; struct mfd_cell *mfd_cell; struct pdev_archdata archdata; };
|
资源
1 2 3 4 5 6 7 8
| struct resource { resource_size_t start; resource_size_t end; const char *name; unsigned long flags; struct resource *parent, *sibling, *child; };
|
步骤:
1.设备树创建设备节点
2.编写platform驱动 注意兼容属性
3.编写platform驱动
LCD驱动
编写流程:
1 2 3 4
| 1.初始化eLCDIF控制器,重点:LCD屏幕宽、高、hspw、hfp、vspw、vbp和vfp等信息 2.初始化LCD像素时钟 3.设置RGBLCD显存 4.应用程序直接直接通过显存来操作,实现在LCD上显示字符、图片等信息
|
framebuffer:帧缓存,简称fb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| static const struct file_operations fb_fops = { .owner = THIS_MODULE, .read = fb_read, .write = fb_write, .unlocked_ioctl = fb_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = fb_compat_ioctl, #endif .mmap = fb_mmap, .open = fb_open, .release = fb_release, #ifdef HAVE_ARCH_FB_UNMAPPED_AREA .get_unmapped_area = get_fb_unmapped_area, #endif #ifdef CONFIG_FB_DEFERRED_IO .fsync = fb_deferred_io_fsync, #endif .llseek = default_llseek, };
|
1.fb_info结构体成员变量很多,重点关注var、fix、fbops、screen_base、screen_size和pseudo_palette.
maxsfb_probe函数主要工作内容:
1.申请fb_info3
2.初始化fb_info结构体中各个成员变量
3.初始化eLCDIF控制器
4.使用register_framebuffer函数向内核注册初始化好的fb_info
int register_framebuffer(struct fb_info *fb_info)
驱动程序编写:
1.LCD所使用的IO配置
2.LCD屏幕节点修改,修改相应的属性值,换成我们使用的LCD屏幕参数
3.LCD背光节点信息修改,根据实际所使用的背光IO来修改相应的设备节点信息
LCD屏幕参数节点信息修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| &lcdif { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_lcdif_dat &pinctrl_lcdif_ctrl &pinctrl_lcdif_reset>; display = <&display0>; status = "okay";
display0: display { bits-per-pixel = <16>; bus-width = <24>;
display-timings { native-mode = <&timing0>; timing0: timing0 { clock-frequency = <9200000>; hactive = <480>; vactive = <272>; hfront-porch = <8>; hback-porch = <4>; hsync-len = <41>; vback-porch = <2>; vfront-porch = <4>; vsync-len = <10>;
hsync-active = <0>; vsync-active = <0>; de-active = <1>; pixelclk-active = <0>; }; }; }; };
|