R Field - 2020年7月
type: channel
link: http://ramlife.org/2020/07/
description: 通玄小论
items: 嵌入式驱动学习
items: buildroot 学习
items: 100ask 韦东山 驱动大全 同步互斥 LCD驱动 学习笔记
items: 嵌入式 qt 学习笔记
items: 内核驱动编译，开启 c99
items: source insight 搜索特定符号
items: 100ask 韦东山 第五篇 嵌入式 Linux 驱动开发基础知识 视频零散知识点
items: 100ask 韦东山 第五篇 嵌入式 Linux 驱动开发基础知识 学习笔记
items: 100ask 韦东山 第四篇 嵌入式 Linux 应用开发 视频零散知识点
items: 100ask 韦东山 第四篇 嵌入式 Linux 应用开发 学习笔记

嵌入式驱动学习
type: item
date: 2020-07-30T14:07:00+08:00
description: 100ask 平台上对嵌入式驱动进行学习。 [...]

buildroot 学习
type: item
date: 2020-07-28T10:17:00+08:00
description: buildroot 可以让简化固件生成。 [...]

100ask 韦东山 驱动大全 同步互斥 LCD驱动 学习笔记
type: item
date: 2020-07-25T20:30:00+08:00
description: 同步与互斥 atomic，适用于单个变量的操作，不可分割，无锁操作。 spinlock, 忙等，一直占用 cpu, 只能由于非常短的临界区，比较适合多核系统。 semaphore, 用于多个线程访问临界资源，阻塞睡眠。 mutex, 只有一个线程访问临界资源，阻塞睡眠，比 spinlock 更加适合单核系统。 LCD驱动 硬件原理 像素表示: 24位的 RGB888, 16位的 RGB565 和 RGB555。 MIPI-DBI, 8080接口：GRAM一般用 SRAM 和 lcd 控制器 与屏幕，封装在一起，是 LCM. MIPI-DPI, TFT RGB 接口: GRAM 一般用可能是外接内存芯片比如 DDR 和 SDRAM，LCD 控制器在 ARM 芯片内部，屏幕就是单纯的屏幕。 Framebuffer驱动程序框架 module_init(fbmem_init) -> fbmem_init -> ret = register_chrdev(FB_MAJOR, "fb", &fb_fops); -> static const struct file_operations fb_fops fb_open static int fb_open(struct inode *inode, struct file *file) __acquires(&info->lock) __releases(&info->lock) __acquires(&info->lock) __releases(&info->lock) 用于注解本函数在使用中会获取和释放锁，同样的还有 __must_hold(&lock) 这样的表示调用函数时必须已经持有锁。 这种注释可以用静态代码分析工具（如 Sparse）来检查。 获取 info，获取时，使用 atomic_inc(&fb_info->count) 增加资源使用量，释放时，使用 atomic_dec_and_test(&fb_info->count) 减少资源使用量，当资源使用量为 0 的时候，返回 true，就可以 destory 资源。 int fbidx = iminor(inode); struct fb_info *info; info = get_fb_info(fbidx); // static struct fb_info *get_fb_info(unsigned int idx) mutex_lock(&registration_lock); fb_info = registered_fb[idx]; if (fb_info) atomic_inc(&fb_info->count); mutex_unlock(&registration_lock); // static void put_fb_info(struct fb_info *fb_info) { if (!atomic_dec_and_test(&fb_info->count)) return; if (fb_info->fbops->fb_destroy) fb_info->fbops->fb_destroy(fb_info); } open if (info->fbops->fb_open) { res = info->fbops->fb_open(info,1); if (res) module_put(info->fbops->owner); } fb_write static ssize_t fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 获取 info struct fb_info *info = file_fb_info(file); write, 先调用 fb_write，然后使用 copy_from_user 从用户空间的 buf 中复制内容到 buffer 中，再使用 fb_memcpy_tofb 把 buffer 中的数据复制到 info 指向的显存地址中去。 if (info->fbops->fb_write) return info->fbops->fb_write(info, buf, count, ppos); // ... buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL); // ... dst = (u8 __iomem *) (info->screen_base + p); // ... while (count) { c = (count > PAGE_SIZE) ? PAGE_SI…

嵌入式 qt 学习笔记
type: item
date: 2020-07-20T11:29:00+08:00
description: 在嵌入式平台 rk3399 上面进行本地编译，会碰到 x86 上没有的问题。 [...]

内核驱动编译，开启 c99
type: item
date: 2020-07-15T14:15:00+08:00
description: c89，不能在 for 循环定义变量，很麻烦，所以决定用 c99。在 Makefile 里面的写法如下： ccflags-y := -std=gnu99 -Wno-declaration-after-statement 参考：https://blog.csdn.net/yangkunhenry/article/details/94703005

