ok6410学习笔记(18.linux串口驱动程序设计)
1@@Linux串口(serial、uart)驱动程序设计

Linux串口(serial、uart)驱动程序设计2012-07-2517:28:53unsigned int iobase;/*IO端口基地址*/unsigned char__iomem*membase;/*IO内存基地址,经映射(如ioremap)后的IO内存虚拟基地址*/unsigned int irq;/*中断号*/unsigned int uartclk;/*串口时钟*/unsigned int fifosize;/*串口FIFO缓冲大小*/unsigned char x_char;/*xon/xoff字符*/unsigned char regshift;/*寄存器位移*/unsigned char iotype;/*IO访问方式*/unsigned char unused1;#define UPIO_PORT(0)/*IO端口*/#define UPIO_HUB6(1)#define UPIO_MEM(2)/*IO内存*/#define UPIO_MEM32(3)#define UPIO_AU(4)/*Au1x00type IO*/#define UPIO_TSI(5)/*Tsi108/109type IO*/#define UPIO_DWAPB(6)/*DesignWare APB UART*/#define UPIO_RM9000(7)/*RM9000type IO*/unsigned int read_status_mask;/*关心的Rx error status*/unsigned int ignore_status_mask;/*忽略的Rx error status*/struct uart_info*info;/*pointer to parent info*/struct uart_icount icount;/*计数器*/struct console*cons;/*console结构体*/#ifdef CONFIG_SERIAL_CORE_CONSOLEunsigned long sysrq;/*sysrq timeout*/#endifupf_t flags;#define UPF_FOURPORT((__force upf_t)(1<<1))#define UPF_SAK((__force upf_t)(1<<2))#define UPF_SPD_MASK((__force upf_t)(0x1030))#define UPF_SPD_HI((__force upf_t)(0x0010))#define UPF_SPD_VHI((__force upf_t)(0x0020))#define UPF_SPD_CUST((__force upf_t)(0x0030))#define UPF_SPD_SHI((__force upf_t)(0x1000))#define UPF_SPD_WARP((__force upf_t)(0x1010))#define UPF_SKIP_TEST((__force upf_t)(1<<6))#define UPF_AUTO_IRQ((__force upf_t)(1<<7))#define UPF_HARDPPS_CD((__force upf_t)(1<<11))#define UPF_LOW_LATENCY((__force upf_t)(1<<13))#define UPF_BUGGY_UART((__force upf_t)(1<<14))#define UPF_MAGIC_MULTIPLIER((__force upf_t)(1<<16))#define UPF_CONS_FLOW((__force upf_t)(1<<23))#define UPF_SHARE_IRQ((__force upf_t)(1<<24))#define UPF_BOOT_AUTOCONF((__force upf_t)(1<<28))#define UPF_FIXED_PORT((__force upf_t)(1<<29))#define UPF_DEAD((__force upf_t)(1<<30))#define UPF_IOREMAP((__force upf_t)(1<<31))#define UPF_CHANGE_MASK((__force upf_t)(0x17fff))#define UPF_USR_MASK((__force upf_t)(UPF_SPD_MASK|UPF_LOW_LATENCY)) unsigned int mctrl;/*当前的moden设置*/unsigned int timeout;/*character-based timeout*/unsigned int type;/*端口类型*/const struct uart_ops*ops;/*串口端口操作函数集*/unsigned int custom_divisor;unsigned int line;/*端口索引*/resource_size_t mapbase;/*IO内存物理基地址,可用于ioremap*/struct device*dev;/*父设备*/unsigned char hub6;/*this should be in the8250driver*/unsigned char suspended;unsigned char unused[2];void*private_data;/*端口私有数据,一般为platform数据指针*/};uart_iconut为串口信息计数器,包含了发送字符计数、接收字符计数等。
ARM11 6410--Linux驱动程序开发演示

