Ipc.c

From WiiBrew
Jump to navigation Jump to search
// Copyright 2007,2008 Segher Boessenkool <segher@kernel.crashing.org>
// Copyright 2007,2008 tmbinc 
// Copyright 2007,2008 bushing
// Licensed under the terms of the GNU GPL, version 2
// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt

#include <console.h>
#include <processor.h>
#include <string.h>
#include <cache.h>
#include <time.h>

#define PHYSADDR(x) ((unsigned long *)(0x7FFFFFFF & ((unsigned long)(x))))
#define VIRTADDR(x) ((unsigned long *)(0x80000000 | ((unsigned long)(x))))

unsigned long IPCReadReg(unsigned long reg) {
  unsigned long *IPCBase=(unsigned long *)0xCD000000;
  return IPCBase[reg];
}

void IPCWriteReg(unsigned long reg, unsigned long value) {
  unsigned long *IPCBase=(unsigned long *)0xCD000000;
  IPCBase[reg] = value;
}

void dump_buffer(unsigned long _ptr, int size) {
  u32 *ptr = VIRTADDR(_ptr);
  int i;
  printf("ptr=%p, size=%x ", ptr, size);
  for (i=0; i<size/4; i++) printf("%08x ", ptr[i]);
  printf("\n");
}

void IPCInterruptHandler() {
	if ((IPCReadReg(1) & 0x14) == 0x14) {
	 printf("IPCInterruptHandler: have reply.\n");
		
	 int reply_ptr = IPCReadReg(2);
	 if (!reply_ptr)
	 printf("no reply ptr?\n");
	 else
		{
		 unsigned int *reply = (void*)(reply_ptr | 0x80000000);
		 dcache_inv(reply, 0x40);
		 
		 IPCWriteReg(1, (IPCReadReg(1) & 0x30) | 4); /* reply early ack */
		 
		 int command = reply[2];
		 printf("reply_command: %d\n", command);
		 
		 switch (command) {
		 case 3: // read
		 {
		 int _ptr = reply[3];
		 int size = reply[1];
		 printf("read: "); dump_buffer(_ptr, size);
		 break;
		 }
		 case 6: // ioctl
		 {
		 printf("command ioctl: retval=%d, fd=%d, request=%x, in={%08x, %08x}, out={%08x, %08x}\n",
			 reply[1], reply[2], reply[3], reply[4], reply[5], reply[6], reply[7]);
		 printf("in: "); dump_buffer(reply[4], reply[5]);
		 printf("out: "); dump_buffer(reply[6], reply[7]);
		 break;
		 }
		 case 7: // ioctlv
		 {
		 int i;
		 unsigned int *vec = (void*)(reply[6] | 0x80000000);
		 int in = reply[4], out = reply[5];
		 dcache_inv(vec, (in+out)*8);
		 printf("command ioctlv: retval=%d, fd=%d, request=%x, in=%d out=%d ptr=%p\n",
				 reply[1], reply[2], reply[3], in, out, vec);
		 for (i=0; i<(in+out); i++) {
			if (i<in) printf("In: ");
			else printf("Out: ");
			dump_buffer(vec[i*2], vec[i*2+1]);
		 }
		 break;
		 } 
		 default:
		 printf("unknown reply command\n");
		 }
		 if (reply[8])
		 printf("callback: ptr=%08x, a=%08x b=%08x\n",
			 reply[8], reply[1], reply[9]); */
		 
		 IPCWriteReg(1, (IPCReadReg(1) & 0x30) | 8); /* end of reply */
		}
	}
	
	if ((IPCReadReg(1) & 0x22 ) == 0x22) {
	 printf("whatever-has-been-done\n");
	 IPCWriteReg(1, (IPCReadReg(1) & 0x30) | 2);
	 *(unsigned long*)0xCD000030 = 0x40000000;
	}
	
}

void ipc_bell(int w)
{
  IPCWriteReg(1, (IPCReadReg(1) & 0x30) | w);
}