source insight 搜索特定符号
type: item
date: 2020-07-14T10:33:00+08:00
description: 当在 source insight 中想要搜索特定的符号，比如说 "struct file {"，需要在搜索的时候，把 search method 中的选择，改为 simple string，这样虽然慢一点，但是确实能搜索出来。 参考： https://zhidao.baidu.com/question/1859160069462990747.html

100ask 韦东山 第五篇 嵌入式 Linux 驱动开发基础知识 视频零散知识点
type: item
date: 2020-07-10T20:15:00+08:00
description: https://www.bilibili.com/video/BV14f4y1Q7ti 2 hello 驱动 可以参考 drivers\char\misc.c 这个是非常经典的字符设备驱动程序。 printk 打印信息，可以通过 dmesg 来查看。 9 总线设备驱动模型 platform_device 用于存放设备的资源相关. struct resource 在 include\linux\ioport.h 中, struct resource 给出明确的定义，需要按照这种形式去写。 struct resource { resource_size_t start; resource_size_t end; const char *name; unsigned long flags; unsigned long desc; struct resource *parent, *sibling, *child; }; 资源也可以放在 platform_device -> struct device dev -> void *platform_data 定义在 include\linux\device.h 中，这个 platform_data 那就是根据需要完全自己定义就行。 platform_driver 用于存放操作相关的函数。 bus 维持两个链表，一个是 device 链表，一个是 driver 链表，两个链表中有任何一个新增，就会扫描对方链表，查看是否有匹配，probe. drivers\base\platform.c 中的 struct bus_type platform_bus_type 定义了匹配用到 platform_match struct bus_type platform_bus_type = { .name = "platform", .dev_groups = platform_dev_groups, .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops, }; 具体的匹配，可以看 drivers\base\platform.c 中的 static int platform_match(struct device *dev, struct device_driver *drv) 可以看到匹配的优先级。 /* When driver_override is set, only bind to the matching driver */ if (pdev->driver_override) return !strcmp(pdev->driver_override, drv->name); /* Attempt an OF style match first */ if (of_driver_match_device(dev, drv)) return 1; /* Then try ACPI style match */ if (acpi_driver_match_device(dev, drv)) return 1; /* Then try to match against the id table */ if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL; /* fall-back to driver name match */ return (strcmp(pdev->name, drv->name) == 0); 11 设备树处理与使用 根节点 of_root 可以通过 device_node 中的 parent child 来遍历所有节点。 子节点能否转换为 platform_device, 取决于父节点的 comptible 是否是 "simple-bus","simple-mfd","isa","arm,amba-bus", 而且子节点也要有 comptible 当前设备树 /sys/firmware/devicetree/base, 搜索 ls -ld *100ask* cd 打开设备节点文件夹，里面有 comptible, name, pin，可以用 cat, hexdump 来读取数据 打开platform_device对应的文件夹 /sys/devices/platform 或者 /sys/devices/soc0 , 然后 ls -ld *100ask* 也可以找到对应的设备节点。文件夹下面的 of_node 指向设备树里面的节点。 加载驱动之后，自动 probe了，在platform_…

100ask 韦东山 第五篇 嵌入式 Linux 驱动开发基础知识 学习笔记
type: item
date: 2020-07-10T19:30:00+08:00
description: 第1章 Hello 驱动(不涉及硬件操作) 1.1 APP 打开的文件在内核中如何表示 使用 open 打开文件时，传入的 flags、mode 等参数， 读写文件时，文件的当前偏移地址，对文件的操作api，都在 include\linux\fs.h 的 struct file 中。 struct file { union { struct llist_node fu_llist; struct rcu_head fu_rcuhead; } f_u; struct path f_path; struct inode *f_inode; /* cached value */ const struct file_operations *f_op; /* * Protects f_ep_links, f_flags. * Must not be taken from IRQ context. */ spinlock_t f_lock; atomic_long_t f_count; unsigned int f_flags; fmode_t f_mode; struct mutex f_pos_lock; loff_t f_pos; ... } 1.2 打开字符设备节点时，内核中也有对应的 struct file // include\linux\fs.h struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ... long (*compat_ioctl) (struct file *, unsigned int, unsigned long); ... int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); ... } 1.3 请猜猜怎么编写驱动程序 谁来注册驱动程序啊？得有一个入口函数：安装驱动程序时，就会去调用这个入口函数 有入口函数就应该有出口函数：卸载驱动程序时，出口函数调用 unregister_chrdev 把 file_operations 结构体告诉内核：register_chrdev 确定主设备号，也可以让内核分配 定义自己的 file_operations 结构体 实现对应的 drv_open/drv_read/drv_write 等函数，填入 file_operations 结构体 其他完善：提供设备信息，自动创建设备节点：class_create, device_create 1.4 编写代码 1.4.1 写驱动程序 入口和出口空程序体 #include <linux/module.h> // init static int __init hello_init(void) { } // exit static void __exit hello_exit(void) { } // module_init(hello_init); module_exit(hello_exit); // license MODULE_LICENSE("GPL"); init 里面调用 drivers\base\core.c 中的 struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...) 。 其中 struct class, struct device 都是在 include/linux/device.h 里面定义的。 exit 里面调用相反的 void device_destroy(struct class *class, dev_t devt) #include <linux/device.h> // static int hello_major; static dev_t hello_devt; struct class * hello_class; struct device * hello_deivce; // in…