LED驱动示例
在开发板上演示控制LED灯 源码位置:实验代码/2_Linux驱动程序/2_LED控制示例 --dev.c:Linux驱动模块 --led_port.c:LED端口驱动 --Makefile:模块编译工程文件,编译出ARM格式目标文件 将编译得到的驱动模块led.ko拷贝到开发板 --关闭已有的流水灯服务:killall led-player --加载模块led.ko,led被初始化成全灭 --创建LED设备文件: mknod devleds c 240 32 下一步需要在上层应用程序通过控制/dev/led_dev设备文件控制LED灯
课程安排
什么是Linux驱动程序 Linux驱动程序开发流程 Linux驱动程序开发示例
Linux驱动模块示例
在PC上演示模块加载、运行、卸载 源码位置:2_Linux驱动程序/1_模块示例 --dev.c:驱动模块 --Makefile:模块编译工程文件,编译出X86格式目标文件 -- 编译模块: make clean && make -- 加载模块: insmod demo.ko -- 查看已加载模块: lsmod | more -- 卸载模块: rmmod demo.ko -- 查看模块打印信息: dmesg
驱动程序
操作系统需要控制硬件设备,就需要来自到设备驱动程序 --驱动程序通常作为操作系统的一部分(OS=Kernel+ DeviceDriver) --驱动负责将操作系统的操作请求,转化为特定物理设备控制 器能够理解的命令
Linux驱动
Linux驱动程序的功能 --向上为Linux系统提供访问硬件的调用接口 --向下用于控制硬件:与Arm裸机程序一样,通过读写硬件寄 存器达到控制硬件的目的
Linux串口终端驱动——S3C6410平台

Linux串口终端驱动——S3C6410平台1、serial文件夹下Kconfig分析config SERIAL_SAMSUNGtristate "Samsung SoC serial support"depends on ARM && PLAT_S3Cselect SERIAL_COREhelpSupport for the on-chip UARTs on the Samsung S3C24XX series CPUs,为支持三星的片上UARTs控制器providing /dev/ttySAC0, 1 and 2 (note, some machines may notprovide all of these ports, depending on how the serial portpins are configured.config SERIAL_SAMSUNG_UARTS有多少个UART控制器intdepends on SERIAL_SAMSUNGdefault 2 if ARCH_S3C2400default 4 if ARCH_S3C64XX || ARCH_S5P64XX || CPU_S3C2443 || ARCH_S5PC1XX default 3helpSelect the number of available UART ports for the Samsung S3Cserial driverconfig SERIAL_SAMSUNG_DEBUG 调试用bool "Samsung SoC serial debug"depends on SERIAL_SAMSUNG && DEBUG_LLhelpAdd support for debugging the serial driver. Since this isgenerally being used as a console, we use our own outputroutines that go via the low-level debug printascii()function.config SERIAL_SAMSUNG_CONSOLE 用于定义串口为控制台终端bool "Support for console on Samsung SoC serial port"depends on SERIAL_SAMSUNG=yselect SERIAL_CORE_CONSOLEhelpAllow selection of the S3C24XX on-board serial ports for use asan virtual console.Even if you say Y here, the currently visible virtual console(/dev/tty0) will still be used as the system console by default, but you can alter that using a kernel command line option such as"console=ttySACx". (Try "man bootparam" or see the documentation of your boot loader about how to pass options to the kernel atboot time.)config SERIAL_S3C2400 用于S3C2400tristate "Samsung S3C2410 Serial port support"depends on ARM && SERIAL_SAMSUNG && CPU_S3C2400default y if CPU_S3C2400helpSerial port support for the Samsung S3C2400 SoCconfig SERIAL_S3C2410 用于S3C2410tristate "Samsung S3C2410 Serial port support"depends on SERIAL_SAMSUNG && CPU_S3C2410default y if CPU_S3C2410helpSerial port support for the Samsung S3C2410 SoCconfig SERIAL_S3C2412tristate "Samsung S3C2412/S3C2413 Serial port support"depends on SERIAL_SAMSUNG && CPU_S3C2412default y if CPU_S3C2412helpSerial port support for the Samsung S3C2412 and S3C2413 SoCconfig SERIAL_S3C2440tristate "Samsung S3C2440/S3C2442 Serial port support"depends on SERIAL_SAMSUNG && (CPU_S3C2440 || CPU_S3C2442)default y if CPU_S3C2440default y if CPU_S3C2442helpSerial port support for the Samsung S3C2440 and S3C2442 SoCconfig SERIAL_S3C24A0tristate "Samsung S3C24A0 Serial port support"depends on SERIAL_SAMSUNG && CPU_S3C24A0default y if CPU_S3C24A0helpSerial port support for the Samsung S3C24A0 SoCconfig SERIAL_S3C6400 用于 (CPU_S3C6400 || CPU_S3C6410 || CPU_S5P6440) tristate "Samsung S3C6400/S3C6410/S5P6440 Serial port support" depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410 || CPU_S5P6440) default yhelpSerial port support for the Samsung S3C6400, S3C6410 and S5P6440 SoCs2、serial文件夹下Makefile文件摘要obj-$(CONFIG_SERIAL_CORE) += serial_core.oobj-$(CONFIG_SERIAL_SAMSUNG) += samsung.oobj-$(CONFIG_SERIAL_S3C2400) += s3c2400.oobj-$(CONFIG_SERIAL_S3C2410) += s3c2410.oobj-$(CONFIG_SERIAL_S3C2412) += s3c2412.oobj-$(CONFIG_SERIAL_S3C2440) += s3c2440.oobj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.oobj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o3、顶层.config配置文件CONFIG_SERIAL_SAMSUNG=yCONFIG_SERIAL_SAMSUNG_UARTS=4# CONFIG_SERIAL_SAMSUNG_DEBUG is not setCONFIG_SERIAL_SAMSUNG_CONSOLE=yCONFIG_SERIAL_S3C6400=yCONFIG_SERIAL_CORE=yCONFIG_SERIAL_CORE_CONSOLE=y4、从上面这些可知,serial串口驱动主要涉及的文件有(1)、serial_core.c(2)、samsung.c(3)、s3c6400.c1、终端设备在Linux系统中,终端是一种字符型设备,它有多种类型,通常使用tty来简称各种类型的终端设备。
linux驱动开发知识点总结