int ipc_receive(void)
{
  if ((IPCReadReg(1) & 0x14) == 0x14)
    {
      int reply_ptr = IPCReadReg(2);
      if (!reply_ptr)
	printf("no reply ptr?\n");
      else
	{
	 unsigned int *reply = (void*)(reply_ptr | 0x80000000);
	 dcache_inv(reply, 0x40);
	 
	 ipc_bell(4); /* reply early ack */
	 printf("reply: ");
	 dump_buffer(reply, 0x30);
			
			printf("reply [cmd=%d, data=%08x/%d, a={%08x,%08x}, b={%08x,%08x}, cb=%p]\n",
			 reply[2], reply[3], reply[1], reply[4], reply[5], reply[6], reply[7], reply[8]);
			
			ipc_bell(8); /* end of reply */
	}
      return 1;
    }
  return 0;
}

void ipc_send(void *cmd)
{
  int i;
  
  printf("ipc_send(%p) sending packet:\n");
  dump_buffer(cmd, 0x30);
  
  dcache_flush(cmd, 0x30);
  IPCWriteReg(0, ((int)cmd) & 0x7FFFFFFF);
  ipc_bell(1);
  
  //	printf("waiting for ack...\n");
  while ((IPCReadReg(1) & 0x22 ) != 0x22);
  ipc_bell(2);
  *(unsigned long*)0xCD000030 = 0x40000000; /* ack IRQ */
}

void ipc_wait(void)
{
	while (1)
	{
		int res = ipc_receive();
		if (res)
			break;
	}
}

/*
	1 - open
	2 - close
	3 - read
	4 - write
	5 - seek
	6 - ioctl
	7 - ioctlv
*/

int ios_ioctl(int fd, int ioctl_no, u8 *inbuf, int inlen, u8 *outbuf, int outlen)
{

  static unsigned int cmd[0x40];
  int inptr = ((int)inbuf) & 0x7FFFFFFF;
  int outptr = ((int)outbuf) & 0x7FFFFFFF;
  memset(cmd, 0, sizeof(cmd));
  printf("ios_ioctl(%d, %d, %p, %d, %p, %d)\n", fd, ioctl_no, inbuf, inlen, outbuf, outlen);

  if(inbuf && inlen) dcache_flush(inptr, inlen);
  if(outbuf && outlen) dcache_flush(outptr, outlen);
  
  cmd[0] = 6;
  cmd[2] = fd;
  cmd[3] = ioctl_no;
  cmd[4] = inptr;
  cmd[5] = inlen;
  cmd[6] = outptr;
  cmd[7] = outlen;
  
  ipc_send(cmd);
  ipc_wait();
  printf("result=%d\n", cmd[1]);
  if(inbuf && inlen) dcache_flush(inptr, inlen);
  if(outbuf && outlen) dcache_flush(outptr, outlen);

  return cmd[1];
}

int ios_ioctlv(int fd, int ioctl_no, int in_count, int out_count, u32* vec)
{

  static unsigned int cmd[0x40];
  int i;
  memset(cmd, 0, sizeof(cmd));
  printf("ios_ioctlv(%d, %d, %d, %d, %p)\n", fd, ioctl_no, in_count, out_count, vec);
  dcache_flush(PHYSADDR(vec), (in_count + out_count)*8);

  printf("vec = ");
  dump_buffer(vec, (in_count + out_count)*8);

  for (i=0; i<in_count; i++) {
    int inptr = PHYSADDR(vec[i*2]);
    printf("in: ");
    dump_buffer(vec[i*2], vec[i*2+1]);
    if(inptr && vec[i*2+1]) dcache_flush(inptr, vec[i*2+1]);
  }

  for (i=in_count; i<in_count+out_count; i++) {
    int outptr = PHYSADDR(vec[i*2]);
    printf("out: ");
    dump_buffer(vec[i*2], vec[i*2+1]);
    if(outptr && vec[i*2+1]) dcache_flush(outptr, vec[i*2+1]);
  }
  
  cmd[0] = 7;
//cmd[1] is retval
  cmd[2] = fd;
  cmd[3] = ioctl_no;
  cmd[4] = in_count;
  cmd[5] = out_count;
  cmd[6] = PHYSADDR(vec);
  
  ipc_send(cmd);
  ipc_wait();
  printf("result=%d\n", cmd[1]);
  
  for (i=0; i<in_count; i++) {
    int inptr = vec[i*2] & 0x7FFFFFFF;
    printf("in: addr %x, len %d\n", inptr, vec[i*2+1]);
    if(inptr && vec[i*2+1]) dcache_flush(inptr, vec[i*2+1]);
  }

  for (i=in_count; i<in_count+out_count; i++) {
    int outptr = vec[i*2] & 0x7FFFFFFF;
    printf("out: addr %x, len %d\n", outptr, vec[i*2+1]);
    if(outptr && vec[i*2+1]) dcache_flush(outptr, vec[i*2+1]);
  }

  return cmd[1];
}

