mirror of
git://projects.qi-hardware.com/iris.git
synced 2025-04-21 12:27:27 +03:00
working fat system on sd card
This commit is contained in:
327
source/bootinit.ccp
Normal file
327
source/bootinit.ccp
Normal file
@@ -0,0 +1,327 @@
|
||||
#pypp 0
|
||||
// Iris: micro-kernel for a capability-based operating system.
|
||||
// boot-programs/bootinit.ccp: Bootstrapping code.
|
||||
// Copyright 2009 Bas Wijnen <wijnen@debian.org>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "devices.hh"
|
||||
#include "iris.hh"
|
||||
#include <elf.h>
|
||||
|
||||
#define ELFRUN_NAME "elfrun.elf"
|
||||
#define INIT_NAME "init.elf"
|
||||
#define NUM_SLOTS 8
|
||||
#define NUM_CAPS 32
|
||||
|
||||
static unsigned _free
|
||||
extern unsigned _end
|
||||
|
||||
void init_alloc ():
|
||||
_free = ((unsigned)&_end + PAGE_SIZE - 1) & PAGE_MASK
|
||||
|
||||
char *alloc_space (unsigned pages):
|
||||
unsigned ret = (_free + PAGE_SIZE - 1) & PAGE_MASK
|
||||
_free = ret + (pages << PAGE_BITS)
|
||||
return (char *)ret
|
||||
|
||||
void *operator new[] (unsigned size):
|
||||
//kdebug ("new ")
|
||||
void *ret = (void *)_free
|
||||
size = (size + 3) & ~3
|
||||
unsigned rest = PAGE_SIZE - (((_free - 1) & ~PAGE_MASK) + 1)
|
||||
if rest < size:
|
||||
unsigned pages = ((size - rest) + PAGE_SIZE - 1) >> PAGE_BITS
|
||||
for unsigned p = 0; p < pages; ++p:
|
||||
Iris::Page page = Iris::my_memory.create_page ()
|
||||
page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME, Iris::Page::PAYING | Iris::Page::FRAME)
|
||||
Iris::my_memory.map (page, _free + rest + (p << PAGE_BITS))
|
||||
Iris::free_cap (page)
|
||||
_free += size
|
||||
//kdebug_num ((unsigned)ret)
|
||||
//kdebug ("+")
|
||||
//kdebug_num (size)
|
||||
//kdebug ("\n")
|
||||
return ret
|
||||
|
||||
void *operator new (unsigned size):
|
||||
return new char[size]
|
||||
|
||||
static unsigned *bss_mapping
|
||||
static Iris::Page bss_page
|
||||
|
||||
// Get the initial block device and filesystem.
|
||||
static Iris::Directory receive_devices ():
|
||||
Iris::String data
|
||||
Iris::Filesystem fs
|
||||
bool have_data = false, have_fs = false
|
||||
for unsigned i = 0; i < 2; ++i:
|
||||
Iris::wait ()
|
||||
if Iris::recv.data[0].l != Iris::Parent::PROVIDE_CAPABILITY:
|
||||
Iris::panic (Iris::recv.data[0].l, "Invalid bootstrap request.")
|
||||
switch Iris::recv.data[1].l:
|
||||
case Iris::String::ID:
|
||||
if have_data:
|
||||
Iris::panic (0, "duplicate device.")
|
||||
data = Iris::get_arg ()
|
||||
Iris::recv.reply.invoke ()
|
||||
have_data = true
|
||||
break
|
||||
case Iris::Filesystem::ID:
|
||||
if have_fs:
|
||||
Iris::panic (0, "duplicate filesystem.")
|
||||
fs = Iris::get_arg ()
|
||||
Iris::recv.reply.invoke ()
|
||||
have_fs = true
|
||||
break
|
||||
default:
|
||||
Iris::panic (Iris::recv.data[1].l, "unexpected device")
|
||||
// Initialize the root file system.
|
||||
Iris::Directory root = fs.use_device_ro (data.copy ())
|
||||
Iris::free_cap (data)
|
||||
Iris::free_cap (fs)
|
||||
return root
|
||||
|
||||
static bool stringcmp (char const *s1, char const *s2, unsigned size):
|
||||
for unsigned t = 0; t < size; ++t:
|
||||
if s1[t] != s2[t]:
|
||||
return false
|
||||
return true
|
||||
|
||||
static Iris::String find (Iris::Directory root, char const *name):
|
||||
unsigned size = 0
|
||||
while name[size]:
|
||||
++size
|
||||
Iris::Num num_files = root.get_size ()
|
||||
for Iris::Num i = 0; i.value () < num_files.value (); i = i.value () + 1:
|
||||
Iris::String n = root.get_name (i)
|
||||
char current_name[16]
|
||||
n.get_chars (0, current_name)
|
||||
Iris::free_cap (n)
|
||||
if !stringcmp (current_name, name, size):
|
||||
continue
|
||||
// Found elfrun.
|
||||
Iris::String ret = root.get_file_ro (i)
|
||||
return ret
|
||||
Iris::panic (0, "bootfile not found")
|
||||
|
||||
static void run (Iris::String data, Iris::Memory parent_memory, Iris::Cap parent):
|
||||
// Get the size.
|
||||
Iris::Num size = data.get_size ()
|
||||
if size.value () == 0:
|
||||
Iris::panic (0, "elfrun is empty")
|
||||
// Allocate a caps with all the pages.
|
||||
unsigned pages = (size.value () + PAGE_SIZE - 1) >> PAGE_BITS
|
||||
Iris::Caps pages_caps = Iris::my_memory.create_caps (pages)
|
||||
unsigned slot = pages_caps.use ()
|
||||
// Map them into the address space as well.
|
||||
char *mapping = alloc_space (pages)
|
||||
// Create a memory for the program.
|
||||
Iris::Memory mem = parent_memory.create_memory ()
|
||||
// Load the file into memory and map it.
|
||||
for unsigned p = 0; p < pages; ++p:
|
||||
//kdebug_num (p)
|
||||
//kdebug ("/")
|
||||
//kdebug_num (pages)
|
||||
//kdebug ("\n")
|
||||
Iris::set_recv_arg (Iris::Cap (slot, p))
|
||||
Iris::my_memory.create_page ()
|
||||
Iris::Page (slot, p).set_flags (Iris::Page::PAYING, Iris::Page::PAYING)
|
||||
data.get_block (p << PAGE_BITS, PAGE_SIZE, 0, Iris::Cap (slot, p))
|
||||
Iris::my_memory.map (Iris::Cap (slot, p), (unsigned)&mapping[p << PAGE_BITS])
|
||||
Iris::Thread thread = mem.create_thread (NUM_SLOTS)
|
||||
Elf32_Ehdr *header = (Elf32_Ehdr *)mapping
|
||||
for unsigned j = 0; j < SELFMAG; ++j:
|
||||
if header->e_ident[j] != ELFMAG[j]:
|
||||
Iris::panic (header->e_ident[j], "invalid ELF magic")
|
||||
return
|
||||
if header->e_ident[EI_CLASS] != ELFCLASS32:
|
||||
kdebug ("invalid ELF class:")
|
||||
kdebug_num (header->e_ident[EI_CLASS])
|
||||
kdebug (" != ")
|
||||
kdebug_num (ELFCLASS32)
|
||||
kdebug ("\n")
|
||||
Iris::panic (0)
|
||||
return
|
||||
if header->e_ident[EI_DATA] != ELFDATA2LSB:
|
||||
Iris::panic (header->e_ident[EI_DATA], "invalid ELF data")
|
||||
if header->e_ident[EI_VERSION] != EV_CURRENT:
|
||||
Iris::panic (header->e_ident[EI_VERSION], "invalid ELF version")
|
||||
if header->e_type != ET_EXEC:
|
||||
Iris::panic (header->e_type, "invalid ELF type")
|
||||
if header->e_machine != EM_MIPS_RS3_LE && header->e_machine != EM_MIPS:
|
||||
Iris::panic (header->e_machine, "invalid ELF machine")
|
||||
thread.set_pc (header->e_entry)
|
||||
thread.set_sp (0x80000000)
|
||||
for unsigned section = 0; section < header->e_shnum; ++section:
|
||||
Elf32_Shdr *shdr = (Elf32_Shdr *)((unsigned)mapping + header->e_shoff + section * header->e_shentsize)
|
||||
if ~shdr->sh_flags & SHF_ALLOC:
|
||||
continue
|
||||
bool readonly = !(shdr->sh_flags & SHF_WRITE)
|
||||
//bool executable = shdr->sh_flags & SHF_EXEC_INSTR
|
||||
if shdr->sh_type != SHT_NOBITS:
|
||||
unsigned file_offset = shdr->sh_offset >> PAGE_BITS
|
||||
if (file_offset + ((shdr->sh_size + PAGE_SIZE - 1) >> PAGE_BITS)) >= (PAGE_SIZE >> 2):
|
||||
Iris::panic (shdr->sh_size, "thread too large")
|
||||
return
|
||||
for unsigned p = (shdr->sh_addr & PAGE_MASK); p < shdr->sh_addr + shdr->sh_size; p += PAGE_SIZE:
|
||||
unsigned section_offset = (p - (shdr->sh_addr & PAGE_MASK)) >> PAGE_BITS
|
||||
unsigned idx = file_offset + section_offset
|
||||
Iris::Page page = mem.mapping ((void *)p)
|
||||
if Iris::recv.data[0].l == Iris::NO_ERROR:
|
||||
// The address already has a mapping; assume that it is correct.
|
||||
Iris::free_cap (page)
|
||||
continue
|
||||
Iris::free_cap (page)
|
||||
page = mem.create_page ()
|
||||
unsigned f
|
||||
if readonly:
|
||||
f = Iris::Page::PAYING | Iris::Page::MAPPED_READONLY
|
||||
else:
|
||||
f = Iris::Page::PAYING
|
||||
page.set_flags (f, f)
|
||||
Iris::Page (slot, idx).share (page, 0)
|
||||
//kdebug ("mapping at ")
|
||||
//kdebug_num (p)
|
||||
//if readonly:
|
||||
// kdebug (" (readonly)")
|
||||
//kdebug ("\n")
|
||||
if !mem.map (page, p):
|
||||
Iris::panic (0, "unable to map page")
|
||||
return
|
||||
Iris::free_cap (page)
|
||||
else:
|
||||
if readonly:
|
||||
Iris::panic (0, "unwritable bss section")
|
||||
return
|
||||
for unsigned p = (shdr->sh_addr & PAGE_MASK); p < shdr->sh_addr + shdr->sh_size; p += PAGE_SIZE:
|
||||
Iris::Page page = mem.mapping ((void *)p)
|
||||
if Iris::recv.data[0].l == Iris::NO_ERROR:
|
||||
// No error means there is a mapping.
|
||||
page.share (bss_page, 0)
|
||||
Iris::free_cap (page)
|
||||
for unsigned a = p; a < ((p + PAGE_SIZE) & PAGE_MASK); a += 4:
|
||||
if a >= shdr->sh_addr + shdr->sh_size:
|
||||
break
|
||||
if a < shdr->sh_addr:
|
||||
continue
|
||||
bss_mapping[(a & ~PAGE_MASK) >> 2] = 0
|
||||
else:
|
||||
Iris::free_cap (page)
|
||||
page = mem.create_page ()
|
||||
if Iris::recv.data[0].l != Iris::NO_ERROR:
|
||||
Iris::panic (Iris::recv.data[0].l, "out of memory")
|
||||
if !page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME, Iris::Page::PAYING | Iris::Page::FRAME):
|
||||
Iris::panic (0, "out of memory")
|
||||
if !mem.map (page, p):
|
||||
Iris::panic (0, "unable to map bss page")
|
||||
Iris::free_cap (page)
|
||||
for unsigned p = 0; p < pages; ++p:
|
||||
Iris::my_memory.destroy (Iris::Page (slot, p))
|
||||
Iris::my_memory.destroy (pages_caps)
|
||||
Iris::free_slot (slot)
|
||||
Iris::Page stackpage = mem.create_page ()
|
||||
stackpage.set_flags (Iris::Page::PAYING | Iris::Page::FRAME, Iris::Page::PAYING | Iris::Page::FRAME)
|
||||
if Iris::recv.data[0].l != Iris::NO_ERROR || !mem.map (stackpage, 0x7ffff000):
|
||||
Iris::panic (Iris::recv.data[0].l, "unable to map initial stack page")
|
||||
Iris::free_cap (stackpage)
|
||||
Iris::Caps caps = mem.create_caps (NUM_CAPS)
|
||||
thread.use (caps, 0)
|
||||
thread.set_info (Iris::Thread::A0, NUM_SLOTS)
|
||||
thread.set_info (Iris::Thread::A1, NUM_CAPS)
|
||||
Iris::Receiver receiver = mem.create_receiver ()
|
||||
receiver.set_owner (thread.copy ())
|
||||
Iris::Cap call = receiver.create_call_capability ()
|
||||
caps.set (__caps_num, caps.copy ())
|
||||
caps.set (__receiver_num, receiver.copy ())
|
||||
caps.set (__thread_num, thread.copy ())
|
||||
caps.set (__memory_num, mem.copy ())
|
||||
caps.set (__call_num, call.copy ())
|
||||
caps.set (__parent_num, parent)
|
||||
thread.run ()
|
||||
Iris::free_cap (receiver)
|
||||
Iris::free_cap (thread)
|
||||
Iris::free_cap (call)
|
||||
Iris::free_cap (caps)
|
||||
|
||||
Iris::Num start ():
|
||||
// Wait for the debugging device to be active, in case there is one.
|
||||
Iris::schedule ()
|
||||
kdebug ("Starting bootinit\n")
|
||||
init_alloc ()
|
||||
bss_mapping = (unsigned *)alloc_space (1)
|
||||
bss_page = Iris::my_memory.create_page ()
|
||||
Iris::my_memory.map (bss_page, (unsigned)bss_mapping)
|
||||
|
||||
Iris::Memory top_memory = Iris::get_top_memory ()
|
||||
Iris::Directory root = receive_devices ()
|
||||
root.lock_ro ()
|
||||
Iris::String run_string = find (root, ELFRUN_NAME)
|
||||
Iris::Cap parent_cap = Iris::my_receiver.create_capability (0)
|
||||
run (run_string, top_memory, parent_cap)
|
||||
Iris::wait ()
|
||||
if Iris::recv.data[0].l != Iris::Parent::PROVIDE_CAPABILITY || Iris::recv.data[1].l != Iris::Elfrun::ID:
|
||||
Iris::panic (0, "elfrun doesn't provide correct capability")
|
||||
Iris::Cap reply = Iris::get_reply ()
|
||||
Iris::Elfrun elfrun = Iris::get_arg ()
|
||||
Iris::my_caps.set (parent_cap.idx (), Iris::Cap (CAP_NONE))
|
||||
Iris::free_cap (parent_cap)
|
||||
reply.invoke ()
|
||||
Iris::free_cap (reply)
|
||||
|
||||
parent_cap = Iris::my_receiver.create_capability (0)
|
||||
Iris::String init_string = find (root, INIT_NAME)
|
||||
Iris::Caps init_caps = elfrun.run_string (top_memory.copy (), init_string.copy (), parent_cap.copy (), 8, 63)
|
||||
|
||||
Iris::Thread init = init_caps.get (__thread_num)
|
||||
init.make_priv ()
|
||||
init.run ()
|
||||
Iris::free_cap (init)
|
||||
Iris::free_cap (init_caps)
|
||||
|
||||
bool have_root = false
|
||||
bool have_elfrun = false
|
||||
while true:
|
||||
Iris::wait ()
|
||||
switch Iris::recv.data[0].l:
|
||||
case Iris::Parent::GET_CAPABILITY:
|
||||
switch Iris::recv.data[1].l:
|
||||
case Iris::Directory::ID:
|
||||
if have_root:
|
||||
Iris::panic (0, "Init requests root directory twice")
|
||||
Iris::recv.reply.invoke (0, 0, root.copy ())
|
||||
have_root = true
|
||||
break
|
||||
case Iris::Elfrun::ID:
|
||||
if have_elfrun:
|
||||
Iris::panic (0, "Init requests elfrun twice")
|
||||
Iris::recv.reply.invoke (0, 0, elfrun.copy ())
|
||||
have_elfrun = true
|
||||
break
|
||||
default:
|
||||
Iris::panic (0, "Invalid device requested by init")
|
||||
break
|
||||
case Iris::Parent::INIT_DONE:
|
||||
if Iris::recv.data[1].value () != 0:
|
||||
Iris::recv.reply.invoke ()
|
||||
break
|
||||
// Special response: kill boot threads.
|
||||
Iris::Cap reply = Iris::get_reply ()
|
||||
root.unlock_ro ()
|
||||
reply.invoke ()
|
||||
Iris::free_cap (reply)
|
||||
top_memory.destroy (Iris::my_memory)
|
||||
Iris::panic (0, "bootinit should be destroyed")
|
||||
default:
|
||||
Iris::panic (Iris::recv.data[0].l, "invalid operation from init")
|
||||
@@ -139,8 +139,9 @@ extern "C":
|
||||
Iris::Num ret = start ()
|
||||
Iris::my_parent.exit (ret)
|
||||
Iris::my_memory.destroy (Iris::my_thread)
|
||||
// The program no longer exists. If it somehow does, generate an address fault.
|
||||
// The program no longer exists. If it somehow does, die again.
|
||||
while true:
|
||||
Iris::panic (0, "this program should no longer exist.")
|
||||
*(volatile unsigned *)~0
|
||||
|
||||
__asm__ volatile ("\t.text\n"
|
||||
|
||||
@@ -83,7 +83,7 @@ static Iris::Caps map_string (Iris::String data):
|
||||
//kdebug_num (pages)
|
||||
//kdebug ("\n")
|
||||
Iris::set_recv_arg (Iris::Cap (slot, p))
|
||||
data.get_page (p << PAGE_BITS)
|
||||
data.get_block (p << PAGE_BITS)
|
||||
Iris::my_memory.map (Iris::Cap (slot, p), (unsigned)&mapping[p << PAGE_BITS])
|
||||
|
||||
static Iris::Caps map_caps (Iris::Caps data, unsigned p):
|
||||
|
||||
550
source/fat.ccp
Normal file
550
source/fat.ccp
Normal file
@@ -0,0 +1,550 @@
|
||||
#pypp 0
|
||||
#include <iris.hh>
|
||||
#include <devices.hh>
|
||||
|
||||
#define SECTOR_BITS 9
|
||||
#define BLOCK_MASK (~((1 << SECTOR_BITS) - 1))
|
||||
#define ROOT_CLUSTER 0x7fffffff
|
||||
|
||||
static unsigned _free
|
||||
extern unsigned _end
|
||||
|
||||
void init_alloc ():
|
||||
_free = ((unsigned)&_end + PAGE_SIZE - 1) & PAGE_MASK
|
||||
|
||||
char *alloc_space (unsigned pages):
|
||||
unsigned ret = (_free + PAGE_SIZE - 1) & PAGE_MASK
|
||||
_free = ret + (pages << PAGE_BITS)
|
||||
return (char *)ret
|
||||
|
||||
void *operator new[] (unsigned size):
|
||||
//kdebug ("new ")
|
||||
void *ret = (void *)_free
|
||||
size = (size + 3) & ~3
|
||||
unsigned rest = PAGE_SIZE - (((_free - 1) & ~PAGE_MASK) + 1)
|
||||
if rest < size:
|
||||
unsigned pages = ((size - rest) + PAGE_SIZE - 1) >> PAGE_BITS
|
||||
for unsigned p = 0; p < pages; ++p:
|
||||
Iris::Page page = Iris::my_memory.create_page ()
|
||||
page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME, Iris::Page::PAYING | Iris::Page::FRAME)
|
||||
Iris::my_memory.map (page, _free + rest + (p << PAGE_BITS))
|
||||
Iris::free_cap (page)
|
||||
_free += size
|
||||
//kdebug_num ((unsigned)ret)
|
||||
//kdebug ("+")
|
||||
//kdebug_num (size)
|
||||
//kdebug ("\n")
|
||||
return ret
|
||||
|
||||
void *operator new (unsigned size):
|
||||
return new char[size]
|
||||
|
||||
static Iris::WString dev
|
||||
static Iris::Num device_size
|
||||
static Iris::Page page
|
||||
static char *data
|
||||
static Iris::Num current_block
|
||||
static void read_block (Iris::Num idx, Iris::Page p = page, unsigned size = 1 << SECTOR_BITS, unsigned offset = 0):
|
||||
if p.code == page.code:
|
||||
if idx.value () == current_block.value () && offset == 0 && size == 1 << SECTOR_BITS:
|
||||
return
|
||||
current_block = idx
|
||||
//kdebug ("fat getting block: ")
|
||||
//kdebug_num (idx.h)
|
||||
//kdebug (":")
|
||||
//kdebug_num (idx.l)
|
||||
//kdebug ("+")
|
||||
//kdebug_num (size)
|
||||
//kdebug ("@")
|
||||
//kdebug_num (offset)
|
||||
//kdebug ("\n")
|
||||
dev.get_block (idx, size, offset, p)
|
||||
|
||||
struct Fat:
|
||||
char oem[8]
|
||||
unsigned sector_size_bits
|
||||
unsigned sectors_per_cluster_bits
|
||||
unsigned reserved_sectors
|
||||
unsigned num_fats
|
||||
unsigned root_entries
|
||||
unsigned sectors
|
||||
unsigned media
|
||||
unsigned sectors_per_fat
|
||||
unsigned sectors_per_track
|
||||
unsigned heads
|
||||
unsigned hidden_sectors
|
||||
unsigned active_fat
|
||||
bool write_all_fats
|
||||
unsigned fs_version
|
||||
unsigned root_cluster
|
||||
unsigned fsinfo_sector
|
||||
unsigned boot_backup_sector
|
||||
unsigned drive
|
||||
unsigned current_head
|
||||
unsigned volume_id
|
||||
char label[0xb]
|
||||
|
||||
unsigned bits
|
||||
unsigned clusters
|
||||
unsigned cluster_size_bits
|
||||
unsigned root_sectors
|
||||
unsigned header_sectors
|
||||
unsigned bad_clusters
|
||||
|
||||
unsigned free_clusters
|
||||
unsigned last_alloced
|
||||
|
||||
unsigned *fat
|
||||
unsigned first_free_cluster, first_bad_cluster
|
||||
|
||||
void print_num (char const *pre, unsigned data):
|
||||
kdebug ("\t")
|
||||
kdebug (pre)
|
||||
unsigned bytes = 1
|
||||
while bytes < 8 && data >> (bytes * 4):
|
||||
++bytes
|
||||
kdebug_num (data, bytes)
|
||||
kdebug ("\n")
|
||||
|
||||
void print_br ():
|
||||
kdebug ("\tOEM: '")
|
||||
for unsigned i = 0; i < 8; ++i:
|
||||
kdebug_char (oem[i])
|
||||
kdebug ("'\n")
|
||||
print_num ("bytes per sector: ", 1 << sector_size_bits)
|
||||
print_num ("sectors per cluster: ", 1 << sectors_per_cluster_bits)
|
||||
print_num ("reserved sectors: ", reserved_sectors)
|
||||
print_num ("number of fats: ", num_fats)
|
||||
print_num ("entries in root directory: ", root_entries)
|
||||
print_num ("sectors: ", sectors)
|
||||
print_num ("media descriptor: ", media)
|
||||
print_num ("sectors per fat: ", sectors_per_fat)
|
||||
print_num ("sectors per track: ", sectors_per_track)
|
||||
print_num ("heads: ", heads)
|
||||
print_num ("hidden sectors: ", hidden_sectors)
|
||||
print_num ("active_fat: ", active_fat)
|
||||
kdebug ("\twrite all: ")
|
||||
kdebug (write_all_fats ? "yes\n" : "no\n")
|
||||
print_num ("fs version: ", fs_version)
|
||||
print_num ("root cluster: ", root_cluster)
|
||||
print_num ("fsinfo sector: ", fsinfo_sector)
|
||||
print_num ("boot sector backup sector: ", boot_backup_sector)
|
||||
print_num ("drive: ", drive)
|
||||
print_num ("current head: ", current_head)
|
||||
print_num ("volume id: ", volume_id)
|
||||
kdebug ("\tlabel: '")
|
||||
for unsigned i = 0; i < 0xb; ++i:
|
||||
kdebug_char (label[i])
|
||||
kdebug ("'\n")
|
||||
print_num ("bits: ", bits)
|
||||
print_num ("clusters: ", clusters)
|
||||
print_num ("header sectors: ", header_sectors)
|
||||
|
||||
unsigned read_num (char *data, unsigned bytes):
|
||||
unsigned ret = 0
|
||||
for unsigned i = 0; i < bytes; ++i:
|
||||
ret |= (data[i] & 0xff) << (i * 8)
|
||||
return ret
|
||||
|
||||
void map_fat_cluster (unsigned c, unsigned offset = 0):
|
||||
read_block ((hidden_sectors + reserved_sectors + ((c * bits + 8 * offset) >> (sector_size_bits + 3))) << sector_size_bits)
|
||||
|
||||
unsigned make_bits (unsigned orig):
|
||||
unsigned ret
|
||||
for ret = 0; ret < 32; ++ret:
|
||||
if orig == 1 << ret:
|
||||
return ret
|
||||
Iris::panic (ret, "non-power of 2")
|
||||
return ret
|
||||
|
||||
void reset ():
|
||||
read_block (0)
|
||||
if data[0x1fe] != 0x55 || (data[0x1ff] & 0xff) != 0xaa:
|
||||
kdebug ("invalid boot record signature in fat device\n")
|
||||
for unsigned i = 0; i < 8; ++i:
|
||||
oem[i] = data[3 + i]
|
||||
sector_size_bits = make_bits (read_num (data + 0xb, 2))
|
||||
sectors_per_cluster_bits = make_bits (read_num (data + 0xd, 1))
|
||||
cluster_size_bits = sector_size_bits + sectors_per_cluster_bits
|
||||
reserved_sectors = read_num (data + 0xe, 2)
|
||||
num_fats = read_num (data + 0x10, 1)
|
||||
root_entries = read_num (data + 0x11, 2)
|
||||
sectors = read_num (data + 0x13, 2)
|
||||
media = read_num (data + 0x15, 1)
|
||||
sectors_per_fat = read_num (data + 0x16, 2)
|
||||
sectors_per_track = read_num (data + 0x18, 2)
|
||||
heads = read_num (data + 0x1a, 2)
|
||||
hidden_sectors = read_num (data + 0x1c, 4)
|
||||
if !sectors:
|
||||
sectors = read_num (data + 0x20, 4)
|
||||
if Iris::Num (sectors).value () << sector_size_bits > device_size.value ():
|
||||
sectors = device_size.value () >> sector_size_bits
|
||||
kdebug ("warning: limiting sectors because of limited device size\n")
|
||||
|
||||
root_sectors = (root_entries * 32 + (1 << sector_size_bits) - 1) >> sector_size_bits
|
||||
header_sectors = hidden_sectors + reserved_sectors + sectors_per_fat * num_fats + root_sectors
|
||||
clusters = (sectors - header_sectors) >> sectors_per_cluster_bits
|
||||
unsigned skip
|
||||
if clusters >= 65525:
|
||||
bits = 32
|
||||
sectors_per_fat = read_num (data + 0x24, 4)
|
||||
active_fat = read_num (data + 0x28, 2)
|
||||
write_all_fats = active_fat & 0x80
|
||||
active_fat &= 0xf
|
||||
fs_version = read_num (data + 0x2a, 2)
|
||||
root_cluster = read_num (data + 0x2c, 4)
|
||||
fsinfo_sector = read_num (data + 0x30, 2)
|
||||
boot_backup_sector = read_num (data + 0x32, 2)
|
||||
skip = 0x40 - 0x24
|
||||
else:
|
||||
if clusters < 4085:
|
||||
bits = 12
|
||||
else:
|
||||
bits = 16
|
||||
skip = 0
|
||||
active_fat = 0
|
||||
write_all_fats = true
|
||||
fs_version = 0
|
||||
root_cluster = 0
|
||||
fsinfo_sector = 0
|
||||
boot_backup_sector = 0
|
||||
unsigned fat_entries_per_sector = (8 << sector_size_bits) / bits
|
||||
unsigned fat_entries = sectors_per_fat * fat_entries_per_sector
|
||||
if clusters + 2 > fat_entries:
|
||||
clusters = fat_entries - 2
|
||||
kdebug ("warning: limiting clusters because of limited sector count\n")
|
||||
drive = read_num (data + skip + 0x24, 1)
|
||||
current_head = read_num (data + skip + 0x25, 1)
|
||||
if data[skip + 0x26] == 0x29:
|
||||
volume_id = read_num (data + skip + 0x27, 4)
|
||||
for unsigned i = 0; i < 0xb; ++i:
|
||||
label[i] = data[skip + 0x2b + i]
|
||||
char *id = data + skip + 0x36
|
||||
if id[0] != 'F' || id[1] != 'A' || id[2] != 'T' || id[5] != ' ' || id[6] != ' ' || id[7] != ' ':
|
||||
kdebug ("warning: file system type field was not 'FATxx '\n")
|
||||
else:
|
||||
switch bits:
|
||||
case 12:
|
||||
if id[3] != '1' || id[4] != '2':
|
||||
kdebug ("warning: id for fat12 is not FAT12\n")
|
||||
break
|
||||
case 16:
|
||||
if id[3] != '1' || id[4] != '6':
|
||||
kdebug ("warning: id for fat16 is not FAT16\n")
|
||||
break
|
||||
case 32:
|
||||
if id[3] != '3' || id[4] != '2':
|
||||
kdebug ("warning: id for fat32 wat not FAT32")
|
||||
break
|
||||
else:
|
||||
volume_id = 0
|
||||
for unsigned i = 0; i < 0xb; ++i:
|
||||
label[i] = 0
|
||||
if fsinfo_sector:
|
||||
read_block (fsinfo_sector << sector_size_bits)
|
||||
if (data[0] & 0xff) != 0x52 || (data[1] & 0xff) != 0x52 || (data[2] & 0xff) != 0x6a || (data[3] & 0xff) != 0x41 || (data[0x1e4] & 0xff) != 0x72 || (data[0x1e5] & 0xff) != 0x72 || (data[0x1e6] & 0xff) != 0x4a || (data[0x1e7] & 0xff) != 0x61 || (data[0x1fe] & 0xff) != 0x55 || (data[0x1ff] & 0xff) != 0xaa:
|
||||
kdebug ("invalid signature in fsinfo structure\n")
|
||||
free_clusters = read_num (data + 0x1e8, 4)
|
||||
last_alloced = read_num (data + 0x1ec, 4)
|
||||
else:
|
||||
free_clusters = ~0
|
||||
last_alloced = ~0
|
||||
|
||||
// Now read the FAT.
|
||||
bad_clusters = 0
|
||||
fat = new unsigned[clusters]
|
||||
unsigned counted_free_clusters = 0
|
||||
for unsigned c = 0; c < clusters; ++c:
|
||||
// reduced cluster.
|
||||
unsigned rc = c & (1 << sector_size_bits) - 1
|
||||
// The next line does nothing most of the time.
|
||||
map_fat_cluster (c)
|
||||
switch bits:
|
||||
case 12:
|
||||
fat[c] = data[(rc + 2) * 2] & 0xff
|
||||
// There may be a sector boundary in the middle of the entry, so optionally reread.
|
||||
map_fat_cluster (c, 1)
|
||||
fat[c] |= (data[(rc + 2) * 2 + 1] & 0xff) << 8
|
||||
if c & 1:
|
||||
fat[c] >>= 4
|
||||
else:
|
||||
fat[c] &= 0xfff
|
||||
break
|
||||
case 16:
|
||||
fat[c] = read_num (data + (rc + 2) * 2, 2)
|
||||
break
|
||||
case 32:
|
||||
fat[c] = read_num (data + (rc + 2) * 4, 4)
|
||||
break
|
||||
// Correct for the crazy +2 offset, and keep a list of bad and free clusters.
|
||||
if fat[c] == 0:
|
||||
// Free cluster.
|
||||
fat[c] = first_free_cluster
|
||||
first_free_cluster = c
|
||||
++counted_free_clusters
|
||||
else if fat[c] == 1:
|
||||
// Invalid value.
|
||||
Iris::panic (0, "entry is '1' in fat.")
|
||||
else if bits == 12 && fat[c] == 0xfff || bits == 16 && fat[c] == 0xffff || bits == 32 && fat[c] == 0xfffffff:
|
||||
// Last cluster in chain.
|
||||
fat[c] = ~0
|
||||
else if bits == 12 && fat[c] == 0xff7 || bits == 16 && fat[c] == 0xfff7 || bits == 32 && fat[c] == 0xffffff7:
|
||||
// Bad cluster.
|
||||
fat[c] = first_bad_cluster
|
||||
first_bad_cluster = c
|
||||
++bad_clusters
|
||||
else:
|
||||
// Non-last cluster in chain.
|
||||
fat[c] -= 2
|
||||
unsigned fat_lookup (unsigned first_cluster, unsigned cluster):
|
||||
while cluster--:
|
||||
first_cluster = fat[first_cluster]
|
||||
if first_cluster == ~0:
|
||||
kdebug ("sector beyond end of file requested\n")
|
||||
return ~0
|
||||
return first_cluster
|
||||
struct File:
|
||||
Fat *fat
|
||||
unsigned size
|
||||
unsigned first_cluster
|
||||
char name[11]
|
||||
bool archive, readonly, system, hidden, directory, volume
|
||||
unsigned create_second, create_minute_hour, create_date, access_date, time, date
|
||||
void load_cluster (unsigned idx, Iris::Page p = page, unsigned offset = 0):
|
||||
unsigned cluster = fat->fat_lookup (first_cluster, idx >> fat->cluster_size_bits)
|
||||
kdebug ("loading cluster ")
|
||||
kdebug_num (idx)
|
||||
kdebug ("@")
|
||||
kdebug_num (cluster)
|
||||
kdebug (" from file\n")
|
||||
if cluster == ~0:
|
||||
kdebug ("invalid cluster requested from file\n")
|
||||
return
|
||||
read_block ((fat->header_sectors + (Iris::Num (cluster).value () << fat->sectors_per_cluster_bits)) << fat->sector_size_bits, p, 1 << fat->cluster_size_bits, offset)
|
||||
kdebug ("sector ")
|
||||
kdebug_num (fat->header_sectors + (Iris::Num (cluster).value () << fat->sectors_per_cluster_bits))
|
||||
kdebug ("\n")
|
||||
void get_dir_entry (unsigned dir, unsigned idx, File *f):
|
||||
f->fat = this
|
||||
unsigned sector = idx >> (sector_size_bits - 5)
|
||||
unsigned num = (idx << 5) & ~BLOCK_MASK
|
||||
Iris::Num hwsector
|
||||
if dir == ROOT_CLUSTER:
|
||||
if sector < root_sectors:
|
||||
hwsector = header_sectors - root_sectors + sector
|
||||
else:
|
||||
hwsector = ~0
|
||||
else:
|
||||
unsigned entry = fat_lookup (dir, sector)
|
||||
if entry == ~0:
|
||||
hwsector = ~0
|
||||
else:
|
||||
hwsector = header_sectors + (Iris::Num (entry).value () << sectors_per_cluster_bits)
|
||||
if hwsector.value () == ~0:
|
||||
kdebug ("invalid sector requested from directory\n")
|
||||
f->first_cluster = ~0
|
||||
return
|
||||
read_block (hwsector.value () << sector_size_bits)
|
||||
char *e = &data[num]
|
||||
for unsigned i = 0; i < 11; ++i:
|
||||
f->name[i] = e[i]
|
||||
f->archive = e[0xb] & 0x20
|
||||
f->readonly = e[0xb] & 0x10
|
||||
f->system = e[0xb] & 0x8
|
||||
f->hidden = e[0xb] & 0x4
|
||||
f->directory = e[0xb] & 0x2
|
||||
f->volume = e[0xb] & 0x1
|
||||
f->create_second = read_num (e + 0xd, 1)
|
||||
f->create_minute_hour = read_num (e + 0xe, 2)
|
||||
f->create_date = read_num (e + 0x10, 2)
|
||||
f->access_date = read_num (e + 0x12, 2)
|
||||
f->time = read_num (e + 0x16, 1)
|
||||
f->date = read_num (e + 0x18, 1)
|
||||
f->size = read_num (e + 0x1c, 4)
|
||||
f->first_cluster = (read_num (e + 0x14, 2) << 16 | read_num (e + 0x1a, 2)) - 2
|
||||
|
||||
// Capability encoding.
|
||||
// 0:ROOT_CLUSTER = non fat-32 root directory.
|
||||
// 0:cluster = other directory.
|
||||
// cluster:index = file index from directory at cluster.
|
||||
// cluster|0x80000000:index = filename for file with index from directory at cluster.
|
||||
|
||||
Iris::Num start ():
|
||||
init_alloc ()
|
||||
current_block = ~0
|
||||
dev = Iris::my_parent.get_capability <Iris::WString> ()
|
||||
if dev.get_align_bits () > SECTOR_BITS:
|
||||
kdebug ("fat device doesn't support 512 byte access")
|
||||
return 1
|
||||
device_size = dev.get_size ()
|
||||
data = (char *)0x15000; //alloc_space (1)
|
||||
page = Iris::my_memory.create_page ()
|
||||
page.set_flags (Iris::Page::PAYING, Iris::Page::PAYING)
|
||||
Iris::my_memory.map (page, (unsigned)data)
|
||||
|
||||
Fat fat
|
||||
|
||||
fat.reset ()
|
||||
fat.print_br ()
|
||||
|
||||
Iris::Cap root
|
||||
if fat.root_cluster:
|
||||
root = Iris::my_receiver.create_capability (Iris::Num (fat.root_cluster, 0))
|
||||
else:
|
||||
root = Iris::my_receiver.create_capability (Iris::Num (ROOT_CLUSTER, 0))
|
||||
|
||||
Iris::my_parent.provide_capability <Iris::Directory> (root.copy ())
|
||||
Iris::free_cap (root)
|
||||
|
||||
while true:
|
||||
Iris::wait ()
|
||||
unsigned dir = Iris::recv.protected_data.h
|
||||
if dir & 0x80000000:
|
||||
// File name.
|
||||
unsigned idx = Iris::recv.protected_data.l
|
||||
Iris::Cap reply = Iris::get_reply ()
|
||||
unsigned num = Iris::recv.data[1].l
|
||||
unsigned size = Iris::recv.data[0].h >> 16
|
||||
unsigned cmd = Iris::recv.data[0].l
|
||||
Fat::File f
|
||||
fat.get_dir_entry (dir & ~0x80000000, idx, &f)
|
||||
switch cmd:
|
||||
case Iris::String::GET_SIZE:
|
||||
kdebug ("filename size requested\n")
|
||||
reply.invoke (11)
|
||||
break
|
||||
case Iris::String::GET_CHARS:
|
||||
//kdebug ("filename chars requested\n")
|
||||
/**/union { unsigned u[4]; char c[16]; } u
|
||||
for unsigned k = 0; k < 4; ++k:
|
||||
u.u[k] = 0
|
||||
for unsigned k = 0; k + num < 11; ++k:
|
||||
u.c[k] = f.name[k + num]
|
||||
reply.invoke (Iris::Num (u.u[0], u.u[1]), Iris::Num (u.u[2], u.u[3]))
|
||||
break
|
||||
case Iris::String::GET_ALIGN_BITS:
|
||||
kdebug ("filename align requested\n")
|
||||
reply.invoke (0)
|
||||
break
|
||||
case Iris::String::GET_BLOCK:
|
||||
default:
|
||||
Iris::panic (Iris::recv.data[0].l, "invalid request for fat filename")
|
||||
Iris::free_cap (reply)
|
||||
else if dir:
|
||||
// File.
|
||||
unsigned idx = Iris::recv.protected_data.l
|
||||
Iris::Cap reply = Iris::get_reply ()
|
||||
Iris::Cap arg = Iris::get_arg ()
|
||||
Iris::Num num = Iris::recv.data[1]
|
||||
unsigned size = Iris::recv.data[0].h >> 16
|
||||
unsigned offset = Iris::recv.data[0].h & 0xffff
|
||||
unsigned cmd = Iris::recv.data[0].l
|
||||
Fat::File f
|
||||
fat.get_dir_entry (dir, idx, &f)
|
||||
switch cmd:
|
||||
case Iris::String::GET_SIZE:
|
||||
kdebug ("file size requested\n")
|
||||
reply.invoke (f.size)
|
||||
break
|
||||
case Iris::String::GET_CHARS:
|
||||
kdebug ("file chars requested\n")
|
||||
unsigned mask = 1 << (fat.cluster_size_bits) - 1
|
||||
f.load_cluster (num.l & ~mask)
|
||||
unsigned n = num.l & mask & ~0xf
|
||||
unsigned *dat = (unsigned *)(data + n)
|
||||
reply.invoke (Iris::Num (dat[0], dat[1]), Iris::Num (dat[2], dat[3]))
|
||||
break
|
||||
case Iris::String::GET_ALIGN_BITS:
|
||||
kdebug ("file align requested\n")
|
||||
reply.invoke (fat.cluster_size_bits)
|
||||
break
|
||||
case Iris::String::GET_BLOCK:
|
||||
kdebug ("file block requested\n")
|
||||
unsigned mask = 1 << (fat.cluster_size_bits) - 1
|
||||
if offset > PAGE_SIZE:
|
||||
kdebug ("invalid offset requested\n")
|
||||
break
|
||||
if size + offset > PAGE_SIZE:
|
||||
size = PAGE_SIZE - offset
|
||||
for unsigned i = 0; i < size; i += 1 << fat.cluster_size_bits:
|
||||
f.load_cluster ((num.l & ~mask) + i, arg, i + offset)
|
||||
reply.invoke ()
|
||||
break
|
||||
case Iris::WString::TRUNCATE:
|
||||
case Iris::WString::SET_CHARS:
|
||||
case Iris::WString::SET_BLOCK:
|
||||
Iris::panic (Iris::recv.data[0].l, "writing to files not supported yet")
|
||||
default:
|
||||
Iris::panic (Iris::recv.data[0].l, "invalid request for fat file")
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (arg)
|
||||
else:
|
||||
// Directory.
|
||||
if Iris::recv.protected_data.l != ROOT_CLUSTER:
|
||||
// Normal directory.
|
||||
switch Iris::recv.data[0].l:
|
||||
case Iris::Directory::GET_SIZE:
|
||||
kdebug ("dir size requested\n")
|
||||
Iris::recv.reply.invoke ()
|
||||
break
|
||||
case Iris::Directory::GET_NAME:
|
||||
kdebug ("dir name requested\n")
|
||||
Iris::recv.reply.invoke ()
|
||||
break
|
||||
case Iris::Directory::GET_FILE_RO:
|
||||
kdebug ("dir file requested\n")
|
||||
Iris::Cap reply = Iris::get_reply ()
|
||||
Iris::Cap ret = Iris::my_receiver.create_capability (Iris::Num (Iris::recv.data[1].l, Iris::recv.protected_data.l))
|
||||
reply.invoke (0, 0, ret.copy ())
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (ret)
|
||||
break
|
||||
case Iris::Directory::GET_FILE_INFO:
|
||||
kdebug ("dir file info requested\n")
|
||||
Iris::recv.reply.invoke ()
|
||||
break
|
||||
case Iris::Directory::LOCK_RO:
|
||||
case Iris::Directory::UNLOCK_RO:
|
||||
kdebug ("dir lock or unlock requested\n")
|
||||
Iris::recv.reply.invoke ()
|
||||
break
|
||||
default:
|
||||
kdebug ("invalid dir operation requested\n")
|
||||
Iris::recv.reply.invoke ()
|
||||
break
|
||||
else:
|
||||
// Non-fat32 root directory.
|
||||
switch Iris::recv.data[0].l:
|
||||
case Iris::Directory::GET_SIZE:
|
||||
kdebug ("root size requested\n")
|
||||
Iris::recv.reply.invoke (fat.root_entries)
|
||||
break
|
||||
case Iris::Directory::GET_NAME:
|
||||
//kdebug ("root name requested\n")
|
||||
Iris::Cap reply = Iris::get_reply ()
|
||||
Iris::Cap ret = Iris::my_receiver.create_capability (Iris::Num (Iris::recv.data[1].l, Iris::recv.protected_data.l | 0x80000000))
|
||||
reply.invoke (0, 0, ret.copy ())
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (ret)
|
||||
break
|
||||
case Iris::Directory::GET_FILE_RO:
|
||||
kdebug ("root file requested\n")
|
||||
Iris::Cap reply = Iris::get_reply ()
|
||||
Iris::Cap ret = Iris::my_receiver.create_capability (Iris::Num (Iris::recv.data[1].l, Iris::recv.protected_data.l))
|
||||
reply.invoke (0, 0, ret.copy ())
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (ret)
|
||||
break
|
||||
case Iris::Directory::GET_FILE_INFO:
|
||||
kdebug ("root file info requested\n")
|
||||
Iris::recv.reply.invoke ()
|
||||
break
|
||||
case Iris::Directory::LOCK_RO:
|
||||
case Iris::Directory::UNLOCK_RO:
|
||||
kdebug ("root lock or unlock requested\n")
|
||||
Iris::recv.reply.invoke ()
|
||||
break
|
||||
default:
|
||||
kdebug ("invalid root operation requested\n")
|
||||
Iris::recv.reply.invoke ()
|
||||
break
|
||||
@@ -182,7 +182,7 @@ static Iris::Caps load (char const *name, unsigned name_len, unsigned &size, Iri
|
||||
unsigned slot = target.use ()
|
||||
for unsigned p = 0; p < pages; ++p:
|
||||
Iris::set_recv_arg (Iris::Cap (slot, p))
|
||||
file.get_page (p << PAGE_BITS)
|
||||
file.get_block (p << PAGE_BITS)
|
||||
Iris::free_slot (slot)
|
||||
Iris::free_cap (file)
|
||||
root.unlock_ro ()
|
||||
@@ -426,9 +426,9 @@ static void parse_line (char *&line, unsigned maxlen):
|
||||
Iris::panic (0, "capability given out twice")
|
||||
(*d)->dev->client = &**p
|
||||
++(*p)->num_waiting
|
||||
kdebug ("registered give device: ")
|
||||
kdebug_num ((*d)->type)
|
||||
kdebug ("\n")
|
||||
//kdebug ("registered give device: ")
|
||||
//kdebug_num ((*d)->type)
|
||||
//kdebug ("\n")
|
||||
else if match (start, maxlen, "include"):
|
||||
unsigned name_len
|
||||
char *name = get_filename (line, maxlen, name_len)
|
||||
@@ -492,11 +492,11 @@ Iris::Num start ():
|
||||
if !d:
|
||||
Iris::panic (type, "unregistered device requested")
|
||||
Iris::recv.reply.invoke (0, 0, (*d)->dev->cap)
|
||||
kdebug ("given device ")
|
||||
kdebug_num (type)
|
||||
kdebug (":")
|
||||
kdebug_num (index)
|
||||
kdebug ("\n")
|
||||
//kdebug ("given device ")
|
||||
//kdebug_num (type)
|
||||
//kdebug (":")
|
||||
//kdebug_num (index)
|
||||
//kdebug ("\n")
|
||||
break
|
||||
case Iris::Parent::PROVIDE_CAPABILITY:
|
||||
if Iris::recv.data[1].h != 0:
|
||||
@@ -515,14 +515,14 @@ Iris::Num start ():
|
||||
if (*d)->client:
|
||||
if !--(*d)->client->num_waiting:
|
||||
(*d)->client->run ()
|
||||
kdebug ("provided ")
|
||||
kdebug_num ((*d)->type)
|
||||
kdebug (":")
|
||||
kdebug_num ((*d)->index)
|
||||
kdebug ("\n")
|
||||
//kdebug ("provided ")
|
||||
//kdebug_num ((*d)->type)
|
||||
//kdebug (":")
|
||||
//kdebug_num ((*d)->index)
|
||||
//kdebug ("\n")
|
||||
break
|
||||
case Iris::Parent::INIT_DONE:
|
||||
kdebug ("init done\n")
|
||||
//kdebug ("init done\n")
|
||||
Iris::recv.reply.invoke ()
|
||||
if caller == sysreq->server:
|
||||
Iris::Cap cap = Iris::my_receiver.create_capability (SYSREQ)
|
||||
@@ -530,6 +530,15 @@ Iris::Num start ():
|
||||
Iris::free_cap (cap)
|
||||
kdebug ("registered sysreq\n")
|
||||
break
|
||||
case Iris::Parent::EXIT:
|
||||
kdebug ("child exits with code ")
|
||||
// TODO: print name.
|
||||
kdebug_num (Iris::recv.data[1].h)
|
||||
kdebug (":")
|
||||
kdebug_num (Iris::recv.data[1].l)
|
||||
kdebug ("\n")
|
||||
// TODO: destroy memory.
|
||||
break
|
||||
default:
|
||||
// TODO.
|
||||
kdebug ("child request: ")
|
||||
|
||||
129
source/partition.ccp
Normal file
129
source/partition.ccp
Normal file
@@ -0,0 +1,129 @@
|
||||
#pypp 0
|
||||
#include <iris.hh>
|
||||
#include <devices.hh>
|
||||
|
||||
#define NUM_PARTITIONS 4
|
||||
#define SECTOR_BITS 9
|
||||
#define BLOCK_MASK (~((1 << SECTOR_BITS) - 1))
|
||||
|
||||
struct Partition:
|
||||
static Iris::Num device_size
|
||||
unsigned lba_start, lba_size
|
||||
unsigned type
|
||||
bool active
|
||||
Iris::Num start, size
|
||||
static unsigned read_num (char *data):
|
||||
return data[0] & 0xff | (data[1] & 0xff) << 8 | (data[2] & 0xff) << 16 | (data[3] & 0xff) << 24
|
||||
void read (char *data):
|
||||
if data[0] == 0:
|
||||
active = false
|
||||
else:
|
||||
active = true
|
||||
if (data[0] & 0xff) != 0x80:
|
||||
kdebug ("Warning: invalid active code ")
|
||||
kdebug_num (data[0], 2)
|
||||
kdebug ("\n")
|
||||
type = data[4] & 0xff
|
||||
lba_start = read_num (data + 8)
|
||||
lba_size = read_num (data + 12)
|
||||
start = Iris::Num (lba_start).value () << SECTOR_BITS
|
||||
size = Iris::Num (lba_size).value () << SECTOR_BITS
|
||||
//kdebug ("Partition read: ")
|
||||
//kdebug_num (lba_start)
|
||||
//kdebug ("+")
|
||||
//kdebug_num (lba_size)
|
||||
//kdebug ("\n")
|
||||
|
||||
Iris::Num Partition::device_size
|
||||
|
||||
static Iris::WString dev
|
||||
static void read_block (Iris::Num idx, Iris::Page page, unsigned size = 1 << SECTOR_BITS, unsigned offset = 0):
|
||||
idx = idx.value () >> SECTOR_BITS
|
||||
offset &= ~PAGE_MASK
|
||||
if size + offset > PAGE_SIZE:
|
||||
size = PAGE_SIZE - offset
|
||||
size >>= SECTOR_BITS
|
||||
for unsigned i = 0; i < size; ++i:
|
||||
dev.get_block ((idx.value () + i) << SECTOR_BITS, 1 << SECTOR_BITS, (i << SECTOR_BITS) + offset, page)
|
||||
|
||||
Iris::Num start ():
|
||||
Partition::device_size = 0
|
||||
dev = Iris::my_parent.get_capability <Iris::WString> ()
|
||||
if dev.get_align_bits () > SECTOR_BITS:
|
||||
kdebug ("partitioned device doesn't support 512 byte access")
|
||||
return 1
|
||||
Partition::device_size = dev.get_size ()
|
||||
Iris::Page page = Iris::my_memory.create_page ()
|
||||
page.set_flags (Iris::Page::PAYING, Iris::Page::PAYING)
|
||||
char *buffer = (char *)0x15000
|
||||
unsigned *ubuffer = (unsigned *)buffer
|
||||
Iris::my_memory.map (page, (unsigned)buffer)
|
||||
read_block (0, page)
|
||||
|
||||
if buffer[0x1fe] != 0x55 || (buffer[0x1ff] & 0xff) != 0xaa:
|
||||
kdebug ("invalid mbr signature\n")
|
||||
|
||||
Partition partition[NUM_PARTITIONS]
|
||||
|
||||
Iris::Cap cap
|
||||
for unsigned i = 0; i < NUM_PARTITIONS; ++i:
|
||||
partition[i].read (buffer + 0x1be + 0x10 * i)
|
||||
cap = Iris::my_receiver.create_capability (i)
|
||||
Iris::my_parent.provide_capability <Iris::WString> (cap.copy (), i)
|
||||
Iris::free_cap (cap)
|
||||
|
||||
page.set_flags (0, Iris::Page::PAYING | Iris::Page::FRAME)
|
||||
|
||||
Iris::my_parent.init_done ()
|
||||
|
||||
while true:
|
||||
Iris::wait ()
|
||||
switch Iris::recv.data[0].l:
|
||||
case Iris::String::GET_SIZE:
|
||||
Iris::recv.reply.invoke (partition[Iris::recv.protected_data.l].size)
|
||||
break
|
||||
case Iris::String::GET_CHARS:
|
||||
Iris::Cap reply = Iris::get_reply ()
|
||||
Iris::Num request = Iris::recv.data[1]
|
||||
Iris::Num offset = (partition[Iris::recv.protected_data.l].start.value () + (request.value () & BLOCK_MASK)) & 0xf
|
||||
unsigned page_offset = request.l & ~BLOCK_MASK
|
||||
page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME, Iris::Page::PAYING | Iris::Page::FRAME)
|
||||
read_block (offset, page)
|
||||
reply.invoke (Iris::Num (ubuffer[page_offset >> 2 + 0], ubuffer[page_offset >> 2 + 1]), Iris::Num (ubuffer[page_offset >> 2 + 2], ubuffer[page_offset >> 2 + 3]))
|
||||
page.set_flags (0, Iris::Page::PAYING | Iris::Page::FRAME)
|
||||
Iris::free_cap (reply)
|
||||
break
|
||||
case Iris::String::GET_ALIGN_BITS:
|
||||
Iris::recv.reply.invoke (SECTOR_BITS)
|
||||
break
|
||||
case Iris::String::GET_BLOCK:
|
||||
Iris::Cap reply = Iris::get_reply ()
|
||||
Iris::Cap arg = Iris::get_arg ()
|
||||
Iris::Num p = Iris::recv.data[1].value () & BLOCK_MASK
|
||||
Iris::Num offset = partition[Iris::recv.protected_data.l].start.value ()
|
||||
unsigned size = Iris::recv.data[0].h >> 16
|
||||
unsigned out_offset = Iris::recv.data[0].h & 0xffff
|
||||
//kdebug ("partition sending sector ")
|
||||
//kdebug_num (offset.h)
|
||||
//kdebug (":")
|
||||
//kdebug_num (offset.l)
|
||||
//kdebug (" + ")
|
||||
//kdebug_num (p.h)
|
||||
//kdebug (":")
|
||||
//kdebug_num (p.l)
|
||||
//kdebug (" = ")
|
||||
//kdebug_num (Iris::Num (offset.value () + p.value ()).h)
|
||||
//kdebug (":")
|
||||
//kdebug_num (Iris::Num (offset.value () + p.value ()).l)
|
||||
//kdebug ("\n")
|
||||
read_block (offset.value () + p.value (), arg, size, out_offset)
|
||||
reply.invoke ()
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (arg)
|
||||
break
|
||||
case Iris::WString::SET_CHARS:
|
||||
case Iris::WString::SET_BLOCK:
|
||||
Iris::panic (Iris::recv.data[0].l, "writing to partitions not supported yet")
|
||||
case Iris::WString::TRUNCATE:
|
||||
default:
|
||||
Iris::panic (Iris::recv.data[0].l, "invalid request for partition handler")
|
||||
@@ -21,9 +21,37 @@
|
||||
#include "arch.hh"
|
||||
|
||||
class Mmc:
|
||||
static unsigned const PORT = 3
|
||||
static unsigned const PIN = 2
|
||||
bool check_sdio ()
|
||||
public:
|
||||
enum Response_type:
|
||||
NONE = MSC_CMDAT_RESPONSE_NONE
|
||||
DATA = MSC_CMDAT_RESPONSE_R1 | MSC_CMDAT_DATA_EN
|
||||
R1 = MSC_CMDAT_RESPONSE_R1
|
||||
R1B = MSC_CMDAT_RESPONSE_R1 | MSC_CMDAT_BUSY
|
||||
R2 = MSC_CMDAT_RESPONSE_R2
|
||||
R3 = MSC_CMDAT_RESPONSE_R3
|
||||
R4 = MSC_CMDAT_RESPONSE_R4
|
||||
R5 = MSC_CMDAT_RESPONSE_R5
|
||||
R6 = MSC_CMDAT_RESPONSE_R6
|
||||
R7 = MSC_CMDAT_RESPONSE_R7
|
||||
static unsigned const POWER_PORT = 3
|
||||
static unsigned const POWER_PIN = 2
|
||||
struct CID:
|
||||
unsigned mid
|
||||
char oid[2]
|
||||
char pnm[5]
|
||||
unsigned prv
|
||||
unsigned psn
|
||||
unsigned year
|
||||
unsigned month
|
||||
struct CSD:
|
||||
unsigned c_size
|
||||
unsigned c_size_mult
|
||||
unsigned read_bl_len, write_bl_len
|
||||
bool copy
|
||||
bool perm_write_protect
|
||||
bool tmp_write_protect
|
||||
bool send (unsigned cmd, unsigned arg, Response_type response_type, unsigned *response = NULL)
|
||||
void check_sd ()
|
||||
void check_sdmem ()
|
||||
void check_mmc ()
|
||||
public:
|
||||
@@ -31,23 +59,158 @@ class Mmc:
|
||||
void detect ()
|
||||
void release ()
|
||||
void interrupt ()
|
||||
CID const &get_cid ():
|
||||
return cid
|
||||
unsigned get_num_blocks ():
|
||||
return num_blocks
|
||||
unsigned get_read_block_size ():
|
||||
return read_block_size
|
||||
unsigned get_block_bits ():
|
||||
return csd.read_bl_len > csd.write_bl_len ? csd.read_bl_len : csd.write_bl_len
|
||||
void fill_page (Iris::Page page, Iris::Num address, unsigned size, unsigned offset)
|
||||
private:
|
||||
unsigned rca
|
||||
bool have_sdmem, have_io
|
||||
CID cid
|
||||
CSD csd
|
||||
unsigned num_blocks, read_block_size
|
||||
Iris::Page buffer_page
|
||||
static unsigned const buffer = 0x15000
|
||||
|
||||
bool Mmc::send (unsigned cmd, unsigned arg, Response_type response_type, unsigned *response):
|
||||
MSC_CMD = cmd
|
||||
MSC_ARG = arg
|
||||
MSC_CMDAT = response_type
|
||||
Iris::register_interrupt (IRQ_MSC)
|
||||
msc_start_op ()
|
||||
Iris::wait_for_interrupt (IRQ_MSC)
|
||||
//kdebug ("cmd: ")
|
||||
//kdebug_num (cmd)
|
||||
unsigned stat = MSC_STAT
|
||||
//kdebug (", stat: ")
|
||||
//kdebug_num (stat)
|
||||
//kdebug ("\n")
|
||||
if stat & MSC_STAT_CRC_RES_ERR:
|
||||
Iris::panic (0, "crc error in mmc response")
|
||||
return false
|
||||
if stat & MSC_STAT_TIME_OUT_RES:
|
||||
//kdebug ("time out waiting for mmc response\n")
|
||||
return false
|
||||
if response_type == R2:
|
||||
unsigned d = MSC_RES
|
||||
if d >> 8 != 0x3f:
|
||||
Iris::panic (d, "invalid r2 response")
|
||||
if cmd == 3:
|
||||
// Read out result.
|
||||
cid.mid = d & 0xff
|
||||
d = MSC_RES
|
||||
cid.oid[0] = d >> 8
|
||||
cid.oid[1] = d & 0xff
|
||||
d = MSC_RES
|
||||
cid.pnm[0] = d >> 8
|
||||
cid.pnm[1] = d & 0xff
|
||||
d = MSC_RES
|
||||
cid.pnm[2] = d >> 8
|
||||
cid.pnm[3] = d & 0xff
|
||||
d = MSC_RES
|
||||
cid.pnm[4] = d >> 8
|
||||
cid.prv = d & 0xff
|
||||
d = MSC_RES
|
||||
cid.psn = d << 16
|
||||
d = MSC_RES
|
||||
cid.psn |= d
|
||||
d = MSC_RES
|
||||
cid.year = 2000 + (d >> 4 & 0xff)
|
||||
cid.month = d & 0xf
|
||||
else:
|
||||
// Header (8) 1.0 1.0
|
||||
// Read out csd.
|
||||
// Ignore csd_structure. 2 (+ 6) 1.0 2.0 ***
|
||||
d = MSC_RES
|
||||
// Ignore taac and nsac. 8 + 8 2.0 4.0 ***
|
||||
d = MSC_RES
|
||||
// Ignore tran_speed, ccc. 8 + 8/12 2.0 6.0 ***
|
||||
d = MSC_RES
|
||||
// Ignore rest of ccc. 4/12 0.4 6.4
|
||||
// 4 0.4 7.0
|
||||
csd.read_bl_len = (d >> 8) & 0xf
|
||||
// Ignore read_bl_partial, write_blk_misalign, read_blk_misalign, dsr_imp. 1 + 1 + 1 + 1 (+ 2) 0.6 7.6
|
||||
// 2/12 0.2 8.0 ***
|
||||
csd.c_size = (d & 0x0003) << 10
|
||||
d = MSC_RES
|
||||
// 10/12 1.2 9.2
|
||||
csd.c_size |= d >> 6
|
||||
// Ignore vdd_r_cur_min, vdd_r_cur_max. 3 + 3 0.6 10.0 ***
|
||||
d = MSC_RES
|
||||
// Ignore vdd_w_cur_min, vdd_w_cur_max. 3 + 3 0.6 10.6
|
||||
// 3 0.3 11.1
|
||||
csd.c_size_mult = (d >> 7) & 0x7
|
||||
// Ignore erase_blk_enable, sector_size. 1 + 6/7 0.7 12.0 ***
|
||||
d = MSC_RES
|
||||
// Ignore rest of sector_size, wp_grp_size, wp_grp_enable, r2w_factor. 1/7 + 7 + 1 (+ 2) + 3 1.6 13.6
|
||||
// 2/4 0.4 14.0 ***
|
||||
csd.write_bl_len = (d << 2) & 0xc
|
||||
d = MSC_RES
|
||||
// 2/4 0.2 14.2
|
||||
csd.write_bl_len |= (d >> 14) & 0x3
|
||||
// Ignore write_bl_partial, file_format_grp. 1 (+ 5) + 1 0.7 15.1
|
||||
// 1 0.1 15.2
|
||||
csd.copy = d & 0x40
|
||||
// 1 0.1 15.3
|
||||
csd.perm_write_protect = d & 0x20
|
||||
// 1 0.1 15.4
|
||||
csd.tmp_write_protect = d & 0x10
|
||||
// Ignore file_format. 2 (+ 2) 0.4 16.0 ***
|
||||
read_block_size = 1 << csd.read_bl_len
|
||||
num_blocks = (csd.c_size + 1) << (csd.c_size_mult + 2)
|
||||
else if response_type != NONE:
|
||||
unsigned r = MSC_RES
|
||||
if response_type == R3:
|
||||
if r >> 8 != 0x3f:
|
||||
Iris::panic (r, "r3 response was not 3f")
|
||||
else if r >> 8 != cmd:
|
||||
Iris::panic (r, "response doesn't match command")
|
||||
r <<= 24
|
||||
r |= MSC_RES << 8
|
||||
r |= MSC_RES & 0xff
|
||||
if response:
|
||||
*response = r
|
||||
//kdebug ("extra response fifo read: ")
|
||||
//for unsigned i = 0; i < 9; ++i:
|
||||
//kdebug (" ")
|
||||
//kdebug_num (MSC_RES, 4)
|
||||
//kdebug ("\n")
|
||||
MSC_IREG = MSC_IREG_END_CMD_RES
|
||||
return true
|
||||
|
||||
void Mmc::reset ():
|
||||
// Create a buffer to use for data transfer.
|
||||
buffer_page = Iris::my_memory.create_page ()
|
||||
Iris::my_memory.map (buffer_page, buffer)
|
||||
// Reset all state, by faking a release event.
|
||||
release ()
|
||||
// Enable slow clock to msc.
|
||||
CPM_MSCCDR = ~0
|
||||
cpm_start_msc ()
|
||||
// Enable msc pins.
|
||||
gpio_as_msc ()
|
||||
// Disable power to card.
|
||||
gpio_as_gpio (PORT, 1 << PIN)
|
||||
gpio_as_output (PORT, 1 << PIN)
|
||||
gpio_disable_pull (PORT, 1 << PIN)
|
||||
gpio_set (PORT, 1 << PIN)
|
||||
gpio_as_gpio (POWER_PORT, 1 << POWER_PIN)
|
||||
gpio_as_output (POWER_PORT, 1 << POWER_PIN)
|
||||
gpio_disable_pull (POWER_PORT, 1 << POWER_PIN)
|
||||
gpio_set (POWER_PORT, 1 << POWER_PIN)
|
||||
|
||||
// Stop the clock.
|
||||
MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_STOP
|
||||
while MSC_STAT & MSC_STAT_CLK_EN:
|
||||
Iris::schedule ()
|
||||
//kdebug (",")
|
||||
Iris::sleep (1)
|
||||
|
||||
// Reset controller and inserted devices.
|
||||
MSC_STRPCL = MSC_STRPCL_RESET
|
||||
while MSC_STAT & MSC_STAT_IS_RESETTING:
|
||||
//kdebug (":")
|
||||
Iris::sleep (1)
|
||||
|
||||
// Initialize registers.
|
||||
MSC_CLKRT = MSC_CLKRT_CLK_RATE_DIV_128
|
||||
@@ -55,82 +218,166 @@ void Mmc::reset ():
|
||||
MSC_RDTO = ~0
|
||||
MSC_BLKLEN = 0x200
|
||||
MSC_NOB = 0
|
||||
MSC_IMASK = ~0
|
||||
MSC_IREG = ~0
|
||||
MSC_IMASK = ~(MSC_IMASK_END_CMD_RES | MSC_IMASK_RXFIFO_RD_REQ | MSC_IMASK_TXFIFO_WR_REQ)
|
||||
MSC_ARG = 0
|
||||
|
||||
// Reset controller and inserted devices.
|
||||
MSC_STRPCL = MSC_STRPCL_RESET
|
||||
while MSC_STAT & MSC_STAT_IS_RESETTING:
|
||||
Iris::schedule ()
|
||||
|
||||
// Start the clock.
|
||||
MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_START
|
||||
// Set cards, if any, to idle.
|
||||
MSC_CMD = 0
|
||||
MSC_CMDAT = MSC_CMDAT_RESPONSE_NONE
|
||||
Iris::register_interrupt (IRQ_MSC)
|
||||
msc_start_op ()
|
||||
while !msc_ireg_end_cmd_res ():
|
||||
Iris::schedule ()
|
||||
Iris::wait_for_interrupt (IRQ_MSC)
|
||||
msc_ireg_clear_end_cmd_res ()
|
||||
|
||||
// Reset SDIO device, if any.
|
||||
MSC_CMD = 52
|
||||
MSC_ARG = 0x88000C08
|
||||
MSC_CMDAT = MSC_CMDAT_RESPONSE_R5
|
||||
Iris::register_interrupt (IRQ_MSC)
|
||||
msc_start_op ()
|
||||
while !msc_ireg_end_cmd_res ():
|
||||
Iris::schedule ()
|
||||
Iris::wait_for_interrupt (IRQ_MSC)
|
||||
msc_ireg_clear_end_cmd_res ()
|
||||
|
||||
bool Mmc::check_sdio ():
|
||||
// 2. Send CMD5 (IO_SEND_OP_CMD) to validate voltage.
|
||||
// 3. If the response is correct and the number of IO functions > 0, then continue, else go to check SDMEM.
|
||||
// 4. If C-bit in the response is ready (the initialization has finished), go to 6.
|
||||
// 5. Send CMD5 (IO_SEND_OP_CMD) to validate voltage, then go to 4.
|
||||
// 6. If memory-present-bit in the response is true, then it is a combo card (SDIO + Memory), else
|
||||
// it is only a SDIO card.
|
||||
// 7. If it is a combo card, go to check SDMEM to initialize the memory part.
|
||||
// 8. Send CMD3 (SET_RELATIVE_ADDR) to let the card publish a RCA. The RCA is returned
|
||||
// from the response.
|
||||
// 9. If do not accept the new RCA, go to 8, else record the new RCA.
|
||||
// 10. Go to check MMC, because we can assure that there is no SDMEM card.
|
||||
return false
|
||||
|
||||
void Mmc::check_sdmem ():
|
||||
// 2. Send CMD55. Here the default RCA 0x0000 is used for CMD55.
|
||||
// 3. If the response is correct (CMD55 has response), then continue, else go to check MMC.
|
||||
// 4. Send ACMD41 (SD_SEND_OP_CMD) to validate voltage (the general OCR value is 0x00FF8000).
|
||||
// 5. If the initialization has finished, go to 7. (The response is the OCR register and it includes a status information bit (bit [31]). This status bit is set if the card power up procedure has been finished. As long as the card is busy, the corresponding bit[31] is set to LOW.)
|
||||
// 6. Send CMD55 and ACMD41 to validate voltage, and then go to 5.
|
||||
// 7. Send CMD2 (ALL_SEND_CID) to get the card CID.
|
||||
// 8. Send CMD3 (SET_RELATIVE_ADDR) to let card publish a RCA. The RCA is returned from the response.
|
||||
// 9. If do not accept the new RCA, go to 8, else record the new RCA.
|
||||
// 10. Go to check MMC.
|
||||
|
||||
void Mmc::check_mmc ():
|
||||
//kdebug ("checking mmc\n")
|
||||
// 1. SEND CMD1 (SEND_OP_CMD) TO VALIDATE VOLTAGE (THE GENERAL OCR VALUE IS 0X00FF88000).
|
||||
// 2. IF THE RESPONSE IS CORRECT, THEN CONTINUE, ELSE GOTO 9.
|
||||
// 3. IF THE INITIALIZATION HAS FINISHED, GO TO 5. (THE RESPONSE IS THE OCR REGISTER AND IT INCLUDES A STATUS INFORMATION BIT (BIT [31]). THIS STATUS BIT IS SET IF THE CARD POWER UP PROCEDURE HAS BEEN FINISHED. AS LONG AS THE CARD IS BUSY, THE CORRESPONDING BIT[31] IS SET TO LOW.)
|
||||
// 4. Send CMD1 (SEND_OP_CMD) to validate voltage, and then go to 3.
|
||||
// 5. Send CMD2 (ALL_SEND_CID) to get the card CID.
|
||||
// 6. If the response timeout occurs, goto 9.
|
||||
// 7. Send CMD3 (SET_RELATIVE_ADDR) to assign the card a RCA.
|
||||
// 8. If there are other MMC cards, then go to 5.
|
||||
// 3. IF THE INITIALIZATION HAS FINISHED, GO TO 5. (THE RESPONSE IS THE OCR REGISTER AND IT INCLUDES A STATUS INFORMATION BIT (BIT [31]). THIS STATUS BIT IS SET IF THE CARD POWER UP PROCEDURE HAS BEEN FINISHED. AS LONG AS THE CARD IS BUSY, THE CORRESPONDING BIT[31] IS SET TO LOW.)
|
||||
// 4. Send CMD1 (SEND_OP_CMD) to validate voltage, and then go to 3.
|
||||
// 5. Send CMD2 (ALL_SEND_CID) to get the card CID.
|
||||
// 6. If the response timeout occurs, goto 9.
|
||||
// 7. Send CMD3 (SET_RELATIVE_ADDR) to assign the card a RCA.
|
||||
|
||||
void Mmc::check_sdmem ():
|
||||
kdebug ("checking sdmem\n")
|
||||
send (0, 0, NONE)
|
||||
// 2. Send CMD55. Here the default RCA 0x0000 is used for CMD55.
|
||||
// 3. If the response is correct (CMD55 has response), then continue, else go to check MMC.
|
||||
unsigned code
|
||||
bool hc = false
|
||||
if send (8, 0x1aa, R7, &code) && (code & 0xff) == 0xaa:
|
||||
kdebug ("hc\n")
|
||||
hc = true
|
||||
if !send (55, 0, R1, &code):
|
||||
check_mmc ()
|
||||
return
|
||||
// 4. Send ACMD41 (SD_SEND_OP_CMD) to validate voltage (the general OCR value is 0x00FF8000).
|
||||
if !send (41, hc ? 0x40800000 : 0x00800000, R3, &code):
|
||||
check_mmc ()
|
||||
return
|
||||
// 5. If the initialization has finished, go to 7. (The response is the OCR register and it includes a status information bit (bit [31]). This status bit is set if the card power up procedure has been finished. As long as the card is busy, the corresponding bit[31] is set to LOW.)
|
||||
// 6. Send CMD55 and ACMD41 to validate voltage, and then go to 5.
|
||||
unsigned retries = 100
|
||||
while !(code & (1 << 31)) && --retries:
|
||||
if !send (55, 0, R1, &code):
|
||||
return
|
||||
if !send (41, hc ? 0x40800000 : 0x00800000, R3, &code):
|
||||
return
|
||||
Iris::sleep (1)
|
||||
if !(code & (1 << 31)):
|
||||
Iris::panic (code, "card fails to finish setting up")
|
||||
// 7. Send CMD2 (ALL_SEND_CID) to get the card CID.
|
||||
if !send (2, 0, R2):
|
||||
Iris::panic (0, "card failed to send CID")
|
||||
// 8. Send CMD3 (SET_RELATIVE_ADDR) to let card publish a RCA. The RCA is returned from the response.
|
||||
// 9. If do not accept the new RCA, go to 8, else record the new RCA.
|
||||
rca = 0
|
||||
while !rca:
|
||||
if !send (3, 0, R6, &rca):
|
||||
Iris::panic (0, "card failed to provide rca")
|
||||
rca &= 0xffff0000
|
||||
kdebug ("received rca ")
|
||||
kdebug_num (rca >> 16, 4)
|
||||
kdebug ("\n")
|
||||
have_sdmem = true
|
||||
|
||||
void Mmc::check_sd ():
|
||||
//kdebug ("checking sdio\n")
|
||||
if !send (0, 0, NONE):
|
||||
Iris::panic (0, "unable to reset cards?")
|
||||
// 2. Send CMD5 (IO_SEND_OP_CMD) to validate voltage.
|
||||
// 3. If the response is correct and the number of IO functions > 0, then continue, else go to check SDMEM.
|
||||
unsigned code
|
||||
if !send (5, 1 << 20, R4, &code) || !(code & (7 << 28)):
|
||||
check_sdmem ()
|
||||
return
|
||||
// 4. If C-bit in the response is ready (the initialization has finished), go to 6.
|
||||
// 5. Send CMD5 (IO_SEND_OP_CMD) to validate voltage, then go to 4.
|
||||
while !(code & (1 << 31)):
|
||||
if !send (5, 1 << 20, R4, &code):
|
||||
Iris::panic (0, "invalid response to cmd 5")
|
||||
// 6. If memory-present-bit in the response is true, then it is a combo card (SDIO + Memory), else it is only a SDIO card.
|
||||
// 7. If it is a combo card, go to check SDMEM to initialize the memory part.
|
||||
have_io = true
|
||||
if code & (1 << 27):
|
||||
check_sdmem ()
|
||||
return
|
||||
// 8. Send CMD3 (SET_RELATIVE_ADDR) to let the card publish a RCA. The RCA is returned from the response.
|
||||
// 9. If do not accept the new RCA, go to 8, else record the new RCA.
|
||||
rca = 0
|
||||
while rca == 0:
|
||||
if !send (3, 0, R6, &rca):
|
||||
Iris::panic (0, "unable to set rca")
|
||||
rca &= 0xffff0000
|
||||
check_mmc ()
|
||||
|
||||
void Mmc::detect ():
|
||||
kdebug ("mmc detect\n")
|
||||
gpio_clear (PORT, 1 << PIN)
|
||||
if check_sdio ():
|
||||
check_sdmem ()
|
||||
gpio_clear (POWER_PORT, 1 << POWER_PIN)
|
||||
check_sd ()
|
||||
check_mmc ()
|
||||
if have_sdmem:
|
||||
if !send (9, rca, R2):
|
||||
Iris::panic (0, "unable to request csd")
|
||||
if !send (7, rca, R1B):
|
||||
Iris::panic (0, "unable to select sdmem")
|
||||
kdebug ("found device; size = ")
|
||||
kdebug_num (num_blocks)
|
||||
kdebug (" * ")
|
||||
kdebug_num (read_block_size)
|
||||
kdebug (" = ")
|
||||
kdebug_num (num_blocks * read_block_size)
|
||||
kdebug ("\n")
|
||||
|
||||
void Mmc::release ():
|
||||
kdebug ("mmc release\n")
|
||||
gpio_set (PORT, 1 << PIN)
|
||||
gpio_set (POWER_PORT, 1 << POWER_PIN)
|
||||
have_sdmem = false
|
||||
have_io = false
|
||||
read_block_size = 0
|
||||
num_blocks = 0
|
||||
|
||||
void Mmc::interrupt ():
|
||||
kdebug ("mmc interrupt\n")
|
||||
|
||||
void Mmc::fill_page (Iris::Page page, Iris::Num address, unsigned size, unsigned offset):
|
||||
if address.h:
|
||||
Iris::panic (0, "page too high: not supported")
|
||||
return
|
||||
unsigned blockmask = ~((1 << get_block_bits ()) - 1)
|
||||
unsigned p = address.l & blockmask
|
||||
size &= blockmask
|
||||
offset &= ~PAGE_MASK
|
||||
if size + offset > PAGE_SIZE:
|
||||
size = PAGE_SIZE - offset
|
||||
page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME, Iris::Page::PAYING | Iris::Page::FRAME)
|
||||
page.share (buffer_page)
|
||||
buffer_page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME, Iris::Page::PAYING | Iris::Page::FRAME)
|
||||
MSC_NOB = 1
|
||||
MSC_BLKLEN = read_block_size
|
||||
for unsigned a = 0; a < size; a += 1 << get_block_bits ():
|
||||
if !send (17, p + a, DATA):
|
||||
Iris::panic (0, "unable to request data")
|
||||
for unsigned aa = 0; aa < read_block_size; aa += 4:
|
||||
Iris::register_interrupt (IRQ_MSC)
|
||||
Iris::wait_for_interrupt (IRQ_MSC)
|
||||
*(unsigned *)(buffer + a + aa + offset) = MSC_RXFIFO
|
||||
MSC_IREG = MSC_IREG_DATA_TRAN_DONE
|
||||
|
||||
static Mmc mmc
|
||||
|
||||
enum types:
|
||||
DETECT
|
||||
@@ -141,7 +388,6 @@ Iris::Num start ():
|
||||
map_gpio ()
|
||||
map_cpm ()
|
||||
|
||||
Mmc mmc
|
||||
mmc.reset ()
|
||||
|
||||
Iris::Event detect = Iris::my_parent.get_capability <Iris::Event> ()
|
||||
@@ -178,7 +424,34 @@ Iris::Num start ():
|
||||
mmc.interrupt ()
|
||||
break
|
||||
case REQUEST:
|
||||
kdebug ("sd+mmc request\n")
|
||||
//kdebug ("sd+mmc request ")
|
||||
//kdebug_num (Iris::recv.data[0].l)
|
||||
//kdebug ("\n")
|
||||
switch Iris::recv.data[0].l:
|
||||
case Iris::String::GET_SIZE:
|
||||
unsigned long long size = mmc.get_num_blocks () * mmc.get_read_block_size ()
|
||||
Iris::recv.reply.invoke (size)
|
||||
break
|
||||
case Iris::String::GET_CHARS:
|
||||
Iris::panic (0, "get chars from mmc not supported yet")
|
||||
break
|
||||
case Iris::String::GET_ALIGN_BITS:
|
||||
Iris::recv.reply.invoke (mmc.get_block_bits ())
|
||||
break
|
||||
case Iris::String::GET_BLOCK:
|
||||
Iris::Cap reply = Iris::get_reply ()
|
||||
Iris::Page page = Iris::get_arg ()
|
||||
mmc.fill_page (page, Iris::recv.data[1], Iris::recv.data[0].h >> 16, Iris::recv.data[0].h & 0xffff)
|
||||
reply.invoke ()
|
||||
Iris::free_cap (page)
|
||||
Iris::free_cap (reply)
|
||||
break
|
||||
case Iris::WString::SET_CHARS:
|
||||
case Iris::WString::SET_BLOCK:
|
||||
// Fall through: don't support writing yet.
|
||||
case Iris::WString::TRUNCATE:
|
||||
default:
|
||||
Iris::panic (0, "unexpected event for sd+mmc")
|
||||
break
|
||||
default:
|
||||
Iris::panic (0, "unexpected request source for sd+mmc")
|
||||
|
||||
60
source/test.ccp
Normal file
60
source/test.ccp
Normal file
@@ -0,0 +1,60 @@
|
||||
#pypp 0
|
||||
// Iris: micro-kernel for a capability-based operating system.
|
||||
// source/test.ccp: Testing program.
|
||||
// Copyright 2009 Bas Wijnen <wijnen@debian.org>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <iris.hh>
|
||||
#include <devices.hh>
|
||||
|
||||
bool match (char const *a, char const *b):
|
||||
for unsigned i = 0; i < 11; ++i:
|
||||
if a[i] != b[i]:
|
||||
return false
|
||||
return true
|
||||
|
||||
Iris::Num start ():
|
||||
Iris::Directory dir = Iris::my_parent.get_capability <Iris::Directory> ()
|
||||
dir.lock_ro ()
|
||||
Iris::Num files = dir.get_size ()
|
||||
for Iris::Num i = 0; i.value () < files.value (); i = i.value () + 1:
|
||||
Iris::String f = dir.get_name (i)
|
||||
char start[16]
|
||||
f.get_chars (0, start)
|
||||
if match (start, "TEST TXT"):
|
||||
Iris::free_cap (f)
|
||||
f = dir.get_file_ro (i)
|
||||
dir.unlock_ro ()
|
||||
Iris::Page p = f.get_block (0)
|
||||
char *mapping = (char *)0x15000
|
||||
Iris::my_memory.map (p, (unsigned)mapping)
|
||||
for unsigned j = 0; j < PAGE_SIZE; ++j:
|
||||
kdebug_char (mapping[j])
|
||||
kdebug_char ('\n')
|
||||
return 0
|
||||
unsigned i
|
||||
for i = 0; i < 16; ++i:
|
||||
if start[i] != 0:
|
||||
break
|
||||
if i < 16:
|
||||
for i = 0; i < 16; ++i:
|
||||
kdebug_num (start[i], 2)
|
||||
kdebug (" ")
|
||||
for i = 0; i < 16; ++i:
|
||||
kdebug_char (start[i])
|
||||
kdebug ("\n")
|
||||
Iris::free_cap (f)
|
||||
kdebug ("file test.txt not found\n")
|
||||
return 1
|
||||
715
source/udc.ccp
Normal file
715
source/udc.ccp
Normal file
@@ -0,0 +1,715 @@
|
||||
#pypp 0
|
||||
// Iris: micro-kernel for a capability-based operating system.
|
||||
// boot-programs/udc.ccp: USB device controller driver.
|
||||
// Copyright 2009 Bas Wijnen <wijnen@debian.org>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "iris.hh"
|
||||
#include "devices.hh"
|
||||
#define ARCH
|
||||
#include "arch.hh"
|
||||
|
||||
class Udc:
|
||||
typedef unsigned char u8
|
||||
typedef unsigned short u16
|
||||
typedef unsigned int u32
|
||||
typedef u8 string
|
||||
// The ugly stuff is because pypp doesn't support __attribute__.
|
||||
/**/struct Setup {
|
||||
u8 request_type;
|
||||
u8 request;
|
||||
u16 value;
|
||||
u16 index;
|
||||
u16 length;
|
||||
} __attribute__ ((packed))
|
||||
/**/struct Device {
|
||||
static u8 const Type = 1;
|
||||
u8 length;
|
||||
u8 type;
|
||||
u16 usb_version;
|
||||
u8 dev_class;
|
||||
u8 subclass;
|
||||
u8 protocol;
|
||||
u8 max_packet_size0;
|
||||
u16 vendor;
|
||||
u16 product;
|
||||
u16 dev_version;
|
||||
string s_manufacturer;
|
||||
string s_product;
|
||||
string s_serial;
|
||||
u8 num_configurations;
|
||||
} __attribute__ ((packed))
|
||||
/**/struct Configuration {
|
||||
static u8 const Type = 2;
|
||||
u8 length;
|
||||
u8 type;
|
||||
u16 total_length;
|
||||
u8 num_interfaces;
|
||||
u8 configuration_value;
|
||||
u8 configuration;
|
||||
u8 attributes;
|
||||
u8 max_power;
|
||||
} __attribute__ ((packed))
|
||||
/**/struct Interface {
|
||||
static u8 const Type = 4;
|
||||
u8 length;
|
||||
u8 type;
|
||||
u8 interface;
|
||||
u8 alternate;
|
||||
u8 num_endpoints;
|
||||
u8 iface_class;
|
||||
u8 subclass;
|
||||
u8 protocol;
|
||||
string name;
|
||||
} __attribute__ ((packed))
|
||||
/**/struct Endpoint {
|
||||
static u8 const Type = 5;
|
||||
u8 length;
|
||||
u8 type;
|
||||
u8 address;
|
||||
u8 attributes;
|
||||
u16 max_packet_size;
|
||||
u8 interval;
|
||||
} __attribute__ ((packed))
|
||||
/**/struct Device_Qualifier {
|
||||
static u8 const Type = 6;
|
||||
u8 length;
|
||||
u8 type;
|
||||
u16 version;
|
||||
u8 dev_class;
|
||||
u8 subclass;
|
||||
u8 protocol;
|
||||
u8 max_packet_size0;
|
||||
u8 num_configurations;
|
||||
u8 reserved;
|
||||
} __attribute__ ((packed))
|
||||
/**/struct Langs {
|
||||
static u8 const Type = 3;
|
||||
u8 length;
|
||||
u8 type;
|
||||
u8 lang;
|
||||
} __attribute__ ((packed))
|
||||
template <unsigned size> struct String {
|
||||
static u8 const Type = 3;
|
||||
u8 length;
|
||||
u8 type;
|
||||
u16 data[size];
|
||||
} __attribute__ ((packed))
|
||||
static unsigned const max_packet_size0 = 64
|
||||
static unsigned const max_packet_size_bulk = 64
|
||||
enum Requests:
|
||||
GET_STATUS = 0
|
||||
CLEAR_FEATURE = 1
|
||||
SET_FEATURE = 3
|
||||
SET_ADDRESS = 5
|
||||
GET_DESCRIPTOR = 6
|
||||
SET_DESCRIPTOR = 7
|
||||
GET_CONFIGURATION = 8
|
||||
SET_CONFIGURATION = 9
|
||||
GET_INTERFACE = 10
|
||||
SET_INTERFACE = 11
|
||||
SYNCH_FRAME = 12
|
||||
enum Request_types:
|
||||
STANDARD_TO_DEVICE = 0
|
||||
VENDOR_TO_DEVICE = 0x40
|
||||
STANDARD_FROM_DEVICE = 0x80
|
||||
VENDOR_FROM_DEVICE = 0xc0
|
||||
enum Endpoint_types:
|
||||
CONTROL = 0
|
||||
ISOCHRONOUS = 1
|
||||
BULK = 2
|
||||
INTERRUPT = 3
|
||||
/**/struct my_config {
|
||||
Configuration config;
|
||||
Interface interface;
|
||||
Endpoint endpoint[2];
|
||||
} __attribute__ ((packed))
|
||||
static Device device_descriptor
|
||||
//static Device_Qualifier device_qualifier_descriptor
|
||||
static my_config config_descriptor; //, other_config_descriptor
|
||||
static String <1> s_langs
|
||||
static String <6> s_manufacturer
|
||||
static String <16> s_product
|
||||
enum State:
|
||||
IDLE
|
||||
TX
|
||||
RX
|
||||
State state
|
||||
char configuration
|
||||
unsigned size
|
||||
unsigned rx_request
|
||||
char const *ptr
|
||||
unsigned cmd_code, cmd_arg
|
||||
bool rebooting
|
||||
bool vendor (Setup *s, unsigned cmd)
|
||||
bool get_descriptor (unsigned type, unsigned idx, unsigned len)
|
||||
bool handle_setup (Setup *s, unsigned cmd)
|
||||
void irq_usb (unsigned cmd)
|
||||
void irq_in (unsigned cmd)
|
||||
void irq_out (unsigned cmd)
|
||||
char log_buffer[1000]
|
||||
unsigned log_buffer_size
|
||||
unsigned log_buffer_start
|
||||
Iris::Cap caller, caller_arg
|
||||
bool have_caller
|
||||
unsigned *page
|
||||
unsigned *p
|
||||
Iris::Page buffer_page
|
||||
public:
|
||||
void init ()
|
||||
void log (unsigned c)
|
||||
void interrupt (unsigned cmd)
|
||||
void send (unsigned code, unsigned narg, Iris::Cap reply, Iris::Cap arg)
|
||||
|
||||
Udc::Device Udc::device_descriptor = { sizeof (Device), Device::Type, 0x200, 0, 0, 0, max_packet_size0, 0xfffe, 0x0002, 0x100, 1, 2, 0, 1 }
|
||||
Udc::my_config Udc::config_descriptor = {
|
||||
(Configuration){ sizeof (Configuration), Configuration::Type, sizeof (my_config), 1, 1, 0, 0xc0, 1 },
|
||||
(Interface){ sizeof (Interface), Interface::Type, 0, 0, 2, 0xff, 0, 0x80, 0 }, {
|
||||
(Endpoint){ sizeof (Endpoint), Endpoint::Type, 1, BULK, max_packet_size_bulk, 0 },
|
||||
(Endpoint){ sizeof (Endpoint), Endpoint::Type, 0x81, BULK, max_packet_size_bulk, 0 }
|
||||
}
|
||||
}
|
||||
Udc::String <1> Udc::s_langs = { sizeof (String <1>), String <1>::Type, { 0x0409 } }
|
||||
Udc::String <6> Udc::s_manufacturer = { sizeof (String <6>), String <6>::Type, { 's', 'h', 'e', 'v', 'e', 'k' } }
|
||||
Udc::String <16> Udc::s_product = { sizeof (String <16>), String <16>::Type, { 'I', 'r', 'i', 's', ' ', 'o', 'n', ' ', 'N', 'a', 'n', 'o', 'N', 'o', 't', 'e' } }
|
||||
|
||||
//Udc::Device_Qualifier const Udc::device_qualifier_descriptor = { sizeof (Device_Qualifier), Device_Qualifier::Type, 0x200, 0, 0, 0, max_packet_size0, 1, 0 }
|
||||
//Udc::my_config const Udc::other_config_descriptor = {
|
||||
// (Configuration){ sizeof (Configuration), 7, sizeof (my_config), 1, 1, 0, 0xc0, 1 },
|
||||
// (Interface){ sizeof (Interface), Interface::Type, 0, 0, 2, 0xff, 0, 0x80, 0 }, {
|
||||
// (Endpoint){ sizeof (Endpoint), Endpoint::Type, 1, BULK, max_packet_size_bulk, 0 },
|
||||
// (Endpoint){ sizeof (Endpoint), Endpoint::Type, 0x81, BULK, max_packet_size_bulk, 0 }
|
||||
// }
|
||||
// }
|
||||
|
||||
void Udc::init ():
|
||||
// Initialize the globals. My method of compiling doesn't do that properly.
|
||||
device_descriptor = (Device){ sizeof (Device), Device::Type, 0x200, 0, 0, 0, max_packet_size0, 0xfffe, 0x0002, 0x100, 1, 2, 0, 1 }
|
||||
config_descriptor = (my_config){
|
||||
(Configuration){ sizeof (Configuration), Configuration::Type, sizeof (my_config), 1, 1, 0, 0xc0, 1 },
|
||||
(Interface){ sizeof (Interface), Interface::Type, 0, 0, 2, 0xff, 0, 0x80, 0 }, {
|
||||
(Endpoint){ sizeof (Endpoint), Endpoint::Type, 1, BULK, max_packet_size_bulk, 0 },
|
||||
(Endpoint){ sizeof (Endpoint), Endpoint::Type, 0x81, BULK, max_packet_size_bulk, 0 }
|
||||
}
|
||||
}
|
||||
s_langs = (String <1>){ sizeof (String <1>), String <1>::Type, { 0x0409 } }
|
||||
s_manufacturer = (String <6>){ sizeof (String <6>), String <6>::Type, { 's', 'h', 'e', 'v', 'e', 'k' } }
|
||||
s_product = (String <16>){ sizeof (String <16>), String <16>::Type, { 'I', 'r', 'i', 's', ' ', 'o', 'n', ' ', 'N', 'a', 'n', 'o', 'N', 'o', 't', 'e' } }
|
||||
have_caller = false
|
||||
log_buffer_size = 0
|
||||
log_buffer_start = 0
|
||||
cmd_code = ~0
|
||||
cmd_arg = 0
|
||||
|
||||
// Use the space which is reserved for the framebuffer, because it will not be in use.
|
||||
// Normally a normal new page should be allocated here, but new isn't implemented at this point.
|
||||
page = (unsigned *)LCD_FRAMEBUFFER_BASE
|
||||
p = page
|
||||
buffer_page = Iris::my_memory.create_page ()
|
||||
buffer_page.set_flags (Iris::Page::FRAME | Iris::Page::PAYING, Iris::Page::FRAME | Iris::Page::PAYING)
|
||||
Iris::my_memory.map (buffer_page, (unsigned)page)
|
||||
|
||||
// Disconnect from the bus and don't try to get high-speed.
|
||||
UDC_POWER = 0
|
||||
UDC_TESTMODE = 0
|
||||
UDC_INDEX = 0
|
||||
state = IDLE
|
||||
configuration = 0
|
||||
size = 0
|
||||
// exit suspend mode by reading the interrupt register.
|
||||
cpm_start_udc ()
|
||||
// reset all pending endpoint interrupts.
|
||||
unsigned i = UDC_INTRUSB
|
||||
i = UDC_INTRIN
|
||||
i = UDC_INTROUT
|
||||
// enable interrupt on bus reset.
|
||||
UDC_INTRUSBE = UDC_INTR_RESET
|
||||
// enable interrupts on endpoint 0.
|
||||
UDC_INTRINE = 1 << 0
|
||||
// and on out endpoint 1.
|
||||
UDC_INTROUTE = 1 << 1
|
||||
// Wait a while.
|
||||
for unsigned w = 0; w < 10000; ++w:
|
||||
Iris::schedule ()
|
||||
// Connect to the host.
|
||||
UDC_POWER = UDC_POWER_SOFTCONN
|
||||
|
||||
bool Udc::vendor (Setup *s, unsigned cmd):
|
||||
if !(s->request_type & 0x80):
|
||||
kdebug ("data to device without size\n")
|
||||
Iris::panic (0)
|
||||
return true
|
||||
if s->request == 10:
|
||||
static unsigned b[2]
|
||||
ptr = (char *)b
|
||||
size = s->length < 8 ? s->length : 8
|
||||
if cmd_code != ~0:
|
||||
b[0] = cmd_code
|
||||
b[1] = cmd_arg
|
||||
if cmd_code == Iris::Directory::LOCK_RO || cmd_code == Iris::Directory::UNLOCK_RO:
|
||||
caller.invoke ()
|
||||
Iris::free_cap (caller)
|
||||
Iris::free_cap (caller_arg)
|
||||
have_caller = false
|
||||
//kdebug ("(un)lock response\n")
|
||||
cmd_code = ~0
|
||||
else:
|
||||
if log_buffer_start == log_buffer_size:
|
||||
b[0] = ~1
|
||||
b[1] = 0
|
||||
else:
|
||||
b[0] = ~0
|
||||
b[1] = (log_buffer[log_buffer_start++] & 0xff) * 0x01010101
|
||||
if log_buffer_start == log_buffer_size:
|
||||
log_buffer_start = 0
|
||||
log_buffer_size = 0
|
||||
else:
|
||||
static char const *name = "Reboot"
|
||||
ptr = name
|
||||
size = s->length < 6 ? s->length : 6
|
||||
rebooting = true
|
||||
state = TX
|
||||
return true
|
||||
|
||||
void Udc::send (unsigned code, unsigned narg, Iris::Cap reply, Iris::Cap arg):
|
||||
if cmd_code != ~0:
|
||||
kdebug ("new code sent while old one wasn't finished.\n")
|
||||
Iris::panic (0)
|
||||
cmd_code = code
|
||||
cmd_arg = narg
|
||||
caller = reply
|
||||
caller_arg = arg
|
||||
have_caller = true
|
||||
|
||||
bool Udc::get_descriptor (unsigned type, unsigned idx, unsigned len):
|
||||
switch type:
|
||||
case Configuration::Type:
|
||||
if idx != 0:
|
||||
return false
|
||||
ptr = reinterpret_cast <char const *> (&config_descriptor)
|
||||
size = (len < sizeof (config_descriptor) ? len : sizeof (config_descriptor))
|
||||
break
|
||||
case Device::Type:
|
||||
if idx != 0:
|
||||
return false
|
||||
ptr = reinterpret_cast <char const *> (&device_descriptor)
|
||||
size = (len < sizeof (device_descriptor) ? len : sizeof (device_descriptor))
|
||||
break
|
||||
case Device_Qualifier::Type:
|
||||
//if idx != 0:
|
||||
// return false
|
||||
//ptr = reinterpret_cast <char const *> (&device_qualifier_descriptor)
|
||||
//size = (len < sizeof (device_qualifier_descriptor) ? len : sizeof (device_qualifier_descriptor))
|
||||
//break
|
||||
return false
|
||||
// The 6 is an arbitrary number, except that String <6> is instantiated already.
|
||||
case String <6>::Type:
|
||||
switch idx:
|
||||
case 0:
|
||||
ptr = reinterpret_cast <char const *> (&s_langs)
|
||||
size = (len < sizeof (s_langs) ? len : sizeof (s_langs))
|
||||
break
|
||||
case 1:
|
||||
ptr = reinterpret_cast <char const *> (&s_manufacturer)
|
||||
size = (len < sizeof (s_manufacturer) ? len : sizeof (s_manufacturer))
|
||||
break
|
||||
case 2:
|
||||
ptr = reinterpret_cast <char const *> (&s_product)
|
||||
size = (len < sizeof (s_product) ? len : sizeof (s_product))
|
||||
break
|
||||
default:
|
||||
return false
|
||||
break
|
||||
default:
|
||||
return false
|
||||
state = TX
|
||||
return true
|
||||
|
||||
bool Udc::handle_setup (Setup *s, unsigned cmd):
|
||||
switch s->request_type:
|
||||
case STANDARD_TO_DEVICE:
|
||||
switch s->request:
|
||||
case SET_ADDRESS:
|
||||
UDC_FADDR = s->value
|
||||
break
|
||||
case SET_CONFIGURATION:
|
||||
if s->value >= 2:
|
||||
return false
|
||||
configuration = s->value
|
||||
break
|
||||
case SET_INTERFACE:
|
||||
if s->value != 0:
|
||||
return false
|
||||
break
|
||||
default:
|
||||
return false
|
||||
UDC_OUTMAXP = 64
|
||||
UDC_OUTCSR = UDC_OUTCSR_CDT | UDC_OUTCSR_FF
|
||||
UDC_OUTCSR = UDC_OUTCSR_CDT | UDC_OUTCSR_FF
|
||||
break
|
||||
case STANDARD_FROM_DEVICE:
|
||||
switch s->request:
|
||||
case GET_STATUS:
|
||||
ptr = "\0\0"
|
||||
size = (s->length < 2 ? s->length : 2)
|
||||
state = TX
|
||||
break
|
||||
case GET_DESCRIPTOR:
|
||||
return get_descriptor ((s->value >> 8) & 0xff, s->value & 0xff, s->length)
|
||||
case GET_CONFIGURATION:
|
||||
ptr = &configuration
|
||||
size = (s->length < 1 ? s->length : 1)
|
||||
state = TX
|
||||
break
|
||||
case GET_INTERFACE:
|
||||
ptr = "\0"
|
||||
size = (s->length < 1 ? s->length : 1)
|
||||
state = TX
|
||||
break
|
||||
default:
|
||||
return false
|
||||
break
|
||||
case VENDOR_TO_DEVICE:
|
||||
case VENDOR_FROM_DEVICE:
|
||||
return vendor (s, cmd)
|
||||
default:
|
||||
return false
|
||||
return true
|
||||
|
||||
void Udc::irq_usb (unsigned cmd):
|
||||
// Reset.
|
||||
state = IDLE
|
||||
|
||||
void Udc::irq_in (unsigned cmd):
|
||||
// Interrupt on endpoint 0.
|
||||
unsigned csr = UDC_CSR0
|
||||
if csr & UDC_CSR0_SENTSTALL:
|
||||
csr &= ~(UDC_CSR0_SENTSTALL | UDC_CSR0_SENDSTALL)
|
||||
state = IDLE
|
||||
if csr & UDC_CSR0_SETUPEND:
|
||||
csr |= UDC_CSR0_SVDSETUPEND
|
||||
state = IDLE
|
||||
switch state:
|
||||
case IDLE:
|
||||
if rebooting:
|
||||
Iris::reboot ()
|
||||
if !(csr & UDC_CSR0_OUTPKTRDY):
|
||||
return
|
||||
union { unsigned d[2]; Setup s; } packet
|
||||
packet.d[0] = UDC_FIFO (0)
|
||||
packet.d[1] = UDC_FIFO (0)
|
||||
if !(packet.s.request_type & 0x80) && packet.s.length > 0:
|
||||
// More data will follow; delay handling of packet.
|
||||
state = RX
|
||||
UDC_CSR0 = csr | UDC_CSR0_SVDOUTPKTRDY
|
||||
rx_request = packet.s.request
|
||||
return
|
||||
UDC_CSR0 = csr | UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY
|
||||
if !handle_setup (&packet.s, cmd):
|
||||
csr |= UDC_CSR0_SENDSTALL
|
||||
break
|
||||
if size == 0:
|
||||
return
|
||||
// Fall through.
|
||||
case TX:
|
||||
unsigned i
|
||||
for i = 0; (size & ~3) > 0 && i < max_packet_size0; i += 4, size -= 4:
|
||||
UDC_FIFO (0) = *(unsigned *)ptr
|
||||
ptr += 4
|
||||
for ; size > 0 && i < max_packet_size0; ++i, --size:
|
||||
UDC_FIFO8 (0) = *ptr++
|
||||
if i == max_packet_size0:
|
||||
csr |= UDC_CSR0_INPKTRDY
|
||||
else:
|
||||
state = IDLE
|
||||
csr |= UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND
|
||||
break
|
||||
case RX:
|
||||
// The protocol that is used doesn't allow large packets, so being here always means the entire packet is received.
|
||||
switch rx_request & 0xff:
|
||||
case Iris::Directory::GET_SIZE & 0xff:
|
||||
if !have_caller:
|
||||
kdebug ("received dir size from server without a caller waiting\n")
|
||||
Iris::panic (0)
|
||||
unsigned size_l = UDC_FIFO (0)
|
||||
unsigned size_h = UDC_FIFO (0)
|
||||
caller.invoke (Iris::Num (size_l, size_h))
|
||||
Iris::free_cap (caller)
|
||||
Iris::free_cap (caller_arg)
|
||||
have_caller = false
|
||||
//kdebug ("get_size response\n")
|
||||
break
|
||||
case Iris::Directory::GET_NAME & 0xff:
|
||||
if !have_caller:
|
||||
kdebug ("received filename from server without a caller waiting\n")
|
||||
Iris::panic (0)
|
||||
unsigned n[4]
|
||||
for unsigned i = 0; i < 4; ++i:
|
||||
n[i] = UDC_FIFO (0)
|
||||
caller.invoke (Iris::Num (n[0], n[1]), Iris::Num (n[2], n[3]))
|
||||
Iris::free_cap (caller)
|
||||
Iris::free_cap (caller_arg)
|
||||
//kdebug ("get_name response\n")
|
||||
have_caller = false
|
||||
break
|
||||
case Iris::String::GET_SIZE & 0xff:
|
||||
if !have_caller:
|
||||
kdebug ("received string size from server without a caller waiting\n")
|
||||
Iris::panic (0)
|
||||
unsigned size_l = UDC_FIFO (0)
|
||||
unsigned size_h = UDC_FIFO (0)
|
||||
caller.invoke (Iris::Num (size_l, size_h))
|
||||
Iris::free_cap (caller)
|
||||
Iris::free_cap (caller_arg)
|
||||
have_caller = false
|
||||
//kdebug ("get_filesize response\n")
|
||||
break
|
||||
case Iris::String::GET_CHARS & 0xff:
|
||||
if !have_caller:
|
||||
kdebug ("received string char data from server without a caller waiting\n")
|
||||
Iris::panic (0)
|
||||
unsigned n[4]
|
||||
for unsigned i = 0; i < 4; ++i:
|
||||
n[i] = UDC_FIFO (0)
|
||||
caller.invoke (Iris::Num (n[0], n[1]), Iris::Num (n[2], n[3]))
|
||||
Iris::free_cap (caller)
|
||||
Iris::free_cap (caller_arg)
|
||||
have_caller = false
|
||||
//kdebug ("get_chars response\n")
|
||||
break
|
||||
default:
|
||||
kdebug ("invalid vendor request: ")
|
||||
kdebug_num (rx_request)
|
||||
kdebug ("\n")
|
||||
Iris::panic (0)
|
||||
UDC_CSR0 = csr | UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY
|
||||
state = IDLE
|
||||
break
|
||||
UDC_CSR0 = csr
|
||||
|
||||
void Udc::irq_out (unsigned cmd):
|
||||
// Interrupt on OUT endpoint 1.
|
||||
UDC_INDEX = 1
|
||||
if !have_caller:
|
||||
kdebug ("received bulk data from server without a caller waiting\n")
|
||||
Iris::panic (0)
|
||||
unsigned size = UDC_OUTCOUNT
|
||||
unsigned csr = UDC_OUTCSR
|
||||
//kdebug ("handling bulk interrupt for ")
|
||||
//kdebug_num (csr)
|
||||
//kdebug (" with ")
|
||||
//kdebug_num (size)
|
||||
//kdebug (" bytes.\n")
|
||||
csr &= ~UDC_OUTCSR_OUTPKTRDY
|
||||
for unsigned i = 0; i < size; i += 4:
|
||||
*p++ = UDC_FIFO (1)
|
||||
if p - page == PAGE_SIZE >> 2:
|
||||
buffer_page.share (caller_arg, Iris::Page::FORGET)
|
||||
buffer_page.set_flags (Iris::Page::FRAME, Iris::Page::FRAME)
|
||||
caller.invoke ()
|
||||
Iris::free_cap (caller)
|
||||
Iris::free_cap (caller_arg)
|
||||
have_caller = false
|
||||
//kdebug ("bulk response\n")
|
||||
p = page
|
||||
UDC_OUTCSR = csr
|
||||
UDC_INDEX = 0
|
||||
|
||||
void Udc::interrupt (unsigned cmd):
|
||||
while true:
|
||||
unsigned usb = UDC_INTRUSB
|
||||
unsigned in = UDC_INTRIN
|
||||
unsigned out = UDC_INTROUT
|
||||
if !(usb & 4) && !(in & 1) && !(out & 2):
|
||||
break
|
||||
//kdebug ("interrupt: ")
|
||||
//kdebug_num (usb, 1)
|
||||
//kdebug ("/")
|
||||
//kdebug_num (in, 1)
|
||||
//kdebug ("/")
|
||||
//kdebug_num (out, 1)
|
||||
//kdebug ("\n")
|
||||
if usb & 4:
|
||||
irq_usb (cmd)
|
||||
if in & 1:
|
||||
irq_in (cmd)
|
||||
if out & 2:
|
||||
irq_out (cmd)
|
||||
|
||||
void Udc::log (unsigned c):
|
||||
if log_buffer_size >= sizeof (log_buffer):
|
||||
return
|
||||
log_buffer[log_buffer_size++] = c
|
||||
|
||||
enum pdata:
|
||||
LOG = 32
|
||||
FS
|
||||
DATA
|
||||
DIRECTORY
|
||||
FILE
|
||||
NAME
|
||||
|
||||
Iris::Num start ():
|
||||
map_udc ()
|
||||
map_gpio ()
|
||||
map_cpm ()
|
||||
Udc udc
|
||||
|
||||
Iris::Cap logcap = Iris::my_receiver.create_capability (LOG)
|
||||
__asm__ volatile ("li $a0, 1\nlw $a1, %0\nbreak" :: "m"(logcap.code): "a0", "a1", "memory")
|
||||
udc.init ()
|
||||
Iris::register_interrupt (IRQ_UDC)
|
||||
Iris::Filesystem fs = Iris::my_receiver.create_capability (FS)
|
||||
Iris::String data = Iris::my_receiver.create_capability (DATA)
|
||||
Iris::my_parent.provide_capability <Iris::Filesystem> (fs.copy ())
|
||||
Iris::my_parent.provide_capability <Iris::String> (data.copy ())
|
||||
Iris::free_cap (fs)
|
||||
Iris::free_cap (data)
|
||||
unsigned state = 0
|
||||
while true:
|
||||
Iris::wait ()
|
||||
Iris::Cap reply = Iris::get_reply ()
|
||||
Iris::Cap arg = Iris::get_arg ()
|
||||
switch Iris::recv.protected_data.l:
|
||||
case IRQ_UDC:
|
||||
udc.interrupt (state)
|
||||
Iris::register_interrupt (IRQ_UDC)
|
||||
break
|
||||
case LOG:
|
||||
udc.log (Iris::recv.data[0].l)
|
||||
break
|
||||
case DATA:
|
||||
//kdebug ("data request\n")
|
||||
switch Iris::recv.data[0].l:
|
||||
case Iris::Device::RESET:
|
||||
case Iris::String::GET_SIZE:
|
||||
case Iris::String::GET_CHARS:
|
||||
reply.invoke (0)
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (arg)
|
||||
continue
|
||||
case Iris::String::GET_BLOCK:
|
||||
default:
|
||||
reply.invoke (Iris::ERR_INVALID_OPERATION)
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (arg)
|
||||
continue
|
||||
break
|
||||
case FS:
|
||||
//kdebug ("fs request\n")
|
||||
switch Iris::recv.data[0].l:
|
||||
case Iris::Device::RESET:
|
||||
reply.invoke (0)
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (arg)
|
||||
continue
|
||||
case Iris::Filesystem::USE_DEVICE:
|
||||
case Iris::Filesystem::USE_DEVICE_RO:
|
||||
Iris::Directory dir = Iris::my_receiver.create_capability (DIRECTORY)
|
||||
reply.invoke (0, 0, dir.copy ())
|
||||
Iris::free_cap (dir)
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (arg)
|
||||
continue
|
||||
default:
|
||||
reply.invoke (Iris::ERR_INVALID_OPERATION)
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (arg)
|
||||
continue
|
||||
break
|
||||
case DIRECTORY:
|
||||
//kdebug ("dir request\n")
|
||||
switch Iris::recv.data[0].l:
|
||||
case Iris::Directory::GET_NAME:
|
||||
Iris::Cap name = Iris::my_receiver.create_capability (Iris::Num (NAME, Iris::recv.data[1].l))
|
||||
reply.invoke (0, 0, name.copy ())
|
||||
Iris::free_cap (name)
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (arg)
|
||||
continue
|
||||
case Iris::Directory::GET_SIZE:
|
||||
case Iris::Directory::LOCK_RO:
|
||||
case Iris::Directory::UNLOCK_RO:
|
||||
state = Iris::recv.data[0].l
|
||||
if Iris::recv.data[1].h != 0:
|
||||
kdebug ("index out of supported range\n")
|
||||
Iris::panic (0)
|
||||
udc.send (Iris::recv.data[0].l, Iris::recv.data[1].l, reply, arg)
|
||||
continue
|
||||
case Iris::Directory::GET_FILE_RO:
|
||||
if Iris::recv.data[1].h != 0:
|
||||
kdebug ("index out of supported range\n")
|
||||
Iris::panic (0)
|
||||
//kdebug ("sending file\n")
|
||||
Iris::Cap file = Iris::my_receiver.create_capability (Iris::Num (FILE, Iris::recv.data[1].l))
|
||||
reply.invoke (0, 0, file.copy ())
|
||||
Iris::free_cap (file)
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (arg)
|
||||
continue
|
||||
case Iris::Directory::GET_FILE_INFO:
|
||||
default:
|
||||
reply.invoke (Iris::ERR_INVALID_OPERATION)
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (arg)
|
||||
continue
|
||||
break
|
||||
case FILE:
|
||||
//kdebug ("file request\n")
|
||||
switch Iris::recv.data[0].l:
|
||||
case Iris::String::GET_BLOCK:
|
||||
if Iris::recv.data[0].h != PAGE_SIZE << 16:
|
||||
Iris::panic (0, "unsupported get_block arguments for boot usb device driver")
|
||||
// Fall through.
|
||||
case Iris::String::GET_SIZE:
|
||||
case Iris::String::GET_CHARS:
|
||||
udc.send (Iris::recv.data[0].l | ((Iris::recv.data[1].l >> PAGE_BITS) << 16), Iris::recv.protected_data.h, reply, arg)
|
||||
continue
|
||||
default:
|
||||
reply.invoke (Iris::ERR_INVALID_OPERATION)
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (arg)
|
||||
continue
|
||||
break
|
||||
case NAME:
|
||||
//kdebug ("name request\n")
|
||||
switch Iris::recv.data[0].l:
|
||||
case Iris::String::GET_SIZE:
|
||||
reply.invoke (16)
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (arg)
|
||||
continue
|
||||
case Iris::String::GET_CHARS:
|
||||
state = Iris::recv.data[0].l
|
||||
udc.send (Iris::Directory::GET_NAME, Iris::recv.protected_data.h, reply, arg)
|
||||
continue
|
||||
default:
|
||||
reply.invoke (Iris::ERR_INVALID_OPERATION)
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (arg)
|
||||
continue
|
||||
default:
|
||||
kdebug ("other request:")
|
||||
kdebug_num (Iris::recv.protected_data.l)
|
||||
kdebug ("\n")
|
||||
udc.log ('~')
|
||||
char digit[] = "0123456789abcdef"
|
||||
for unsigned i = 0; i < 8; ++i:
|
||||
udc.log (digit[(Iris::recv.protected_data.l >> (4 * (7 - i))) & 0xf])
|
||||
udc.log ('\n')
|
||||
break
|
||||
reply.invoke ()
|
||||
Iris::free_cap (reply)
|
||||
Iris::free_cap (arg)
|
||||
Reference in New Issue
Block a user