linux驱动开发知识点总结Linux驱动开发是指在Linux操作系统下开发和编写设备驱动程序的过程。
Linux作为一种开源操作系统,具有广泛的应用领域,因此对于驱动开发的需求也非常重要。
本文将从驱动程序的概念、驱动开发的基本步骤、常用的驱动类型以及驱动开发的注意事项等方面进行总结。
一、驱动程序的概念驱动程序是指控制计算机硬件和软件之间通信和交互的程序。
在Linux系统中,驱动程序负责与硬件设备进行交互,实现对硬件的控制和管理。
二、驱动开发的基本步骤1. 确定驱动的类型:驱动程序可以分为字符设备驱动、块设备驱动和网络设备驱动等。
根据具体的硬件设备类型和需求,选择合适的驱动类型。
2. 编写设备注册函数:设备注册函数用于向系统注册设备,使系统能够识别和管理该设备。
3. 实现设备的打开、关闭和读写操作:根据设备的具体功能和使用方式,编写设备的打开、关闭和读写操作函数。
4. 实现设备的中断处理:如果设备需要进行中断处理,可以编写中断处理函数来处理设备的中断请求。
5. 编写设备的控制函数:根据设备的需求,编写相应的控制函数来实现对设备的控制和配置。
6. 编译和安装驱动程序:将编写好的驱动程序进行编译,并将生成的驱动模块安装到系统中。
三、常用的驱动类型1. 字符设备驱动:用于控制字符设备,如串口、打印机等。
字符设备驱动以字符流的方式进行数据传输。
2. 块设备驱动:用于控制块设备,如硬盘、U盘等。
块设备驱动以块为单位进行数据传输。
3. 网络设备驱动:用于控制网络设备,如网卡。
网络设备驱动实现了数据包的收发和网络协议的处理。
4. 触摸屏驱动:用于控制触摸屏设备,实现触摸操作的识别和处理。
5. 显示驱动:用于控制显示设备,实现图像的显示和刷新。
四、驱动开发的注意事项1. 熟悉硬件设备的规格和寄存器的使用方法,了解硬件设备的工作原理。
2. 确保驱动程序的稳定性和可靠性,避免出现系统崩溃或死机等问题。
3. 对于需要频繁访问的设备,要考虑性能问题,尽量减少对硬件的访问次数。
Linux字符设备驱动之Tiny6410 LED驱动编写

