PS2SDK
PS2 Homebrew Libraries
sys_arch.c
1 /*
2 # _____ ___ ____ ___ ____
3 # ____| | ____| | | |____|
4 # | ___| |____ ___| ____| | \ PS2DEV Open Source Project.
5 #-----------------------------------------------------------------------
6 # Copyright 2001-2004, ps2dev - http://www.ps2dev.org
7 # Licenced under Academic Free License version 2.0
8 # Review ps2sdk README & LICENSE files for further details.
9 */
10 
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <kernel.h>
15 #include <kernel_util.h>
16 #include <time.h>
17 #include <limits.h>
18 
19 #include "lwip/sys.h"
20 #include "lwip/opt.h"
21 #include "lwip/stats.h"
22 #include "lwip/debug.h"
23 #include "lwip/pbuf.h"
24 #include "arch/sys_arch.h"
25 
26 #include "ps2ip_internal.h"
27 
28 static arch_message msg_pool[SYS_MAX_MESSAGES];
29 static arch_message *free_head;
30 
31 /* Function prototypes */
32 static inline arch_message *alloc_msg(void);
33 static void free_msg(arch_message *msg);
34 static int WaitSemaTimeout(int sema, unsigned int msec);
35 
36 static int MsgCountSema;
37 
38 extern void *_gp;
39 
40 static inline arch_message *try_alloc_msg(void)
41 {
42  arch_message *message;
43 
44  if(PollSema(MsgCountSema)==MsgCountSema)
45  {
46  DI();
47 
48  message = free_head;
49  free_head = free_head->next;
50 
51  EI();
52  }else message=NULL;
53 
54  return message;
55 }
56 
57 static inline arch_message *alloc_msg(void)
58 {
59  arch_message *message;
60 
61  WaitSema(MsgCountSema);
62  DI();
63 
64  message = free_head;
65  free_head = free_head->next;
66 
67  EI();
68 
69  return message;
70 }
71 
72 static void free_msg(arch_message *msg)
73 {
74  DI();
75 
76  msg->next = free_head;
77  free_head = msg;
78 
79  EI();
80  SignalSema(MsgCountSema);
81 }
82 
83 //Create a new thread.
84 sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
85 {
86  ee_thread_t thp;
87  int tid, rv;
88 
89  (void)name;
90 
91  thp.attr = 0;
92  thp.option = 0;
93  thp.func = thread;
94  thp.stack_size = stacksize;
95  thp.stack = malloc(stacksize);
96  thp.initial_priority = prio;
97  thp.gp_reg = &_gp;
98 
99  if((tid = CreateThread(&thp)) < 0)
100  {
101  dbgprintf("sys_thread_new: CreateThread failed, EC: %d\n", tid);
102  return ERR_MEM;
103  }
104 
105  if((rv = StartThread(tid, arg)) < 0)
106  {
107  dbgprintf("sys_thread_new: StartThread failed, EC: %d\n", rv);
108  DeleteThread(tid);
109  return ERR_MEM;
110  }
111 
112  dbgprintf("sys_thread_new(): %d\n", tid);
113 
114  return((sys_thread_t)tid);
115 }
116 
117 err_t sys_mbox_new(sys_mbox_t *mbox, int size)
118 {
119  struct MboxData *MBox;
120  ee_sema_t sema;
121 
122  (void)size;
123 
124  *mbox=SYS_MBOX_NULL;
125 
126  if((MBox=malloc(sizeof(struct MboxData)))!=NULL)
127  {
128  //Last = first, empty mbox.
129  MBox->LastMessage=MBox->FirstMessage=NULL;
130 
131  sema.attr = 0;
132  sema.option = (u32)"PS2IP-msgcount";
133  sema.init_count=0;
134  sema.max_count=SYS_MAX_MESSAGES;
135  if((MBox->MessageCountSema=CreateSema(&sema))<0)
136  {
137  printf("sys_mbox_new: CreateMbx failed. Code: %d\n", MBox->MessageCountSema);
138  free(MBox);
139  return ERR_MEM;
140  }
141  }
142  else{
143  printf("sys_mbox_new: Out of memory.\n");
144  return ERR_MEM;
145  }
146 
147  dbgprintf("sys_mbox_new(): sema %d\n", MBox->MessageCountSema);
148 
149  *mbox=MBox;
150 
151  return ERR_OK;
152 }
153 
154 //Delete the messagebox, (*pMBox).
155 void sys_mbox_free(sys_mbox_t *pMBox)
156 {
157  arch_message *Message, *NextMessage;
158 
159  /* Free all messages that were not freed yet. */
160  Message=(*pMBox)->FirstMessage;
161  while(Message!=NULL)
162  {
163  NextMessage=Message->next;
164  free_msg(Message);
165  Message=NextMessage;
166  }
167 
168  /* Delete all allocated resources for this message box and mark the message box as invalid. */
169  DeleteSema((*pMBox)->MessageCountSema);
170  free((*pMBox));
171  (*pMBox)=SYS_MBOX_NULL;
172 }
173 
174 int sys_mbox_valid(sys_mbox_t *mbox)
175 {
176  return((*mbox)!=SYS_MBOX_NULL);
177 }
178 
179 void sys_mbox_set_invalid(sys_mbox_t *mbox)
180 {
181  *mbox=SYS_MBOX_NULL;
182 }
183 
184 static void RetrieveMbxInternal(sys_mbox_t mBox, arch_message **message)
185 {
186  arch_message *NextMessage;
187 
188  DI();
189 
190  *message=mBox->FirstMessage; //Take first message in mbox
191 
192  //The next message is next. If there is no next message, NULL is assigned,
193  NextMessage=((*message != NULL) && ((unsigned int)(*message)->next!=0xFFFFFFFF))?(*message)->next:NULL;
194 
195  //if the mbox only had one message, then update LastMessage as well.
196  if(mBox->FirstMessage == mBox->LastMessage)
197  mBox->LastMessage = NULL;
198 
199  mBox->FirstMessage = NextMessage; //The next message becomes the first message. Or NULL, if there are no next messages.
200 
201  EI();
202 }
203 
204 static int WaitSemaTimeout(int sema, unsigned int msec)
205 {
206  int ret;
207  u64 timeoutUsec;
208  u64 *timeoutPtr;
209 
210  if (msec == 0) {
211  if (PollSema(sema) < 0) {
212  return -1;
213  }
214  return sema;
215  }
216 
217  timeoutPtr = NULL;
218 
219  if (msec > 0 && msec != UINT32_MAX) {
220  timeoutUsec = msec * 1000;
221  timeoutPtr = &timeoutUsec;
222  }
223 
224  ret = WaitSemaEx(sema, 1, timeoutPtr);
225 
226  if (ret < 0)
227  return -1; //Timed out.
228  return sema; //Wait condition satisfied.
229 }
230 
231 static int ReceiveMbx(arch_message **message, sys_mbox_t mBox, u32_t timeout)
232 {
233  int result;
234 
235  if(timeout > 0) {
236  result = WaitSemaTimeout(mBox->MessageCountSema, timeout);
237  } else {
238  result = WaitSema(mBox->MessageCountSema);
239  }
240 
241  if(result == mBox->MessageCountSema)
242  {
243  RetrieveMbxInternal(mBox, message);
244  result=0;
245  }
246  else result=-1;
247 
248  return result;
249 }
250 
251 static int PollMbx(arch_message **message, sys_mbox_t mBox)
252 {
253  int result;
254 
255  if(PollSema(mBox->MessageCountSema)==mBox->MessageCountSema)
256  {
257  RetrieveMbxInternal(mBox, message);
258  result=0;
259  }
260  else result=-1;
261 
262  return result;
263 }
264 
265 static u32_t sys_arch_mbox_fetch_internal(sys_mbox_t pMBox, void** ppvMSG, u32_t u32Timeout, char block)
266 {
267  arch_message *pmsg;
268  unsigned int TimeElasped;
269  int result;
270 
271  pmsg = NULL;
272  result = -1;
273  TimeElasped=0;
274  if(block){
275  int start;
276  start=clock();
277 
278  if((result=ReceiveMbx(&pmsg, pMBox, u32Timeout))==0){
279  TimeElasped = (clock() - start) / (1000);
280  }
281  else{
282  return SYS_ARCH_TIMEOUT;
283  }
284  }
285  else{
286  TimeElasped=((result=PollMbx(&pmsg, pMBox))!=0)?SYS_MBOX_EMPTY:0;
287  }
288 
289  if(result==0){
290  if (pmsg != NULL)
291  {
292  if(ppvMSG!=NULL) *ppvMSG = pmsg->sys_msg;
293  free_msg(pmsg);
294  }
295  }
296 
297  //Return the number of msec waited.
298  return TimeElasped;
299 }
300 
301 u32_t sys_arch_mbox_fetch(sys_mbox_t *pMBox, void** ppvMSG, u32_t u32Timeout)
302 {
303  return sys_arch_mbox_fetch_internal(*pMBox, ppvMSG, u32Timeout, 1);
304 }
305 
306 u32_t sys_arch_mbox_tryfetch(sys_mbox_t *pMBox, void** ppvMSG)
307 {
308  return sys_arch_mbox_fetch_internal(*pMBox, ppvMSG, 0, 0);
309 }
310 
311 static void SendMbx(sys_mbox_t *mbox, arch_message *msg, void *sys_msg){
312  DI();
313 
314  /* Store the message and update the message chain for this message box. */
315  msg->sys_msg = sys_msg;
316  msg->next = NULL;
317  if((*mbox)->FirstMessage==NULL) (*mbox)->FirstMessage=msg; //If this is the first message, it goes at the front.
318  if((*mbox)->LastMessage!=NULL) (*mbox)->LastMessage->next=msg; //Otherwise, it becomes the next message of the last message.
319  (*mbox)->LastMessage=msg; //The message becomes the new message at the end of the queue.
320 
321  EI();
322  SignalSema((*mbox)->MessageCountSema);
323 }
324 
325 err_t sys_mbox_trypost(sys_mbox_t *mbox, void *sys_msg)
326 {
327  arch_message *msg;
328  err_t result;
329 
330  /* Attempt to allocate one more message. */
331  if((msg=try_alloc_msg())!=NULL){
332  SendMbx(mbox, msg, sys_msg);
333 
334  result=ERR_OK;
335  }
336  else result=ERR_MEM;
337 
338  return result;
339 }
340 
341 /* lwIP 2.2.x distinguishes ISR-context posts from task-context posts.
342  The PS2 EE has preemptive scheduling, but our netif input does not run
343  in interrupt context (it goes through ps2ip / SIF callbacks on the EE
344  tcpip thread), so the two paths are equivalent here. */
345 err_t sys_mbox_trypost_fromisr(sys_mbox_t *mbox, void *msg)
346 {
347  return sys_mbox_trypost(mbox, msg);
348 }
349 
350 void sys_mbox_post(sys_mbox_t *mbox, void *sys_msg)
351 {
352  SendMbx(mbox, alloc_msg(), sys_msg);
353 }
354 
355 err_t sys_sem_new(sys_sem_t *sem, u8_t count)
356 {
357  //Create a new semaphore.
358  ee_sema_t sema;
359 
360  sema.init_count = count;
361  sema.max_count = 1;
362  sema.attr = 0;
363  sema.option = (u32)"PS2IP";
364 
365  if((*sem=CreateSema(&sema))<0)
366  {
367  printf("sys_sem_new: CreateSema failed, EC: %d\n", *sem);
368  return ERR_MEM;
369  }
370 
371  dbgprintf("sys_sem_new: CreateSema (CNT: %d) %d\n", count, *sem);
372 
373  return ERR_OK;
374 }
375 
376 u32_t sys_arch_sem_wait(sys_sem_t *sema, u32_t timeout)
377 {
378  u32_t result;
379 
380  //Wait timeout msec for the Sema to receive a signal.
381  if(timeout==0)
382  {
383  //Wait with no timeouts.
384  result=(WaitSema(*sema)==*sema?0:SYS_ARCH_TIMEOUT);
385  }
386  else if(timeout==1)
387  {
388  //Poll.
389  result=(PollSema(*sema)==*sema?0:SYS_ARCH_TIMEOUT);
390  }
391  else
392  {
393  //Use alarm to timeout.
394  unsigned int start;
395  u32_t WaitTime;
396 
397  start=clock();
398  if(WaitSemaTimeout(*sema, timeout) == *sema)
399  {
400  WaitTime=(clock()-start)/(1000);
401  result=(WaitTime <= timeout ? WaitTime : timeout);
402  }
403  else result=SYS_ARCH_TIMEOUT;
404  }
405 
406  return result;
407 }
408 
409 void sys_sem_signal(sys_sem_t *Sema)
410 {
411  SignalSema(*Sema);
412 }
413 
414 void sys_sem_free(sys_sem_t *Sema)
415 {
416  DeleteSema(*Sema);
417 }
418 
419 int sys_sem_valid(sys_sem_t *sem){
420  return(*sem>=0);
421 }
422 
423 void sys_sem_set_invalid(sys_sem_t *sem){
424  *sem=SYS_SEM_NULL;
425 }
426 
427 /* Semaphore-based critical section for lwIP. Replaces the previous
428  * DIntr/EIntr approach, which was unsafe: any code path inside a
429  * SYS_ARCH_PROTECT region that ended up calling newlib's malloc/free
430  * would try to WaitSema on the heap recursive mutex with interrupts
431  * disabled, deadlocking the EE. The sema-based variant lets nested
432  * waits work normally and removes the EE-specific incompatibility
433  * between lwIP and any other library that uses newlib's locks.
434  *
435  * Recursive ownership: lwIP allows SYS_ARCH_PROTECT to nest, so we
436  * track the owning thread + a recursion counter and only Wait/Signal
437  * on the outermost transitions.
438  */
439 static int s_protect_sem;
440 static int s_protect_count = 0;
441 static int s_protect_owner;
442 
443 sys_prot_t sys_arch_protect(void)
444 {
445  int tid = GetThreadId();
446  if (s_protect_count > 0 && s_protect_owner == tid)
447  {
448  s_protect_count++;
449  return 0; /* nested re-entry; outer call will release */
450  }
451  WaitSema(s_protect_sem);
452  s_protect_owner = tid;
453  s_protect_count = 1;
454  return 1; /* outermost level; matching unprotect will release */
455 }
456 
457 void sys_arch_unprotect(sys_prot_t level)
458 {
459  if (level == 0)
460  {
461  /* nested unprotect; just decrement */
462  if (s_protect_count > 0)
463  {
464  s_protect_count--;
465  }
466  return;
467  }
468  s_protect_count = 0;
469  s_protect_owner = 0;
470  SignalSema(s_protect_sem);
471 }
472 
473 void sys_init(void)
474 {
475  arch_message *prev;
476  unsigned int i;
477  ee_sema_t sema;
478 
479  dbgprintf("sys_init: Initializing...\n");
480 
481  sema.attr = 0;
482  sema.option = (u32)"PS2IP";
483  sema.init_count = sema.max_count = SYS_MAX_MESSAGES;
484  MsgCountSema=CreateSema(&sema);
485 
486  /* Critical-section sema: binary mutex (init=1, max=1). */
487  sema.attr = 0;
488  sema.option = (u32)"PS2IP_PROTECT";
489  sema.init_count = 1;
490  sema.max_count = 1;
491  s_protect_sem = CreateSema(&sema);
492  s_protect_count = 0;
493  s_protect_owner = 0;
494 
495  free_head = &msg_pool[0];
496  prev = &msg_pool[0];
497 
498  for(i = 1; i < SYS_MAX_MESSAGES; i++)
499  {
500  prev->next = &msg_pool[i];
501  prev = &msg_pool[i];
502  }
503 
504  //NULL-terminate free message list
505  prev->next = NULL;
506 }
507 
508 u32_t sys_now(void)
509 {
510  return(clock()/1000);
511 }
512 
513 void *ps2ip_calloc64(size_t n, size_t size)
514 {
515  void *ptr = NULL;
516  size_t sz = n * size;
517 
518  if ((ptr = memalign(64, sz)) == NULL)
519  return ptr;
520 
521  memset(ptr, 0, sz);
522  return ptr;
523 }
kernel.h
thread
Definition: thcommon.h:143
t_ee_sema
Definition: kernel.h:193
MboxData
Definition: sys_arch.h:18
count
u32 count
start sector of fragmented bd/file
Definition: usbhdfsd-common.h:3
_gp
void * _gp
time.h
stdio.h
mbox
Definition: thcommon.h:98
kernel_util.h
t_ee_thread
Definition: kernel.h:203
stdlib.h
st_arch_message
Definition: sys_arch.h:12