libgui 2.01
libgui is a GUI library for the GameCube, Wii, and Wii U
 
Loading...
Searching...
No Matches
WiiUsbMulti.cpp
1/****************************************************************************
2 * Platform Abstraction Layer (OGC driver)
3 * Daryl Borth 2026
4 * WiiUsbMulti.cpp
5 ***************************************************************************/
6#include <string.h>
7
8#define LIBOGC_INTERNAL
9#include <ogc/disc_io.h>
10#undef LIBOGC_INTERNAL
11
12#include <ogc/usb.h>
13#include <ogc/usbstorage.h>
14
15#include "WiiUsbMulti.h"
16#include "../../Mutex.h"
17
18// Not exported by ogc/usb.h - standard USB mass-storage class code (0x08),
19// matches the private #define of the same name in libogc2's usbstorage.c.
20#define USB_CLASS_MASS_STORAGE 0x08
21#define USB_DEVLIST_MAXSIZE 8
22#define DEVICE_TYPE_WII_USB_MULTI (('W'<<24)|('U'<<16)|('S'<<8)|'M')
23
24namespace
25{
26 struct UsbSlot
27 {
28 usbstorage_handle handle;
29 u8 lun;
30 s32 deviceId;
31 u16 vid, pid;
32 bool open;
33 DISC_INTERFACE disc;
34 };
35
36 UsbSlot slots[WII_USB_MAX_DEVICES];
37
46 Mutex & SlotLock()
47 {
48 static Mutex lock;
49 return lock;
50 }
51
52 template<int N> bool Startup(DISC_INTERFACE *) { MutexLock g(SlotLock()); return slots[N].open; }
53 template<int N> bool IsInserted(DISC_INTERFACE *) { MutexLock g(SlotLock()); return slots[N].open; }
54
55 template<int N> bool ReadSectors(DISC_INTERFACE *, sec_t sector, sec_t numSectors, void * buffer)
56 {
57 MutexLock g(SlotLock());
58 if(!slots[N].open)
59 return false;
60 return USBStorage_Read(&slots[N].handle, slots[N].lun, sector, numSectors, (u8 *)buffer) >= 0;
61 }
62
63 template<int N> bool WriteSectors(DISC_INTERFACE *, sec_t sector, sec_t numSectors, const void * buffer)
64 {
65 MutexLock g(SlotLock());
66 if(!slots[N].open)
67 return false;
68 return USBStorage_Write(&slots[N].handle, slots[N].lun, sector, numSectors, (const u8 *)buffer) >= 0;
69 }
70
71 template<int N> bool EraseSectors(DISC_INTERFACE *, sec_t, sec_t) { return false; }
72 template<int N> bool Flush(DISC_INTERFACE *) { return true; }
73
78 template<int N> bool Shutdown(DISC_INTERFACE *) { return true; }
79
82 template<int N> void InitSlotInterface()
83 {
84 slots[N].disc = DISC_INTERFACE
85 {
86 DEVICE_TYPE_WII_USB_MULTI,
87 FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB,
88 Startup<N>,
89 IsInserted<N>,
90 ReadSectors<N>,
91 WriteSectors<N>,
92 EraseSectors<N>,
93 Flush<N>,
94 Shutdown<N>,
95 0, 0, 0
96 };
97 }
98
101 bool SlotOpen(int i, s32 deviceId, u16 vid, u16 pid)
102 {
103 UsbSlot & s = slots[i];
104 memset(&s.handle, 0, sizeof(s.handle));
105
106 s32 openResult = USBStorage_Open(&s.handle, deviceId, vid, pid);
107 if(openResult < 0)
108 {
109 return false;
110 }
111
112 s32 maxLun = USBStorage_GetMaxLUN(&s.handle);
113
114 for(s32 lun = 0; lun < maxLun; lun++)
115 {
116 s32 retval = USBStorage_MountLUN(&s.handle, (u8)lun);
117 if(retval < 0)
118 {
119 // Matches __io_usbstorage's own recovery step - a comms error on this LUN
120 // shouldn't be left in a wedged state before trying the next one.
121 USBStorage_Reset(&s.handle);
122 continue;
123 }
124
125 s.lun = (u8)lun;
126 s.deviceId = deviceId;
127 s.vid = vid;
128 s.pid = pid;
129 s.open = true;
130
131 s.disc.numberOfSectors = s.handle.n_sectors[lun];
132 s.disc.bytesPerSector = s.handle.sector_size[lun];
133 return true;
134 }
135
136 USBStorage_Close(&s.handle);
137 return false;
138 }
139}
140
141void WiiUsbMulti::init()
142{
143 InitSlotInterface<0>();
144 InitSlotInterface<1>();
145 InitSlotInterface<2>();
146
147 for(auto & s : slots)
148 {
149 s.open = false;
150 s.deviceId = -1;
151 s.vid = s.pid = 0;
152 }
153}
154
155void WiiUsbMulti::shutdown()
156{
157 MutexLock g(SlotLock());
158
159 for(auto & s : slots)
160 {
161 if(s.open)
162 USBStorage_Close(&s.handle);
163 s.open = false;
164 }
165}
166
167bool WiiUsbMulti::scan()
168{
169 // USB_GetDeviceList() goes over IOS IPC, which requires a 32-byte cache-aligned buffer
170 alignas(32) usb_device_entry buffer[USB_DEVLIST_MAXSIZE];
171 u8 deviceCount = 0;
172 bool changed = false;
173
174 MutexLock g(SlotLock());
175
176 if(USB_GetDeviceList(buffer, USB_DEVLIST_MAXSIZE, USB_CLASS_MASS_STORAGE, &deviceCount) < 0)
177 deviceCount = 0;
178
179 // Drop any open slot whose device_id is no longer in the current list.
180 for(auto & s : slots)
181 {
182 if(!s.open)
183 continue;
184
185 bool stillPresent = false;
186 for(u8 j = 0; j < deviceCount; j++)
187 {
188 if(buffer[j].device_id == s.deviceId)
189 {
190 stillPresent = true;
191 break;
192 }
193 }
194
195 if(!stillPresent)
196 {
197 USBStorage_Close(&s.handle);
198 s.open = false;
199 s.deviceId = -1;
200 s.vid = s.pid = 0;
201 changed = true;
202 }
203 }
204
205 // Assign newly-seen devices to free slots, in USB_GetDeviceList() order
206 for(u8 j = 0; j < deviceCount; j++)
207 {
208 u16 vid = buffer[j].vid;
209 u16 pid = buffer[j].pid;
210 if(vid == 0 || pid == 0)
211 continue;
212
213 bool alreadyOpen = false;
214 for(auto & s : slots)
215 {
216 if(s.open && s.deviceId == buffer[j].device_id)
217 {
218 alreadyOpen = true;
219 break;
220 }
221 }
222 if(alreadyOpen)
223 continue;
224
225 int freeSlot = -1;
226 for(int i = 0; i < WII_USB_MAX_DEVICES; i++)
227 {
228 if(!slots[i].open)
229 {
230 freeSlot = i;
231 break;
232 }
233 }
234 if(freeSlot < 0)
235 break; // all slots full - additional devices are ignored
236
237 if(SlotOpen(freeSlot, buffer[j].device_id, vid, pid))
238 changed = true;
239 }
240
241 return changed;
242}
243
244DISC_INTERFACE * WiiUsbMulti::getInterface(int slot)
245{
246 if(slot < 0 || slot >= WII_USB_MAX_DEVICES)
247 return nullptr;
248 return &slots[slot].disc;
249}
RAII lock guard - locks on construction, unlocks on destruction.
Definition Mutex.h:37
A simple mutual-exclusion lock.
Definition Mutex.h:14