Linux字符设备驱动之Tiny6410 LED驱动分析摘要:驱动程序是应用程序和底层硬件之间的桥梁,非常重要。
字符设备是一种可以当做一个字节流来存取的设备,这样的设备只能一个字节一个字节的进行数据传输,这样的驱动常常至少实现open、close、read、和write系统条用,常见的有串口、LED、文本控制台等,字符设备通过文件系统节点来存取,例如/dev/tty1和/dev/lp0.在一个字符设备和一个普通文件之间唯一相关的不同就是,你可以在普通的文件中移来移去,但是大部分字符社诶仅仅是数据通道,只能顺序存取。
重要概念1.用户空间和内核空间一个驱动模块在内核空间运行,而应用程序则是在用户空间运行,这个概念是操作系统的理论基础。
Linux为这两种空间之间的数据传输定义了两个函数,分别为copy_to_user()和copy_from_user(),从字面意思可以知道函数的意义。
比如在编写驱动程序时,很显然驱动程序属于内核空间,会经常使用copy_from_user()函数,从应用程序中获取数据给驱动程序。
2.编译模块使用make xxxx modules 生成xxx.ko模块。
3.加载和卸载模块(驱动)驱动生成的模块需要加载到内核中,加载模块使用insmod指令,如:insmodxxx.ko卸载驱动则用rmmod命令。
4.所需头文件#include <linux/module.h> //包含大量加载模块所需的函数和符号定义#include<linux/init.h> //制定初始化和清理函数许可凭证指令:MODULE_LICENSE(“GPL”);5.初始化和关停初始化指令:static int __initinitialization_function(void){}module_init(initialization_function);初始化函数应声明为静态,因为他们不会再特定文件之外可见,毕竟static的重点应用是“隐藏”。
Linux设备驱动程序设计入门.doc

深入浅出Linux设备驱动编程之引言目前,Linux软件工程师大致可分为两个层次:(1)Linux应用软件工程师(Application Software Engineer):主要利用C库函数和Linux API进行应用软件的编写;(2)Linux固件工程师(Firmware Engineer):主要进行Bootloader、Linux的移植及Linux设备驱动程序的设计。
一般而言,固件工程师的要求要高于应用软件工程师的层次,而其中的Linux设备驱动编程又是Linux 程序设计中比较复杂的部分,究其原因,主要包括如下几个方面:(1)设备驱动属于Linux内核的部分,编写Linux设备驱动需要有一定的Linux操作系统内核基础;(2)编写Linux设备驱动需要对硬件的原理有相当的了解,大多数情况下我们是针对一个特定的嵌入式硬件平台编写驱动的;(3)Linux设备驱动中广泛涉及到多进程并发的同步、互斥等控制,容易出现bug;(4)由于属于内核的一部分,Linux设备驱动的调试也相当复杂。
目前,市面上的Linux设备驱动程序参考书籍非常稀缺,少有的经典是由Linux社区的三位领导者Jonathan Corbet、Alessandro Rubini、Greg Kroah-Hartman编写的《Linux Device Drivers》(目前该书已经出版到第3版,中文译本由中国电力出版社出版)。
该书将Linux设备驱动编写技术进行了较系统的展现,但是该书所列举实例的背景过于复杂,使得读者需要将过多的精力投放于对例子背景的理解上,很难完全集中精力于Linux驱动程序本身。
往往需要将此书翻来覆去地研读许多遍,才能有较深的体会。
(《Linux Device Drivers》中英文版封面)本文将仍然秉承《Linux Device Drivers》一书以实例为主的风格,但是实例的背景将非常简单,以求使读者能将集中精力于Linux设备驱动本身,理解Linux内核模块、Linux设备驱动的结构、Linux设备驱动中的并发控制等内容。
〖Linux〗OK6410a蜂鸣器的驱动程序编写全程实录

