/**************************************************************************************/ /* Packets.c, Libcyc source file */ /* Functions to create and control packet transfers between playstations */ /* Copyright(C)1999 Peter Armstrong */ /* Release 2.0 */ /**************************************************************************************/ /* includes */ #include #include "stdlib.h" #include "sys\ioctl.h" #include "sys\file.h" #include "pad.h" #include "sendrec.h" #include "packets.h" #include "cycfcntl.h" #include "misc.h" /* externs */ extern port_fd; extern volatile u_int vs_timer; extern u_int time[]; extern vs_cmd; /* globals */ /* ptr to this struct returned to app */ struct link_info link = { {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0,0,0,LK_PSX}; u_char *pkt_info = link.info; /* keep track of all pkt movement etc with this array */ volatile struct cmds *cmd = &link.cmd_info; /* ptr to command structure */ /* malloc'ed packet mem ptrs */ u_char *r_data_mem, /* remote packets read in and stored here */ *l_pkt_mem, /* local data packet storage */ *work_mem; /* used for sending er & psx_end packets */ u_char *send_p, /* ptr to packet to be sent */ *receive_p; /* read in remote packet here */ /* max frame differences between local and remote psx's, depending on connection type */ int diffs[3] = {MAXD_PSX,MAXD_PC,MAXD_MODEM}; /* offset positions for local/remote packets */ int offset[MAX_PKTS_16]; int data_size,pkt_size; /* packet data size and total size */ int PKT_NUMTYPE,PKT_CHKSUM; /* indexes for added packet info */ u_int start_time,p_sent; /* timer and packet sent flag */ /**************************************************************************************/ /* Create a connection to a second playstation */ /* Arguments: */ /* none */ /* Return value: */ /* address of main link structure always returned, containing the following: */ /* link.loc_status: */ /* -1 - can't create a connection via the PC */ /* 0 - connection cancelled by user */ /* 1 - connection established, link.connected will also equal 1 */ /* if connection established: */ /* link.lk_type, the connection type */ /* 0 - direct psx-to-psx connection via a link cable */ /* 1 - psx-to-psx connection via a PC,playstations connected to PC serial ports*/ /* 2 - PC modem connection to remote PC/psx */ /* link.m_state, master or slave status of playstation */ /* 2 - slave */ /* 3 - master */ /**************************************************************************************/ struct link_info *link_connect(volatile char *padbuf) { u_char cnt_cmd[CMD_SIZE] = {PSX_CMD,'C','N','T',0x00,0,0,0x00}; /* ²CNT00 */ u_char canx_cmd[CMD_SIZE] = {PSX_CMD,'X','C','N','T',0,0,0x00}; /* ²XCNT00 */ int loc_connect = 0,rem_connect = 0,pc_connect = 0; int first_time = 1,m_state,type; int ser_num,rem_reply; int padd; if(link.connected) { link.loc_status = I_CONNECTED; /* already connected */ return &link; } if(padbuf) padbuf += 3; link.lk_type = LK_PSX; link.loc_status = I_CNT_FAIL; if(Send_PsxCmd(cnt_cmd) == OK) /* send connect command */ { while(1) { /* look for user exit if not connecting via pc */ if((padbuf) && (!pc_connect)) { if(!(*padbuf & PADRup)) /* exit on 'triangle' pressed */ { link.loc_status = I_USER_INT; Send_PsxCmd(canx_cmd); /* send cancel cmd */ break; } } if(cmd->connect) /* connect cmd received */ { cmd->connect = 0; type = cmd->cnt_type; /* read connection type */ if(type == LK_PSX) /* psx's connected via link cable */ { if(first_time) { m_state = MASTER; /* remote the master */ } else m_state = SLAVE; /* remote slave */ if(Cmd_Reply(m_state) != OK) /* inform remote of it's m_state */ break; } else /* connected via PC */ pc_connect = 1; /* prevent connection cancellation by user */ rem_connect = 1; } if(cmd->acknowledge) { cmd->acknowledge = 0; link.m_state = cmd->ack_val; /* get m_state from remote */ if(link.m_state) loc_connect = 1; else /* can't connect via PC */ break; } first_time = 0; if((loc_connect) && (rem_connect)) { link.connected = link.loc_status = I_CONNECTED; link.lk_type = type; cmd->exit = 0; /* clear this in case it's set */ return &link; } } } return &link; /* return link struct, check loc_status */ } /**************************************************************************************/ /* Break connection to second playstation */ /* Arguments: */ /* none */ /* Return value: */ /* none */ /**************************************************************************************/ void link_disconnect(void) { u_char disc_cmd[8] = {PSX_CMD,'D','I','S','C',0,0,0x00}; /* ²DISC00 */ if(link.connected) { Send_PsxCmd(disc_cmd); link.connected = 0; } } /**************************************************************************************/ /* Start packet transfer session */ /* Arguments: */ /* size - number of bytes to transfer each frame, 6 max */ /* mode - transfer mode type, see packets.h */ /* Return value: */ /* 1 (S_READY) - session started */ /* 0 (S_NOT_CONNECTED) - not connected */ /* -1 (S_START_FAIL) - session cannot be started */ /* -2 (S_BAD_SIZE) - data size out of range */ /* -3 (S_MEM_FAIL) - memory allocation failure */ /**************************************************************************************/ int pkt_start(u_int size, int mode) { int loc_start = NG,rem_start = NG; int i,ser_num,matched = 1; int rtn = S_MEM_FAIL; u_char *info_p = pkt_info; /* must be connected for real transfers */ if((!mode) && (!link.connected)) return S_NOT_CONNECTED; /* check data size within bounds */ if((!size) || (size > MAX_PKT_DATA)) return S_BAD_SIZE; link.max_diff = diffs[link.lk_type]; /* get max frame diff for connection type */ data_size = size; /* make it global */ pkt_size = (data_size+2); /* plus packet number/type, and checksum */ if(Pkt_Alloc()) /* attempt to alloc memory */ { if(Cmd_Start(size,mode,link.max_diff)) { /* both psxs ready to start, initialize packet variables */ vs_cmd = 0; /* disable command processing */ receive_p = r_data_mem; /* init ptr to first location */ /* packet indexes */ PKT_NUMTYPE = (pkt_size -2); /* pkt/frame number & type info */ PKT_CHKSUM = (pkt_size -1); /* checksum for packet */ link.loc_status = link.rem_status = 0; /* clear packet info array */ for(i = 0;i < INFO_SIZE;i++) *(info_p++) = 0; /* initial settings */ pkt_info[L_STORED] = pkt_info[L_SENT] = pkt_info[R_STORED] = 15; pkt_info[LAST_STATUS] = OK; /* pre-calculate packet position offset values */ for(i = 0; i < MAX_PKTS_16; i++) { offset[i] = pkt_size * i; } VSync(0); return S_READY; } else { rtn = S_START_FAIL; /* mismatch in data, or PC connect fail */ Sio_Flush(); } } return rtn; } /*------------------------------------------------------------------------------------*/ /* Send, receive and match start commands */ /* Arguments: */ /* size - packet data size, transferred per frame */ /* mode - transfer mode, real or test */ /* diff - max frame difference allowed between playstations */ /* Return value: */ /* 1 - data matched, session started */ /* 0 - data mismatch or connection error */ /*------------------------------------------------------------------------------------*/ int Cmd_Start(u_int size, int mode, int diff) { u_char start_cmd[8] = {PSX_CMD,'S','T',0x00,0x00,0,0,0x00}; /* ²ST00*/ int loc_start = NG,rem_start = NG,started = NG; int rem_replied = 0; if(mode) start_cmd[2] = 't'; /* test transfer */ start_cmd[ST_SIZE] = size; /* put data size in start string for remote comparison */ start_cmd[ST_DIFF] = diff; /* put frame difference in string, set by master */ if(Send_PsxCmd(start_cmd) == OK) /* send start command */ { while(!rem_replied) { /* quit if other psx disconnects */ if(mode == P_TFR_REAL) { if(!link.connected) break; } if(cmd->start) /* start command received from remote */ { cmd->start = 0; if(size == cmd->st_size) /* match packet data size */ { started = OK; if((link.lk_type) && (link.connected)) /* real PC connection */ link.max_diff = cmd->st_diff; /* PC may change diff value */ } if(Cmd_Reply(started) == OK) /* inform remote of result of comparison */ loc_start = started; else break; } if(cmd->acknowledge) { cmd->acknowledge = 0; rem_replied++; rem_start = cmd->ack_val; /* read remote's comparison result */ } } } return (loc_start & rem_start); } /*------------------------------------------------------------------------------------*/ /* Allocate packet memory */ /* Arguments: */ /* none */ /* Return value: */ /* 1 - memory allocation successful */ /* 0 - failed to allocate required memory */ /*------------------------------------------------------------------------------------*/ int Pkt_Alloc(void) { int fail = 0; /* allocate space */ if(work_mem = (u_char *)malloc(pkt_size)) /* error report and psx_end packet */ { fail++; if(l_pkt_mem = (u_char *)malloc(pkt_size*MAX_PKTS_16)) /* local packet storage */ { fail++; if(r_data_mem = (u_char *)malloc(pkt_size*MAX_PKTS_16)) /* remote packet storage */ { return OK; } } } /* malloc failure */ switch(fail) { case 2: free(l_pkt_mem); case 1: free(work_mem); } return NG; } /*------------------------------------------------------------------------------------*/ /* Free packet memory */ /* Arguments: */ /* none */ /* Return value: */ /* none */ /*------------------------------------------------------------------------------------*/ void Pkt_Free(void) { free(r_data_mem); free(l_pkt_mem); free(work_mem); } /**************************************************************************************/ /* End packet transfer session */ /* Arguments: */ /* none */ /* Return value: */ /* none */ /**************************************************************************************/ void pkt_end(void) { link.end_tfr = TFR_END; while((!link.loc_status) || (!link.rem_status)) { VSync(0); pkt_transfer(NULL); } if(link.rem_status == T_TIMEOUT) { link.connected = 0; /* if timeout assume lost connection */ if(link.ping == PNG_PC) { ioctl(port_fd,TIOCDTR,1); /* get PC out of transfer mode */ ioctl(port_fd,TIOCDTR,0); } Sio_Flush(); } Pkt_Free(); vs_cmd = 1; /* re-enable command processing */ } /**************************************************************************************/ /* Send and receive frame data packets */ /* Arguments: */ /* *data_p - ptr to data to send */ /* Return value: */ /* 1 (T_DATA_AVAIL) - local and remote available for next frame */ /* 0 (T_NO_DATA) - no (remote) data available */ /* -1 (T_TFR_ENDED) - remote has sent end-of-transfer notification */ /* -2 (T_TIMEOUT) - packets no longer received from remote */ /**************************************************************************************/ int pkt_transfer(u_char *data_p) { start_time = vs_timer; /* start timer, prevent infinite Check_Diff loop */ /* create packet and store */ if(data_p) Store_Packet(data_p); do { Next_Packet(); /* decide on next packet/type to send */ Packet_Transfers(); /* send and receive data packets */ } while(!Check_Diff()); /* check local isn't too far ahead of remote */ if(Next_Frame_Data()) return T_DATA_AVAIL; /* remote data received */ else return link.rem_status; } /*------------------------------------------------------------------------------------*/ /* Store local data as a packet and send in turn */ /* Arguments: */ /* *data_p - ptr to this frame's data */ /* Return value: */ /* none */ /*------------------------------------------------------------------------------------*/ void Store_Packet(u_char *data_p) { int i; u_char *store_p; u_char store_num,checksum = 0; store_num = (pkt_info[L_STORED]+1) & 0x0F; store_p = &l_pkt_mem[offset[store_num]]; /* next packet store location */ /* create packet */ for(i = 0;i < data_size;i++) { checksum += *(store_p++) = *(data_p++); /* store data part in packet */ } /* packet number and packet status share the same byte */ *store_p = (store_num<<4); /* packet number */ *store_p = *store_p | PSX_OK; /* packet status */ checksum += *(store_p++); *store_p = checksum; /* checksum at end of packet */ pkt_info[L_STORED] = store_num; pkt_info[L_STORED_NUM]++; /* number of packets waiting to be sent */ } /*------------------------------------------------------------------------------------*/ /* Decide which packet type, if any, needs to be sent */ /* Arguments: */ /* none */ /* Return value: */ /* none */ /*------------------------------------------------------------------------------------*/ void Next_Packet(void) { int i,sent_num; u_char *work_p; /* if no error problems and packet stored, send data packet */ if((pkt_info[LAST_STATUS] == OK) && (pkt_info[L_STORED_NUM])) { sent_num = pkt_info[L_SENT] = (pkt_info[L_SENT]+1) & 0x0F; send_p = &l_pkt_mem[offset[sent_num]]; /* next packet location */ pkt_info[L_STORED_NUM]--; /* one less to send */ pkt_info[L_OUT] = 1; /* sending packet */ } else { if(link.end_tfr == TFR_END) /* send psx_end pkt? */ { pkt_info[END_PKT] = pkt_info[L_SENT]; /* in case of error remember when sent */ pkt_info[END_AWAIT] = 1; /* remember we have sent psx_end packet */ work_p = work_mem; for(i = 0;i < data_size; i++) { *(work_p++) = 0; /* clear data part of packet */ } *(work_p++) = PSX_END; /* inform pc/psx we are ending pkt tfr */ *work_p = PSX_END; /* chksum */ send_p = work_mem; link.end_tfr = 0; link.loc_status = TFR_END; if(link.rem_status != TFR_TIMEOUT) /* don't send end packet on remote time-out */ pkt_info[L_OUT] = 1; } else { if(pkt_info[LAST_STATUS] == NG) /* error last time round, send e_r packet to remote */ { work_p = work_mem; /* sending empty packet, clr data part */ for(i = 0;i < data_size; i++) { *(work_p++) = 0; } /* pkt number and pkt status in the same byte */ *work_p = pkt_info[R_STORED]<<4; /* last good pkt received by us */ *work_p = *work_p | PSX_NG; /* packet status = er_packet */ *(++work_p) = work_mem[PKT_NUMTYPE]; /* checksum */ send_p = work_mem; pkt_info[L_OUT] = 1; /* sending packet */ } } } } /*------------------------------------------------------------------------------------*/ /* Check local/remote frame difference isn't too great, allow leeway of 1 frame */ /* Arguments: */ /* none */ /* Return value: */ /* 1 - difference within bounds */ /* 0 - local too far ahead of remote */ /*------------------------------------------------------------------------------------*/ int Check_Diff(void) { int reverse_diff = 10; /* remote psx can get ahead of local */ int max_diff = link.max_diff; static int rtn = OK; u_char diff; diff = (pkt_info[L_STORED] - pkt_info[R_STORED]) & 0x0F; if((diff < max_diff) || (diff > reverse_diff) || (link.rem_status)) { rtn = OK; } else { if((vs_timer - start_time) > time[THREE_SEC]) /* timeout if no more rem packets */ { link.rem_status = TFR_TIMEOUT; rtn = OK; } else { if(diff > max_diff) /* local too far ahead of remote */ { rtn = NG; } } } if((p_sent == vs_timer) && (rtn == NG)) /* send one packet only each vsync */ VSync(0); return rtn; } /*------------------------------------------------------------------------------------*/ /* Bad packet received from remote, prepare for sending an error report packet */ /* Arguments: */ /* none */ /* Return value: */ /* none */ /*------------------------------------------------------------------------------------*/ void In_Error_Set(void) { pkt_info[LAST_STATUS] = NG; /* send error report packet next time */ /* keep track of er packet */ pkt_info[ERROR_PKT] = pkt_info[L_SENT]; /* remember when er_packet sent */ pkt_info[ERROR_AWAIT] = 1; /* awaiting re-send */ } /*------------------------------------------------------------------------------------*/ /* If good packet received, read status and process packet data */ /* Arguments: */ /* none */ /* Return value: */ /* none */ /*------------------------------------------------------------------------------------*/ void Packet_Transfers(void) { u_char packet_status,packet_num,next_r_pkt; if(!Send_Rec_Packets()) /* send and receive packets */ return; /* in_packet checksum good */ pkt_info[LAST_STATUS] = OK; /* pkt number and status sharing byte */ packet_status = (receive_p[PKT_NUMTYPE] & 0x0F); packet_num = receive_p[PKT_NUMTYPE]>>4; switch(packet_status) { case PSX_OK: /* remote data packet received */ next_r_pkt = (pkt_info[R_STORED]+1) & 0x0F; /* and check this packet is the next in order to be received */ if(packet_num == next_r_pkt) { pkt_info[R_STORED] = next_r_pkt; /* correct packet received */ pkt_info[R_STORED_NUM]++; /* point remote data store pointer to next location */ next_r_pkt = (next_r_pkt+1) & 0x0F; if(next_r_pkt) receive_p += pkt_size; else receive_p = r_data_mem; pkt_info[ERROR_AWAIT] = 0; } else /* if packet missed, report error, if not one reported already */ { if(!pkt_info[ERROR_AWAIT]) { // In_Error_Set(); /* problem, do not report error */ } } break; case PSX_NG: /* bad packet received by remote */ /* packet_num is the last good data packet remote received from us */ /* check if the remote is sending an er_packet because of a bad er_packet from us */ if((pkt_info[ERROR_AWAIT]) && (packet_num == pkt_info[ERROR_PKT])) { In_Error_Set(); /* er_packet will be re-sent */ } else /* may be a bad psx_end packet */ { if((pkt_info[END_AWAIT]) && (packet_num == pkt_info[END_PKT])) link.end_tfr = TFR_END; /* re-send */ else /* re-start sends from the last packet received by remote +1 */ { /* any packets stored after this? */ if(pkt_info[L_STORED] - packet_num) { pkt_info[L_SENT] = packet_num; /* +1'ed in Next_Packet() */ pkt_info[L_STORED_NUM] = (pkt_info[L_STORED] - pkt_info[L_SENT]) & 0x0F; } } } break; case PSX_END: /* remote psx has ended pkt transfer */ link.rem_status = TFR_END; break; } } /*------------------------------------------------------------------------------------*/ /* Send and receive data packets */ /* Arguments: */ /* none */ /* Return value: */ /* 1 - received good data packet */ /* 0 - no data received */ /*------------------------------------------------------------------------------------*/ int Send_Rec_Packets(void) { int i; int rtn = NG; int to_read = pkt_size; u_char *rec_p = receive_p; u_char getsum = 0; /* have we packet to send? */ if(pkt_info[L_OUT]) { write(port_fd,(char *)send_p,pkt_size); /* send packet to psx or pc(->pc)->psx */ pkt_info[L_OUT] = 0; p_sent = vs_timer; } /* look for packet */ if(Pkt_Read(receive_p,pkt_size)) { /* checksum in_packet */ for(i = 0;i < (pkt_size - 1); i++) getsum += *(rec_p++); /* compare checksums */ if(getsum == *rec_p) rtn = OK; else { Sio_Flush(); In_Error_Set(); /* bad checksum, send error report packet next time */ } } return rtn; } /*------------------------------------------------------------------------------------*/ /* If remote data stored, point link.loc/rem_data ptrs to the next frame's data */ /* Arguments: */ /* none */ /* Return value: */ /* 1 - local and remote data ready for next frame */ /* 0 - no remote data stored */ /*------------------------------------------------------------------------------------*/ int Next_Frame_Data(void) { int rtn = NG; u_int frame; /* remote data stored? */ if(pkt_info[R_STORED_NUM]) { pkt_info[R_STORED_NUM]--; frame = (u_int)pkt_info[FRAME]; /* next frame number */ link.rem_data = &r_data_mem[offset[frame]]; /* ptr to remote frame data */ link.loc_data = &l_pkt_mem[offset[frame]]; /* ptr to local frame data */ pkt_info[FRAME] = (pkt_info[FRAME]+1) & 0x0F; /* increment frame number */ return T_DATA_AVAIL; } else return T_NO_DATA; } /*------------------------------------------------------------------------------------*/ /* Attempt to read a data packet from serial port */ /* Arguments: */ /* *read_p - ptr to buffer to store data */ /* to_read - number of bytes to read */ /* Return value: */ /* 1 - data read */ /* 0 - no data found */ /*------------------------------------------------------------------------------------*/ int Pkt_Read(char *read_p, int to_read) { int r_num; u_int starttime; ioctl(port_fd,FIOCSCAN,1); if((r_num = read(port_fd,read_p,to_read)) > 0) { read_p += r_num; to_read -= r_num; starttime = vs_timer; while((to_read) && (vs_timer == starttime)) { ioctl(port_fd,FIOCSCAN,1); if((r_num = read(port_fd,read_p,to_read)) > 0) { read_p += r_num; to_read -= r_num; } } return OK; } return NG; }