首页 《通信软件设计》实验指导书

《通信软件设计》实验指导书

举报
开通vip

《通信软件设计》实验指导书《通信软件设计》实验指导书 《通信软件设计》 实 验 指 导 书 通信工程系计算机通信教研室 2008年6月 1 前 言 通信软件几乎存在于所有通信设备和系统,通信工程专业的学生应该了解通信软件的组成和主要设计要点,所以《通信软件设计》(Design of Communications Software)是通信工程专业一门重要的选修课。本课程技术性、实践性很强。 通信软件的开发平台和运行平台都是在某个操作系统之上,涉及到较多操作系统概念和技术。软件的设计基础是数据结构,这门课程也是操作系统、数据结构...

《通信软件设计》实验指导书
《通信软件 设计 领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计 》实验指导书 《通信软件设计》 实 验 指 导 书 通信工程系计算机通信教研室 2008年6月 1 前 言 通信软件几乎存在于所有通信设备和系统,通信工程专业的学生应该了解通信软件的组成和主要设计要点,所以《通信软件设计》(Design of Communications Software)是通信工程专业一门重要的选修课。本课程技术性、实践性很强。 通信软件的开发平台和运行平台都是在某个操作系统之上,涉及到较多操作系统概念和技术。软件的设计基础是数据结构,这门课程也是操作系统、数据结构、程序设计语言等基础课程的综合运用。 通信技术的范围很大,就其软件的类型来说,也非常复杂繁多。实验和课堂学时有限,只能选择目前流行的IP和LINUX作为实验环境和目标,目的是使学生了解通信软件的设计过程和 方法 快递客服问题件处理详细方法山木方法pdf计算方法pdf华与华方法下载八字理论方法下载 ,了解通信软件的核心问 快递公司问题件快递公司问题件货款处理关于圆的周长面积重点题型关于解方程组的题及答案关于南海问题 ,而非学习每个通信技术种类的软件开发和运行情况,比如程控交换机软件、以太网络交换机软件、第三层交换机软件等等。 本实验选择LINUX作为基础操作系统,所以实验要从LINUX的结构/组成、开发入手,掌握基本的编辑、编译、运行方法;再深入进程间通信、内核构造;最后是协议实现。 I 目 录 前 言............................................................................................................................................... I 目 录.............................................................................................................................................. II 实验要求........................................................................................................................................... 1 实验一 LINUX程序设计环境 ..................................................................................................... 2 1(实验目的..................................................................................................................................... 2 2(实验内容..................................................................................................................................... 2 3(实验步骤及调试环境 ................................................................................................................. 2 3.1调试环境 .................................................................................................................... 2 3.2实验步骤: ................................................................................................................ 2 3.3程序样本: ................................................................................................................ 3 3.3.1 c-h.c文件 ............................................................................................................ 3 3.3.2 makefile文件 ...................................................................................................... 3 实验二 利用LINUX的/dev/tty接口的通信程序设计 ............................................................... 4 1(实验目的..................................................................................................................................... 4 2(实验内容..................................................................................................................................... 4 3(实验步骤及调试环境 ................................................................................................................. 4 3.1调试环境 .................................................................................................................... 4 3.2实验步骤: ................................................................................................................ 4 3.3原理和程序样本: .................................................................................................... 5 3.3.1 串口简介 ............................................................................................................... 5 3.3.2 计算机串口的引脚说明 ....................................................................................... 5 3.3.3 串口操作 ............................................................................................................... 5 3.3.4 打开串口 ............................................................................................................... 6 3.3.5 设置串口 ............................................................................................................... 6 3.3.6 需要注意: ........................................................................................................... 10 3.3.7 读写串口 ............................................................................................................. 10 3.3.8 关闭串口 ............................................................................................................. 10 3.3.9 主程序例子 ......................................................................................................... 10 3.3.10 例子程序(样本1) ........................................................................................ 11 3.3.10.1 makfile文件 ................................................................................................ 11 3.3.10.2 头文件 ........................................................................................................... 12 3.3.10.3 tty_comm_init.c文件(初始化子程序库) ............................................ 12 3.3.10.4 tty_send_file.c文件(文件发送程序) ................................................ 18 3.3.10.5 tty_recv_file.c文件(文件接收程序) ................................................ 19 实验三 底层232通信程序 ......................................................................................................... 22 1(实验目的................................................................................................................................... 22 2(实验内容................................................................................................................................... 22 3(实验步骤及调试环境 ............................................................................................................... 22 3.1调试环境 .................................................................................................................. 22 3.2实验步骤: .............................................................................................................. 22 3.3原理和程序样本: .................................................................................................. 23 3.3.1程序功能 .............................................................................................................. 23 II 3.3.2 核外进程进入内核原理 ..................................................................................... 23 3.3.3 核内外的接口设备 ............................................................................................. 24 3.3.4 设备文件操作表和文件操作程序 ..................................................................... 24 3.3.5 设备加载和注销形式 ......................................................................................... 26 3.3.6 内核程序的特殊性 ............................................................................................. 26 3.3.7 232串口的控制和数据读写寄存器 .................................................................. 26 3.3.8 232串口中断服务程序 ...................................................................................... 27 3.3.9 XON/XOFF协议程序 ............................................................................................ 27 3.3.10 编写用户空间串口通信程序 ........................................................................... 28 3.3.11 例子程序(样本2) ........................................................................................ 28 3.3.11.1 makefile文件 .............................................................................................. 28 3.3.11.2 头文件 ........................................................................................................... 28 3.3.11.3 rs232_comm.c文件 ...................................................................................... 29 3.3.11.4 rs232_comm_send.c文件 ............................................................................ 30 3.3.11.5 rs232_comm_recv.c文件 ............................................................................ 32 3.3.11.6 rs232_comm_driver.c文件 ........................................................................ 34 实验四 状态机程序设计 ............................................................................................................. 48 1(实验目的................................................................................................................................... 48 2(实验内容................................................................................................................................... 48 3(实验步骤及调试环境 ............................................................................................................... 48 3.1调试环境 .................................................................................................................. 48 3.2实验步骤: .............................................................................................................. 49 3.3简化的SDLC规程 .................................................................................................... 49 3.3.1体系关系 .............................................................................................................. 49 3.3.2状态机设计 .......................................................................................................... 49 3.3.3帧结构 .................................................................................................................. 51 3.4系统结构与实验环境 .............................................................................................. 52 3.4.1系统结构 .............................................................................................................. 52 3.4.2 任务设置 ............................................................................................................. 53 3.4.3测试数据和运行 .................................................................................................. 54 3.5流程与数据结构 ...................................................................................................... 54 3.5.1数据流程与缓冲区/队列 .................................................................................... 54 3.5.2主要程序流程 ...................................................................................................... 55 3.5.2.1 S_open()流程 ................................................................................................. 55 3.5.2.2 S_write()流程 ............................................................................................... 55 3.5.2.3 S_close()流程 ............................................................................................... 55 3.5.2.4 文件发送流程(File_Send) ....................................................................... 55 3.5.2.5 R_read()流程 ................................................................................................. 56 3.5.2.6 文件接收流程(File_Recv) ....................................................................... 56 3.5.2.7 SDLC状态机处理流程(sdlc_state_process()) ........................................ 56 3.5.2.8事件队列结构(SDLC_event_q,sdlc_data)和SDLC_read()流程 ............... 58 3.5.2.9 232接口中断处理流程(232_intr()) .......................................................... 59 3.5.3 数据结构说明 ..................................................................................................... 59 3.5.4 程序样本(样本3) .......................................................................................... 60 III 3.5.4.1 makefile文件 ................................................................................................ 60 3.5.4.2 File_Copy.h 文件(上层文件传输头文件) ............................................. 61 3.5.4.3 File_Send.c 文件 ......................................................................................... 61 3.5.4.4 File_Recv.c 文件 ......................................................................................... 64 3.5.4.5 File_Copy.c 文件 ......................................................................................... 65 3.5.4.6 sdlc.h 文件 ................................................................................................... 66 3.5.4.7 Sdev.c 文件 ................................................................................................... 69 3.5.4.8 Rdev.c 文件 ................................................................................................... 72 3.5.4.9 sdlc.c 文件 ................................................................................................... 75 3.5.4.10 Msdlc.c 文件 ............................................................................................... 76 3.5.4.11 Ssdlc.c 文件 ............................................................................................... 90 实验五 IP交换软件设计 ............................................................................................................ 91 1(实验目的................................................................................................................................... 91 2(实验内容................................................................................................................................... 91 3(实验步骤及调试环境 ............................................................................................................... 92 3.1调试环境 .................................................................................................................. 92 3.2实验步骤: .............................................................................................................. 92 3.2.1实验五.1实验步骤: ......................................................................................... 92 3.2.2实验五.2实验步骤: ......................................................................................... 94 3.3原理和要求 .............................................................................................................. 94 3.3.1程序功能 .............................................................................................................. 94 3.3.2 LINUX操作系统内核原理 .................................................................................. 94 3.3.3 IP原理和简化的IP转发程序功能要求 .......................................................... 94 3.3.3.1 IP数据包格式 ................................................................................................ 95 3.3.3.2 IP路由表 ........................................................................................................ 96 3.3.3.3路由选择算法 .................................................................................................. 96 3.3.3.4 路由表的建立与刷新 ..................................................................................... 97 3.3.3.5 路由软件应处理的主要内容 ......................................................................... 97 3.3.3.6 简化的路由程序 ............................................................................................. 98 3.3.3.7 简单的维护程序 ............................................................................................. 98 3.4 缓冲区队列设计 ..................................................................................................... 98 3.4.1 自由队列 ............................................................................................................. 98 3.4.2 接收队列 ............................................................................................................. 98 3.4.3 发送队列 ............................................................................................................. 98 3.5 程序流程设计 ......................................................................................................... 99 3.5.1 文件接收和发送程序流程 ................................................................................. 99 3.5.2 232驱动程序流程 .............................................................................................. 99 3.5.3 简化的SDLC程序流程 ....................................................................................... 99 3.5.4 IP初始化和管理程序程序流程 ........................................................................ 99 3.5.5 IP转发程序流程 .............................................................................................. 100 3.5.6 缓冲区操作程序流程 ....................................................................................... 101 3.6 完整程序 ............................................................................................................... 101 实验六 RH LINUX 2.4版本下的U盘使用 ............................................................................ 102 1(实验目的................................................................................................................................. 102 IV 2(实验内容................................................................................................................................. 102 3(实验步骤及调试环境 ............................................................................................................. 102 3.1调试环境 ................................................................................................................ 102 3.2实验步骤: ............................................................................................................ 102 V 实验要求 《通信软件设计》实验课程主要是编写程序和调试程序,要求在知道某个通信协议的情况下,学会如何在设备中实现这个协议。因此学会开发环境的搭建、运行、编写程序、编辑、编译、运行、试验等等方法和步骤。 本指导书已经写出了初步的实验步骤和程序样本。整体步骤和完整程序必须要求学生自己去完成。所以在《通信软件设计》的课程实验过程中,要求学生做到: (1)预习实验指导书有关部分,认真做好实验内容的准备,就实验可能出现的情况提前作出思考和分析,提前编写代码。 (2)仔细观察调试时出现的各种现象,记录主要情况,作出必要说明和分析。 (3)认真书写 实验报告 化学实验报告单总流体力学实验报告观察种子结构实验报告观察种子结构实验报告单观察种子的结构实验报告单 。实验报告包括实验目的和要求,实验情况及其分析。对需编程的实验,写出程序设计说明,给出源程序框图和清单。 (4)遵守机房纪律,服从老师指挥,爱护实验设备。 (5)实验课程不迟到。如有事不能出席,必须请假,且所缺实验不补。 实验的验收将分为两个部分。第一部分是上机操作,包括检查程序运行、设备配置和即时提问。第二部分是提交书面的实验报告。上机检查逐个进行,按初步规定的时间检查,都将应当在规定的时间内完成并检查通过,过期视为未完成该实验,不计成绩。希望同学们抓紧时间,合理安排,认真完成。 实验报告格式: 1)实验名称 三号字体、宋体 2)实验日期、地点 五号字体、宋体 3)班级、年级、学号、姓名 五号字体、宋体 4)实验目的 五号字体、标题黑体、内容宋体 5)实验环境 五号字体、标题黑体、内容宋体 6)实验步骤 五号字体、标题黑体、内容宋体 7)实验关键程序分析 五号字体、标题黑体、内容宋体 8)实验过程分析和体会 五号字体、标题黑体、内容宋体 9)实验结论分析 五号字体、标题黑体、内容宋体 1 实验一 LINUX程序设计环境 (Linux环境下的C语言源程序编辑、源程序编译、可执行文件的执行) 1(实验目的 掌握LINUX程序设计的基本方法和LINUX的编辑和编译操作。 2(实验内容 用C语言编写和调试一个在显示器上显示“Hello,XX~”的程序。XX可替代任何字串。 3(实验步骤及调试环境 3.1调试环境 PC以及Linux操作系统。 3.2实验步骤: 1) 安装Linux操作系统(请找到合适的PC机器上安装,学校实验室-省略该步骤); 2) 启动Linux操作系统,用root用户登陆; 3) 用鼠标寻找主菜单-->系统设置-->用户和组管理-->添加用户,产生一个普通用户(自己 的选择,比如stu1);[注意观察这个用户的主 目录 工贸企业有限空间作业目录特种设备作业人员作业种类与目录特种设备作业人员目录1类医疗器械目录高值医用耗材参考目录 在整个Linux中的位置] 4) 在主菜单中注销并退出root用户,然后再用你产生的用户(比如stu1)登陆系统; 5) 在桌面上空白处点击鼠标右键,然后“新建终端”,并进入终端窗口;[终端是命令行操 作模式] 6) 在终端命令提示状态下,用mkdir c-test命令做一个子目录(自己的选择,比如c-test), 并用cd c-test命令进入这个子目录; 7) 在这个目录下用vi c-h.c编辑源程序文件;[学习vi编辑器或其它编辑器] 8) 用vi makefile编辑makefile文件; 9) 用make命令编译你刚输入的源程序; 10) 在目录/home/stu1/c-test下,出现命令行提示符号$,输入./c-h lili就可以执行你编写的 程序。观察结果,思考编辑、编译、执行三者关系。 11) 在主菜单中选择关闭系统。 2 3.3程序样本: 3.3.1 c-h.c文件 int main(int argc, char *argv[]) { if (argc < 2) { printf(“命令参数不正确\n”); exit(-1); } printf(“\nHello,%s!\n”,argv[1]); exit(1); } 3.3.2 makefile文件 c-h:c-h.o gcc -o c-h c-h.c 3 实验二 利用LINUX的/dev/tty接口的通信程序设计 (利用Linux 串口设备文件编通信程序) 1(实验目的 掌握LINUX程序设计的基本方法和LINUX的操作;掌握LINUX串口文件的使用,学会如何在LINUX之上采用串口作通信。 2(实验内容 用C语言编写一个基于/dev/ttySx串口设备的文件发送通信程序和文件接收通信程序(利用LINUX提供的串口内核程序实现),然后编辑输入和编译调试。完成任务的标志是两个机器之间通过232电缆实现任何文件的传输。 文本文本文件文件 发送程序接收程序 (进程)(进程) /dev/ttySx/dev/ttySx ttySx设备ttySx设备 接口程序接口程序 COMx硬件接口COMx硬件接口 驱动程序驱动程序 接口接口 3(实验步骤及调试环境 3.1调试环境 PC以及Linux操作系统。 3.2实验步骤: 12) 脱机编写基于/dev/ttySx串口设备的通信程序(参见样本1); 13) 做一个普通用户并这个普通用户登陆系统; 4 14) 做一个子目录,并在这个目录下编辑文件; 15) 学习vi编辑器或其它编辑器; 16) 编辑输入程序和编辑makefile文件; 17) 编译程序; 18) 执行程序; 19) 退出系统,关闭系统。 3.3原理和程序样本: 3.3.1 串口简介 串行口是计算机一种常用的接口,具有连接线少,通讯简单,得到广泛的使用。常用的串口是 RS-232-C 接口(又称 EIA RS-232-C)它是在 1970 年由美国电子工业协会(EIA)联合贝尔系统、 调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。它的全名是"数据终端设备(DTE)和数据通讯设备(DCE)之间串行二进制数据交换接口技术标准"该标准规定采用一个 25 个脚的 DB25 连接器,对连接器的每个引脚的信号内容加以规定,还对各种信号的电平加以规定。传输距离在码元畸变小于 4% 的情况下,传输电缆长度应为 15 米。 Linux 操作系统从一开始就对串行口提供了很好的支持,需要详细了解串口使用,建议看参考书 《Serial Programming Guide for POSIX Operating Systems》。 3.3.2 计算机串口的引脚说明 序号 信号名称 符号 流向 功能 2 发送数据 TXD DTE?DCE DTE发送串行数据 3 接收数据 RXD DTE?DCE DTE 接收串行数据 4 请求发送 RTS DTE?DCE DTE 请求 DCE 将线路切换到发送方式 5 允许发送 CTS DTE?DCE DCE 告诉 DTE 线路已接通可以发送数据 6 数据设备准备好 DSR DTE?DCE DCE 准备好 7 信号地 信号公共地 8 载波检测 DCD DTE?DCE 表示 DCE 接收到远程载波 20 数据终端准备好 DTR DTE?DCE DTE 准备好 22 振铃指示 RI DTE?DCE 表示 DCE 与线路接通,出现振铃 3.3.3 串口操作 串口操作需要的头文件 5 #include /*标准输入输出定义*/ #include /*标准函数库定义*/ #include /*Unix 标准函数定义*/ #include #include #include /*文件控制定义*/ #include /*PPSIX 终端控制定义*/ #include /*错误号定义*/ 3.3.4 打开串口 在 Linux 下串口文件是位于 /dev 下的 串口一 为 /dev/ttyS0 串口二 为 /dev/ttyS1 打开串口是通过使用标准的文件打开函数操作: int fd; /*以读写方式打开串口*/ fd = open( "/dev/ttyS0", O_RDWR); if (-1 == fd) { /* 不能打开串口一*/ perror(" 提示错误~"); } 3.3.5 设置串口 最基本的设置串口包括波特率设置,效验位和停止位设置。 串口的设置主要是设置 struct termios 结构体的各成员值。 struct termio { unsigned short c_iflag; /* 输入模式标志 */ unsigned short c_oflag; /* 输出模式标志 */ unsigned short c_cflag; /* 控制模式标志*/ unsigned short c_lflag; /* local mode flags */ unsigned char c_line; /* line discipline */ unsigned char c_cc[NCC]; /* control characters */ }; 设置这个结构体很复杂,这里就只说说常见的一些设置: 波特率设置 下面是修改波特率的代码 struct termios Opt; tcgetattr(fd, &Opt); cfsetispeed(&Opt,B19200); /*设置为19200Bps*/ cfsetospeed(&Opt,B19200); tcsetattr(fd,TCANOW,&Opt); 设置波特率的例子函数: /** set_speed(fd, speed) 设置串口通信速率 参数 fd 类型 int 打开串口的文件句柄 6 参数 speed 类型 int 串口速度 返回 void */ int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300, B38400, B19200, B9600, B4800, B2400, B1200, B300, }; int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300, 38400, 19200, 9600, 4800, 2400, 1200, 300, }; void set_speed(int fd, int speed) { int i; int status; struct termios Opt; tcgetattr(fd, &Opt); for (i= 0; i < sizeof(speed_arr) / sizeof(int); i++) { if (speed == name_arr[i]) { tcflush(fd, TCIOFLUSH); cfsetispeed(&Opt, speed_arr[i]); cfsetospeed(&Opt, speed_arr[i]); status = tcsetattr(fd1, TCSANOW, &Opt); if (status != 0) { perror("tcsetattr fd1"); return; } tcflush(fd,TCIOFLUSH); } } } 效验位和停止位的设置: Option.c_cflag &= ~PARENB; Option.c_cflag &= ~CSTOPB; 无效验 8位 Option.c_cflag &= ~CSIZE; Option.c_cflag |= ~CS8; Option.c_cflag |= ~PARENB; Option.c_cflag &= ~PARODD; 奇效验(Odd) 7位 Option.c_cflag &= ~CSTOPB; Option.c_cflag &= ~CSIZE; Option.c_cflag |= ~CS7; Option.c_cflag &= ~PARENB; 偶效验(Even) 7位 Option.c_cflag |= ~PARODD; Option.c_cflag &= ~CSTOPB; 7 Option.c_cflag &= ~CSIZE; Option.c_cflag |= ~CS7; Option.c_cflag &= ~PARENB; Option.c_cflag &= ~CSTOPB; Space效验 7位 Option.c_cflag &= &~CSIZE; Option.c_cflag |= CS8; 设置效验的函数: /** set_Parity(fd, databits,stopbits,parity) 设置串口数据位,停止位和效验位 参数 fd 类型 int 打开的串口文件句柄 参数 databits 类型 int 数据位 取值 为 7 或者8 参数 stopbits 类型 int 停止位 取值为 1 或者2 参数 parity 类型 int 效验类型 取值为,E,O,N,S 返回 整数(TRUE成功,FALSE错误) */ int set_Parity(int fd,int databits,int stopbits,int parity) { struct termios options; if ( tcgetattr( fd,&options) != 0) { perror("SetupSerial 1"); return(FALSE); } options.c_cflag &= ~CSIZE; switch (databits) /*设置数据位数*/ { case 7: options.c_cflag |= CS7; break; case 8: options.c_cflag |= CS8; break; default: fprintf(stderr,"Unsupported data size\n"); return (FALSE); } switch (parity) { case 'n': case 'N': options.c_cflag &= ~PARENB; /* Clear parity enable */ options.c_iflag &= ~INPCK; // Enable parity checking break; case 'o': 8 case 'O': options.c_cflag |= (PARODD | PARENB); // 设置为奇效验 options.c_iflag |= INPCK; // Disnable parity checking */ break; case 'e': case 'E': options.c_cflag |= PARENB; /* Enable parity */ options.c_cflag &= ~PARODD; /* 转换为偶效验*/ options.c_iflag |= INPCK; /* Disnable parity checking */ break; case 'S': case 's': /*as no parity*/ options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB;break; default: fprintf(stderr,"Unsupported parity\n"); return (FALSE); } /* 设置停止位*/ switch (stopbits) { case 1: options.c_cflag &= ~CSTOPB; break; case 2: options.c_cflag |= CSTOPB; break; default: fprintf(stderr,"Unsupported stop bits\n"); return (FALSE); } /* Set input parity option */ if (parity != 'n') options.c_iflag |= INPCK; tcflush(fd,TCIFLUSH); options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/ options.c_cc[VMIN] = 0; /* Update the options and do it NOW */ if (tcsetattr(fd,TCSANOW,&options) != 0) { perror("SetupSerial 3"); return (FALSE); } 9 return (TRUE); } 3.3.6 需要注意: 如果不是开发终端之类的,只是串口传输数据,而不需要串口来处理,那么使用原始模式(Raw Mode)方式来通讯,设置方式如下: options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/ options.c_oflag &= ~OPOST; /*Output*/ 3.3.7 读写串口 设置好串口之后,读写串口就很容易了,把串口当作文件读写就是。 发送数据 char buffer[1024]; int Length; int nByte = write(fd, buffer ,Length); 读取串口数据 使用文件操作read函数读取,如果设置为原始模式(Raw Mode)传输数据,那么read函数返回的字符数是实际串口收到的字符数。 可以使用操作文件的函数来实现异步读取,如fcntl,或者select等来操作。 char buff[1024]; int Len; int readByte = read(fd,buff,Len); 3.3.8 关闭串口 关闭串口就是关闭文件。 close(fd); 3.3.9 主程序例子 下面是一个简单的读取串口数据的例子,使用了上面定义的一些函数和头文件 /******************************************************* 代码说明:使用串口二测试的,发送的数据是字符, 但是没有发送字符串结束符号,所以接收到后,后面加上了结束符号。 ***********************************************************/ #define FALSE -1 #define TRUE 0 /**********************************************************/ int OpenDev(char *Dev) { int fd = open( Dev, O_RDWR ); //| O_NOCTTY | O_NDELAY if (-1 == fd) { perror("Can't Open Serial Port"); 10 return -1; } else return fd; } int main(int argc, char **argv) { int fd; int nread; char buff[512]; char *dev = "/dev/ttyS1"; //串口二 fd = OpenDev(dev); set_speed(fd,19200); if (set_Parity(fd,8,1,'N') == FALSE) { printf("Set Parity Error\n"); exit (0); } while (1) //循环读取数据 { While ((nread = read(fd, buff, 512)) > 0) { printf("\nLen %d\n",nread); buff[nread+1] = '\0'; printf( "\n%s", buff); } } // close(fd); // exit (0); } 3.3.10 例子程序(样本1) 3.3.10.1 makfile文件 all:send recv send:tty_send_file.o tty_comm_init.o gcc -o send tty_send_file.o tty_comm_init.o tty_send_file.o:tty_send_file.c tty_comm.h gcc -c tty_send_file.c recv:tty_recv_file.o tty_comm_init.o gcc -o recv tty_recv_file.o tty_comm_init.o tty_recv_file.o:tty_recv_file.c tty_comm.h gcc -c tty_recv_file.c 11 clean:# rm *.o send recv tty_comm_init.o:tty_comm_init.c tty_comm.h gcc -c tty_comm_init.c 3.3.10.2 头文件 #define FALSE 0 #define TRUE 1 #define NAME_LEN 50 #define BUFFER_LEN 512 #define C1 '*' #define E1 'Q' #define E2 'T' #define S_N 0 #define S_E1 1 #define S_E2 2 #define S_C 3 void get_file_end_flag(char *, int *); int check_file_end(char *, int *); void insert_chang_char(char *, int *); 3.3.10.3 tty_comm_init.c文件(初始化子程序库) //-------------------------------------------------- #include /*标准输入输出定义*/ #include /*标准函数库定义*/ #include /*Unix标准函数定义*/ #include /**/ #include /**/ #include /*文件控制定义*/ #include /*PPSIX终端控制定义*/ #include /*错误号定义*/ #include /*错误号定义*/ #include "tty_comm.h" 12 /* void set_speed(fd, speed) 设置串口通信速率 参数 fd 类型 int 打开串口的文件句柄 参数 speed 类型 int 串口速度 返回 void */ int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300, B38400, B19200, B9600, B4800, B2400, B1200, B300, }; int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300, 38400, 19200, 9600, 4800, 2400, 1200, 300, }; void set_speed(int fd, int speed) { int i; int status; struct termios Opt; tcgetattr(fd, &Opt); for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++) { if (speed == name_arr[i]) { tcflush(fd, TCIOFLUSH); cfsetispeed(&Opt, speed_arr[i]); cfsetospeed(&Opt, speed_arr[i]); status = tcsetattr(fd, TCSANOW, &Opt); if (status != 0) perror("tcsetattr fd1"); return; } tcflush(fd, TCIOFLUSH); } } /** int set_Parity(fd, databits, stopbits, parity) 设置串口数据位,停止位和效验位 参数 fd 类型 int 打开的串口文件句柄* 参数 databits 类型 int 数据位 取值 为 7 或者8* 参数 stopbits 类型 int 停止位 取值为 1 或者2* 参数 parity 类型 int 效验类型 取值为N,E,O,S */ int set_Parity(int fd,int databits,int stopbits,int parity) { struct termios options; if (tcgetattr(fd, &options) != 0) { 13 perror("SetupSerial 1"); return(FALSE); } options.c_cflag &= ~CSIZE; switch (databits) /*设置数据位数*/ { case 7: options.c_cflag |= CS7; break; case 8: options.c_cflag |= CS8; break; default: fprintf(stderr,"Unsupported data size\n"); return (FALSE); } switch (parity) { case 'n': case 'N': options.c_cflag &= ~PARENB; /* Clear parity enable */ options.c_iflag &= ~INPCK; // Enable parity checking break; case 'o': case 'O': options.c_cflag |= (PARODD | PARENB); //设置为奇效验 options.c_iflag |= INPCK; /* Disnable parity checking */ break; case 'e': case 'E': options.c_cflag |= PARENB; /* Enable parity */ options.c_cflag &= ~PARODD; /* 转换为偶效验*/ options.c_iflag |= INPCK; /* Disnable parity checking */ break; case 'S': case 's': /*as no parity*/ options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; break; default: fprintf(stderr,"Unsupported parity\n"); return (FALSE); } 14 switch (stopbits) /* 设置停止位*/ { case 1: options.c_cflag &= ~CSTOPB; break; case 2: options.c_cflag |= CSTOPB; break; default: fprintf(stderr,"Unsupported stop bits\n"); return (FALSE); } if (parity != 'n') /* Set input parity option */ options.c_iflag |= INPCK; options.c_cc[VTIME] = 150; // 15 seconds options.c_cc[VMIN] = 0; tcflush(fd,TCIFLUSH); /* Update the options and do it NOW */ if (tcsetattr(fd,TCSANOW,&options) != 0) { perror("SetupSerial 3"); return (FALSE); } return (TRUE); } /* * 打开串口 */ int OpenDev(char *Dev) { int fd = open( Dev, O_RDWR ); //| O_NOCTTY | O_NDELAY if (-1 == fd) { /*设置数据位数*/ perror("Can't Open Serial Port"); return -1; } else return fd; } ///////////////////////////// 文件结束字段 void get_file_end_flag(char *bufp, int *buf_len_p) //生成文件结束字串 { char *p = bufp; *p++ = E1; *p++ = E2; *buf_len_p = 2; 15 } void memncopy(char *t1, char *f1, int n) { register char *t = t1, *f = f1; register int i; for (i = 0; i < n; i++) *t++ = *f++; } static int EndStat = S_N; int check_file_end(char *bufp, int *buf_len_p) //检查文件传输完成否? { char *p, *p1 = bufp; int ti = 0, bi = 0, ei = *buf_len_p; int r = FALSE; p = malloc(ei); if (p == NULL) return FALSE; memncopy(p, bufp, ei); while (bi < ei) { *p1++ = *p++; bi++; ti++; switch (*p) { case E1: EndStat = S_E1; break; case E2: if (EndStat == S_E1) { EndStat = S_N; r = TRUE; goto leave; } break; case C1: if (EndStat == S_C) { EndStat = S_N; } else { EndStat = S_C; p1--; ti--; } break; default: EndStat = S_N; break; 16 } } leave: *buf_len_p = bi; free(p); return r; } void insert_change_char(char *bufp, int *buf_len_p) //插入文件传输完 成转意字符 { char *p, *p1 = bufp; int ti = 0, bi = 0, ei = *buf_len_p; int r = FALSE; p = malloc(ei); if (p == NULL) { *buf_len_p = 0; return ; } memncopy(p, bufp, ei); while (bi < ei) { switch (*p) { case E1: EndStat = S_E1; break; case E2: if (EndStat == S_E1) { *p1++ = C1; ti++; } EndStat = S_N; break; case C1: *p1++ = C1; ti++; EndStat = S_N; break; default: EndStat = S_N; break; } *p1++ = *p++; bi++; ti++; } *buf_len_p = ti; free(p); 17 } 3.3.10.4 tty_send_file.c文件(文件发送程序) //-------------------------------------------------- #include /*标准输入输出定义*/ #include /*标准函数库定义*/ #include /*Unix标准函数定义*/ #include /**/ #include /**/ #include /*文件控制定义*/ #include /*PPSIX终端控制定义*/ #include /*错误号定义*/ #include "tty_comm.h" int main(int argc, char **argv) { int serial_fd, s_file_fd; int nread; char buff[BUFFER_LEN]; char dev[NAME_LEN], send_file_name[NAME_LEN]; if (argc < 3) { printf("Usge: send /dev/ttySx local_file_name\n"); exit(-3); } bzero(dev, NAME_LEN); bzero(send_file_name, NAME_LEN); strcpy(dev, argv[1]); strcpy(send_file_name, argv[2]); s_file_fd = open(send_file_name, O_RDWR); if (s_file_fd == -1) { printf("\nCan't open file: %s\n", send_file_name); exit(-1); } serial_fd = OpenDev(dev); if (serial_fd > 0) set_speed(serial_fd, 19200); else { printf("Can't Open Serial Port!\n"); exit(-2); 18 } if (set_Parity(serial_fd, 8, 1, 'N') == FALSE) { printf("Set Parity Error\n"); exit(-2); } while(1) { nread = read(s_file_fd, buff, BUFFER_LEN/2); //读普通文件有三个返回值: 0 - 文件没有数据; >0 - 有数据; =-1 - 错误 if (nread == -1) { printf("Read Serial dev File Error\n"); exit(-1); } if (nread == 0) { get_file_end_flag(buff, &nread); if (write(serial_fd, buff, nread) == -1) { printf("Write Local File Error\n"); exit(-2); } break; //文件传输完毕 } insert_change_char(buff, &nread, 1); //插入文件传输完成转意字符 if (write(serial_fd, buff, nread) == -1) { printf("Write Local File Error\n"); exit(-2); } } close(serial_fd); close(s_file_fd); exit(0); } 3.3.10.5 tty_recv_file.c文件(文件接收程序) //-------------------------------------------------- #include /*标准输入输出定义*/ #include /*标准函数库定义*/ #include /*Unix标准函数定义*/ #include /**/ #include /**/ #include /*文件控制定义*/ #include /*PPSIX终端控制定义*/ #include /*错误号定义*/ #include "tty_comm.h" 19 int main(int argc, char **argv) { int serial_fd, r_file_fd; int nread; char buff[BUFFER_LEN]; char dev[NAME_LEN], recv_file_name[NAME_LEN]; if (argc < 3) { printf("Usge: recv /dev/ttySx local_file_name\n"); exit(-3); } bzero(dev, NAME_LEN); bzero(recv_file_name, NAME_LEN); strcpy(dev, argv[1]); strcpy(recv_file_name, argv[2]); r_file_fd = open(recv_file_name, O_RDWR|O_CREAT|O_TRUNC); if (r_file_fd == -1) { printf("\nCan't open file: %s\n", recv_file_name); exit(-1); } serial_fd = OpenDev(dev); if (serial_fd > 0) set_speed(serial_fd, 19200); else { printf("Can't Open Serial Port!\n"); exit(-2); } if (set_Parity(serial_fd, 8, 1, 'N') == FALSE) { printf("Set Parity Error\n"); exit(-2); } while(1) { int file_end_flag; while((nread = read(serial_fd, buff, BUFFER_LEN/2)) > 0) { //读串口设备文件有三个返回值: 0 - 可能缓冲区没有数据; >0 - 有数据; =-1 - 错误 if (nread == -1) { printf("Read Serial dev File Error\n"); exit(-2); } file_end_flag = check_file_end(buff, &nread); //到尾部,文 20 件传输完成 if (nread != 0 && write(r_file_fd, buff, nread) == -1) { printf("Write Local File Error\n"); exit(-1); } if (file_end_flag == TRUE) break; } } close(serial_fd); close(r_file_fd); exit(0); } 21 实验三 低层232通信程序 (基于Linux 232串口接口的驱动程序以及通信软件设计) 1(实验目的 学习内核程序设计,了解内核空间和用户空间关系;学习进程间通信;学习低层通信软件程序设计和简单的流控协议程序设计。 2(实验内容 根据实验提出的软件功能要求编写接收文件进程(程序)、发送文件进程(程序)、内核系统调用接口(RS232设备)程序和232硬件接口驱动程序,然后编辑输入和编译调试。完成任务的标志是两个机器之间通过232电缆实现任何文件的传输。 任意类型任意类型文件文件 发送程序接收程序 (进程)(进程) /dev/RS232_Test/dev/RS232_Test RS232设备RS232设备 接口程序接口程序 232硬件接口232硬件接口 驱动程序驱动程序 接口接口 3(实验步骤及调试环境 3.1调试环境 PC以及Linux操作系统。 3.2实验步骤: 1) 脱机编写要求的驱动程序和文件传输程序; 2) 用ROOT用户登陆系统,并产生若干个操作终端; 3) 进入(cd)源程序以及调试程序的子目录; 22 4) 用ll命令了解LINUX设备目录以及目录下的设备名称、主设备号、次设备号; 5) 制造(mknod)一个接口通信的设备;[先选择好设备名称、主设备号、次设备号]; 6) 编辑(vi)内核程序和编辑makefile文件 7) 编译(make)驱动程序; 8) 动态加载模块(insmod); 9) 编辑(vi)核外程序; 10) 编译(make)核外文件传输程序; 11) 启动和调试整个系统; 12) 上机完毕:退出系统,关闭系统。 3.3原理和程序样本: 3.3.1程序功能 在 Linux 环境或者嵌入式 Linux 中,串口上已经开发好完整使用的通信协议,比如支持终端的行协议和网络上网用的PPP、SLIP等协议。本课程需要从最底层来了解一个通信协议是如何实现的,所以本实验要求从底层设计一个可以实现文件传输的软件系统。程序模块将包括文件接收程序、文件发送程序、内核系统调用程序、232硬件接口驱动程序。 3.3.2 核外进程进入内核原理 核外进程通过标准的open()、close()、read()、write()、ioctl()等等函数进入内核,实际上open()等函数的程序实体是放在内核空间的,而open()等仅仅是入口调用而已,这个调用称为系统调用。系统调用是通过软中断的方式进入内核的。下图表示device用RS232替换。 23 3.3.3 核内外的接口设备 内核的接口程序非常多,核外进程如何调用内核的具体程序呢,这个问题是通过建设设备文件来实现。在/dev目录下几乎都是设备文件,我们可以用mknod命令来完成: mknod /dev/RS232_Test c 253 0,其中/dev/RS232_Test是接口设备文件,c是字符设备类型,253是主设备号码,0是次设备号码。open(“/dev/RS232_Test”, „)函数的执行来获得253和0这两个号码,主设备号码就是选择内核程序实体的依据,次设备号码用来使得内核程序区别具体控制或驱动哪台设备。 3.3.4 设备文件操作表和文件操作程序 struct file_operations是内核的一个数据类型,称为设备文件操作表,相应的接口程序的地址(指针)都要放在这个表中,每个接口设备文件有一个,系统调用时通过主设备号码找到这个表,并调用相应的程序实体。 样本实例如下:(LINUX 2.4.20版本) struct file_operations RS232_Fops = { open: RS232_open, release: RS232_release, /* close */ read: RS232_read, write: RS232_write, // nothing more, fill with NULLs }; 字符设备文件操作结构RS232_Fops中定义的指向以上函数的函数指针的原形: static int RS232_open(struct inode *, struct file *); static int RS232_release(struct inode *, struct file *); static int RS232_read(struct file *, char *, size_t, loff_t *); static int RS232_write(struct file *, const char *, size_t, loff_t *); 操作int RS232_open(struct inode *inode,struct file *file)是设备节点上的第一个操作,如果多个设备共享这一个操作函数,必须区次设备号区分。 操作int RS232_read(struct file *file,char *buffer,size_t length, loff_t *offset)是读取设备数据的操作。参数含义如下图所示(device用RS232替换)。 24 从设备中读取数据(用户空间调用read()系统调用)的时候,需要从内核空间把数据拷贝到用户空间,put_user ()或copy_to_user()可完成此功能,它和memcpy()此类函数有本质的区别,memcpy()不能完成不同用户空间数据的交换。如果需要数据临界区的保护,使用spin_lock()内核API负责加锁(或关闭中断),spin_unlock()负责解锁(或打开中断),防止数据污染。程序代码如下: { cli(); //关中断 // 如缓冲区RS232_Buffer_R空,则等待;‘空’ == RS232_Buffer_R_H == RS232_Buffer_R_T while (RS232_Buffer_R_H == RS232_Buffer_R_T) { interruptible_sleep_on(&RS232_dev_buffer_RP); } // 循环拷贝数据到buffer,每次一个字节。缓冲区空或需要拷贝的字节数为0,则循环结束; while (length && (RS232_Buffer_R_H != RS232_Buffer_R_T)) { put_user(RS232_Buffer_R[RS232_Buffer_R_H], buffer++); RS232_Buffer_R_H = NextLoc(RS232_Buffer_R_H); length--; i++; } sti(); //开中断 return i; // } 其中interruptible_sleep_on(&RS232_dev_buffer_RP)表示读取的缓冲区空时将在 RS232_dev_buffer_RP队列中等待(有数据到达时被唤醒),RS232_dev_buffer_RP的说明语句是:wait_queue_head_t RS232_dev_buffer_RP; 25 3.3.5 设备加载和注销形式 本实验的程序的整个内核部分是以LKM(Loadable Kernel Module)形式实现的。LKM加载的时候完成伪网络设备、发送字符设备、接收字符设备的初始化和注册。注册的目的是让操作系统可以识别用户进程所要操作的设备,并完成在其上的操作(比如read,write等系统调用)。Linux加载模块,实际上就是将文件操作表插入模块链表;删除模块是将文件操作表从模块链表中删除。 初始化内核模块入口函数init_module()调用result = register_chrdev(RS232_Major, RS232_interface, &RS232_Fops)来完成插入文件操作表;调用request_irq(RS232_irq, &RS232_interrupt, SA_SHIRQ, RS232_interface, &RS232_irq)来完成232中断服务程序的地址初始化;内核模块离开函数cleanup_module()调用free_irq(RS232_irq, &RS232_irq) 来实现清除中断服务程序;调用unregister_chrdev(RS232_MAJOR, RS232_interface)来实现删除文件操作表。 命令insmod对应init_module()函数,命令rmmod对应cleanup_module()函数。 3.3.6 内核程序的特殊性 由于内核空间和进程核外空间的差异,一些标准C语言函数在内核是不能用的,举2个例子:printf()和malloc(),在内核改成printk()和kmalloc()。 3.3.7 232串口的控制和数据读写寄存器 1、8250的内部寄存器的端口地址 端口地址 输入/输出 名称-用途 0x3f8* Out 写发送保持寄存器。含有将要发送的字符。 0x3f8* In 读接收缓冲寄存器。含有将要接收的字符。 0x3f8** Out/in 读写波特率因子(LSB)- 低字节 0x3f9** Out/in 读写波特率因子(MSB)- 高字节 0x3f9* Out/in 读写中断允许寄存器。位1-发送空中断,位0-数据到达中断。 0x3fa In 读中断识别寄存器。位12-10-数据到达,位12-01-数据发送。 0x3fb Out 写线路控制寄存器。232传输方式。位7-1-DLAB 0x3fc Out 写modem控制寄存器。位3-1-允许中断。 26 0x3fd In 读线路状态寄存器。错误识别。 0x3fe In 读modem状态寄存器。了解modem的状态变化。 2、波特率除数锁存器的值与波特率的对应关系(16进制数) 3、8250的初始化编程 (1)设置波特率 (2)设置通讯数据格式 (3)设置操作方式 (4)设置中断允许寄存器 (详细细节必须搞清楚,参见相关书籍) 3.3.8 232串口中断服务程序 static void RS232_interrupt(int irq, void *dev_id, struct pt_regs *regs)是中断服务程序,由request_irq(RS232_irq, &RS232_interrupt, SA_SHIRQ, RS232_interface, &RS232_irq)来实现地址向量的设置。本实验在发送时采用发送寄存器空中断来实现发送每个字节数据,当接口有数据到达时发生数据到达中断,实现接收一个字节。中断服务程序和RS232_read()以及RS232_write()存在同步关系, 唤醒接收进程用wake_up_interruptible(&RS232_dev_buffer_RP); 唤醒发送进程用wake_up_interruptible(&RS232_dev_buffer_SP); 3.3.9 XON/XOFF协议程序 X_Check_Recv_char()是判断是否接收到XON和XOFF命令;X_Check_Send_State() 检查是否可以允许发送;X_Check_Recv_Buffer ()是判断是否需要发送XON或XOFF命令,如 27 需要则发送之。接收缓冲区的5%-15%作为发送XON、XOFF的临界值。注意:采用XON/XOFF协议后只能发送文本文件。 3.3.10 编写用户空间串口通信程序 接收程序从/dev/RS232_Test设备文件接收数据(调用open()和read()实现),放入某个普通文件之中。反之发送程序从某个普通文件取得数据,再放入/dev/RS232_Test设备文件中即可实现文件的传输(拷贝)。该文件传输协议是先传送数据大小,让接收方控制文件的结尾。 3.3.11 例子程序(样本2) 3.3.11.1 makefile文件 all:send recv rs232_driver.o send:rs232_comm_send.o rs232_comm.o gcc -o send rs232_comm_send.o rs232_comm.o rs232_comm_send.o:rs232_comm_send.c rs232_comm.h gcc -c rs232_comm_send.c recv:rs232_comm_recv.o rs232_comm.o gcc -o recv rs232_comm_recv.o rs232_comm.o rs232_comm_recv.o:rs232_comm_recv.c rs232_comm.h gcc -c rs232_comm_recv.c rs232_driver.o:rs232_comm_driver.c rs232_comm.h gcc -c rs232_comm_driver.c -DMODULE -D__KERNEL__ -I/usr/src/linux-2.4.20-8/include -O2 mv rs232_comm_driver.o rs232_driver.o clean:# rm *.o send recv rs232_comm.o:rs232_comm.c rs232_comm.h gcc -c rs232_comm.c 3.3.11.2 头文件 #define FILENAMELEN 20 #define BUFFER_LEN 256 struct filehead { char filename[FILENAMELEN]; 28 long filesize; }; int write_rs232(int, char *, int); int read_rs232(int, char *, int); ///////////////////////////////////////////////////////////////// ////////////// #define RS232_INTERFACE_FILE "/dev/RS232_Test" #define RS232_INTERFACE_FLAG "RS232_Comm_test" #define RS232_MAJOR 253 //系统调用接口主设备号码 #define RS232_MINOR 0 //系统调用接口次设备号码 3.3.11.3 rs232_comm.c文件 #include "rs232_comm.h" #include #include #include int write_rs232(int sd, char *buf, int sum_l) { int cnt = 0, l = sum_l; char *bp = buf; while (1) { bp += cnt; cnt = write(sd, bp, l); if (cnt == -1) return -1; l -= cnt; if (l == 0) break; } return sum_l; } int read_rs232(int sd, char *buf, int sum_l) { int cnt = 0, l = sum_l; char *bp = buf; 29 while (1) { bp += cnt; cnt = read(sd, bp, l); if (cnt == -1) return -1; l -= cnt; if (l == 0) break; } return sum_l; } 3.3.11.4 rs232_comm_send.c文件 ///////////////////////////// 发送进程 by 杨雄 ////////////////////// #include "rs232_comm.h" #include #include #include #include #include #define BEBUG_SEND int datasend(int sd, int fd, long fl) { char *filebuffer; long filelen = 0; int filebuffersize = BUFFER_LEN, cnt; if ((filebuffer = malloc(filebuffersize)) == NULL) return -1; #ifdef BEBUG_SEND printf("Before Send File Block\n"); #endif while ((cnt = read(fd, filebuffer, filebuffersize)) > 0) { if (write_rs232(sd, filebuffer, cnt) == -1) return -1; #ifdef BEBUG_SEND printf(" %d.", cnt); #endif 30 filelen += cnt; if (filelen >= fl) break; } free(filebuffer); #ifdef BEBUG_SEND printf("Data Size %ld\n", filelen); #endif return 1; } int filesend(int sd, int fd, char *fn) { struct filehead filehead; int cnt; struct stat s_file_stat; #ifdef BEBUG_SEND printf("In filesend subroutine\n"); #endif bzero(&s_file_stat, sizeof s_file_stat); bzero(&filehead, sizeof filehead); #ifdef BEBUG_SEND printf("Before fstat [%s]\n", fn); #endif if (fstat(fd, &s_file_stat) < 0) { printf("\nfstat: error [%s]\n", fn); return -1; } #ifdef BEBUG_SEND printf("FileHead Size: %d, Send File Size: %ld\n", sizeof filehead, s_file_stat.st_size); #endif filehead.filesize = s_file_stat.st_size; strcpy(filehead.filename, fn); cnt = write_rs232(sd, (char *)&filehead, sizeof filehead); if (cnt == -1) return -1; #ifdef BEBUG_SEND printf("Send FileName & FileSize OK!\n"); #endif if (datasend(sd, fd, filehead.filesize) < 0) 31 return -1; else return 1; } main(int argc, char *argv[]) { int RS232_devfd, fd; char *filename = argv[1]; if (argc < 2) { printf("\nUsge: send filename\n"); exit(-1); } if ((fd = open(filename, 0)) == -1) { printf("文件不能打开!"); exit(-1); } #ifdef BEBUG_SEND printf("Before open %s\n", RS232_INTERFACE_FILE); #endif RS232_devfd = open(RS232_INTERFACE_FILE, O_RDWR); if (RS232_devfd == -1 ) { printf("Cann't open file %s\n", RS232_INTERFACE_FILE); exit(0); } #ifdef BEBUG_SEND printf("Before File Send [%s]\n", filename); #endif if (filesend(RS232_devfd, fd, filename) == -1) printf("\nSend Data Not Finish!\n"); else printf("\nSend Data OK!\n"); close(fd); close(RS232_devfd); } 3.3.11.5 rs232_comm_recv.c文件 ///////////////////////////// 接收进程 by 杨雄 ////////////////////// #include "rs232_comm.h" #include #include 32 #include #include #include int datacopy(int fd, int sd, long fl) { char *filebuffer; long filelen = 0; int filebuffersize = BUFFER_LEN, cnt; if ((filebuffer = malloc(filebuffersize)) == NULL) return -1; while ((cnt = read(sd, filebuffer, filebuffersize)) > 0) { write(fd, filebuffer, cnt); printf(" %d.", cnt); filelen += cnt; if (filelen >= fl) break; } printf("\nTotol File Size %ld\n", filelen); free(filebuffer); return 1; } int filecopy(int sd) { struct filehead filehead; char fn[FILENAMELEN]; int cnt; int fd; bzero(&filehead, sizeof filehead); printf("Before read interface device\n"); cnt = read_rs232(sd, (char *)&filehead, sizeof filehead); if (cnt == -1) return -1; strcpy(fn, filehead.filename); 33 printf("After read FileHead, cnt[%d], FileName[%s], FileSize[%ld]\n", cnt, filehead.filename, filehead.filesize); fd = open(fn, O_RDWR|O_CREAT|O_TRUNC); if (fd == -1 ) { printf("Cann't open file %s\n", fn); exit(0); } if (datacopy(fd, sd, filehead.filesize) == -1) { printf("A File Data Copy Error!"); return -1; } close(fd); printf("A File Data Copy Finish!\n"); return 1; } main() { int RS232_devfd; int rec_n = 0; RS232_devfd = open(RS232_INTERFACE_FILE, O_RDWR); if (RS232_devfd == -1 ) { printf("Cann't open file %s\n", RS232_INTERFACE_FILE); exit(0); } while (1) { if (filecopy(RS232_devfd) < 0) { printf("文件拷贝错误"); break; } printf("receive data OK! file number = %2d\n", ++rec_n); } close(RS232_devfd); } 3.3.11.6 rs232_comm_driver.c文件 ////////////////////////Linux 2.4-20 版本 RS232 驱动测试程序 by 杨雄 in 2007.8.1 //////////////////////// #define DEBUG_RS232 //调试硬件232接口 //#define DEBUG_P //调试-记录过程 34 #define THIS_LINUX_DEBUG //本机调试整个程序 #define __NO_VERSION__ #include "linux/kernel.h" #include "linux/module.h" #include "linux/config.h" #include "linux/compatmac.h" #include "linux/sched.h" MODULE_LICENSE("GPL"); #ifdef CONFIG_SMP #define __SMP__ #endif #include "linux/version.h" char kernel_version [] = UTS_RELEASE; ///////////////////////////////////////////////////////////////// ///////////////////// #include "linux/types.h" #include "linux/fs.h" #include "linux/mm.h" #include "linux/errno.h" #include "linux/interrupt.h" #include "asm/segment.h" #include "asm/uaccess.h" #include #include #include #include ///////////////////////////////////////////////////////////////// ///////////////////// #include "rs232_comm.h" static int RS232_Major = RS232_MAJOR; //系统调用接口主设备号码 #ifdef DEBUG_RS232 35 #define RS232_BASE_ADDR 0x3f8 //COM1 硬件传输接口的基地址 static int RS232_irq = 4; #endif static int RS232_open(struct inode *inode, struct file *file); // 系统调用接口 static int RS232_release(struct inode *inode, struct file *file); // 系统调用接口 static int RS232_read(struct file *filp, char *buffer, size_t length, loff_t *offset); //系统调用接口 static int RS232_write(struct file *filp, const char *buffer, size_t length, loff_t *offset); //系统调用接口 #define RS232_BUF_LEN 1024 //接收和发送缓冲区 长度,字节数 static char RS232_Buffer_R[RS232_BUF_LEN], RS232_Buffer_S[RS232_BUF_LEN]; //接收和发送缓冲区 static int RS232_Buffer_R_H, RS232_Buffer_R_T, RS232_Buffer_S_H, RS232_Buffer_S_T; //缓冲区头尾下标 //注意:头 == 尾,则缓冲区空;头 == 尾+1, 则缓冲区满 wait_queue_head_t RS232_dev_buffer_RP; //调用进程等待接收数据,即 缓冲区无任何接收数据字节 wait_queue_head_t RS232_dev_buffer_SP; //调用进程等待发送缓冲区有 空间 static int RS232_open_time; //标记接口逻辑设备被打开的次数,本设备只能 被打开一次,如是本地测试可以2次 //接口设备驱动程序, 进程进入内核接口程序 struct file_operations RS232_Fops = { open: RS232_open, release: RS232_release, /* close */ read: RS232_read, write: RS232_write, // nothing more, fill with NULLs }; ///////////////////////////////////////////////////////// //缓冲区下标移动一个位置 int NextLoc(int index) { 36 index++; if (index == RS232_BUF_LEN) index = 0; return index; } static char *RS232_interface = RS232_INTERFACE_FLAG; //设备接口标记字串 //Linux内核定时器的应用 struct timer_list check_Xon_timer; void timer_check_S_Xon_State(unsigned long data) { cli(); if (X_Check_Send_State() && RS232_Buffer_S_T != RS232_Buffer_S_H) { outb(inb(RS232_BASE_ADDR+1) | 0x02, RS232_BASE_ADDR+1); // 开发送中断 } if (!X_Check_Send_State()) add_timer(&check_Xon_timer); sti(); } static void timer_init(void) { init_timer(&check_Xon_timer); check_Xon_timer.expires = jiffies + 500; check_Xon_timer.data = 0; check_Xon_timer.function = timer_check_S_Xon_State; } static void timer_exit(void) { del_timer(&check_Xon_timer); } ////// timer end //static irqreturn_t RS232_interrupt(int irq, void *dev_id, struct pt_regs *regs) //2.6版本 37 static void RS232_interrupt(int irq, void *dev_id, struct pt_regs *regs) //2.4版本 { register int temp_t; register int irf; register char rc, sc; #ifdef DEBUG_RS232 irf = inb(RS232_BASE_ADDR+2); //取中断标识寄存器 #ifdef DEBUG_P printk("\n%s: RS232_interrupt: 1, [中断标识寄存器内容 = %x]\n", RS232_interface, irf); #endif if (irf & 0x01) goto leave ; //无中断 switch (irf & 0x06) { case 0x04: //是接收中断,则接收处理(取得的字节放在rc) rc = inb(RS232_BASE_ADDR); //从硬件端口取数据,一个字节 if (X_Check_Recv_char(rc)) //rc是当前接收的字符,返回0-接收rc, 返回1-丢掉rc goto rp; temp_t = NextLoc(RS232_Buffer_R_T); if (temp_t == RS232_Buffer_R_H) //判断缓冲区有空闲否,==无空闲 goto rp;//IRQ_NONE; //没有任何空间放一个字节 RS232_Buffer_R[RS232_Buffer_R_T] = rc; RS232_Buffer_R_T = temp_t; X_Check_Recv_Buffer(RS232_BUF_LEN, (RS232_Buffer_R_T < RS232_Buffer_R_H)? ( RS232_Buffer_R_H - RS232_Buffer_R_T): RS232_BUF_LEN - (RS232_Buffer_R_T - RS232_Buffer_R_H)); rp: wake_up_interruptible(&RS232_dev_buffer_RP); goto leave ;//IRQ_; case 0x02: //是发送中断,则继续发送(如缓冲区RS232_Buffer_S空,则不发送) if (RS232_Buffer_S_T == RS232_Buffer_S_H) goto sp;//ERQ_NONE; //缓冲区空,无发送数据字节 if (!X_Check_Send_State()) //可以发送数据(返回1), 不可以发送数据(返回0) goto sp; 38 sc = RS232_Buffer_S[RS232_Buffer_S_H]; RS232_Buffer_S_H = NextLoc(RS232_Buffer_S_H); outb(sc, RS232_BASE_ADDR); // 发送sc字节 sp: if (!X_Check_Send_State() || RS232_Buffer_S_T == RS232_Buffer_S_H) { sc = inb(RS232_BASE_ADDR+1); //读取中断允许寄存器 outb(sc & 0xfd, RS232_BASE_ADDR+1); //关发送中断,因为 无数据了,位1为发送中断 if (!X_Check_Send_State()) add_timer(&check_Xon_timer); } wake_up_interruptible(&RS232_dev_buffer_SP); goto leave ;//IRQ_; default://接收错 #ifdef DEBUG_P printk("%s: 接收错 interrupt!\n", RS232_interface); #endif inb(RS232_BASE_ADDR+5); //读取线路状态寄存器,复位 inb(RS232_BASE_ADDR+6); //取Modem状态寄存器,复位 goto leave ;//ERQ_NONE; } // switch end leave: outb_p(0x20, 0x20); //hard int return #endif } int init_module(void) { int result; printk(KERN_ALERT "%s: RS232_Comm_Test Initing......\n", RS232_interface); result = register_chrdev(RS232_Major, RS232_interface, &RS232_Fops); if (result < 0) { printk(KERN_INFO "%s: can't get major number\n", RS232_interface); return result; } if (RS232_Major == 0) RS232_Major = result; // dynamic 39 init_waitqueue_head(&RS232_dev_buffer_RP); init_waitqueue_head(&RS232_dev_buffer_SP); //接收和发送缓冲区清空 RS232_Buffer_R_H = RS232_Buffer_R_T = RS232_Buffer_S_H = RS232_Buffer_S_T = 0; printk(KERN_INFO "%s: init OK! major=%d\n", RS232_interface, RS232_Major); #ifdef DEBUG_RS232 if (request_irq(RS232_irq, &RS232_interrupt, SA_SHIRQ, RS232_interface, &RS232_irq)) { printk(KERN_ERR "%s: RS232_irqtest: cannot register IRQ %d\n", RS232_interface, RS232_irq); return -EIO; } printk("%s: Request on IRQ %d succeeded\n", RS232_interface, RS232_irq); outb_p(0x00, RS232_BASE_ADDR+3); //DLAB位为0 outb_p(0x00, RS232_BASE_ADDR+1); //设置中断允许寄存器,0x00不允所有 232接口中断 #endif RS232_open_time = 0; timer_init(); return 0; } static int RS232_open(struct inode *inode, struct file *file) { // 打开成员变量累加计数 #ifndef THIS_LINUX_DEBUG if (RS232_open_time == 0) { #else if (RS232_open_time <= 1) { #endif RS232_open_time++; } else { printk(KERN_ALERT "%s: another process open the char device. open time=%d\n", RS232_interface, RS232_open_time); 40 return -1; } if (RS232_open_time == 1) { #ifdef DEBUG_RS232 RS232_Buffer_R_H = RS232_Buffer_R_T = RS232_Buffer_S_H = RS232_Buffer_S_T = 0; //接收和发送缓冲区清空 outb_p(0x80, RS232_BASE_ADDR+3); //设置线路控制寄存器的DLAB位(位7) outb_p(0x30, RS232_BASE_ADDR+0); //发送波特率因子低字节 outb_p(0x00, RS232_BASE_ADDR+1); //发送波特率因子高字节,0x0030为2400波特 outb_p(0x00, RS232_BASE_ADDR+3); //DLAB位为0 outb_p(0x07, RS232_BASE_ADDR+3); //DLAB位为0, 位3--0:无校验, 位2--1:1位停止位, 位0/1--11:8位数据传输 outb_p(0x0b, RS232_BASE_ADDR+4); //设置MODEM的:位0-DTR,位1-RTS,位2-无用,位3-允许中断到系统,位4-自发自收(设置为1则是内部自发自收,但是不能用中断方式读取字符) // outb_p(0xc7, RS232_BASE_ADDR+2); //势能FIFO inb(RS232_BASE_ADDR+5); //复位 inb(RS232_BASE_ADDR+0); //复位 inb(RS232_BASE_ADDR+6); //复位 outb_p(0x0d, RS232_BASE_ADDR+1); //设置中断允许寄存器,1101-位1为0:数据发送空暂时不允许 outb(inb_p(0x21) & 0xe7, 0x21); //允许8259A芯片的IRQ3,IRQ4中断请求 #endif } // 模块使用计数加1 MOD_INC_USE_COUNT; #ifdef DEBUG_P printk("%s: open OK!\n", RS232_interface); #endif return 0; } static int RS232_release(struct inode *inode, struct file *file) { 41 // 关中断 cli(); // 模块使用计数减1 RS232_open_time--; MOD_DEC_USE_COUNT; // 关闭端口 #ifdef DEBUG_RS232 if (RS232_open_time == 0) outb_p(0x00, RS232_BASE_ADDR+1); //设置中断允许寄存器,0x00不允所有232接口中断 #endif // 开中断 sti(); #ifdef DEBUG_P printk("%s: close OK!\n", RS232_interface); #endif return 0; } static int RS232_read(struct file *filp, char *buffer1, size_t length1, loff_t *offset) { char *buffer = buffer1; size_t length = length1; int i = 0; if (verify_area(VERIFY_WRITE, buffer, length) == -EFAULT ) //检验buf是否可以读写! return -EFAULT; #ifdef DEBUG_P printk("%s: RS232_read: 1\n", RS232_interface); #endif cli(); //关中断 // 如缓冲区RS232_Buffer_R空,则等待;„空? == RS232_Buffer_R_H == RS232_Buffer_R_T while (RS232_Buffer_R_H == RS232_Buffer_R_T) { interruptible_sleep_on(&RS232_dev_buffer_RP); } 42 // 循环拷贝数据到buffer,每次一个字节。缓冲区空或需要拷贝的字节数为0,则循环结束; while (length && (RS232_Buffer_R_H != RS232_Buffer_R_T)) { put_user(RS232_Buffer_R[RS232_Buffer_R_H], buffer++); RS232_Buffer_R_H = NextLoc(RS232_Buffer_R_H); length--; i++; } #ifndef DEBUG_RS232 while (RS232_Buffer_S_T != RS232_Buffer_S_H && RS232_Buffer_R_H != NextLoc(RS232_Buffer_R_T)) { int rc; rc = RS232_Buffer_S[RS232_Buffer_S_H]; RS232_Buffer_S_H = NextLoc(RS232_Buffer_S_H); RS232_Buffer_R[RS232_Buffer_R_T] = rc; RS232_Buffer_R_T = NextLoc(RS232_Buffer_R_T); } #ifdef DEBUG_P printk("%s: RS232_read: 2\n", RS232_interface); #endif wake_up_interruptible(&RS232_dev_buffer_SP); #endif sti(); //开中断 return i; // 返回拷贝的字节数; } static int RS232_write(struct file *filp, const char *buffer1, size_t length1, loff_t *offset) { const char *buffer = buffer1; size_t length = length1; register int temp_t; int i = 0; if (verify_area(VERIFY_READ, buffer, length) == -EFAULT ) //检验buf是否可以读! return -EFAULT; #ifdef DEBUG_P printk("%s: RS232_write: 1\n", RS232_interface); #endif cli(); //关中断 43 //如缓冲区RS232_Buffer_S无空闲,则等待;„无空闲? RS232_Buffer_S_H == RS232_Buffer_S_T的下一位置 while (NextLoc(RS232_Buffer_S_T) == RS232_Buffer_S_H) //判断缓冲区有空闲否,==无空闲 interruptible_sleep_on(&RS232_dev_buffer_SP); //循环拷贝从buffer拷贝数据到缓冲区RS232_Buffer_S,每次一个字节。缓冲区满或需要拷贝的字节数为0,则循环结束; while (length && ((temp_t = NextLoc(RS232_Buffer_S_T)) != RS232_Buffer_S_H)) { get_user(RS232_Buffer_S[RS232_Buffer_S_T], buffer+i); RS232_Buffer_S_T = temp_t; length--; i++; } #ifdef DEBUG_RS232 if (RS232_Buffer_S_T != RS232_Buffer_S_H) { outb(inb_p(RS232_BASE_ADDR+1) | 0x02, RS232_BASE_ADDR+1); // 允许发送空中断 #ifdef DEBUG_P printk("\n%s: RS232_write: 设置允许发送空中断\n", RS232_interface); #endif } #else while (RS232_Buffer_S_T != RS232_Buffer_S_H && RS232_Buffer_R_H != NextLoc(RS232_Buffer_R_T)) { int sc; sc = RS232_Buffer_S[RS232_Buffer_S_H]; RS232_Buffer_S_H = NextLoc(RS232_Buffer_S_H); RS232_Buffer_R[RS232_Buffer_R_T] = sc; RS232_Buffer_R_T = NextLoc(RS232_Buffer_R_T); } wake_up_interruptible(&RS232_dev_buffer_RP); #endif #ifdef DEBUG_P printk("\n%s: RS232_write: 2\n", RS232_interface); #endif sti(); //开中断 return i; // 返回拷贝的字节数; } 44 void cleanup_module(void) { printk(KERN_ALERT"%s: Unloading..........\n", RS232_interface); #ifdef DEBUG_RS232 free_irq(RS232_irq, &RS232_irq); #endif timer_exit(); unregister_chrdev(RS232_MAJOR, RS232_interface); } ///////////////////////// //////////////////////// XON XOFF 协议处理程序 #define DEBUG_X #ifdef DEBUG_X #define BUFFER_UP 40 #define BUFFER_DOWN 5 #define CTRL_Q 0x11 #define CTRL_S 0x13 #define XON CTRL_Q #define XOFF CTRL_S #define S_XON 0 #define S_XOFF 1 static int R_X_State = S_XON; static int S_X_State = S_XON; int IsSend() { int cs = 0, r = 0; while ((inb(0x3fd)&0x20) == 0) { // 0x20 - 位5 - 1, 表示发送寄存器空了, 可以发送 if (cs++ > 30000) { cs = 0; if (r++ > 100) { printk("%s: 发送XON/XOFF失败![发送寄存器不空]\n", RS232_interface); return 0; } } 45 } return 1; } void ToXon() { char intrb = inb_p(RS232_BASE_ADDR+1); outb(intrb & 0xfd, RS232_BASE_ADDR+1); //不允许发送空中断 if (IsSend()) outb(XON, 0x3f8); outb(intrb, RS232_BASE_ADDR+1); //恢复 } void ToXoff() { char intrb = inb_p(RS232_BASE_ADDR+1); outb(intrb & 0xfd, RS232_BASE_ADDR+1); //不允许发送空中断 if (IsSend()) outb(XOFF, 0x3f8); outb(intrb, RS232_BASE_ADDR+1); //恢复 } #endif int X_Check_Recv_char(char rc) //rc是当前接收的字符,返回0-接收rc, 返回 1-丢掉rc { #ifdef DEBUG_X if (rc == XON) { S_X_State = S_XON; return 1; } if (rc == XOFF) { S_X_State = S_XOFF; return 1; } return 0; #else return 0; #endif } int X_Check_Send_State() //检查发送状态,S_XON - 可以发送数据(返回1), S_XOFF- 不可以发送数据(返回0) { #ifdef DEBUG_X 46 if (S_X_State == S_XON) return 1; if (S_X_State == S_XOFF) return 0; #else return 1; #endif } void X_Check_Recv_Buffer(int r_buff_len, int cur_r_buff_free) //条件满足发送XON/XOFF { #ifdef DEBUG_X register int rate = 100 * cur_r_buff_free / r_buff_len; if (rate > BUFFER_UP && R_X_State == S_XOFF) { R_X_State = S_XON; ToXon(); } else if (rate < BUFFER_DOWN && R_X_State == S_XON) { R_X_State = S_XOFF; ToXoff(); } #else return; #endif } 47 实验四 状态机程序设计 (设计简化的SDLC协议通信软件) 1(实验目的 学习协议的制订;掌握状态机程序设计方法;在232通信接口的基础上如何实现链路层功能,实现一个简化的SDLC协议。 2(实验内容 设计简化了的SDLC状态机,对状态机程序设计。将这个设计了的状态机程序加入到基于232接口的文件传输系统中。如图,将设计文件发送程序/进程、文件接收程序/进程、SDLC主站程序/链路层模块、SDLC次站程序/链路层模块、232接口驱动程序。在实验二基础上,本实验的重点是SDLC主次站的程序设计(包含协议状态机设计以及程序设计)。如能实现任何类型任何大小文件的单向传输,则实验完成。 文件send_data文件recv_data 文件接收文件发送进程进程File_RecvFile_Send /dev/R/dev/S LINUX数SDLCSDLC 据主站程序次站程序 链 内路232硬件接口232硬件接口核层驱动程序驱动程序 接口接口 3(实验步骤及调试环境 3.1调试环境 PC以及Linux操作系统。 48 3.2实验步骤: 1) 学习SDLC协议; 2) 设计简化SDLC协议和状态机; 3) 脱机编写简化SDLC的程序以及上下接口子程序; 4) 用ROOT登陆系统和进入相应的目录; 5) 编辑简化SDLC的程序; 6) 编辑应用程序并编译; 7) 生成测试数据,调试可靠的通信软件系统; 8) 上机完毕:退出系统,关闭系统 3.3简化的SDLC规程 3.3.1体系关系 1) 点到点主从模式 2) 主站发起通信链路控制 链路控制:建立和关闭 3) 主站发起数据传输请求 数据传输请求:主站需要次站同意才可以向次站发送数据;次站不能向主站发送数 据。即尽管底层是同时双向的,简化的SDLC也可以进行双向数据包传递,但只是用 来完成协议,对上层文件传输提供的服务只能单方向数据通信方式。 4) 其他约定(为了程序简单) 忽略定时重发;数据校验采用X16+X12+X5+1;采用否认后连续重发。 3.3.2状态机设计 1) 状态设计 四个状态:链路关闭;链路正在建立;链路建立;链路正在关闭 链路关闭(LINK_CLOSE):不能传送数据,只能接受建立链路的请求 链路正在建立(LINK_CONNECTING):不能传输数据,等待对方对建立链路请求的应 答 链路建立(LINK_CONNECTED):可以传输数据 链路正在关闭(LINK_SHUTDOWN):不能传输数据,等待对方对链路关闭请求的应答 49 2) 事件设计 I到达(I_FRAME):数据帧 U帧到达(U_FRAME):建立链路请求和关闭链路请求以及相应的应答 S帧到达(S_FRAME):数据帧的确认 主站初始化(M_CALL):主站开机并初始化本方接口后向远端次站发起需要建立链路的请求 主站有数据要发(M_DATA):主站关机或不再需要数据传送,则向远端次站发起需要关闭链路的请求 主站结束(M_STOP):主站关机或不再需要数据传送,则向远端次站发起需要关闭链路的请求 ) 动作设计 3 本站初始化(Initial):变量和资源申请; 数据提取和放入缓冲区(Get_Info):接收方I帧数据提取,包括缓冲区队列管理; I帧数据获得和构造I帧(Put_Info):发送方I帧数据获得和构造一个I帧,包 括缓冲区队列管理; U 帧构造和发送(Make_U_Frame):根据要求和当前状态设计U帧并发送; S 帧构造和发送(Make_S_Frame):根据要求和当前状态设计S帧并发送; 若干缓冲区管理程序:为了设计程序方便,帧是固定大小组织和构造的,所以缓冲 区设计成一个固定大小的数组,数组的成员就是一个‘帧’,顺序处理缓冲区即可。 缓冲区的设计:主站只有发送缓冲区(Master_SDLC_Buffer);次站只有接收缓冲区(Second_SDLC_Buffer) 4) 状态机 50 简化的SDLC协议状态机-主站 次站-S_FRAME_U_RES--------------------------------- 主站-M_DATA主站-M_CALLLINK_---------------------------------------I 帧构造和发送主站资源申请CONNECTING缓冲区操作次站-S_FRAME_S_RES资源初始化----------------------------向次站发送U 帧(请求建立链路)---- LINK_LINK_CLOSECONNECTED 主站-M_STOP -------------------次站-S_FRAME_U_RES向次站发送关闭请求次站-S_FRAME_S_RES----------------------------U 帧(请求关闭链路)---------------------主站资源关闭LINK_应答处理SHUTDOWN缓冲区操作资源:文件、变量、参数、通信接口等等 简化的SDLC协议状态机-次站 -忽略CONNECTING状态 -忽略SHUTDOWN状态 主站-U_FRAME-建立链路请求----------------------------------------次站资源初始化向主站发送U-FRAME,表示建立响应向主站发送S-FRAME,确认初始顺序号 LINK_LINK_CLOSECONNECTED 主站-I_FRAME-数据主站-U_FRAME-关闭链路请求-------------------------------------------------------------------提取I 帧数据,接收数据,放入缓冲区向主站发送U-FRAME,表示关闭响应向主站发送S-FRAME,作为应答 3.3.3帧结构 F(8bit)C(8bit)I[Ilen(8bit),Idata,~]FCS(16bit)F(8bit) 51 简化的SDLC协议帧 比特88固定1688 信息控制信息长度标志帧检验序列标志 InfoCILenFFCSF 11100 建立链路SABM0N(S)P/FN(R)信息帧I00010 拆除链路DISC10SP/FN(R)监督帧S00110 响应UA 11MP/FM10001 命令拒绝CMDR无编号帧U23456781位 00RR 接收准备就绪准备接收下一帧,确认序号为N(R)-1及以前的帧 RNR 接收未就绪10暂停接收下一帧,确认序号为N(R)-1及以前的帧 01REJ 拒绝否认为N(R)开始的帧及其以后所有的帧进行重发 11无定义 由于底层232是8位传输,正好是一个字节,也因为传输的文件也是按一个字节来处理的,因此假设简化的SDLC按字节来处理帧头和帧尾,数据内容为了区别头尾F,要进行转义处理(插入转义字符)。 3.4系统结构与实验环境 3.4.1系统结构 用SDLC来传递一个数据,SDLC是一个独立任务。如图: 主站次站 文件send_data 物理通道 文件接收次站主站文件发送进程Second_SDLCMaster_SDLC进程File_Recv任务任务File_Send 文件recv_data 52 3.4.2 任务设置 主站和次站任务都设计成为独立的一个功能层次,其程序就是一个独立的模块,放入内核,由上层sdlc程序/进程来调用。Sdlc进程的实体几乎都在内核。 文件send_data文件recv_data 文件接收文件发送进程进程File_RecvFile_Send /dev/R/dev/S LINUX数SDLC链路程序SDLC链路程序据Master_SDLCSecond_SDLC 链内路核232硬件接口232硬件接口层驱动程序驱动程序 接口接口 文件send_data文件recv_data 文件接收SDLCSDLC进程文件发送处理进程处理进程File_Recv进程 File_Send /dev/sdlc/dev/sdlc/dev/R/dev/S S设备接口R设备接口 系统调用程序系统调用程序 SDLC链路程序SDLC链路程序LINUXMaster_SDLCSecond_SDLC 内232硬件接口232硬件接口核驱动程序驱动程序 接口接口 53 文件send_data •SDLC是有状态协议 •状态是靠事件来驱动的SDLC •状态的变化会引起相应动作处理进程文件发送 进程 File_Send/dev/sdlc/dev/S S设备接口 系统调用程序 SDLC链路程序LINUXSDLC事件队列Master_SDLC 事件队列数据结构内232硬件接口核中断处理程序 接口 3.4.3测试数据和运行 由于是实验设计(实际网络要考虑各种情况),启动次序要先规定好: Second_SDLC和Master_SDLC加入内核 File_Recv File_Send(可以多次启动) send_data可以是任何格式的文件,用文本文件可以方便检查结果。 3.5流程与数据结构 3.5.1数据流程与缓冲区/队列 1)缓冲区设置 文件send_data文件recv_data 文件接收文件发送进程send_data缓冲区recv_data缓冲区进程BUFFER_LEN长度BUFFER_LEN长度File_RecvFile_Send /dev/R/dev/S S设备接口R设备接口 系统调用程序系统调用程序Master_Second_Master_SDLCSecond_SDLCRecv_Send_缓冲区队列缓冲区队列BufferLINUXBuffer(8个帧)(8个帧)SDLC链路程序SDLC链路程序 Master_SDLCSecond_SDLC TranFrameInBTranFrameOutBTranFrameInB内缓冲区(存在转义符的帧)缓冲区(插入转义符的帧)缓冲区(存在转义符的帧)核232硬件接口232硬件接口TranFrameOutB缓冲区(插入转义符的帧)驱动程序驱动程序#define INFO_LEN 60 // 帧的信息部分长度接口接口 54 2)数据流程 文件send_data文件recv_data send_data缓冲区recv_data缓冲区BUFFER_LEN长度BUFFER_LEN长度 /dev/R/dev/S MasterSdlcBufferSecondSdlcBufferMasterSecond(8个帧)(8个帧)RecvSendBufferBuffer TranFrameOutBTranFrameInBTranFrameOutBTranFrameInB缓冲区(插入转义符的帧)缓冲区(存在转义符的帧)缓冲区(插入转义符的帧)缓冲区(存在转义符的帧) 3.5.2主要程序流程 3.5.2.1 S_open()流程 在SDLC_event_q队列设置M_CALL事件, 3.5.2.2 S_write()流程 while (存在需要发送的数据) { bl = 在Master_SDLC_Buffer申请空闲缓冲区, //如果SDLC状态不是可以发送数据状态~则bl也是0 如没有空闲缓冲区~则sleep_on(Swq); //Swq-等待Master_SDLC_Buffer有空间队列 while (bl && 存在需要发送的数据) { 从用户空间拷贝1个数据;bl--; } if (bl == 0) { 设置当前缓冲区的占用情况, } } 在SDLC_event_q队列设置M_DATA事件, 3.5.2.3 S_close()流程 在SDLC_event_q队列设置M_STOP事件, 3.5.2.4 文件发送流程(File_Send) 打开send_data文件, 55 打开/dev/S文件, 计算send_data长度和填写文件头数据结构, 送文件头数据结构, 循环将send_data文件的数据传送完, 关闭所有文件, 3.5.2.5 R_read()流程 while (需要接收的数据没有完) { bl = 在Master_SDLC_Buffer取得数据缓冲区, 如没有数据缓冲区~则sleep_on(Rwq); //Rwq-等待Second_SDLC_Buffer有数据队列 while (bl && 需要接收的数据没有完) { 从用户空间拷贝1个数据;bl--; } if (bl == 0) { 设置当前缓冲区的空闲情况, } } 3.5.2.6 文件接收流程(File_Recv) 打开recv_data文件, 打开/dev/R文件, 接收文件头数据结构, 循环将接收数据放入recv_data文件~直到需要读取的数据读完, 关闭所有文件, 3.5.2.7 SDLC状态机处理流程(sdlc_state_process()) 1,主站主流程图 56 开始 环境初始化 取得事件Master_get_event(int*EventTypeP, union sdlc_data*SDP) 发送帧CASESend_Frame(structframe *Send_Frame_P)事件类型可以且有帧要发送?EventTypeP? 是 M_CALLM_CALL状态变化状态合适?做U帧(如存在)主站请求建立链路事件处理 是 M_STOPM_STOP状态变化状态合适?做U帧(如存在)主站关闭链路事件处理是 M_DATAM_DATA数据放入状态变化状态合适?(如存在)主站有数据要发事件处理缓冲区是 S_FRAME_REQS_FRAME状态变化状态合适?做I 帧次站S帧到达(如存在)事件处理是U_FRAME_REQ U_FRAME状态变化状态合适?次站U帧到达(如存在)事件处理 “状态合适,”是当前状态和当前取得的事件是否在状态图/机中存在。 2,次站主流程图 开始 环境初始化 取得事件Second_get_event(int*EventTypeP, union sdlc_data*SDP) 发送帧CASESend_Frame(structframe *Send_Frame_P)事件类型可以且有帧要发送?EventTypeP? 是 U_FRAME(建立链路)U 帧到达做U状态合适?状态变化主站U帧到达建立事件应答帧 是 U_FRAME(拆除链路)U 帧到达做U状态合适?状态变化主站U帧到达拆除事件应答帧计算数据是校验确定I_FRAME(数据)I 帧到达数据提取(做S帧)状态合适?主站I 帧到达数据事件放入缓冲区次站数据接收 否定(做S帧) 3,M_CALL事件处理 57 M_CALL事件处理帧队列Master_M_CALL_Process();structframeMasterFrameBuffer[FRAME_NUMBER];IntMasterFirstFrame, MasterSendFrame, MasterLastFrame; 采用静态内存做帧缓冲区队列固固固固固固固固Master_Make_Buffer()定定定定定定定定帧帧帧帧帧帧帧帧大大大大大大大大小小小小小小小小做U 帧:在缓冲区队列中空帧缓冲区设置U帧字段;设置有帧发送(本设计用队列下标表示了)已发待确定空闲待发送帧空闲 结束 帧队列条件:采用静态内存队列无空闲缓冲区:做帧缓冲区队列MasterFirstFrame== (MasterLastFrame+1)mod 8[仅仅空闲一个缓冲区时候表示缓冲区队列满]队列无占用缓冲区:MasterFirstFrame== MasterLastFrameMasterFirstFrame= 0;队列无待发帧MasterLastFrame= 0;MasterSendFrame== MasterLastFrameMasterSendFrame= 0; 结束 做U 帧帧队列Master_Make_U_Frame()structframe MasterFrameBuffer[FRAME_NUMBER];IntMasterFirstFrame, MasterSendFrame, MasterLastFrame; 取得一个空闲帧FBP固固固固固固固固r = Find_Free_Frame(FBP)定定定定定定定定帧帧帧帧帧帧帧帧大大大大大大大大yes小小小小小小小小没有?错误(r == 0)结束 已发待确定空闲待发送帧空闲在FBP 帧填写:请求建立链路要求的各个字段structframe *FBP; intFind_Free_Frame(structframe **fbp_p)把FBP 帧放回队列{r = PutFrame(FBP)if (MasterFirstFrame== (MasterLastFrame+1) mod 8)return 0;*fbp_p= &MasterFrameBuffer[MasterLastFrame];return 1;结束}intPut_Frame(structframe*fbp){MasterLastFrame= (MasterLastFrame+1) mod 8;return 1; } 构(SDLC_event_q,sdlc_data)和SDLC_read()流程 3.5.2.8事件队列结 1,sdlc_data数据结构类型 union sdlc_data { }; 2) sdlc_event_entry 数据结构类型 struct sdlc_event_entry { int SDLC_Event; union sdlc_data SDLC_Data; 58 } 3) sdlc_event_q 变量定义 struct sdlc_event_entry sdlc_event_q[]; 4) SDLC_read() 流程 While (1) { 从sdlc_event_q 队列取事件, 如没有事件则 sleep_on(SDLC_event_wq); //等待事件 M_sdlc_state_process(); //主站调用 或 S_sdlc_state_process(); //次站调用 } 断处理流程(232_intr()) 3.5.2.9 232接口中 取中断类型, 是数据到达 { 从接口取数据, 放入缓冲区当前缓冲区, } 是发送中断 { 从当前缓冲区取得一个字符, 如没有数据则 关闭中断, } 3.5.3 数据结构说明 名称约定: 宏名称:大写字_组合 类型名称:小写字_小写字 变量和结构类型域名称:第一字母大写的字组合 程序名称:第一字母大写的字_组合 例如: struct sdlc_event_entry { //SDLC事件实体类型定义 int Sdlc_Event; //事件类型 union sdlc_data Sslc_Data; //相应事件参数 }; #define SDLC_EVENT_ENTRY_NUMBER 100 //事件队列长度~如超过这个数据将认为系统不正常 #define INFO_LEN 60 //帧的信息部分的长度 struct sdlc_frame { char SF_FlagH; //帧头 01111110 char SF_Control; //控制字段 char SF_ILen; //信息长度 char SF_Info[INFO_LEN]; //信息 char SF_FCS[2]; //校验和 59 char SF_FlagT; //帧尾 01111110 }; #define TRAN_FRAME_LEN (2+2*(INFO_LEN + 4)) //底层发送帧最大长度 struct tran_frame { char TF_Data[TRAN_FRAME_LEN]; //底层232发送帧,可能包含转义字符, }; 3.5.4 程序样本(样本3) 3.5.4.1 makefile文件 all:fs fr sdlc Sdev.o Rdev.o Msdlc.o Ssdlc.o #all:fs fr sdlc Sdev.o Rdev.o Msdlc.o fr:File_Recv.o File_Copy.o gcc -o fr File_Recv.o File_Copy.o File_Recv.o:File_Recv.c File_Copy.h gcc -c File_Recv.c fs:File_Send.o File_Copy.o gcc -o fs File_Send.o File_Copy.o File_Send.o:File_Send.c File_Copy.h gcc -c File_Send.c sdlc:sdlc.c sdlc.h gcc -o sdlc sdlc.c Sdev.o:Sdev.c File_Copy.h sdlc.h gcc -c Sdev.c -DMODULE -D__KERNEL__ -I/usr/src/linux-2.4.20-8/include -O2 Rdev.o:Rdev.c File_Copy.h sdlc.h gcc -c Rdev.c -DMODULE -D__KERNEL__ -I/usr/src/linux-2.4.20-8/include -O2 Msdlc.o:Msdlc.c sdlc.h gcc -c Msdlc.c -DMODULE -D__KERNEL__ -I/usr/src/linux-2.4.20-8/include -O2 Ssdlc.o:Ssdlc.c sdlc.h gcc -c Ssdlc.c -DMODULE -D__KERNEL__ -I/usr/src/linux-2.4.20-8/include -O2 60 clean:# rm *.o fr fs sdlc File_Copy.o:File_Copy.c File_Copy.h gcc -c File_Copy.c 3.5.4.2 File_Copy.h 文件(上层文件传输头文件) #define FILENAMELEN 20 #define BUFFER_LEN 256 struct filehead { char filename[FILENAMELEN]; long filesize; }; int write_rs232(int, char *, int); int read_rs232(int, char *, int); #define SDEV_INTERFACE_FILE "/dev/S" #define RDEV_INTERFACE_FILE "/dev/R" 3.5.4.3 File_Send.c 文件 ///////////////////////////// 发送进程 by 杨雄 ////////////////////// #include "File_Copy.h" #include #include #include #include #include #define BEBUG_SEND int datasend(int sd, int fd, long fl) { char *filebuffer; long filelen = 0; int filebuffersize = BUFFER_LEN, cnt; if ((filebuffer = malloc(filebuffersize)) == NULL) 61 return -1; #ifdef BEBUG_SEND printf("Before Send File Block\n"); #endif while ((cnt = read(fd, filebuffer, filebuffersize)) > 0) { if (write_rs232(sd, filebuffer, cnt) == -1) return -1; #ifdef BEBUG_SEND printf(" %d.", cnt); #endif filelen += cnt; if (filelen >= fl) break; } free(filebuffer); #ifdef BEBUG_SEND printf("Data Size %ld\n", filelen); #endif return 1; } int filesend(int sd, int fd, char *fn) { struct filehead filehead; int cnt; struct stat s_file_stat; #ifdef BEBUG_SEND printf("In filesend subroutine\n"); #endif bzero(&s_file_stat, sizeof s_file_stat); bzero(&filehead, sizeof filehead); #ifdef BEBUG_SEND printf("Before fstat [%s]\n", fn); #endif if (fstat(fd, &s_file_stat) < 0) { printf("\nfstat: error [%s]\n", fn); return -1; } #ifdef BEBUG_SEND printf("FileHead Size: %d, Send File Size: %ld\n", sizeof filehead, 62 s_file_stat.st_size); #endif filehead.filesize = s_file_stat.st_size; strcpy(filehead.filename, fn); cnt = write_rs232(sd, (char *)&filehead, sizeof filehead); if (cnt == -1) return -1; #ifdef BEBUG_SEND printf("Send FileName & FileSize OK!\n"); #endif if (datasend(sd, fd, filehead.filesize) < 0) return -1; else return 1; } main(int argc, char *argv[]) { int devfd, fd; char *filename = argv[1]; if (argc < 2) { printf("\nUsge: send filename\n"); exit(-1); } if ((fd = open(filename, 0)) == -1) { printf("文件不能打开!"); exit(-1); } #ifdef BEBUG_SEND printf("Before open %s\n", RDEV_INTERFACE_FILE); #endif devfd = open(RDEV_INTERFACE_FILE, O_RDWR); if (devfd == -1 ) { printf("Cann't open file %s\n", RDEV_INTERFACE_FILE); exit(0); } #ifdef BEBUG_SEND printf("Before File Send [%s]\n", filename); #endif if (filesend(devfd, fd, filename) == -1) printf("\nSend Data Not Finish!\n"); else printf("\nSend Data OK!\n"); 63 close(fd); close(devfd); } 3.5.4.4 File_Recv.c 文件 //////////////////////////// 接收进程 by 杨雄 / ////////////////////// #include "File_Copy.h" #include #include #include #include #include int datacopy(int fd, int sd, long fl) { char *filebuffer; long filelen = 0; int filebuffersize = BUFFER_LEN, cnt; if ((filebuffer = malloc(filebuffersize)) == NULL) return -1; while ((cnt = read(sd, filebuffer, filebuffersize)) > 0) { write(fd, filebuffer, cnt); filelen += cnt; if (filelen >= fl) break; } free(filebuffer); return 1; } int filecopy(int sd) { struct filehead filehead; char fn[FILENAMELEN]; int cnt; int fd; bzero(&filehead, sizeof filehead); 64 cnt = read_rs232(sd, (char *)&filehead, sizeof filehead); if (cnt == -1) return -1; strcpy(fn, filehead.filename); fd = open(fn, O_RDWR|O_CREAT|O_TRUNC); if (fd == -1 ) { printf("Cann't open file %s\n", fn); exit(0); } if (datacopy(fd, sd, filehead.filesize) == -1) { printf("A File Data Copy Error!"); return -1; } close(fd); printf("A File Data Copy Finish!\n"); return 1; } main() { int devfd; int rec_n = 0; devfd = open(SDEV_INTERFACE_FILE, O_RDWR); if (devfd == -1 ) { printf("Cann't open file %s\n", SDEV_INTERFACE_FILE); exit(0); } while (1) { if (filecopy(devfd) < 0) { printf("文件拷贝错误"); break; } printf("receive data OK! file number = %2d\n", ++rec_n); } close(devfd); } 3.5.4.5 File_Copy.c 文件 #include "File_Copy.h" 65 #include #include #include int write_rs232(int sd, char *buf, int sum_l) { int cnt = 0, l = sum_l; char *bp = buf; while (1) { bp += cnt; cnt = write(sd, bp, l); if (cnt == -1) return -1; l -= cnt; if (l == 0) break; } return sum_l; } int read_rs232(int sd, char *buf, int sum_l) { int cnt = 0, l = sum_l; char *bp = buf; while (1) { bp += cnt; cnt = read(sd, bp, l); if (cnt == -1) return -1; l -= cnt; if (l == 0) break; } return sum_l; } 3.5.4.6 sdlc.h 文件 #define SDEV_INTERFACE_FLAG "Sdev" #define RDEV_INTERFACE_FLAG "Rdev" #define SDLC_INTERFACE_FLAG "Sdlcdev" #define SDLC_DEV_INTERFACE_FILE "/dev/sdlc" 66 #define SDEV_MAJOR 251 //系统调用接口主设备号码 #define SDEV_MINOR 0 //系统调用接口次设备号码 #define RDEV_MAJOR 252 //系统调用接口主设备号码 #define RDEV_MINOR 0 //系统调用接口次设备号码 #define SDLC_DEV_MAJOR 253 //系统调用接口主设备号码 #define SDLC_DEV_MINOR 0 //系统调用接口次设备号码 ////////////////// sdlc 数据结构定义 union sdlc_data { int Sdlc_Data; }; struct sdlc_event_entry { int Sdlc_Event; union sdlc_data Sslc_Data; }; #define SDLC_EVENT_ENTRY_NUMBER 100 #define INFO_LEN 60 struct sdlc_frame { char SF_FlagH; char SF_Control; char SF_ILen; char SF_Info[INFO_LEN]; char SF_FCS[2]; char SF_FlagT; }; #define TRAN_FRAME_LEN (2+2*(INFO_LEN + 4)) struct tran_frame { char TF_Data[TRAN_FRAME_LEN]; }; /////////////////// 主站 数据结构定义 #define MASTER_SDLC_BUFFER_NUMBER 8 //主站事件定义 #define M_CALL 0 //主站请求建立链路 #define M_DATA 1 //主站有数据要发 #define M_STOP 2 //主站关闭链路 67 #define S_FRAME_U_RES 3 //次站U帧到达 #define S_FRAME_S_RES 4 //次站S帧到达 //事件来源 #define KEY_COMM 0 //主站键盘 #define SEC_FRAME 1 //次站帧 #define MASTER_DATA 2 //主站数据,这三个量的值必须从0开始,且连续 #define LINK_CLOSE 0 //状态定义 #define LINK_CONNECTING 1 //状态定义 #define LINK_CONNECTED 2 //状态定义 #define LINK_SHUTDOWN 3 //状态定义 #define SEND_DATA_STAT 0 //发送 //状态定义 #define WAIT_REQ_STAT 1 //等待 //状态定义 // 主站帧类型造帧命令定义 #define U_UA 0 //响应 #define U_CMDR 1 //命令拒绝 #define U_SABM 2 //建立链路 #define U_DISC 3 //拆除链路 void Put_Master_Sdlc_Event(int); void Get_Master_Sdlc_Event(struct sdlc_event_entry *); int Master_SDLC_Buffer_Is_Empty(char **); //extern wait_queue_head_t Swq; void Process_Master_Sdlc_Event(struct sdlc_event_entry *MasterEventP); int Master_Modify_MasterSdlcBuffer(int SRcvSeq, char S_Frame_S); void Log_Error(char *); void Sdlc_Memncpy(char *t, char *f, int ); int Get_Send_Frame(struct sdlc_frame **); int Master_Make_I_Frame(); void Send_Frame(struct sdlc_frame *); void RS232_Send_Frame(struct sdlc_frame *); // 次站 数据结构定义 //次站事件定义 #define M_FRAME_U_REQ 0 //主站U帧到达 68 #define M_FRAME_S_REQ 1 //主站S帧到达 #define M_FRAME_INFO 2 //主站I帧到达 #define S_BUFFER_FREE 5 //次站缓冲区有空闲 void Put_Second_Sdlc_Event(int); int Get_Char_Fr_Second_SDLC_Buffer(char *); //extern wait_queue_head_t Rwq; 3.5.4.7 Sdev.c 文件 ////////////////////////Linux 2.4-20 版本 /dev/S 设备系统调用程序 by 杨雄 in 2007.8.10 //////////////////////// #define __NO_VERSION__ #include "linux/kernel.h" #include "linux/module.h" #include "linux/config.h" #include "linux/compatmac.h" #include "linux/sched.h" MODULE_LICENSE("GPL"); #ifdef CONFIG_SMP #define __SMP__ #endif #include "linux/version.h" char kernel_version [] = UTS_RELEASE; ///////////////////////////////////////////////////////////////// ///////////////////// #include "linux/types.h" #include "linux/fs.h" #include "linux/mm.h" #include "linux/errno.h" #include "linux/interrupt.h" #include "asm/segment.h" #include "asm/uaccess.h" #include ///////////////////////////////////////////////////////////////// ///////////////////// 69 #include "sdlc.h" static int SdevMajor = SDEV_MAJOR; //系统调用接口主设备号码 static int S_open(struct inode *inode, struct file *file); //系统调 用接口 static int S_release(struct inode *inode, struct file *file); //系 统调用接口 static int S_write(struct file *filp, const char *buffer, size_t length, loff_t *offset); //系统调用接口 wait_queue_head_t Swq; //调用进程缓冲区空间 static int SdevOpenTime = 0; //标记接口逻辑设备被打开的次数 //接口设备驱动程序, 进程进入内核接口程序 struct file_operations SdevFops = { open: S_open, release: S_release, /* close */ write: S_write, // nothing more, fill with NULLs }; ///////////////////////////////////////////////////////// static char *SdevInterface = SDEV_INTERFACE_FLAG; //设备接口标记字 串 int init_module(void) { int result; printk(KERN_ALERT "%s: Sdev Test Initing......\n", SdevInterface); result = register_chrdev(SdevMajor, SdevInterface, &SdevFops); if (result < 0) { printk(KERN_INFO "%s: can't get major number\n", SdevInterface); return result; } if (SdevMajor == 0) SdevMajor = result; // dynamic init_waitqueue_head(&Swq); 70 printk(KERN_INFO "%s: init OK! major=%d\n", SdevInterface, SdevMajor); SdevOpenTime = 0; return 0; } static int S_open(struct inode *inode, struct file *file) { if (SdevOpenTime == 1) { SdevOpenTime++; } else { printk(KERN_ALERT "%s: another process open the char device. open time=%d\n", SdevInterface, SdevOpenTime); return -1; } Put_Master_Sdlc_Event(M_CALL); // 模块使用计数加1 MOD_INC_USE_COUNT; return 0; } static int S_release(struct inode *inode, struct file *file) { // 模块使用计数减1 SdevOpenTime--; MOD_DEC_USE_COUNT; return 0; } static int S_write(struct file *filp, const char *buffer1, size_t length1, loff_t *offset) { const char *buffer = buffer1; size_t length = length1; int i = 0; char *bp; if (verify_area(VERIFY_READ, buffer, length) == -EFAULT ) //检验buf是否可以读! 71 return -EFAULT; while (length) { //存在需要发送的数据 if (Master_SDLC_Buffer_Is_Empty(&bp)) { //返回1-没有空间和没有进入状态 if (i) //已经传递了部分数据 Put_Master_Sdlc_Event(M_DATA); interruptible_sleep_on(&Swq); } get_user(*bp, buffer+i); i++; length--; } Put_Master_Sdlc_Event(M_DATA); return i; // 返回拷贝的字节数; } void cleanup_module(void) { printk(KERN_ALERT"%s: Unloading..........\n", SdevInterface); unregister_chrdev(SdevMajor, SdevInterface); } 3.5.4.8 Rdev.c 文件 ////////////////////////Linux 2.4-20 版本 /dev/S 设备系统调用程序 by 杨雄 in 2007.8.10 //////////////////////// #define __NO_VERSION__ #include "linux/kernel.h" #include "linux/module.h" #include "linux/config.h" #include "linux/compatmac.h" #include "linux/sched.h" MODULE_LICENSE("GPL"); #ifdef CONFIG_SMP #define __SMP__ #endif #include "linux/version.h" 72 char kernel_version [] = UTS_RELEASE; ///////////////////////////////////////////////////////////////// ///////////////////// #include "linux/types.h" #include "linux/fs.h" #include "linux/mm.h" #include "linux/errno.h" #include "linux/interrupt.h" #include "asm/segment.h" #include "asm/uaccess.h" #include ///////////////////////////////////////////////////////////////// ///////////////////// #include "sdlc.h" static int RdevMajor = RDEV_MAJOR; //系统调用接口主设备号码 static int R_open(struct inode *inode, struct file *file); //系统调用接口 static int R_release(struct inode *inode, struct file *file); //系统调用接口 static int R_read(struct file *filp, const char *buffer, size_t length, loff_t *offset); //系统调用接口 wait_queue_head_t Rwq; //调用进程缓冲区空间无数据 static int RdevOpenTime = 0; //标记接口逻辑设备被打开的次数 //接口设备驱动程序, 进程进入内核接口程序 struct file_operations RdevFops = { open: R_open, release: R_release, /* close */ write: R_read, // nothing more, fill with NULLs }; ///////////////////////////////////////////////////////// static char *RdevInterface = RDEV_INTERFACE_FLAG; //设备接口标记字串 73 int init_module(void) { int result; printk(KERN_ALERT "%s: Sdev Test Initing......\n", RdevInterface); result = register_chrdev(RdevMajor, RdevInterface, &RdevFops); if (result < 0) { printk(KERN_INFO "%s: can't get major number\n", RdevInterface); return result; } if (RdevMajor == 0) RdevMajor = result; // dynamic init_waitqueue_head(&Rwq); printk(KERN_INFO "%s: init OK! major=%d\n", RdevInterface, RdevMajor); RdevOpenTime = 0; return 0; } static int R_open(struct inode *inode, struct file *file) { if (RdevOpenTime == 1) { RdevOpenTime++; } else { printk(KERN_ALERT "%s: another process open the char device. open time=%d\n", RdevInterface, RdevOpenTime); return -1; } // 模块使用计数加1 MOD_INC_USE_COUNT; return 0; } static int R_release(struct inode *inode, struct file *file) { // 模块使用计数减1 74 RdevOpenTime--; MOD_DEC_USE_COUNT; return 0; } static int R_read(struct file *filp, const char *buffer1, size_t length1, loff_t *offset) { const char *buffer = buffer1; size_t length = length1; int i = 0; char gc; if (verify_area(VERIFY_READ, buffer, length) == -EFAULT ) //检验buf是否可以读! return -EFAULT; while (length) { //还需要接收的数据 if (Get_Char_Fr_Second_SDLC_Buffer(&gc)) { //返回1-缓冲区无数据 if (i) //已经传递了部分数据 Put_Second_Sdlc_Event(S_BUFFER_FREE); //发出缓冲区有空闲事件 interruptible_sleep_on(&Rwq); } get_user(gc, buffer+i); i++; length--; } Put_Second_Sdlc_Event(S_BUFFER_FREE); //发出缓冲区有空闲事件 return i; // 返回拷贝的字节数; } void cleanup_module(void) { printk(KERN_ALERT"%s: Unloading..........\n", RdevInterface); unregister_chrdev(RdevMajor, RdevInterface); } 3.5.4.9 sdlc.c 文件 ///////////////////////////// sdlc 处理进程 by 杨雄 75 ////////////////////// #include "sdlc.h" #include #include #include #include #include main(int argc, char *argv[]) { int devfd; if (argc < 2) { printf("\nUsge: sdlc 参数\n"); exit(-1); } devfd = open(SDLC_DEV_INTERFACE_FILE, O_RDWR); if (devfd == -1 ) { printf("Cann't open file %s\n", SDLC_DEV_INTERFACE_FILE); exit(0); } read(devfd, 0, 0); close(devfd); } 3.5.4.10 Msdlc.c 文件 ////////////////////////Linux 2.4-20 版本 sdlc 主站 样本程序 by 杨雄 in 2007.8.12 //////////////////////// #define __NO_VERSION__ #include "linux/kernel.h" #include "linux/module.h" #include "linux/config.h" #include "linux/compatmac.h" #include "linux/sched.h" MODULE_LICENSE("GPL"); #ifdef CONFIG_SMP #define __SMP__ 76 #endif #include "linux/version.h" char kernel_version [] = UTS_RELEASE; ///////////////////////////////////////////////////////////////// ///////////////////// #include "linux/types.h" #include "linux/fs.h" #include "linux/mm.h" #include "linux/errno.h" #include "linux/interrupt.h" #include "asm/segment.h" #include "asm/uaccess.h" #include #include #include #include //////////////////////////////////////////////////// #include "sdlc.h" //////////////////////////////////////////////////// 主站sdlc 设备文件操作接口层 /// static int SdlcDevMajor = SDLC_DEV_MAJOR; //系统调用接口主设备号码 static int Sdlc_open(struct inode *inode, struct file *file); //系统调用接口 static int Sdlc_release(struct inode *inode, struct file *file); // 系统调用接口 static int Sdlc_read(struct file *filp, char *buffer, size_t length, loff_t *offset); //系统调用接口 static int SdlcOpenTime; //标记接口逻辑设备被打开的次数,本设备只能被打开一次,如是本地测试可以2次 //接口设备驱动程序, 进程进入内核接口程序 struct file_operations SdlcFops = { 77 open: Sdlc_open, release: Sdlc_release, /* close */ read: Sdlc_read, // nothing more, fill with NULLs }; static char *SdlcInterface = SDLC_INTERFACE_FLAG; //设备接口标记字 串 int init_module(void) { int result; printk(KERN_ALERT "%s: Test Initing......\n", SdlcInterface); result = register_chrdev(SdlcDevMajor, SdlcInterface, &SdlcFops); if (result < 0) { printk(KERN_INFO "%s: can't get major number\n", SdlcInterface); return result; } if (SdlcDevMajor == 0) SdlcDevMajor = result; // dynamic //接收和发送缓冲区清空 printk(KERN_INFO "%s: init OK! major=%d\n", SdlcInterface, SdlcDevMajor); RS232_Init(); SdlcOpenTime = 0; return 0; } static int Sdlc_open(struct inode *inode, struct file *file) { // 打开成员变量累加计数 if (SdlcOpenTime <= 1) { SdlcOpenTime++; } else { printk(KERN_ALERT "%s: another process open the char device. open time=%d\n", SdlcInterface, SdlcOpenTime); 78 return -1; } Master_Make_Buffer(); //接收和发送缓冲区清空 RS232_Ready(); // 模块使用计数加1 MOD_INC_USE_COUNT; printk("%s: open OK!\n", SdlcInterface); return 0; } static int Sdlc_release(struct inode *inode, struct file *file) { // 关中断 cli(); // 模块使用计数减1 SdlcOpenTime--; MOD_DEC_USE_COUNT; // 关闭端口 #ifdef DEBUG_RS232 if (SdlcOpenTime == 0) outb_p(0x00, RS232_BASE_ADDR+1); //设置中断允许寄存器,0x00不允所有232接口中断 #endif // 开中断 sti(); return 0; } static int Sdlc_read(struct file *filp, char *buffer1, size_t length1, loff_t *offset) { char *buffer = buffer1; size_t length = length1; int i = 0; struct sdlc_event_entry MasterEvent; if (verify_area(VERIFY_WRITE, buffer, length) == -EFAULT ) //检验buf是否可以读写! return -EFAULT; 79 printk("%s: Sdlc read\n", SdlcInterface); while (1) { Get_Master_Sdlc_Event(&MasterEvent); //取得事件 Process_Master_Sdlc_Event(&MasterEvent); //分析 处理事件 } return i; // 返回拷贝的字节数; } void cleanup_module(void) { printk(KERN_ALERT"%s: Unloading..........\n", SdlcInterface); #ifdef DEBUG_RS232 free_irq(RS232_irq, &RS232_irq); timer_exit(); #endif unregister_chrdev(SdlcDevMajor, SdlcInterface); } ///////////////////////////////////////////////////////////////// ////////////////////////////// ///////////////////////////////////////////////// 事件处理层 ///////////////// int MasterCurrentState = LINK_CLOSE; void Process_Master_Sdlc_Event(struct sdlc_event_entry *MasterEventP) //分析 处理事件 { int MasterEventType = MasterEventP->Sdlc_Event; struct sdlc_frame *SFP = NULL; switch (MasterEventType) { case M_CALL: if (MasterCurrentState == LINK_CLOSE) { int r = Master_M_CALL_Process(); if (r == -1) { Log_Error("资源不满足~"); return ; } MasterCurrentState = LINK_CONNECTING; } else { Log_Error("当前态:不是LINK_CLOSE, 当前事件:命令初始?); } break; 80 case M_STOP: if (MasterCurrentState == LINK_CONNECTED) { int r = Master_M_STOP_Process(); if (r == -1) { Log_Error("资源不满足~"); return ; } MasterCurrentState = LINK_SHUTDOWN; } else { Log_Error("当前状态不是LINK_CONNECTED, 当前事件:命令初始"); } break; case M_DATA: if (MasterCurrentState == LINK_CONNECTED) { int r = Master_M_DATA_Process(); if (r == -1) { Log_Error("资源不满足~"); return ; } //状态不变 } else { Log_Error("当前状态不是LINK_CONNECTED, 当前事件:命令初始"); } break; case S_FRAME_S_RES://次站S帧响应 if (MasterCurrentState == LINK_CONNECTING) { MasterCurrentState = LINK_CONNECTED; } else if (MasterCurrentState == LINK_CONNECTED) { //应答处理,即发送数据已经有接收确认 int r = Master_S_FRAME_I_Answer_Process(); if (r == -1) { Log_Error("资源不满足~"); return ; } //状态不变 } else { Log_Error("当前状态: 不是LINK_CONNECTED, 当前事件:命令初始化"); } break; case S_FRAME_U_RES: //次站U帧响应 if (MasterCurrentState == LINK_SHUTDOWN) { 81 int r = Master_S_FRAME_U_RES_Process(); //关闭资源 if (r == -1) { Log_Error("资源不满足~"); return ; } MasterCurrentState = LINK_CLOSE; } else { ; //不做任何处理 } default: Log_Error("状态: 不是LINK_CONNECTED, 当前事件:命令初始化"); break; } //事件 switch if (Get_Send_Frame(&SFP)) //有帧要发吗, Send_Frame(SFP); //发送帧; } // 事件处理主函数 end int Master_M_CALL_Process() { int r; r = Master_Make_Buffer();//初始化缓冲区 if (r == -1) return -1; r = Master_Make_U_Frame(U_SABM); //做U帧,准备向次站发送建立链路请 求 if (r == -1) return -1; return 1; } int Master_M_DATA_Process() { int r; r = Master_Make_I_Frame(); //做I帧,准备向次站发送 if (r == -1) return -1; return 1; } int Master_M_STOP_Process() { int r; r = Master_Make_U_Frame(U_DISC); //做U帧,准备向次站发送建立链路拆 除 if (r == -1) return -1; return 1; 82 } int Master_S_FRAME_U_RES_Process() //关闭主站资源,样本实例中假设没有任何资源 { return 1; } int Master_S_FRAME_I_Answer_Process() { char S;//RR-00, RNR-10 (低位在前) int SRcvSeq = Master_Get_Seq_Fr_MasterRecvBuffer(&S); Master_Modify_MasterSdlcBuffer(SRcvSeq, S); return 1; } int Master_Make_U_Frame(int U_Type) { // U_Type 为帧的类型 int r; struct sdlc_frame *FBP; r = Find_Free_Frame(&FBP); if (r == 0) { Log_Error("没有空闲缓冲区"); return -1; //向管理员报告~ } switch (U_Type) { case U_UA: FBP->SF_Control = 0x73;// 二进制0111 0011 <- 1100 0 110 F 位=1 FBP->SF_ILen = 0; FBP->SF_Info[0] = 0; FBP->SF_FCS[0] = FBP->SF_FCS[1] = 0; //要计算检查和 break; case U_CMDR: break; case U_SABM: FBP->SF_Control = 0x3f;// 二进制0011 1111 <- 1111 1100 P 位=1 FBP->SF_ILen = 0; FBP->SF_Info[0] = 0; FBP->SF_FCS[0] = FBP->SF_FCS[1] = 0; //要计算检查和 break; case U_DISC: FBP->SF_Control = 0x53;// 二进制0101 0011 <- 1100 1010 P 位=1 83 FBP->SF_ILen = 0; FBP->SF_Info[0] = 0; FBP->SF_FCS[0] = FBP->SF_FCS[1] = 0; //要计算检查和 break; default: break; } Put_Frame(); //一定和Find_Free_Frame成对使用 return 1; } ///////////////////////////////////////////////// 缓冲区队列操作层 static struct sdlc_frame MasterSdlcBuffer[MASTER_SDLC_BUFFER_NUMBER]; static int MasterFirstFrame, MasterSendFrame, MasterLastFrame; static struct sdlc_frame MasterRecvBuffer; int Master_Make_Buffer() { bzero(&MasterSdlcBuffer[0], sizeof MasterSdlcBuffer[0] * MASTER_SDLC_BUFFER_NUMBER); bzero(&MasterRecvBuffer, sizeof MasterRecvBuffer); MasterFirstFrame = 0; MasterLastFrame = 0; MasterSendFrame = 0; return 1; } int Find_Free_Frame(struct sdlc_frame **FBP) { if (MasterFirstFrame == Next_Index(MasterLastFrame)) //无空闲帧缓冲区 return 0; *FBP = &MasterSdlcBuffer[MasterLastFrame]; return 1; } int Put_Frame() { MasterLastFrame = Next_Index(MasterLastFrame); return 1; } int Master_SDLC_Buffer_Is_Empty(char **cp) { register struct sdlc_frame *SFP = 84 &MasterSdlcBuffer[MasterLastFrame]; if (Next_Index(MasterLastFrame) == MasterFirstFrame && SFP->SF_ILen == INFO_LEN) return 1; if (MasterCurrentState != LINK_CONNECTED) return 1; if (SFP->SF_ILen == INFO_LEN) { MasterLastFrame = Next_Index(MasterLastFrame); SFP = &MasterSdlcBuffer[MasterLastFrame]; bzero(SFP, sizeof *SFP); } *cp = &SFP->SF_Info[SFP->SF_ILen++]; return 0; } int Master_Get_Seq_Fr_MasterRecvBuffer(char *SP) { int Seq = 0; Seq = (MasterRecvBuffer.SF_Control >> 5) & 0x07; *SP = (MasterRecvBuffer.SF_Control >> 2) & 0x03; return Seq; } int Master_Modify_MasterSdlcBuffer(int SRcvSeq, char S_Frame_S) { int Seq; while (1) { if (MasterFirstFrame == MasterSendFrame) break; Seq = (MasterRecvBuffer.SF_Control >> 5) & 0x07; //Seq SRcvSeq 比较顺序号 //RR - 移动MasterFirstFrame(向前) //RNR - 移动MasterSendFrame(向后) } } int Get_Send_Frame(struct sdlc_frame **SFP) { if (MasterSendFrame == MasterLastFrame && RS232_Can_Send())// 如232接口正在发送一个帧,则暂时不再组织新帧 return 0; *SFP = &MasterSdlcBuffer[MasterSendFrame]; MasterSendFrame = Next_Index(MasterSendFrame); return 1; } 85 void Send_Frame(struct sdlc_frame *SendFrameP) { RS232_Send_Frame(SendFrameP); //加入转义字符并放入缓冲区,用中断方式发送 } int Next_Index(int i) { register j = i+1; if (j == MASTER_SDLC_BUFFER_NUMBER) j = 0; return j; } ///////////////////////////////////////////////// 事件队列操作层 struct sdlc_event_entry Sdlc_Event_Q[SDLC_EVENT_ENTRY_NUMBER]; int Sdlc_Event_H, Sdlc_Event_T; void Put_Master_Sdlc_Event(int E) { if (Next_Index(Sdlc_Event_T) == Sdlc_Event_H) return ; Sdlc_Event_Q[Sdlc_Event_T].Sdlc_Event = E; Sdlc_Event_T = Next_Index(Sdlc_Event_T); } void Get_Master_Sdlc_Event(struct sdlc_event_entry *EP) { if (Sdlc_Event_T == Sdlc_Event_H) return ; Sdlc_Memncpy((char *)EP, (char *)&Sdlc_Event_Q[Sdlc_Event_H], sizeof *EP); Sdlc_Event_H = Next_Index(Sdlc_Event_H); } void Sdlc_Event_Q_init() { Sdlc_Event_H = Sdlc_Event_T = 0; } ///////////////////////////////////////////////// 232接口操作层 static struct tran_frame TranFrameOutB, TranFrameInB; static int TranFrameOutB_Index, TranFrameInB_Index; static int TranFrameBusy = 0; // 1 - 忙, 缓冲区不空 int RS232_Can_Send() { 86 if (TranFrameBusy) return 0; return 1; } void RS232_Send_Frame(struct sdlc_frame *SFP) { if (TranFrameBusy) return ; //转义,要发送的帧作F标记转义处理,并拷贝TranFrameOutB缓冲区中 TranFrameBusy = 1;//标记已经有发送数据了 //启动232发送中断 } #define RS232_BASE_ADDR 0x3f8 //COM1 硬件传输接口的基地址 static int RS232_irq = 4; static void RS232_interrupt(int irq, void *dev_id, struct pt_regs *regs) //2.4版本 { register int temp_t; register int irf; register char rc, sc; if (irf & 0x01) goto leave ; //无中断 switch (irf & 0x06) { case 0x04: //是接收中断,则接收处理(取得的字节放在rc) rc = inb(RS232_BASE_ADDR); //从硬件端口取数据,一个字节 if (Check_Flag(rc)) { //返回1 - 是帧标记 //头 -- 清空TranFrameInB缓冲区 TranFrameInB_Index = 1; //尾 -- TranFrameInB,设置次站数据包事件,等待sdlc任务拷贝到MasterRecvBuffer goto leave ; } else { //如果没有接收到头,则抛弃这个数据;goto leave; } if (TranFrameInB_Index == TRAN_FRAME_LEN) { TranFrameInB_Index = 0; 87 //设置无正帧头标记 goto leave; } else TranFrameInB.TF_Data[TranFrameInB_Index++] = rc; goto leave ; case 0x02: //是发送中断,则继续发送(如缓冲区 RS232_Buffer_S空,则不发送) if (TranFrameOutB_Index == TRAN_FRAME_LEN) { TranFrameOutB_Index = 0; goto leave; //缓冲区空,无发送数据字节 } sc = TranFrameOutB.TF_Data[TranFrameOutB_Index++]; outb(sc, RS232_BASE_ADDR); // 发送sc字节 goto leave ; default://接收错 printk("%s: 接收错 interrupt!\n", "RS232"); inb(RS232_BASE_ADDR+5); //读取线路状态寄存器,复位 inb(RS232_BASE_ADDR+6); //取Modem状态寄存器,复位 goto leave ; } // switch end leave: outb_p(0x20, 0x20); //hard int return } int RS232_Init() { if (request_irq(RS232_irq, &RS232_interrupt, SA_SHIRQ, "RS232_interface", &RS232_irq)) { printk(KERN_ERR "%s: RS232_irqtest: cannot register IRQ %d\n", "RS232", RS232_irq); return -1; } printk("%s: Request on IRQ %d succeeded\n", "RS232", RS232_irq); outb_p(0x00, RS232_BASE_ADDR+3); //DLAB位为0 outb_p(0x00, RS232_BASE_ADDR+1); //设置中断允许寄存器,0x00不允所有232接口中断 88 return 1; } int RS232_Ready() { outb_p(0x80, RS232_BASE_ADDR+3); //设置线路控制寄存器的DLAB位(位7) outb_p(0x30, RS232_BASE_ADDR+0); //发送波特率因子低字节 outb_p(0x00, RS232_BASE_ADDR+1); //发送波特率因子高字节,0x0030为2400波特 outb_p(0x00, RS232_BASE_ADDR+3); //DLAB位为0 outb_p(0x07, RS232_BASE_ADDR+3); //DLAB位为0, 位3--0:无校验, 位2--1:1位停止位, 位0/1--11:8位数据传输 outb_p(0x0b, RS232_BASE_ADDR+4); //设置MODEM的:位0-DTR,位1-RTS,位2-无用,位3-允许中断到系统,位4-自发自收(设置为1则是内部自发自收,但是不能用中断方式读取字符) // outb_p(0xc7, RS232_BASE_ADDR+2); //势能FIFO inb(RS232_BASE_ADDR+5); //复位 inb(RS232_BASE_ADDR+0); //复位 inb(RS232_BASE_ADDR+6); //复位 outb_p(0x0d, RS232_BASE_ADDR+1); //设置中断允许寄存器,1101-位1为0:数据发送空暂时不允许 outb(inb_p(0x21) & 0xe7, 0x21); //允许8259A芯片的IRQ3,IRQ4中断请求 return 1; } ///////////////////////////////////////////////// void Log_Error(char *s) { } void Sdlc_Memncpy(char *t, char *f, int l) { register char *t1 = t, *f1 = f; register int l1 = l; while (l1--) *t1++ = *f1++; } 89 3.5.4.11 Ssdlc.c 文件 (参照Msdlc.c 和次站状态图来设计) 90 实验五 IP交换软件设计 (基于以太网络或232接口的通信软件设计) 1(实验目的 学生学习IP原理和算法,并把算法写成程序和在前面几个实验基础上实现一个IP交换机。让学生深刻理解路由器原理和作用,初步掌握通信软件设计的方法。 2(实验内容 实验五包含两个实验,实验五.1的实验内容如下图: 用一台PC来模拟一个IP路由器,这个PC机装上 LINUX以及TCP/IP软件包,采用IP交换的功能实现一个 软件路由器。本实验不需要编写任何程序,只要准备3台 以上PC,其中PC路由器装上2个以太网络或者1个以太 网络及一个232串口网络(PPP/SLIP)。分别配置PC主 机和PC路由器即可。如PC主机1能够pingPC主机2就完 PC主机2成了本实验。 以太网线 或232串口线以太网线 PC主机3以太交换机PC主机1PC路由器 实验五.2的实验内容如下图: 用一台PC来模拟一个IP路由器,在实验二、实验三基础上 设计一个IP交换机软件系统。所以需要设计的程序是链路层程序、 232接口驱动程序、IP转发程序。本实验的重点是实现IP转发程序。 如实现的从主机1到主机2的文件传输则本实验完成. 232串口线232串口线 PC主机2 文件IP初始化进程PC主机1PC路由器 接收进程文件发送进程 /dev/IP_Test/dev/RS232_Test/dev/RS232_TestIP 转发程序IP 转发程序 数IP 转发程序据SDLC程序SDLC程序流SDLC程序动示 意232接口232接口232接口 驱动程序驱动程序驱动程序 91 3(实验步骤及调试环境 3.1调试环境 PC以及Linux操作系统。 3.2实验步骤: 3.2.1实验五.1实验步骤: 1) 安装实验网络,如下图 eth0 PC主机1 192.168.0.2PC主机2eth010.25.25.251以太网线 以太网线 10.25.25.1192.168.0.1 eth1 以太交换机eth0eth2PC路由器 eth0PC主机310.25.25.254以太交换机 2) 原理概述 当IP子网中的一台主机发送IP分组给同一IP子网的另一台主机时(比如主机2发给主机3),它将直接把IP分组送到网络上,对方就能收到。而要送给不同IP子网上的主机时,它要选择一个能到达目的子网上的路由器,把IP分组送给该路由器,由路由器负责把IP分组送到目的地(比如主机1 -> PC路由器 -> 主机2)。 3) PC路由器的配置 (1)配置eth0 (a)给这个网络接口分配地址10.25.25.1。配置命令为: # ifconfig eth0 10.25.25.1 netmask 255.0.0.0 (b)为了使这个地址不在计算机重启后消失,编辑 /etc/sysconfig/network-scripts/ifcfg-eth0文件,修改为如下格式: DEVICE = eth0;BOOTPROTO=none;BROADCAST = 10.255.255.255; 92 IPADDR = 10.25.25.1;NETMASK = 255.0.0.0; NETWORK = 10.0.0.0;ONBOOT = yes; USERCTL=no;PEERDNS=no; TYPE=Ethernet (c)增加一条静态路由: # route add -net 10.0.0.0 netmask 255.0.0.0 dev eth0 (d)查看路由表(网络中当前路由表): # route (2)配置eth1 (a)给这个网络接口分配地址192.168.0.1。配置命令为: # ifconfig eth1 192.168.0.1 netmask 255.255.255.0 (b)为了使这个地址不在计算机重启后消失,编辑 /etc/sysconfig/network-scripts/ifcfg-eth1文件,修改为如下格式: DEVICE = eth1;BOOTPROTO=none;BROADCAST = 192.168.0.255; IPADDR = 192.168.0.1;NETMASK =255.255.255.0 ; NETWORK = 192.168.0.0;ONBOOT = yes; USERCTL=no;PEERDNS=no; TYPE=Ethernet、 (c)增加一条静态路由: # route add -net 192.168.0.0 netmask 255.255.255.0 dev eth1 (d)查看路由表(网络中当前路由表): # route 10.0.0.0 * 255.0.0.0 U 0 0 0 eth0 192.168.0.0 * 255.255.255.0 U 0 0 0 eth1 (3)增加一条缺省路由: # route add default gw 192.168.0.254 这样系统的静态路由表建立完成,它的内容是 10.0.0.0 * 255.0.0.0 U 0 0 0 eth0 192.168.1.0 * 255.255.255.0 U 0 0 0 eth1 default 192.168.0.254 0.0.0.0 UG 0 0 0 eth1 (4)增加系统的IP转发功能: echo 1 > /proc/sys/net/ipv4/ip_forward 4) 配置主机1 93 IP地址:192.168.0.2 子网掩码:255.255.255.0 默认网关:192.168.0.1 3.2.2实验五.2实验步骤: 1) 设计和编写简化的IP程序(模块) 2) 设计和编写232接口驱动程序(模块); 3) 设计和编写简化的SDLC链路层程序(模块); 4) 设计和编写文件接收和文件发送程序(模块) 5) 设计和编写的IP初始化程序(模块) 6) 用ROOT登陆系统和进入相应的目录; 7) 将写好的各个模块程序用vi编辑为ip_forword.c; 8) 编辑makefile文件; 9) 编译和检查语法错误; 10) 调试设计的通信软件系统; 11) 上机完毕:退出系统,关闭系统 3.3原理和要求 3.3.1程序功能 利用232串口来组建一个网络系统,完全可以模拟现实中的网络情况(带宽和速度不同,网络拓扑和软件实现原理是一样的)。本实验(以下特指实验四.2)要设计六大模块程序:文件发送程序(进程)、简化的IP转发模块、简化的SDLC程序模块、232接口驱动程序模块、文件接收程序(进程)、IP初始化程序(进程)。简化的IP转发模块既可以在主机1和主机2上运行,也可以在PC路由器上运行。 3.3.2 LINUX操作系统内核原理 LINUX操作系统相关原理和实现技术参见实验二、三。这里不再详细叙述。 3.3.3 IP原理和简化的IP转发程序功能要求 IP互联网采用表驱动的路由选择算法,需要路由选择的设备保存一张IP路由表,路由表存储有关目的地址及怎样到达目的地的信息,通过查询路由表决定把数据报发往何处。IP 94 地址是路由选择的重要参数,IP路由选择利用IP地址隐藏主机信息,连接到同一网络的所有主机共享同一网络号。 3.3.3.1 IP数据包格式 0 4 8 12 16 19 24 31 版本 首部长度 服务类型 长度 认证 标志 段偏移量 协议 首部校验和 TTL 源IP地址 目的IP地址 选项 ... IP包头字段说明 版本号 (4位) 、IP头长度 (4位) 、服务类型 (8位) 、数据包长度 (16位) 、标识段 (16位) 、标志段 (16位) 、生存时间 (8位) 、传输协议 (8位) 、头校验和 (16位) 、发送地址 (16位) 、目标地址 (16位) 、选项/填充。 简单说明 ============ 1. IP头长度计算所用单位为32位字, 常用来计算数据开始偏移量; 2. 数据包长度用字节表示, 包括头的长度, 因此最大长度为65535字节; 3. 生存时间表示数据被丢失前保存在网络上的时间, 以秒计; 4. 头校验和的算法为取所有16位字的16位和的补码; 5. 选项长度是可变的, 填充区域随选项长度变化, 用于确保长度为整字节的倍数; C语言描述IP头 ============ struct iphdr { BYTE versionihl; BYTE tos; WORD tot_len; WORD id; WORD frag_off; BYTE ttl; BYTE protocol; 95 WORD check; DWORD saddr; DWORD daddr; /* Put options here. */ }; 3.3.3.2 IP路由表 掩码 目的地址 下一跳地址 标志 引用计数 使用 接口 255.255.255.192 201.21.52.121 201.21.52.89 U 5 90 S0 ………… ………….. …………. … … … … 4byte 4byte 4byte 1byte 1int 1long 1int struct RoutingTable { char rt_mash[4]; char rt_ip_address[4]; char rt_ip_next[4]; char rt_flag; int rt_datagram_count; long rt_user_conut; int rt_interface; } 路由表举例 20.0.0.530.0.0.640.0.0.7 10.0.0.020.0.0.030.0.0.040.0.0.0 QSR 10.0.0.520.0.0.630.0.0.7 路由器R的路由表 要到达的网络下一个路由器/下一跳 20.0.0.5直接 30.0.0.5直接 10.0.0.520.0.0.5 40.0.0.530.0.0.7 3.3.3.3路由选择算法 标准IP路由表包含许多(N、R)对序偶,N:目的地址(既可以是目的网络地址也可以是目的主机地址),R:到网络N路径上的“下一个”路由器的IP地址。路由表仅指定了 96 从当前路由器R到目的网络路径上的下一步/跳,而不是完整路径,即路由器不知道到达目的地的完整路径。 路由选择的次序为:直接交付,特定主机路由选择,特定网络路由选择,默认路由选择。 下面是一个统一的路由算法: , 从数据报中提取目的IP地址D~并计算网络前缀N A,对路由表中的每一个项目I A,若N与I的网络的地址匹配,和目的地址比较, 1,如是网关,G出现, 使用路由表中的下一跳项目作为下一跳地址,间接交付, 2,如不是网关,G不出现, 使用分组的目的地址作为下一跳地址,直接交付, 3,把分组连同下一跳地址传送到分片模块 4,停止 B,找不到匹配~则发送给报文源ICMP差错报文 C,停止 3.3.3.4 路由表的建立与刷新 有两种建立和更新路由表方法:静态路由、动态路由。其原理和优劣势不再详述。 3.3.3.5 路由软件应处理的主要内容 为经过的IP数据报选择路由 处理IP数据报TTL域中的数值 分片处理 处理IP数据报选项 重新计算IP数据报的头部校验和 生成和处理ICMP报文 实现动态路由协议、维护静态路由 实现ARP协议、形成数据帧 97 3.3.3.6 简化的路由程序 着重精力于路由的选择与IP数据报的转发 忽略分片处理、选项处理、动态路由、校验等功能 日志:显示本机的网络接口、IP数据报的接收情况、IP数据报的选路情况、IP数据报的发送情况 3.3.3.7 简单的维护程序 IP初始化 静态路由表生成和维护(静态路由的添加、修改和删除等维护功能) 日志信息的读取和保留 3.4 缓冲区队列设计 3.4.1 自由队列 初始化时候申请一块内存区域作为缓冲区队列。下面是C语言的说明: struct IPbuffer { //缓冲区块说明 struct buffer *buf_nxt_buffer; struct buffer *buf_prv_buffer; int buf_this_buffer_free_char_count; char buf_space[BUFFER_LEN]; } struct IPbufferq { //自由缓冲区队列头说明 struct buffer *bq_head; struct buffer *bq_tail; int bq_free_count, bq_used_count; } Struct IPbufferq freeq; 3.4.2 接收队列 根据初始化配置,申请若干内存为每个接口一个队列。 struct IPbufferq ip_recv_q[]; 3.4.3 发送队列 根据初始化配置,申请若干内存为每个接口一个队列。 struct IPbufferq ip_send_q[]; 98 3.5 程序流程设计 3.5.1 文件接收和发送程序流程 (参见实验三) 3.5.2 232驱动程序流程 (参见实验三) 3.5.3 简化的SDLC程序流程 (参见实验四) 3.5.4 IP初始化和管理程序程序流程 路由表数据结构修改结构 struct rb_operation { char rb_op_conmmand; //add, modulfy, delect struct RoutingTable rb_item; }; 日志数据结构 struct rb_log_data { int rb_log_if; //显示本机的网络接口 long rb_log_packet_rev_num; //IP数据报的接收情况 long rb_log_packet_snd_num; //IP数据报的发送情况 }; struct rb_log { char rb_log_conmand; // get, clean struct rb_log_data rb_log_data; }; main() { 打开”/dev/IP_Test”设备文件;//初始化内核IP软件包 99 打开”路由表文件” wihle (路由表文件不空) { 调用ioctl将路由表写入内核IP模块; } 生成人机操作界面; while (1) { 等待操作员命令; 如命令是结束IP,则退出循环; 执行日志操作(清除、获得)或者路由表操作(增加、删除、修改) } ”/dev/IP_Test”设备文件; //关闭IP内核的软件功能 关闭 } 3.5.5 IP转发程序流程 IP_open() { 申请缓冲区并制造所有队列; 对路由表清空; 对日志表清空; 标记路由表可用; } IP_close() { 标记路由表不可用; 释放所有队列; 释放内存空间; } IP_ioctl() { switch (命令) { 执行日志操作; 执行路由表操作; } } IP_read() { if (本机队列有数据) { 100 传送数据到接收进程; } } IP_write() { if (本机发送队列有空间){ 从发送进程传数据到队列; } if (本机发送队列有数据){ 调用SDLC发送处理程序;//放在SDLC发送队列,启动232中断,由中断 发送程序发送到网络中 } } IP_forword() // 由SDLC程序调用 { 取得根据数据包的目的地址D; if (D是本机地址) { 数据包放入本机队列; } D查路由表; if (存在) { 根据路由表中的接口号码,数据包放入相应队列; } 调用SDLC处理程序; } 3.5.6 缓冲区操作程序流程 (纯粹的数据结构操作程序,由学生独立编写) 3.6 完整程序 (由学生独立编写) 101 实验六 RH LINUX 2.4版本下的U盘使用 (Linux环境和WIN环境U盘的使用) 1(实验目的 了解LINUX文件系统结构和U盘的使用。 2(实验内容 用一个WIN 下的U在LINUX拷贝文件。 3(实验步骤及调试环境 3.1调试环境 PC以及WINOWS XP 和 Linux操作系统。 3.2实验步骤: 1) 在WIN产生一个可以使用的U盘,并拷贝你关心的文件; 2) 启动Linux操作系统,用root用户登陆; 3) 把U盘插入PC的USB接口; 4) 进入终端模式,输入命令fdisk -l /dev/sd?,找出U盘的设备文件(/dev/sda,/dev/sdb, /dev/sdc等),比如认为是/dev/sda1,这个设备文件代表了U盘设备; 5) 用 ll /mnt检查/mnt目录下面有什么可以使用的空闲子目录,如没有则用 mkdir 来建 立一个(比如usb,mkdir /mnt/usb); 6) 然后mount -t vfat /dev/sda1 /mnt/usb,其中mount命令称为挂载命令,把U盘所在的 /dev/sda1设备文件挂载在目录/mnt/usb下。 7) 用cd /mnt/usb就可以进入U盘文件系统,对U盘上的文件进行操作; 8) Linux的文件拷贝命令是cp d1/a d2/b,即把目录d1下的文件a拷贝到目录d2下并改 名为b;[cp命令花样很多~~] 9) 用完U盘之后确保退出/mnt/usb目录(用cd命令,即退出U盘),用umount命令卸 载U盘,umount /mnt/usb。 102
本文档为【《通信软件设计》实验指导书】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_769254
暂无简介~
格式:doc
大小:509KB
软件:Word
页数:162
分类:生活休闲
上传时间:2018-09-12
浏览量:8