〖Linux〗OK6410a蜂鸣器的驱动程序编写全程实录最近在看⼀本书,受益匪浅,作者是李宁,下边是编写本次蜂鸣器的全程实录:1. 了解开发板中的蜂鸣器 1) 查看蜂鸣器buzzer在底板中的管脚信息 2) 查看蜂鸣器在总线中的信息 3) 翻看S3C6410芯⽚⼿册,查看GPF15相关信息2. 在了解了开发板中蜂鸣器之后,编写代码对它进⾏控制。
由于蜂鸣器是通过PWM(脉冲宽度调制)进⾏开关控制的,故也称为PWM。
1) 编写pwm.c(包含Linux驱动模块的主要模型代码)#include "pwm_fun.h"static struct semaphore lock; /* 创建信号量*///⽂件打开时,⾃动操作此函数,使⽤信号量控制其访问static int s3c6410_pwm_open(struct inode *inode, struct file *file){if (!down_trylock(&lock)) /* 使⽤信号量控制只能由⼀个进程打开 */{return0;}else{return -EBUSY;}}//⽂件关闭时,⾃动操作此函数,使⽤信号量控制其访问static int s3c6410_pwm_close(struct inode *inode, struct file *file){up(&lock); /* 释放信号量 */return0;}//⽂件中关于ioctl的操作static long s3c6410_pwm_ioctl(struct file *filep, unsigned int cmd, unsigned long arg){switch ( cmd ) {case PWM_IOCTL_START:pwm_start();break;case PWM_IOCTL_STOP:pwm_stop();break;default:break;} /* ----- end switch ----- */return0;}//⽂件操作的指针static struct file_operations dev_fops = {.owner = THIS_MODULE,.open = s3c6410_pwm_open,.release = s3c6410_pwm_close,.unlocked_ioctl = s3c6410_pwm_ioctl,};//设备的属性static struct miscdevice misc = {.minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = &dev_fops,};//驱动的⼊⼝函数static int __init dev_init(void){int ret;init_MUTEX(&lock); /* 初始化信号量 */ret = misc_register(&misc);printk(DEVICE_NAME"\tinitialized\n");return ret;}//驱动的退出函数static void __exit dev_exit(void){misc_deregister(&misc);printk(DEVICE_NAME"\texited\n");}module_init(dev_init);module_exit(dev_exit); 2) 编写pwm_fun.c(包含对蜂鸣器控制的主要代码)#include "pwm_fun.h"void pwm_start(void){unsigned int tmp;tmp = ioread32(S3C64XX_GPFCON);tmp &= ~(0x3U<<30); /* 最⾼两位清零,保留其他位 */tmp |= (0x2U<<30); /* 最⾼两位设置为10,蜂鸣器发出尖叫声 */iowrite32(tmp, S3C64XX_GPFCON);}void pwm_stop(void){unsigned tmp;tmp = ioread32(S3C64XX_GPFCON);tmp &= ~(0x3U<<30); /* 最⾼两位清零,蜂鸣器停⽌尖叫 */iowrite32(tmp, S3C64XX_GPFCON);} 3) 编写pwm_fun.h(包含⼀些必须的头⽂件及宏定义信息)#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <asm/io.h>#include <linux/interrupt.h>#include <linux/device.h>#include <asm/uaccess.h>#include <linux/miscdevice.h>#include <mach/map.h>#include <mach/regs-clock.h>#include <mach/regs-gpio.h>#include <mach/gpio-bank-f.h>#include <mach/gpio-bank-k.h>#define DEVICE_NAME "pwm_dev"#define PWM_IOCTL_START 1#define PWM_IOCTL_STOP 0extern void pwm_start(void);extern void pwm_stop(void); 4) 编写Makefile⽂件(使编译过程⾃动完成)obj-m := pwm_driver.opwm_driver-objs := pwm.o pwm_fun.oPWD := $(shell pwd)CROSS_COMPILE ?= arm-none-linux-gnueabi-CC := $(CROSS_COMPILE)gccCFLAGS += -staticKPATH := /media/Source/Forlinx/android2.3_kernel_v1.01all: ioctlmake -C $(KPATH) M=$(PWD) modulesioctl: ioctl.c$(CC) $(CFLAGS) $^ -o $@clean:rm -rf *.o *.ko *.mod.c Module.* modules.* ioctl 5) 编写测试程序ioctl(⽤于对驱动程序的测试)#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/ioctl.h>/** === FUNCTION ====================================================================== * Name: main* Description: 对驱动程序进⾏测试* ===================================================================================== */int main ( int argc, char *argv[] ){int file_handler = 0;int cmd = 0;int arg = 0;if (argc < 4){printf("Usage: ioctl <DEVICE_NAME> <cmd> <arg>\n");return EXIT_FAILURE;}cmd = atoi(argv[2]);arg = atoi(argv[3]);printf("dev:%s\n", argv[1]);printf("cmd:%d\n", cmd);printf("arg:%d\n", arg);file_handler = open(argv[1], 0);if (file_handler<0){printf("open %s failure.\n", argv[1]);}ioctl(file_handler, cmd, arg);close(file_handler);return EXIT_SUCCESS;3. 编写好源代码程序之后,交叉编译,上传⾄OK6410A开发板,进⾏测试 1) 配置好交叉编译⼯具链arm-none-linux-gnueabi-gcc之后,在终端输⼊make即可⾃动完成编译 2) OK6410A(Android2.3.4操作系统)通过USB线连接⾄PC端,通过adb上传⾄开发板: adb push pwm_driver.ko /tmp/pwm_driver.ko adb push ioctl /tmp/ioctl 3) 通过adb shell打开并控制OK6410A开发板,输⼊命令安装驱动模块: adb shell insmod /tmp/pwm_driver.ko 4) 测试蜂鸣器驱动程序 /tmp/ioctl /dev/pwm_dev 1 0 #打开蜂鸣器 /tmp/ioctl /dev/pwm_dev 0 0 #关闭蜂鸣器。
基于Mini6410的Linux驱动学习总结

