嵌入式软件工程师&&太极拳
全部博文(548)
分类: LINUX
2011-02-20 20:23:58
Kernel_API: 申请: region: struct: init: register_chrdev_region alloc_chrdev_region exit: unregister_chrdev_region use: note: 主要干的事就是在/proc里可以看到 注册: 注册干的事就是根据设备号,绑定file_operations old: register_chrdev: struct: struct file_operations, struct file, struct node init: register_chrdev exit: unregister_chrdev use: note: 2.6内核以前用的,现在不推荐用 new: register_chrdev: struct: struct cdev init: cdev_init cdev_add exit: cdev_del use: note: 2.6内核新出的,cdev_add要放到最后,因为cdev_add将程序投入到内核,立即可以被使用,所以没有初始化好就投入用 file_operations 必须单独初始化 实现系统调用: open, release open和release可以不实现,因为内核已经初始化好了,系统调用仅仅是应用程序访问内核的途径 使用open,release的情况有要使用private_data私有变量,还有request_irq,因为中断号是珍贵资源,所以要用的时候才注册 read, write copy_to_user/copy_from_user 带参数检查的拷贝函数(地址检查): asm/uaccess.h llseek + pos 在用seek的时候注册要加当前位置 error: linux/errno.h 错误号 容错处理goto用法 note: 配对,泄露问题 private_data: container_of 结构体继承 llseek: note: read write的时候注册要加当前位置 ioctl: ioctl-number 传参(unsigned long ->结构体指针) switch 要对应default ioctl_cmd.h (在两个地方存在,在用户和内核两份) 阻塞I/O: struct: wait_queue_head_t init: init_waitqueue_head exit: use: read: wait_event_interruptible write: wake_up note: 睡眠机制 轮询: linux/poll.h (select / poll) struct: struct poll_table_struct init: poll(file_operations) exit: use: poll_wait note: POLLIN/POLLOUT (read/write) 相对给FD_ISSET看的 select 在应用程序: FD_SET/FD_ISSET 一定要放在监视循环里 异步通知: struct: struct fasync_struct fasync: fasync_helper 将文件描述符放到内核信号响应队列 exit: 调用自己的fasync函数: test_fasync(-1, filp, 0); use: kill_fasync note: signal,相当于软件中的中断,在应用时用sigaction 应用: sigaction, 干的事注册,设置属性和标志 并发和竞态(互斥): 单核: 内核编程注意事项: 少用浮点 stack 4K左右 栈小 地址对齐,地址的非法性 要遵守GNUC C标准 并发竞态 why: 调用你的人,有可能是并发的 kernel中断会产生并发 smp,多核 进程与进程之间: preempt 进程和中断之间 local_irq 中断和中断之间 可重入性好,会保护现场 local_irq_save; local_irq_restore; 多核: CPU自旋: _raw_write_lock spin_lock: 自旋锁 struct: struct spinlock_t init: spin_lock_init exit: use: spin_lock/spin_unlock note: 不能睡眠(引起睡眠函数:copy_from_user sleep, kmalloc) semaphore: struct : struct semaphore init: init_MUTEX exit: use: down/up/down_interruptible note: if (down_ifterruptible(struct semaphore *sem)) return -ERESTARTSYS; down_interruptible要加判断,可被信号打断 分配内存: linux/slab.h kmalloc 一定判断返回值 flags GPF_ATOMIC 不会睡眠, GPF_KERNEL 常用 I/O访问: asm/io.h mem_region: struct: struct resource init: request_mem_region exit: release_resource use: note: 动态映射物理地址: ioremap / iounmap 静态映射物理地址: arch/arm/mach-s3c2410/mach-bit2440.c 编译内核生效 操作I/O: ioread8/iowrite8 v为值,p为地址 中断: asm/irq.h linux/interrupt.h include/asm-arm/arch-s3c2410/irqs.h irqno 中断号,是要加16 struct: struct irqdesc open: set_irq_type request_irq release: free_irq note: SA_SHIRQ为共享中断可以执行多个函数,SA_INTERRUPT, 一定要判断request_irq的返回值 描述中断机制: 1.中断是低电频有效,硬件中断发生后经过中断控制器->CPU的i位或f位,init/main.c的start_kernel第一个函数初始化vector(trap_init), 再跳到vector找到irq异常切换成irq工作模式->asm_do_irq函数,调用了一个函数指针,这个函数指针是struct irqdesc的handler成员 2. start_kernel的init_IRQ,最后会找到和平台相关的mach_bit2440.c里的is3c24xx_init_irq函数->do_edge_IRQ函数的do_irq循环struct irqdesc 的action成员(是一个链表指针)链表里的handler函数指针 3. request_irq注册函数->setup_irq函数,而这个函数则就是把我们的中断处理函数指针赋给了action的handler 中断处理函数使用: Note: 中断处理函数中的注意事项 1. 代码的执行时间, 在irq工作模式上时间尽量短 2. return IRQ_HANDLED; 3. 不能睡眠 1. not use semphone should be use spin_lock 2. not use kmalloc(GFP_KERNEL) should be use kmalloc(GFP_ATOMIC) 3. 不能跨系统层函数 copy_xxx_user Use: 1. 顶半部通知, 启动底半部处理 2. complete, wake_up, up, kill 底半部: tasklet: struct: struct tasklet_struct init: tasklet_init exit: use: tasklet_schedule note: 不可以睡眠,软中断上下文, 效率高 struct_work: struct: struct work_struct init: INIT_WORK 是宏 exit: use: schedule_work note: 可以睡眠 进程上下文 timer: 也可以称为底半部,也是多进程 struct : struct timer_list (文档,此知识点的主角对象,结构体) init: init_timer(struct timer_list *); (初始化函数) add_timer(struct timer_list *); (启动) exit: del_timer(struct timer_list *); (退出函数) use: mod_timer(struct timer_list *, unsigned long); (用法) note: 在init_timer和add_timer之间要先给struct timer_list成员赋值。 (注意事项) 时间是1秒=jiffies + 1*HZ linux设备模型组件: 在/sys目录下 struct kobject 属于所有的基类,可以把所有的对象连接起来, 一个目录 struct kset 同一类kobject的容器, 可以用于class,归类 struct subsystem 子系统 顶目录 struct attribute 属性就是文件 总线驱动: 总线: struct: struct bus_type init: bus_register exit: bus_unregister use: note: 设备: struct: struct device init: device_register exit: device_unregister use: note: 驱动: struct: struct device_driver init: driver_register exit: driver_unregister use: note: 一个驱动可以对多个设备,一个设备绝对只能对一个驱动 回调函数:在什么地方用: 在分层结构里常用,可移植性好 共公层 分层驱动 热插拨(hotplug): 原理就是设备插入时会产生一个中断,然后中断处理函数来注册设备(用工作队列) platform: 平台类总线设备,bus一般都是写好的 struct : struct platform_device init: platform_device_register exit: platform_device_unregister use: note: 静态映射平台类设备资源: 即编译进内核 1. arch/arm/mach-s3c2410/mach-bit2440.c struct platform_device *bit2440_devices[] __initdata 里加一句自己的设备资源 2. arch/arm/mach-s3c2410/devs.c 加一个 struct platform_device s3c_*_resource[] 这个结构体 再加一个 struct platform_device s3c_device* 结构体,模仿上面的结构体写 EXPORT_SYMBOL 再打出 3. arch/arm/mach-s3c2410/devs.h extern struct platform_device s3c* 导出 .Use: struct platform_device *pdev = container_of(dev, struct platform_device, dev); class: 类 字符设备驱动 + class(udev) struct: struct class init: class_create 给系统调试用,在sys/class创建目录 class_device_create 创建相对于sys/class目录里设备的设备文件 exit: class_device_destroy class_destroy use: 用于udev,自动建立/dev/xxx设备文件 note: misc:做的事就是cdev + class + major(主设备号为10) struct: struct miscdevice init: misc_register exit: misc_deregister use: note: 初始化struct miscdevice, file_operations要的是指针 wdt:平台类设备, 字符设备, misc 触摸屏: 电阻式 工业用的最多,用笔点的 电容式 用手摸的 红外式 电阻式触摸屏: 主要靠电阻分压器,根据电压不同算出按了哪点,再通过A/D转换成数字信号 INT_ADC 为A/D转换完后产生一个中断 INT_TC 按了和抬起都会产生一个中断 四种模式: 为A/D转换模式,就是除了A/D转换,没有其它功能 分离X/Y模式 只给一个值 Auto 模式 会给两个值 等待模式 等待中断 ADCCON: 15 转换完成标志,状态寄存器,可以看是否A/D转换完成 14 开关A/D要不要分频 13:6 A/D转换频率值,最大为PCLK / 5 5:3 A/D的,不能设置,跟触模屏无关 2 ADC的事,使ADC能挂起 1 一读就开始转换或转换完给中断 0 如果上面的没有设置就要打开 ADCTSC: 8 设置按下或弹起才产生中断 7:0 0xd3 为等待模式, 这个设置决定用哪种模式 2 为自动转换,如果为1上面5个就没用了 1:0 为等待模式,设置模式 ADCDLY: 延时用的,没用 ADCDAT0: ADCDAT1: 15:12 为只读的,只为取值用 9:0 取x和y坐标值,ADCDAT0取X, ADCDAT1取Y ADCUPDN: 看是按下或弹起发生了中断 中断完成后要清零 request_irq注册失败 中断号被用了,记得要加上16看 然后在cat /proc/interrupts 看哪个在用,到内核里全局搜索,然后不要编译到内核 没有钟,不可能会出来的 内核的clock: arch/arm/mach-s3c2410/mach-bit2440.c 找到MACHINE_START这个宏里的map_io = bit2440_map_io --> s3c24xx_init_clocks(12000000)-->(cpu->init_clocks)(xtal) 找到cpu这个结构体,找到在哪给init_clocks赋值 s3c2410_init_clocks-->s3c24xx_setup_clocks--> s3c24xx_clk_enable(S#C2410_CLKCON_ADC, 0); 找到这,初始化内核是关了,后面为1开,暴力开钟 注意 clk clk_get clk_use clk_enable/disable input: struct: init: input_register_device exit: input_unregister_device use: input_report_key input_sync 按键不需要用 note: 核心EVENT事件 EV_KEY 按键 EV_ABS 触摸屏 EV_REL 鼠标 1.HW,datasheet En(硬件和文档英文) 2.熟读内核源代码 3.调试和优化、容错 调试: 第一怀疑HW的问题,要非常熟悉硬件和datasheet 第二怀疑内核和程序的融合度 优化: 不是代码精简,而是时间和空间的优化,而且空间对于现在不用考虑 驱动优化80%优化硬件 字符设备和块设备有什么? 字符设备是以字节流访问,没有缓冲概念。 LCD: LCD设备,fb两部分 dma_alloc_writecombine 只会关掉MMU页表的C位,没有关B位 dma_alloc_coherent 会关掉MMU页表的C和B位 返回一个虚拟地址,在显示的时候用,给fb添数的时候,和一个实际物理地址,在设置寄存器用的 1. kernel src 组织结构 2.内核的配置 make menuconfig(读.config) 先到arch/arm/configs/s3c24xx_defconfg 拷贝成.config 然后再看板子的不同,再到menuconfig里配置 arch/arm/mach-s3c2410/mach-bit2440.c 移植最重要的就是这个,跟开发板相关,跟平台,资源(地址和中断)相关 drivers/ 查看板子相关的外设驱动在内核里有没有 Kconfig + Makefile 3.内核编程入口 module_init 模块不等于ko文件,只是内核提供的一个编程入口 MODULE_LICENCE("GPL");在公司要注意这个 4.驱动函数,完成工具 字符设备 一切以字节流访问 提供系统调用的实现 ,应用程序调到内核 拷贝: ZhangArm/Down_Class