C语言实现BMC KCS接口代码
*
KCS接口本质上就是一组映射到系统I/O空间的I/O地址,这组地址包含连续4字节长的存储空间,如下图所示,2字节提供给KCS状态和命令寄存器,2字节提供给数据输入和输出寄存器,KCS默认base address即数据寄存器地址为CA2,状态和命令寄存器地址即为base address+1(CA3)。
状态寄存器相对比较复杂,高位的前两位(S1,S0)用来标识KCS接口的状态(空闲状态0x00,读状态0x40,写状态0x80,错误状态0xc0)。
上面的状态寄存器的数值都是由BMC设置的,系统端只需要读取即可,就可以获取当前KCS的状态,如果系统端需要控制KCS的状态,则需要向KCS发送控制码,BMC端接收后,设置KCS为对应的状态,直到一条命令执行结束。
该C文件名为KCS-rawdata.c
//加入代码实现需要的C标准库和文件
#include <stdio.h>
#include <stdlib.h>
#include <sys/io.h>
#include <string.h>
#include "linux/capability.h"
//KCS接口定义了一组I/O映射的通信寄存器
#define KCS_CMD_REG 0xCA3 //cmd/status寄存器
#define KCS_DATA_REG 0xCA2 //data_in/data_out寄存器
//定义KCS控制码
#define GET_STATUS 0x60 //发送0x60到cmd/status寄存器获取KCS寄存器状态
#define WRITE_START 0x61 //发送0x61到cmd/status寄存器开始写入数据
#define WRITE_END 0x62 //发送0x62到cmd/status寄存器结束写入数据
#define READ 0x68 //发送0x68到data_in/data_out寄存器读取数据
//定义KCS状态码
#define IDLE_STATE 0 //KCS闲置状态寄存器读值
#define READ_STATE 0x40 //读取数据时状态寄存器的读值
#define WRITE_STATE 0x80 //写入数据时状态寄存器的读值
#define ERROR_STATE 0xC0 //出现错误是状态寄存器的读值
//延时函数
void timedelay(int ms)
{
int x,y,k=0;
for(x=100;x>0;x--)
for(y=ms;y>0;y--)
k++;
}
//等待IBF被清空
void WaitIBFClear()
{
int IBFStatus;
do{
timedelay(100);
IBFStatus = inb(KCS_CMD_REG); //从命令寄存器读取数据直到IBF为1
}while ((IBFStatus & 0x02) == 1);
}
//主动清空OBF
void ClearOBF()
{
inb(KCS_DATA_REG); //BMC读取数据寄存器时,OBF就会被清空
}
//等待OBF被设置
void WaitOBFSet()
{
int OBFStatus;
do{
timedelay(100);
OBFStatus = inb(KCS_CMD_REG); //从命令寄存器读取数据直到OBF为0
}while ((OBFStatus & 0x01) == 0);
}
//获取KCS接口状态
KCS_State()
{
int KCSState;
KCSState = inb(KCS_CMD_REG);
//return KCSState;
return (KCSState & 0xC0);
}
//读取数据
void Read()
{
int i = 0;
int state;
int responseArray[100]; //设置足够长的接收数据的数组
printf(" ");
do
{
state = KCS_State(); //判断当前KCS接口状态,执行不同操作
if(state == READ_STATE)
{
WaitOBFSet(); //如果是读状态,等待OBF被设置
responseArray[i] = inb(KCS_DATA_REG); //获取数据寄存器的值
state = KCS_State();
if((i>1) && (state == READ_STATE)) //如果完成码不是0x00,表示输入的命令错误或者不支持该命令
{
if((responseArray[2]) & 0xff != 0)
{
printf("%02x\n ",responseArray[2]);
printf("the command you input is not correct, please check it carefully!\n");
exit(1);
}
printf("%02x ",responseArray[i]);
}
outb(READ,KCS_DATA_REG); //每次只能读取一个字节的数据,每次都需要发送READ控制码请求读取更多数据,直到数据接收完成
i++;
}
else if((state | IDLE_STATE) == IDLE_STATE) //读取完所有数据后,KCS接口则为空闲状态
{
printf("\n");
WaitOBFSet(); //等待OBF被设置
responseArray[i] = inb(KCS_DATA_REG); //Read dummy data byte from 数据寄存器
exit(1);
}
else
{
exit(0);
}
} while (1);
}
//主函数,程序运行的入口
int main(int argc,char *argv[])
{
int i=0,res;
unsigned char ReqCmdData = 0;
int state;
if (argc<3){ //KCS的命令最少需要3组
return printf("Not KCS-to-BMC request message!\n");
}
res=iopl(3); //设置当前进程的I/O级别为最大
if (res < 0)
{
perror("iopl set error!");
return -1;
}
WaitIBFClear();
ClearOBF();
outb(WRITE_START,KCS_CMD_REG); //发送WRITE_START控制码到命令寄存器开始写入操作
WaitIBFClear();
state = KCS_State();
if ((state == WRITE_STATE)|(state == ERROR_STATE))
{
ClearOBF();
ReqCmdData = strtoul(argv[1],0,0); //将输入的第一个数据转化为十六进制字符串
ReqCmdData = ReqCmdData << 2; //向左偏移两位
outb(ReqCmdData,KCS_DATA_REG); //将得到的数据写入数据寄存器
WaitIBFClear();
ClearOBF();
if(argc-1 == 2) //如果输入的数据只有两个,接着就需要将WRITE_END控制码发送到命令寄存器准备结束写操作
{
outb(WRITE_END,KCS_CMD_REG); //将WRITE_END控制码发送到命令寄存器
WaitIBFClear();
state = KCS_State();
if(state = WRITE_STATE)
{
ClearOBF();
ReqCmdData = strtoul(argv[2],0,0);
outb(ReqCmdData,KCS_DATA_REG); //写入最后1byte数据
WaitIBFClear();
Read(); //开始读取BMC返回的数据
}
else
{
return 0;
}
}
else
{
for (i=2; i<argc-1; i++) //如果输入的数据大于2byte,可以循环接收数据直到最后1byte
{
ReqCmdData = strtoul(argv[i],0,0);
outb(ReqCmdData,KCS_DATA_REG);
WaitIBFClear();
ClearOBF();
}
outb(WRITE_END,KCS_CMD_REG); //写完倒数第二byte是,将WRITE_END控制码发送到命令寄存器
WaitIBFClear();
state = KCS_State();
if(state = WRITE_STATE)
{
ClearOBF();
ReqCmdData = strtoul(argv[argc-1],0,0);
outb(ReqCmdData,KCS_DATA_REG); //接收最后1byte数据
WaitIBFClear();
Read(); //开始写操作
}
else
{
return 0;
}
}
}
else
{
printf("BMC is not prepared to receiving this message!\n");
exit(0);
}
}
编译KCS-rawdata.c,生成名为KCS的可执行文件
gcc -o KCS -c KCS-rawdata.c
执行命令
./KCS 0x06 0x01
即实现了系统通过KCS接口与BMC的交互。(此功能与ipmitool发送raw data过程是一样的)