100ask 韦东山 第四篇 嵌入式 Linux 应用开发 视频零散知识点
type: item
date: 2020-07-08T21:10:00+08:00
description: https://www.bilibili.com/video/BV1kk4y117Tu 4【第四章】文件I/O 4_6_综合实验_处理表格 hexdump -C main.c 可以用来查看二进制数据，并且在右侧显示相应的文本对照。 4_7_文件IO系统调用内部机制 /proc/pid/fd 对应进程打开的 fd，默认有 0,1,2 三个，对应 stdin, stdout, stderr. 内部调用 fs/open.c SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode) -> do_sys_open(AT_FDCWD, filename, flags, mode) do_sys_open(AT_FDCWD, filename, flags, mode) -> fd = get_unused_fd_flags(flags) 获取未使用的文件描述符, struct file *f = do_filp_open(dfd, tmp, &op) 打开文件结构体 , fd_install(fd, f) 把文件结构体放入文件描述符数组中。 fs/namei.c struct file *do_filp_open(int dfd, struct filename *pathname, const struct open_flags *op) fs/file.c int get_unused_fd_flags(unsigned flags) -> __alloc_fd(current->files, 0, rlimit(RLIMIT_NOFILE), flags) -> fdt = files_fdtable(files) -> fd = find_next_fd(fdt, fd), files->next_fd = fd + 1 fs/file.c void fd_install(unsigned int fd, struct file *file) -> __fd_install(current->files, fd, file) -> fdt = rcu_dereference_sched(files->fdt), rcu_assign_pointer(fdt->fd[fd], file) current 来源于 include/asm-generic #define current get_current() -> #define get_current() (current_thread_info()->task) -> thread_info.h 里面的 struct thread_info -> sched.h struct task_struct *task -> fdtable.h struct files_struct *files -> struct fdtable fdtab 4_8_dup函数的使用 int dup(int old); , 返回一个指向 old 的新的最小未使用 fd. int dup2(int old,int new); , 先检查 new 有没有被打开，如果打开，就先关闭。 然后让 new 指向 old. 第7章 输入系统应用编程 7.2 现场编程获取输入设备信息 drivers\input\evdev.c, static long evdev_do_ioctl(struct file *file, unsigned int cmd, void __user *p, int compat_mode) 内核中 ioctl 对应的函数 evdev_do_ioctl 中 switch 判断分支 EVIOCGVERSION 在 include\uapi\linux\input.h 中 #define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */ #define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */ #define EVIOCGREP _IOR('E', 0x03, unsigned int[2]) /* get repeat settings */ #define EVIOCSREP _IOW('E', 0x03, unsigned int[2]) /* set repeat settings */ // #define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + (ev), len) /* get…

100ask 韦东山 第四篇 嵌入式 Linux 应用开发 学习笔记
type: item
date: 2020-07-08T20:05:00+08:00
description: 2 编译器的使用 2.1 gcc 编译过程 使用 gcc -o hello hello.c -v 之后，会打印出详细的编译过程，去除不重要的信息后，最后剩余的就是三步，分别是：编译为 .s 汇编文件，汇编为 .o 机器码，链接为最终的可执行文件。具体三条信息如下： /usr/lib/gcc/x86_64-linux-gnu/7/cc1 -quiet -v -imultiarch x86_64-linux-gnu hello.c -quiet -dumpbase hello.c -mtune=generic -march=x86-64 -auxbase hello -version -fstack-protector-strong -Wformat -Wformat-security -o /tmp/ccX6eBn7.s as -v --64 -o /tmp/cclwBtJD.o /tmp/ccX6eBn7.s /usr/lib/gcc/x86_64-linux-gnu/7/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/7/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper -plugin-opt=-fresolution=/tmp/cc7xr459.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o hello /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/7 -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/7/../../.. /tmp/cclwBtJD.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o 进一步去除编译选项后，命令主题如下: # 编译 /usr/lib/gcc/x86_64-linux-gnu/7/cc1 hello.c -o /tmp/ccX6eBn7.s # 汇编 as -o /tmp/cclwBtJD.o /tmp/ccX6eBn7.s # 链接 /usr/lib/gcc/x86_64-linux-gnu/7/collect2 -o hello /tmp/cclwBtJD.o 手动分步编译: gcc -E -o hello.i hello.c gcc -S -o hello.s hello.i gcc -c -o hello.o hello.s gcc -o hello hello.o 2.1.2 编译多个文件 一起编译 gcc -o test main.c sub.c 分开编译，统一链接 gcc -c -o main.o main.c gcc -c -o sub.o…