int isfs_readdir(const char *filename) {
  u32 *ipc_buf_1 = 0x933e0820, *ipc_buf_2 = 0x933e0840, *ipc_buf_3 = 0x933e0880;

  int fs_fd = ios_open("/dev/fs");
  int retval;
  printf("ios_open(/dev/fs) = %d\n", fs_fd);
  if (fs_fd<0) return fs_fd;
  
  ipc_buf_1[0]=PHYSADDR(ipc_buf_2);
  ipc_buf_1[1]=0x40;
  ipc_buf_1[2]=PHYSADDR(ipc_buf_3);
  ipc_buf_1[3]=4;

  strcpy(ipc_buf_2, "/");
  printf("ipc_buf_2 (%p) = %08x\n", ipc_buf_2, *ipc_buf_2);
  retval = ios_ioctlv(fs_fd, 4, 1, 1, ipc_buf_1);
  
  printf("retval = %d\n", retval);
  return retval;
}

int ios_open(const char *filename)
{
  unsigned char *filename_ptr = (u8 *)0x91330800;
  u32 *cmd = (u32 *)0x91330840;
  strcpy(filename_ptr, filename);
  dcache_flush(PHYSADDR(filename_ptr), 0x40);
  
  memset(cmd, 0, 0x40);
  cmd[0] = 1;
  cmd[4] = 1;
  cmd[3] = filename_ptr;
  dcache_flush(PHYSADDR(cmd), 0x40);
  printf("sending ios_open(%s)\n", filename);
  dump_buffer(cmd, 0x40);
  ipc_send(cmd);
  ipc_wait();
  printf("ios_open returned %d\n", cmd[1]);
  return cmd[1];
}

int ios_read(int fd, void *ptr, int len)
{
	int addr = ((int)ptr) & 0x7FFFFFFF;
	dcache_inv(ptr, len);
	
	static unsigned int cmd[0x40];
	memset(cmd, 0, sizeof(cmd));
	cmd[0] = 3;
	cmd[2] = fd;
	cmd[3] = addr;
	cmd[4] = len;
	
//	printf("ios_read(%d, %08x, %d)=", fd, addr, len);
	ipc_send(cmd);
	ipc_wait();
//	printf("%d\n", cmd[1]);
	return cmd[1];
}

	/* posix behaviour */
int ios_seek(int fd, int where, int whence)
{
	static unsigned int cmd[0x40];
	memset(cmd, 0, sizeof(cmd));
	cmd[0] = 5;
	cmd[2] = fd;
	cmd[3] = where;
	cmd[4] = whence;
	
//	printf("ios_seek(%d, %08x, %d)=", fd, where, whence);
	ipc_send(cmd);
	ipc_wait();
//	printf("%d\n", cmd[1]);
	return cmd[1];
}

int ios_close(int fd)
{
	static unsigned int cmd[0x40];
	memset(cmd, 0, sizeof(cmd));
	cmd[0] = 2;
	cmd[2] = fd;
	
	ipc_send(cmd);
	ipc_wait();
	return cmd[1];
}

void flush_IPC(void) {
	int i;

  static unsigned char *buffer = (void*)0x80100000;
  unsigned int *result = (unsigned int *)buffer;

	for (i=0; i<10; ++i) /* make sure to flush it all out */
	{
		mdelay(100);
		IPCInterruptHandler();
	}
	*(unsigned long*)0xCD000030 = 0x40000000; /* ACK irq */

		// close all files.. up to 0xE should be enough.
  printf("Closing file descriptors ");
  for (i=0; i<15; ++i) printf(".");
  printf("done.\n");
	 //		printf("ios_close(%d) = %d\n", i, ios_close(i));
	
	delay(1);
	*(unsigned long*)0xCD000030 = 0x40000000; /* ACK irq */
	//	printf("our work is done, bye.\n");

	return;
}