ok6410学习笔记(18.linux串口驱动程序设计)
- 格式:doc
- 大小:31.00 KB
- 文档页数:9
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为串口信息计数器,包含了发送字符计数、接收字符计数等。
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系统中,驱动程序负责与硬件设备进行交互,实现对硬件的控制和管理。
二、驱动开发的基本步骤1. 确定驱动的类型:驱动程序可以分为字符设备驱动、块设备驱动和网络设备驱动等。
根据具体的硬件设备类型和需求,选择合适的驱动类型。
2. 编写设备注册函数:设备注册函数用于向系统注册设备,使系统能够识别和管理该设备。
3. 实现设备的打开、关闭和读写操作:根据设备的具体功能和使用方式,编写设备的打开、关闭和读写操作函数。
4. 实现设备的中断处理:如果设备需要进行中断处理,可以编写中断处理函数来处理设备的中断请求。
5. 编写设备的控制函数:根据设备的需求,编写相应的控制函数来实现对设备的控制和配置。
6. 编译和安装驱动程序:将编写好的驱动程序进行编译,并将生成的驱动模块安装到系统中。
三、常用的驱动类型1. 字符设备驱动:用于控制字符设备,如串口、打印机等。
字符设备驱动以字符流的方式进行数据传输。
2. 块设备驱动:用于控制块设备,如硬盘、U盘等。
块设备驱动以块为单位进行数据传输。
3. 网络设备驱动:用于控制网络设备,如网卡。
网络设备驱动实现了数据包的收发和网络协议的处理。
4. 触摸屏驱动:用于控制触摸屏设备,实现触摸操作的识别和处理。
5. 显示驱动:用于控制显示设备,实现图像的显示和刷新。
四、驱动开发的注意事项1. 熟悉硬件设备的规格和寄存器的使用方法,了解硬件设备的工作原理。
2. 确保驱动程序的稳定性和可靠性,避免出现系统崩溃或死机等问题。
3. 对于需要频繁访问的设备,要考虑性能问题,尽量减少对硬件的访问次数。
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设备驱动编程之引言目前,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蜂鸣器的驱动程序编写全程实录最近在看⼀本书,受益匪浅,作者是李宁,下边是编写本次蜂鸣器的全程实录: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驱动学习总结(一驱动程序介绍)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、设计字符设备驱动的步骤设备号用来做什么?设备号作用:主设备号用来标识与设备文件相连的驱动程序。
Tiny6410开发板Linux系统自学笔记一、嵌入式Linux系统自学计划:0、学习嵌入式Linux系统启动、加载的工作原理。
1、搭建针对目标CPU的Linux编译环境。
2、移植、编译针对目标CPU单板的Bootloader。
3、移植、编译针对目标CPU单板的Linux内核。
4、制作用于Linux内核启动的根文件系统。
4、定制、开发针对目标CPU单板的硬件驱动程序。
5、开发基于嵌入式Linux平台的应用程序。
二、参考文档:《ARM学习报告1~3》《嵌入式Linux入门笔记》《嵌入式Linux性能详解》《Tiny6410 Linux开发指南》6410 BootLoader启动过程分析:S3C6410支持从SD卡或Nand FLASH直接启动,但是实际上板子上电后,会首先执行芯片内部iRom中的代码,iROM中的代码会根据GPN[15:13]的管脚来判断从哪个存储设备中读取4KB的启动代码,并放入SteppingStone中运行,这段代码被称为Bootloader1(BL1)。
【因为SteppingStone 是SRAM,所以不需要初始化就可以使用】所以,整个启动过程可以分成BL0, BL1, BL2 三个阶段, 其中BL0是固化在s3c6410内部的iROM中的,1、BL0处理流程如下图所示2、BL1的处理流程对于SD卡, BL1代码位于(totalSector - 18) 的扇区对于SDHC卡,BL1代码位于(totalSector-1042)的扇区对于NANDFLASH,BL1代码位于最前面的4K空间因为最终的系统需要运行在DDR内存中,所以,BL1首先需要执行DDR的初试化,然后再将GPN[15:13]管脚指定的存储设备中bootloader代码(BL1+BL2)拷贝到DDR中,然后重定位到编译器编译时指定的位置,然后跳转。
3、此时,DDR中的BL2就是完成一些复杂的初始化,以及很多辅助调试功能,比如网络命令,usb命令,nand命令等等,最后执行你的bootcmd,将内核加载到指定位置,将bootargs放到指定位置,传递给内核mach_type,bootargs 的存放地址,然后跳转到内核执行,到这里bootloader就完成使命然后消失了。
linux设备驱动程序的设计与实现
Linux设备驱动程序的设计与实现是一个涉及底层系统编程和硬件交互的复杂过程。
下面是一个简单的步骤指南,以帮助你开始设计和实现Linux设备驱动程序:
1. 了解硬件:首先,你需要熟悉你要驱动的硬件设备的规格和特性。
这包括硬件的内存空间、I/O端口、中断请求等。
2. 选择驱动程序模型:Linux支持多种设备驱动程序模型,包括字符设备、块设备、网络设备等。
根据你的硬件设备和需求,选择合适的驱动程序模型。
3. 编写Makefile:Makefile是一个文本文件,用于描述如何编译和链接你的设备驱动程序。
它告诉Linux内核构建系统如何找到并编译你的代码。
4. 编写设备驱动程序:在Linux内核源代码树中创建一个新的驱动程序模块,并编写相应的C代码。
这包括设备注册、初始化和卸载函数,以及支持读写和配置硬件的函数。
5. 测试和调试:编译你的设备驱动程序,并将其加载到运行中的Linux内核中。
使用各种测试工具和方法来验证驱动程序的正确性和稳定性。
6. 文档和发布:编写清晰的文档,描述你的设备驱动程序的用途、用法和已知问题。
发布你的代码以供其他人使
用和改进。
嵌入式Linux设备驱动开发读书笔记经过第十二章的学习,我大致了解了linux设备驱动开发的意义所在,在linux 系统中设备被抽象化,系统通过调用标准的驱动接口来实现各种硬件的操作,同时,系统上层对于硬件设备的调用无需关心具体的硬件细节,系统调用设备使用文件的方式进行操作,通过这样的屏蔽,系统以标准和统一的方式去完成硬件的操作,对于硬件设备更新速度越来越快的今天,硬件的更新,只需要去更改驱动就能够实现设备的升级,而不需要去改变上层的应用软件,同时也利于上层和软件和硬件之间开发的分工和合作。
设备驱动最终被添加到内核里,与系统成为一个整体,ARM处理器的指令集与我们通常使用的PC机的X86指令集不同,ARM 的RISC指令集也可以说是一个精简版本的X86指令集,虽然没有X86指令集那么强大的功能,但是同样可以支持LINUX系统的运算,因此基于ARM的LINUX系统需要使用支持ARM指令集的编译器来进行编译,也就是ARM-GCC,在宿主机,也就是X86指令集下使用ARM-GCC编译出来的LINUX系统能够在ARM硬件系统上运行。
设备驱动程序是内核的一部分,因此驱动的稳定对系统的稳定非常重要,设备驱动程序必须为内核提供一个标准的接口,并且为了防止系统过于臃肿,对于某些系统设备驱动要求能够实时的进行加载,对于设备的更新也更加的方便,以安装的方式进行加载。
以下列举我对几种设备驱动编写的理解1字符设备:一般应用程序都有一个main入口点,而设备驱动却没有main函数,其实模块在调用insmod命令时会进入设备驱动的module_init函数,在该函数中注册设备,在调用rmmod函数时驱动被卸载,此时入口点为module_exit函数,他们的关系如图设备被加载之后,系统便能够在内核中调用驱动,在LINUX内核中,dev_t 类型用来保存主设备号和次设备号,dev_t四个字节,其中12bit用于表示主设备号,其余20bit表示次设备号,使用系统函数给设备分配一个设备号,获得这个设备号之后,便能够通过特定的系统函数来对设备进行注册,当设备成功注册之后,系统就能够通过open和release操作来打开设备和卸载设备。
这半个月因肺部感染而不得不暂时终止那令人生厌的中石油巡检工作,闭门在家安静的修养。
整月的工钱自然是泡汤了,可却得来了极其珍贵的个人闲暇时光,让我能淋漓尽致的做软件方面的研究,虽是粗茶淡饭,针剂苦药,但可静心埋头于书房,却比天堂还甜美!恍惚已至月末,工作单位来了音讯,让我一下子从甜美的梦中惊醒,从哪里来,回哪里去,这种如"主体思想"一样可怕的思维是我挥之不去的梦魇,无奈、不知所措、病弱的身体却不由自主的向那发声的地方靠去!好了,还是不再发牢骚了,只是个人觉得这种臃肿低效的国企能够存在,本身就是对“国富论”绝佳的嘲讽,我只能用世界是多元的来啊Q一下了!切入正题,这段时间做GSM/GPRG和GPS的小东西,需要通过串口发送AT指令来控制,以前调试一直在用串口助手和minicom之类的现成软件,可是一点都不爽,为什么不自己写个操作串口的软件,就像在ARM和stm32上一样!这文章其实只是我的一个笔记,分为两篇,一篇是《storysnail的Windows串口编程笔记》,另一篇是《storysnail的Linux串口编程笔记》,由于网上已经有非常多的类似文章,有些大论,有些短小精悍,连我自己都思考过是否有必要再写一篇,但在Ling的鼓动下还是写了!本篇是Linux串口编程笔记,详细介绍了串口通信会用到的api函数,并提供了一个示例程序,这个示例程序是在EEEPC701的debian系统上编写测试的。
一:写串口程序用到的函数1:Linux与windows串口设备文件名对照2:写串口程序用到的函数串行通讯函数定义在termios.h头文件中,所以需要包含该文件。
下面是要介绍的函数列表open用途:打开串口原型:int open( const char * pathname,int flags);参数说明:pathname: 指向欲打开的文件路径字符串flags 所能使用的标志位:O_RDONLY 以只读方式打开文件O_WRONLY 以只写方式打开文件O_RDWR 以可读写方式打开文件。
linux下串口驱动测试程序2009-12-26 20:42:20| 分类:技术| 标签:|字号大中小订阅【转载时请注明文章出处:】为了测试串口驱动,我们需要用串口驱动测试程序先向串口写入数据,然后再从串口读出来,以证明串口驱动的正确性。
下面我们来分析一下串口驱动测试程序的流程。
1、串口程序需要的头文件#include <stdio.h> /*标准输入输出定义*/#include <stdlib.h> /*标准函数库定义*/#include <unistd.h> /*Unix 标准函数定义*/#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h> /*文件控制定义*/#include <termios.h> /*PPSIX 终端控制定义*/#include <errno.h> /*错误号定义*/2、打开串口设备fd = open( "/dev/ttyS0", O_RDWR); 我们以/dev/ttyS0即一般机子上的COM1为例。
3、设置串口设备:串口设备的设置主要通过struct termio的结构体来完成:struct termio{ unsigned short c_iflag; /* 输入模式标志 */unsigned short c_oflag; /* 输出模式标志 */unsigned short c_cflag; /* 控制模式标志*/unsigned short c_lflag; /* 当前模式标志 */unsigned char c_line; /* line discipline */unsigned char c_cc[NCC]; /* 控制字 */};我们用下面函数实现具体的串口设置:int setup_port(int fd, int baud, int databits, int parity, int stopbits){struct termio term_attr;if (ioctl(fd, TCGETA, &term_attr) < 0) { //将fd所对应的终端信息存入termio结构,得到当前配置return -1;}memcpy(&oterm_attr, &term_attr, sizeof(struct termio)); //保持当前配置到oterm_attr,用于系统恢复term_attr.c_iflag &= ~(INLCR | IGNCR | ICRNL | ISTRIP);term_attr.c_oflag &= ~(OPOST | ONLCR | OCRNL);term_attr.c_lflag &= ~(ISIG | ECHO | ICANON | NOFLSH);term_attr.c_cflag &= ~CBAUD;term_attr.c_cflag |= CREAD | speed_to_flag(baud); //设置波特率//标志各种标志可选范围term_attr.c_cflag &= ~(CSIZE); //设置数据位长度switch (databits) {case 5:term_attr.c_cflag |= CS5; //设置数据位长度为5,下同break;case 6:term_attr.c_cflag |= CS6;break;case 7:term_attr.c_cflag |= CS7;break;case 8:default:term_attr.c_cflag |= CS8;break;}switch (parity) { //设置奇偶校验位case 1:term_attr.c_cflag |= (PARENB | PARODD); //奇校验break;case 2:term_attr.c_cflag |= PARENB; //偶校验term_attr.c_cflag &= ~(PARODD);break;case 0:default:term_attr.c_cflag &= ~(PARENB); //不校验break;}switch (stopbits) { //设置停止位case 2: //有停止位为两位,下同term_attr.c_cflag |= CSTOPB;break;case 1:default:term_attr.c_cflag &= ~CSTOPB;break;}term_attr.c_cc[VMIN] = 1; //更新设置,并且立即完成term_attr.c_cc[VTIME] = 0; //设置超时if (ioctl(fd, TCSETAW, &term_attr) < 0) { //将更改后的属性加载到设备中return -1;}if (ioctl(fd, TCFLSH, 2) < 0) { //将输入输出队列全部发出,清空串口中队列return -1;}return 0;}4、向串口写入数据:while ( (len = read(0, buf, MAX_BUF_SIZE)) > 0 ) { if (len == 1) { //如果当前缓冲区为空,则写入停止位,发送buf[0] = MY_END_CHAR;buf[1] = 0;write_data(fd, buf, len);break;}i = write_data(fd, buf, len); //发送缓冲区内容if (i == 0) {fprintf(stderr, "Send data error!\n");break;}}5、从串口读数据:len = MAX_BUF_SIZE;while (1) {i = read_data(fd, buf, len); //从串口读取数据if (i > 0) {//count += i;//fprintf(stderr, "Recv %d byte\n", i);printf("%s", buf);if (buf[i-1] == MY_END_CHAR) { //如果读入的倒数第二位为停止位,则中断接受程序break;}}}在具体的测试中,在PC机上gcc编译程序,然后用arm-linux-gcc编程程序发送到友善之臂2440的板子上,然后一边发送一边接受,这样如果发送的数据和接受的数据相同,则测试成功。
Linux驱动程序设计全文详解[日期:2007-10-19] 来源:Linux公社作者:Linuxidclinux驱动程序设计PROGRAM FOR BLOCK DEVICE DRIVER OF DEVFS TYPE对linux的devfs类型的驱动程序的编写可以从以下几大内容理解和入手:通过分析驱动程序源代码可以发现驱动程序一般可分三部分:核心数据结构;核心数据和资源的初始化,注册以及注消,释放;底层设备操作函数;A.核心数据结构struct file_operations fops 设备驱动程序接口struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char *, size_t, loff_t *);ssize_t (*write) (struct file *, const char *, size_t, loff_t *);int (*readdir) (struct file *, void *, filldir_t);unsigned int (*poll) (struct file *, struct poll_table_struct *);int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, struct dentry *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, u nsigned long);};block_device_operations 块设备驱动程序接口{ int (*open) (struct inode *, struct file *);int (*release) (struct inode *, struct file *);int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);int (*check_media_change) (kdev_t);int (*revalidate) (kdev_t);struct module *owner;};块设备的READ().WRITE()不在这里注册,而是在设备的读写请求队列里注册,内核在这里将调用通用的bl k_read(),blk_write().向读写队列发出读写请求.Linux 利用这些数据结构向内核注册open(),release(),ioctl(),check_media_change(),rvalidate()等函数的入口句柄.我们将要编写的open(),release(),ioctl(),check_media_change(),revalidate()等函数,将在驱动初始化的时候,通过一个此结构类型的变量向内核提供函数的入口.struct request_queue_t 设备请求队列的数据结构struct request_list {unsigned int count;unsigned int pending[2];struct list_head free;};struct request {struct list_head queue;int elevator_sequence;kdev_t rq_dev;int cmd; /* READ or WRITE */int errors;unsigned long start_time;unsigned long sector;unsigned long nr_sectors;unsigned long hard_sector, hard_nr_sectors;unsigned int nr_segments;unsigned int nr_hw_segments;unsigned long current_nr_sectors, hard_cur_sectors;void * special;char * buffer;struct completion * waiting;struct buffer_head * bh;struct buffer_head * bhtail;request_queue_t *q;};struct request_queue{/** the queue request freelist, one for reads and one for writes*/struct request_list rq;* The total number of requests on each queue*/int nr_requests;/** Batching threshold for sleep/wakeup decisions*/int batch_requests;/** The total number of 512byte blocks on each queue*/atomic_t nr_sectors;/** Batching threshold for sleep/wakeup decisions*/int batch_sectors;/** The max number of 512byte blocks on each queue*/int max_queue_sectors;/** Together with queue_head for cacheline sharing*/struct list_head queue_head;elevator_t elevator;request_fn_proc * request_fn;merge_request_fn * back_merge_fn;merge_request_fn * front_merge_fn;merge_requests_fn * merge_requests_fn;make_request_fn * make_request_fn;plug_device_fn * plug_device_fn;/** The queue owner gets to use this for whatever they like. * ll_rw_blk doesn't touch it.*/void * queuedata;/** This is used to remove the plug when tq_disk runs.struct tq_struct plug_tq;/** Boolean that indicates whether this queue is plugged or not.*/int plugged:1;/** Boolean that indicates whether current_request is active or* not.*/int head_active:1;/** Boolean that indicates you will use blk_started_sectors* and blk_finished_sectors in addition to blk_started_io* and blk_finished_io. It enables the throttling code to* help keep the sectors in flight to a reasonable value*/int can_throttle:1;unsigned long bounce_pfn;/** Is meant to protect the queue in the future instead of* io_request_lock*/spinlock_t queue_lock;/** Tasks wait here for free read and write requests*/wait_queue_head_t wait_for_requests;struct request *last_request;};缓冲区和对缓冲区相应的I/O操作在此任务队列中相关联,等待内核的调度.如果是字符设备就不需要此数据结构.而块设备的read(),write()函数则在buffer_queue的initize和设备请求队列进行处理请求时候传递给request _fn().struct request_queue_t{}设备请求队列的变量类型,驱动程序在初始化的时候需要填写request_fn().其他的数据结构还有I/O port,Irq,DMA 资源分配,符合POSIX标准的ioctl的cmd的构造和定义,以及描述设备自身的相关数据结构定义-如设备的控制寄存器的相关数据结构定义,BIOS里的参数定义,设备类型定义等.B.初始化和注册和注消,模块方式驱动程序的加载和卸载.设备驱动程序在定义了数据结构后,首先开始初始化:如I/O 端口的检查和登记,内核对I/O PORT的检查和登记提供了两个函数check_region(int io_port, int off_set)和request_region(int io_port, int off_set,char *devname).I/O Port登记后,就可以用inb()和outb()进行操作了 .还有DMA和Irq的初始化检查和登记,int request_irq(unsigned int irq ,void(*handle)(int,void *,struct pt_regs *),unsigned int long flags, const char *device);irq: 是要申请的中断。
Linux系统驱动开发1:Linux 设备通常划分为三种:字符设备、块设备和网络接口设备。
字符设备是指:那些只能一个字节一个字节读写数据的设备,不能随即读取设备内存中的某一数据。
其读取数据需要按照先后顺序,从这点上看,字符设备是面向数据流的设备。
常用的字符设备有鼠标、键盘、串口、控制台和LED等设备。
块设备是指:可以从设备的任意位置读取一定长度数据的设备。
其读取数据不必按照先后的顺序,可以定位到设备的某一具体的位置,读取数据。
常见的块设备有硬盘,磁盘,U 盘和SD卡。
每一个字符设备或者块设备都在/dev目录下对应一个设备文件。
进入/dev目录下,执行ls –l 命令,以C开头的是字符设备,以b开头的是块设备。
2:主设备号和次设备号一个字符设备或者一个块设备都有一个主设备号和次设备号。
主设备号和次设备号统称为设备号。
主设备号用来表示一个特定的驱动程序。
此设备号用来表示使用该驱动程序的各设备。
例如一个嵌入式系统,有两个LED指示灯,LED灯需要独立的打开或者关闭。
那么可以写一个LED灯的字符设备驱动程序,可以将其主设备号注册成5号设备,次设备号分别为1和2.这里次设备号为别表示两个LED灯。
2.1 主设备号和次设备号的表示:在linux内核中,设备号用dev_t类型来表示。
在linux 2.6.29.4中,dev_t定义为一个无符号长整型变量,如下:typedef u_long dev_t ;u_long 在32位机中占4个字节,在64位机中占8个字节,以32位机为例,其中高12位表示主设备号,低20位表示次设备号。
2.2 动态分配设备号和静态分配设备号静态分配设备号,就是驱动程序开发者静态的指定一个设备号。
对于一部分成用的设备,内核开发者已经为其分配了设备号,这些设备号可以在内核源码documentation/divice.txt文件中找到,如果只有开发者自己使用这些设备驱动程序,那么可以选择一个尚未使用的一个设备号。
OK6410A学习笔记二:嵌入式Linux驱动开发环境的配
置和测试
环境配置:Window7 32-bit OS + VMware Workstation 6.5 + Ubuntu9.10 FORLINX OK6410A 开发板
嵌入式Linux 驱动开发环境的搭建
1. 安装arm-linux-gcc 交叉编译器,并设置环境变量
2. 将飞凌提供的FORLINX_linux-
3.0.1.tar.gz 拷贝到/usr/src,解压并进入linux-
3.0.1 目录
3. 执行以下命令:#make oldconfig; #make prepare; 这两个命令用来测试交叉编译器和驱动开发需要的源文件包是否能正常使用,可能会出现提示arm-
linux-gcc 找不到的情况,这是因为步骤1 中交叉编译器没有配置好。
另外,可
以修改顶层Makefile 文件中的CROSS_COMPILE 来设置交叉编译器,然后执
行#make scripts/使能新的交叉编译器。
嵌入式Linux 驱动开发环境的测试
最好的方法莫过于用HelloWorld 实例来测试一下。
//hello.c
#include
#include
MODULE_LICENSE(“Dual BSD/GPL”);。
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的情况下,串口收到一个数据,然后再发送出这个数据。