基于Mini6410的Linux驱动学习总结基于Mini6410的Linux驱动学习总结基于mini6410的linux驱动学习总结(一驱动程序介绍)1、什么是驱动程序?使硬件工作的软件。
2、驱动分类1)字符设备驱动2)网络接口驱动3)块设备驱动2.1 字符设备字符设备是一种按字节来访问的设备,字符驱动则负责驱动字符设备,这样的驱动通常实现open, close,read和write 系统调用。
2.2块设备1)在大部分的Unix 系统, 块设备不能按字节处理数据,只能一次传送一个或多个长度是5 12字节( 或一个更大的2 次幂的数)的整块数据。
2)而Linux则允许块设备传送任意数目的字节。
因此, 块和字符设备的区别仅仅是驱动的与内核的接口不同。
2.3 网络接口任何网络事务都通过一个接口来进行, 一个接口通常是一个硬件设备(eth0), 但是它也可以是一个纯粹的软件设备, 比如回环接口(lo)。
一个网络接口负责发送和接收数据报文。
3、驱动程序安装1)模块方式(已知J)2)直接编译进内核直接编译进内核1)Kconfig2)Makefile例:将helloWorld编译进内核在类unix系统中,字符设备和主要有以下3点不同:1、字符设备是以字节为单位进行访问。
块设备是以块为最小单位进行访问。
块可以是512字节或一个更大的2次幂的数。
2、在linux系统中,字符设备和块设备都可以以字节为单位进行访问,区别仅仅是二者访问的接口函数不同。
3、块设备与字符设备访问的顺序不同,字符设备只能顺序访问,而块设备可以随机访问。
基于mini6410的linux驱动学习总结(三使用驱动程序)Linux用户如何使用驱动程序?Linux用户程序通过设备文件(又名:设备节点)来使用驱动程序操作字符设备和块设备下图是linux系统中应用程序、驱动、硬件之间的关系图。
设备(字符、块)文件在何处?设备文件存放在dev目录下。
背景:阅读新闻基于Mini6410的Linux驱动学习总结[日期:2012-04-28] 来源:Linux社区作者:yinjiabin [字体:大中小] 基于mini6410的linux驱动学习总结(四设计字符设备驱动程序)涉及的知识点1、设备号2、创建设备文件3、重要数据结构4、设计字符设备驱动的步骤设备号用来做什么?设备号作用:主设备号用来标识与设备文件相连的驱动程序。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
ok6410学习笔记(18.linux串口驱动程序设计)串口这节的资料真的是少的可怜啊~~~国嵌这节讲的真心不敢恭维,网上的资料基本都是一个样子,不是说说s3c6400.c和samsung.c这两个文件就是说说驱动的结构。
都是些意义不大的东西。
首先,我先说说本节的学习资料:1.<linux设备驱动开发详解> 宋宝华这本说里面对uart的基本驱动结构讲的是比较细致的是深入tty 设备驱动讲解的,但是缺乏从驱动到应用的一贯性。
2.国嵌视频和ppt 主要是将驱动的框架进行了大致讲解,即终端设备的基本概念3.<精通linux设备驱动程序开发> 宋宝华译有一个详细的uart驱动程序可以和s3c6400.c和samsung.c文件进行对比学习4.linux内核源码包括s3c6400.c samsung.c samsung.h serial.c termbits.h serial_core.c文件等5.<linux高级程序设计> 杨宗德第七章这本说的是对于终端串口的应用开发。
可以把底层驱动和应用进行联系。
6.<嵌入式linux应用开发完全手册> 韦东山我觉得这节对终端的概念讲解还是挺好的,但是对于串口驱动讲的挺一般的因为主要是讲8250的(可以不看)本节的学习目的:1.能够使用ok6410上面的串口进行通信,即应用程序的开发。
2.理解uart驱动结构和tty驱动的结构。
这个结构很复杂理解就可以了~~~~~~ 本节保留问题:最大的问题就是下面的问题一!!!本节知识点:1.本节最大的知识点就是uart的驱动结构,我们就先入手对结构的分析(分析的是linux中串口驱动的结构)。
第一:首先要知道在linux中串口驱动是终端设备的一种,终端设备是字符设备,所有串口驱动是字符设备驱动,是通过dev目录下的文件节点对设备进行操作的。
这样我们就有了一点初步的从驱动到应用的想法。
第二:我们看下s3c6400.c samsung.c 6410串口驱动文件。
这个两个文件是让我最迷糊的两个文件,初一看跟什么uart_driver uart_op uart_port一点关系都没有。
那是因为好多samsung的片子共用一套串口驱动,什么2440,6410等,所以就在这个层次上有进行了一次封装,就有了s3c6400.c这个文件了。
第三:对于s3c6400.c samsung.c具体的我还不是很懂,但是大体上我可以猜一下,应该是在s3c6400.c中通过类似s3c6400_uart_inf中的type变量,告诉驱动类型应该是s3c6410的,然后改了uart各种寄存器的基地址,让这些寄存器地址变成了6410的,并且也调用了6410的uart_port 和uart_driver。
因为uart_op的功能都是一样的,所以共用同一套op。
第四:其实自己也可以写这个结构的uart驱动,步骤为 1.填写uart_port 和uart_driver 填写uart_driver主要的目的是为了在注册uart_driver的时候能够创建出字符设备其实就是注册一个uart的字符设备填写uart_port主要是为了在往对应uart驱动中添加port的时候能够跟uart寄存器(即基地址),中断,fifo建立联系,但是同时也产生了问题一:因为在s3c6400.c文件中,对这一步进行了封装,我没有找到对应6410的uart_driver和uart_port赋值,不知道基地址应该填写那个基地址,是否进行ioremap映射,也不知道什么console,tty_driver什么都进行赋值。
2.uart_op结构填写,然后uart_register_driver()对uart_driver进行注册,uart_add_one_port()对uart_port进行注册,这里出现了问题二:uart_op中的函数都分别有对应的功能,这些函数应该是给上层tty驱动调用的。
但是具体都是怎么调用的,都在那里调用就不知道了,是通过什么联系到应该程序中的write和read函数和tcsetattr等终端函数的也不知道。
这些函数那些有用,那些需要填写,因为不知道上面是怎么调用的所以也不知道怎么写。
3.但是有一个问题就是字符设备的注册是在uart_register_driver的时候进行的,uart的初始化是在uart_add_one_port的时候进行的,我觉得应该是系统完成的,包括uart初始化,fifo模式,uart中断设置等应该都不用我们自己设置,中断的初始化是在uart_op->startup函数调用的时候完成的,这个函数是在应用程序打开uart设备节点的时候调用的。
4.根据datasheet根据uart_op中函数的功能根据上层tty驱动都是怎么一个调用关系来写op中的所有函数。
这里体现了tty体系的主要功能,不去考虑终端设备具体是什么,对于这一类设备都用一套统一的接口给应用程序。
问题三:也不知道uart这个具体的终端设备怎么跟tty体系建立关系,还是uart这个驱动的结构本身就是tty驱动体系的一个封装啊。
第五:上面第四点已经说明了,我没有写出这个结构的串口驱动的最重要的三点原因,其实samsung.c文件,也是按照第四点的那几步进行的,主要是他把uart_driver和uart_port进行了封装,没有找到。
然后我按照裸机的uart驱动,写了一个利用ioremap,没有fifo,没有中断,轮询判断标志位的uart驱动。
但是也没有运行成功,原因是samsung.c 这个uart驱动,一次设置了s3c6410的4个uart,也就是说它先初始化了fifo,初始化了各种乱七八糟的uart寄存器,我觉得其实ioremap的这个方式一定可以的,如果想成功就把其他不用的uart寄存器都写入初始值就可以了。
2.比较一下利用ioremap的uart驱动和linux这个终端模式的uart驱动:第一:linux的这个驱动虽然层次多,麻烦,难以理解,但是这是一个tty终端驱动框架,我们是应该了解的。
第二:linux的这个驱动框架,不用我们自己去配置uart初始化的寄存器,尤其是fifo啊uart中断啊这些比较麻烦的寄存器,这些步骤应是在系统移植的过程的时候完成的。
第三:linux的这个uart驱动,层次很多,但是能完成把所有tty终端驱动(键盘,显示器等)不管硬件是什么的情况,给终端应用留出统一的接口。
其实也是无形的减少了中间的代码量。
如果是自己写uart驱动,还得匹配write和read 函数。
第四:linux的uart驱动,在tty体系中,里面是一套完整的接受体系,linux写好的首发缓存区要稳定一些。
3.对于uart驱动我想说,最重要的还是来了解下uart驱动和tty驱动的驱动结构,然后能学会写终端应用程序能够控制串口就好了,想写一个uart驱动还是有困难的,因为这个tty 驱动结构的层次有点多。
并且tty驱动和硬件相连的部分(即带有fifo的uart初始化)也不知道是在linux源码的那个地方(那个文件)完成的,这还涉及到了linux系统移植了。
4.宋宝华在<linux设备驱动开发详解>中提到,uart设备驱动完全可以遵循tty驱动来设计,也就是其实仅仅利用tty 终端驱动也能写出uart驱动,就是把uart当成一个普通的tty_driver结构,根据终端的operation函数集(即终端的读与写),把uart的硬件功能添加到operation函数集中去,就是对应fifo啊,中断啊这些寄存器配置得自己写了。
其实linux 已经完成把tty_operation到uart_op的过程,tty_regiser到uart_register的过程了,叫做串口核心层在serial_core.c文件中。
其实就是把tty驱动封装成了uart 驱动。
这就变相的解决了上面的问题二和问题三,对于问题三可以说uart本身就是tty是在serial_core.c文件中封装的,应该是不用建立什么关系的,对于问题二,找到了uart_op是怎么被调用的了,是跟tty_operation对应的,具体tty_operation是怎么到read,write的,那是tty驱动体系的事情,我们在下面进行分析。
对于中断啊,fifo啊在那里初始化的,可以去找uart_add_one_port函数,在serial_core.c文件里面,应该是在这个函数中对uart进行的硬件初始化的。
5.终端是一类字符设备的统称:控制台,串口,伪终端控制台:打印内核信息,能映射到一个真正的终端上。
伪终端:成对出现类似telnet串口:就是真实的串口设备,一般映射到真正的终端上。
6.终端体系结构:tty核心层,tty线路规程,tty驱动层tty_operation结构在tty驱动层,其中一部分函数是给tty 核心层调用的,一部分是像字符设备驱动一样通过vfs虚拟文件系统留给应用程序的接口的。
所有到这里就明白了uart_op到底是怎么到应用程序的。
tty核心层主要负责在用户层接受数据,tty线路规程的作用是来格式化的,来修改协议的,比如说键盘输入中的tab 按键问题,tty驱动层是负责硬件控制的。
如图:两条路:1.串口驱动层----->tty驱动层-------->tty核心层------->tty线路规程--------->tty核心层------------->用户空间2.串口驱动层----->tty驱动层-------->tty核心层------->用户空间相反依然,这节tty驱动不是重点,所以不再多说了,对于tty驱动的学习我觉得最好的还是,宋宝华的<linux设备驱动开发详解>讲的很详细。
7.uart应用程序详解:第一:打开设备文件,国嵌内核的串口1设备文件是/dev/s3c2410_serial1第二:设置tio.c_cflag=B115200|CS8|CREAD|CLOCAL 设置的是波特率第三:通过这个终端控制函数tcsetattr(fd,TCSANOW,&tio) 把波特率设置给串口1 第四:通过read和write进行读写设备文件,即对串口进行读写,其实这里是对终端进行读写。
第五:关闭设备文件这样就对串口进行了控制,可以实现gps,gsm等设备了。
对于终端应用程序的细节应该看,杨宗德的<linux高级程序设计>本节代码:本节代码就是一个uart_test.c的一个串口应用程序,驱动是内核里面的,内核是国嵌2.6.36版本的,实现了在波特率115200的情况下,串口收到一个数据,然后再发送出这个数据。