Hey guys,
I have two AdHoc examples for 1.5 psps.
Sends
1. the alphabet
2. a stream of character data |. recursively
I was thinking of making a tutorial for these examples and posting them here for the development community.
Would anyone be interested?
*currently writing tutorial* - DONE
Wrong forum, but that would be interesting.
Lol, wrong forum.. could a mod make a "Post tutorial" forum then :p how could I have known which one.. I'm inatly thick
Okie here is the tutorial.. and could a mod move this to the *correct forum*
#### Adhoc Tutorial, Send Alphabet from one 1.5 PSP to Another
FOR EXPERIENCED USERS: the
full source code.. I'm sure you don't want to read the basics, just the comments!
It would be a good idea to have read Yeldarb's tutorials before continuing with this one.
Download the source files..Okie, this tutorial will help you to understand the fundamentals of getting homebrew adhoc to work with your 1.5 (only) PSPs. I'm not going to go into extreme detail as you really don't need a great deal of background knowledge to get this working. I have taken all of my lib code from SMS 1.2 emulator (thanks AhMan for the heads up).
Right, to start off here is the first part of the code:
HEADER/* psp & std libs */
#include <pspkernel.h>
#include <pspmodulemgr.h>
#include <pspdisplay.h>
#include <pspdebug.h>
#include <pspthreadman.h>
#include <pspctrl.h>
#include <pspsdk.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define printf pspDebugScreenPrintf
/* 0x1000 = kernel mode set */
PSP_MODULE_INFO("AdHoc", 0x1000, 0, 1);
/* user mode */
PSP_MAIN_THREAD_ATTR(0);
PSP_MODULE_INFO is used to set kernel mode.. this programme
NEEDS to be started in kernel mode for this to work. This is because certain modules need to be loaded and kernel mode gives us that ability. PSP_MAIN_THREAD_ATTR is set so we can call it later for our user mode thread.
For those of you asking yourself WTF is a thread.. well you really should have some previous knowledge on this subject.. so read this artical en.wikipedia.org/wiki/Thread_(computer_science)
..just to speed things up!
Here is the next bit of code:
EXIT CALLBACK THREAD/* exit callback */
int exit_callback(int arg1, int arg2, void *common){
adhocTerm();
sceKernelExitGame();
return 0;
}
/* callback thread */
int CallbackThread(SceSize args, void *argp){
int cbid;
cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
sceKernelRegisterExitCallback(cbid);
sceKernelSleepThreadCB();
return 0;
}
/* debug output */
void sdl_psp_exception_handler(PspDebugRegBlock *regs){
pspDebugScreenInit();
pspDebugScreenSetBackColor(0x00FF0000);
pspDebugScreenSetTextColor(0xFFFFFFFF);
pspDebugScreenClear();
printf("oh **** BSOD...\n\n");
printf("Exception Details:\n");
pspDebugDumpException(regs);
printf("\nPut this in your cygwin and smoke it:\n\n"
"\tpsp-addr2line -e target.elf -f -C 0x%x 0x%x 0x%x\n",
regs->epc, regs->badvaddr, regs->r[31]);
}
/* setup the callback thread */
int SetupCallbacks(void){
int thid = 0;
thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
if(thid >= 0)
sceKernelStartThread(thid, 0, 0);
return thid;
}
SetupCallbacks() is an example of a thread, remember
Yeldbard's second tutorial? All that code that had to go before your main function.. well it helps with exiting the programme.. when you press your home button and quit a homebrew app this thread is called and closes the app properly.. if you didn't add this code into your programme your PSP would crash and turn itself off and you'd be back at the XMB (main PSP menu screen) when you turn it back on.
In the exit_callback function I have added a function called adhocTerm() this will terminate adhoc on exit.
You may be looking at the function sdl_psp_exception_handler() that is amongst the callback thread code.. it was new to me before this, most of you have probably seen the PSP blue screen of death.. well this is a custom one and is called as and when it is needed (on a programme crash mostly).
Here is the next bit of code for both the alphabet and data stream tutorials:
ALPHABETint user_main(SceSize args, void *argp){
/* var init */
int done, err, i, loop, server, size = 0;
unsigned int length;
char *data;
SceCtrlData pad;
pspDebugScreenInit();
/* adhoc initalise functions */
if((adhocInit("") >= 0) && ((server = adhocSelect()) >=0)){
printf("\n\n## AdHoc INITALISED ##\n\n");
sceKernelDelayThread(1000000);
/* Display server, or client */
if(server)
printf("Server Assigned\n\n");
else
printf("Client Assigned\n\n");
/* if this psp is the server */
if(server){
/* client must be waiting for server, so slight delay for server */
sceKernelDelayThread(1000000);
/* alphabet string */
data = "abcdefghijklmnopqrstuvwxyz";
size = sizeof(data);
printf("Sending data to the client.. size: %d \n", size);
err = adhocSendRecvAck(&size, 4); /* let client know the size of data */
printf("Done sending size: %d\ndata: %s\n", size, data);
sceKernelDelayThread(1000000);
err = adhocSendRecvAck(&data, 4); /* send data as void */
printf("Done sending data: %s\nsize: %d\n", data, size);
} else {
printf("Waiting for data..\n");
/* get data from server */
size = 0;
length = 4;
err = adhocRecvSendAck(&size, &length); /* get size of data */
printf("Done receiving size: %d \nlength: %d \n", size, length);
length = size;
err = adhocRecvSendAck(&data, &length); /* get data */
data = (char *)data; /* type cast received data */
printf("Received data from server: %s \n", data);
}
}
/* terminate adhoc */
adhocTerm();
printf("Press START to exit..\n");
done = 0;
do {
sceCtrlReadBufferPositive(&pad, 1);
if(pad.Buttons != 0)
if(pad.Buttons & PSP_CTRL_START)
done = 1;
} while(!done);
}
DATA STREAMint user_main(SceSize args, void *argp){
SetupCallbacks();
static char buffer[0x8000];
unsigned int length;
int err, bD = 0;
int i, done, mainDone, server=0;
char *data;
int size = 0;
SceCtrlData pad;
pspDebugScreenInit();
do {
if((adhocInit("") >= 0) && ((server = adhocSelect()) >=0)){
pspDebugScreenPrintf("\n\n## AdHoc INITALISED ##\n\n");
sceKernelDelayThread(1000000);
if(server)
pspDebugScreenPrintf("Server Assigned\n\n");
else
pspDebugScreenPrintf("Client Assigned\n\n");
if(server){
sceKernelDelayThread(5000000);
done = 0;
do {
sceCtrlReadBufferPositive(&pad, 1);
if(pad.Buttons != 0)
if(pad.Buttons & PSP_CTRL_CROSS)
done = 1;
if((i % 2) == 0){
data = ".";
i = 0;
} else
data = "|";
i++;
if(done == 1)
data = "x";
size = sizeof(data);
err = adhocSendRecvAck(&size, 4);
err = adhocSendRecvAck(&data, 4); //# send data
pspDebugScreenPrintf("%s", data);
} while(done==0);
} else {
done = 0;
do {
/*sceCtrlReadBufferPositive(&pad, 1);
if(pad.Buttons != 0)
if(pad.Buttons & PSP_CTRL_CROSS)
done = 1;*/
size = 0;
length = 4;
err = adhocRecvSendAck(&size, &length);
length = size;
err = adhocRecvSendAck(&data, &length);
data = (char *)data;
if(data == "x")
done = 1;
pspDebugScreenPrintf("%s", data);
} while(done==0);
}
}
pspDebugScreenPrintf("\n\n");
adhocTerm();
done = 0;
pspDebugScreenInit();
pspDebugScreenPrintf("\n\nPress START to Exit, X to Continue\n\n");
do {
sceCtrlReadBufferPositive(&pad, 1);
if(pad.Buttons != 0){
if(pad.Buttons & PSP_CTRL_START){
done = 1; mainDone=1;
sceKernelExitGame();
}
if(pad.Buttons & PSP_CTRL_CROSS)
done = 1; mainDone=0;
}
} while(!done);
} while(!mainDone);
/* pspDebugScreenPrintf("Press any key to exit..\n");
done = 0;
do {
sceCtrlReadBufferPositive(&pad, 1);
if(pad.Buttons != 0)
done = 1;
} while(!done);*/
sceKernelExitGame();
}
This function will be our user mode thread and is called by our main function (below). It contains the procedural workings for our adhoc communication programme. It starts with initialising the variables we will need.. pad data, character data etc.. pspDebugScreenInit() initialises the debug text that we will need to see what is going on.
The next part:
adhocInit() and
adhocSelect()Simply sets up adhoc..
adhocInit will connect using what ever adhoc settings that you have got selected in the XMB "Network Settings > Ad Hoc Mode".. I have only tested this with the Automatic setting, so make sure yours is set to Automatic too before continuing.
The next function
adhocSelect will display a list of possible connections that are available and allow you to select which one to connect to.. the possible connections are displayed in green and have the device mac address followed by the name given to its adhoc process. The code for this tutorial has been given the process name "adhoc" (this has been hard coded into the lib file ad-hoc/pspadhoc.c, search for "adhoc\0" if you wanted to change it.. just don't change anything else if you don't know what your doing).
The next part of the code to be executed will depend on whether or not the psp has been assigned as the client or the server. If server is selected then a character string containing the alphabet will be set to the data variable, its size recorded and then sent to the client.. a reply from the client is required before continuing, this is all taken care of by the lib code so don't worry about its inner workings. Once a reply has been received the data is sent and the server waits for a reply.
There are a few more things to remember about this procedure..
- the client MUST always be waiting for the server to send the data, that is why there is a delay thread call for the server procedure and not for the client.. if the server tries to send data to the client before it is ready then it will get stuck and you may need to reset your psp.
- the data sent with the function adhocSendRecvAck is actually sent as type void*.. this was new to me, I didn't know you could do something like that.. yet if you send it as a char type and type cast it on receipt there is no problem, it still seems to act as the type you sent it as.. it's just a way of sending whatever data you want without worrying about what type it is.
Once the data has been sent and received the programme will loop round a control input loop waiting for the start button to be pressed so it can exit the programme..
Right to explain the kernel mode to user mode stuff.. here is the final function main()
MAIN/* kernel mode thread */
int main(int argc, char *argp[]){
/* exit callback */
SetupCallbacks();
/* load adhoc modules */
if (adhocLoadDrivers(&module_info) != 0){
printf("Driver load error\n");
return 0;
}
/* setup debug info handler */
pspDebugInstallErrorHandler(sdl_psp_exception_handler);
/* create user thread */
SceUID thid = sceKernelCreateThread("User Mode Thread", user_main,
0x11, // default priority
256 * 1024, // stack size (256KB is regular default)
PSP_THREAD_ATTR_USER, NULL); //# user mode
// start user thread, then wait for it to do everything else
sceKernelStartThread(thid, 0, 0);
sceKernelWaitThreadEnd(thid, NULL);
/* quick clean exit */
sceKernelExitGame();
return 0;
}
This function is the first to be called, and starts with the SetupCallbacks() function.. remember this function sets up the ability to close down our application properly.. which is particularly important as we are using the PSPs adhoc functions that need to be closed before exit.
The next bit of code loads the adhoc driver modules into memory (remember at this point we are still in kernel mode so we can access kernel mode modules).
The next line of code initialises the debug handler or blue screen of death screen.
Now comes the all important create user mode thread.. the function user_main() is called from here. The function is assigned a name "User Mode Thread", a default priority for execution and a set a mount of memory.. along with the user mode assignment "PSP_THREAD_ATTR_USER". After this the thread is destroyed and game exit is called, this invokes the exit callback thread we talked about earlier..
So the user_main() function operates in user mode and the main() function operates in kernel mode.
If you wanted to mod this into your programme you may want to look at the lib files that are in the ad-hoc folder (source file download is at the top of this post)..
Oh yeah.. might want to look at the Makefile:
MAKEFILETARGET = adhoc
PSPSDK=$(shell psp-config --pspsdk-path)
PSPBIN = $(PSPSDK)/../bin
OBJS = main.o
OBJS += ad-hoc/selectorMenu.o \
ad-hoc/loadutil.o \
ad-hoc/pspadhoc.o \
ad-hoc/stubs.o
INCDIR =
CFLAGS = -g -O2 -G0 \
-I. -Icpu -Ipsp -DLSB_FIRST \
-DALIGN_DWORD -DDOS -DPSP -DUSE_ZLIB
CXXFLAGS = -g $(CFLAGS) -fno-exceptions -fno-rtti -Wpointer-arith
ASFLAGS = -c $(CFLAGS)
LIBDIR =
LDFLAGS =
LIBS = -lpspgu -lpspwlan -lpsppower -lz -lstdc++ -lm
#EXTRA_TARGETS = kxploit
PSP_EBOOT_TITLE = Adhoc
include $(PSPSDK)/lib/build.mak
Nothing too fansy, the most important part of this makefile is the -lpspwlan lib and the ad-hoc objects. Some of this makefile and indeed the example has left over code from the SMS 1.2 source code it was derived from.
If you have any questions please ask me (BlackPhoenix) on the relevant forum (qj.net, psp-programming.com).
Please Note: This programme has worked for me on two 1.5 PSPs but I have changed the code a little for this tutorial and I don't have access to a second 1.5 PSP this week (bloody friend went on holiday to Mexico with it). Could someone confirm that this code still works.. if not please give me as much detail so I can fix it
