Skip to content

Commit d9c2659

Browse files
authored
Merge pull request #17 from OrangeFox86/16-implement-usb-cdc-device-as-debug-device-instead-of-onboard-uart
16 implement usb cdc device as debug device
2 parents 29226cd + 839679e commit d9c2659

20 files changed

Lines changed: 432 additions & 32 deletions

inc/hal/System/LockGuard.hpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#ifndef __LOCK_GUARD_H__
2+
#define __LOCK_GUARD_H__
3+
4+
#include <stdint.h>
5+
#include "MutexInterface.hpp"
6+
7+
//! This class is similar to std::lock_guard, but it is specifically meant to be used with
8+
//! MutexInterface. The special thing with MutexInterface is that doing a blocking lock has the
9+
//! potential to cause a deadlock (because spin lock is used, and there is no way to yield from IRQ
10+
//! context). As such, the functionality using the mutex must check this and perform necessary
11+
//! operations (even if it's just to log an error and spin forever).
12+
class LockGuard
13+
{
14+
public:
15+
LockGuard() = delete;
16+
17+
//! Initializes data and locks mutex as long as it wouldn't cause a deadlock
18+
inline LockGuard(MutexInterface& mutex) :
19+
mMutex(mutex),
20+
mIsLocked(false)
21+
{
22+
int8_t lockValue = mMutex.tryLock();
23+
if (lockValue == 0)
24+
{
25+
mMutex.lock();
26+
mIsLocked = true;
27+
}
28+
else if (lockValue > 0)
29+
{
30+
mIsLocked = true;
31+
}
32+
// Else: not locked, would cause deadlock
33+
}
34+
35+
//! Unlocks mutex if it was previously locked
36+
virtual inline ~LockGuard()
37+
{
38+
if (mIsLocked)
39+
{
40+
mMutex.unlock();
41+
}
42+
}
43+
44+
//! @returns true iff the mutex was successfully locked in the constructor or false if
45+
//! locking would cause a deadlock
46+
inline bool isLocked()
47+
{
48+
return mIsLocked;
49+
}
50+
51+
private:
52+
MutexInterface& mMutex;
53+
bool mIsLocked;
54+
};
55+
56+
#endif // __LOCK_GUARD_H__

inc/hal/System/MutexInterface.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef __MUTEX_INTERFACE_H__
22
#define __MUTEX_INTERFACE_H__
33

4+
#include <stdint.h>
5+
46
//! This interface is used to decouple mutex functionality in HAL from the Dreamcast functionality.
57
class MutexInterface
68
{
@@ -16,6 +18,12 @@ class MutexInterface
1618

1719
//! Releases the previously locked mutex
1820
virtual void unlock() = 0;
21+
22+
//! Tries to obtain mutex without blocking
23+
//! @returns 1 if mutex was obtained
24+
//! @returns 0 if mutex was not obtained, and blocking would be valid in this context
25+
//! @returns -1 if mutex was not obtained, and blocking would cause deadlock
26+
virtual int8_t tryLock() = 0;
1927
};
2028

2129
#endif // __MUTEX_INTERFACE_H__

inc/hal/Usb/usb_interface.hpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//! @returns array of the USB controller observers
99
DreamcastControllerObserver** get_usb_controller_observers();
1010
//! USB initialization
11-
void usb_init();
11+
void usb_init(MutexInterface* mscMutex, MutexInterface* cdcMutex);
1212
//! USB task that needs to be called constantly by main()
1313
void usb_task();
1414
//! @returns number of USB controllers
@@ -17,8 +17,5 @@ uint32_t get_num_usb_controllers();
1717
//! Must return the file system
1818
UsbFileSystem& usb_msc_get_file_system();
1919

20-
//! Set mutex to use between the above two calls and file access
21-
void usb_msc_set_mutex(MutexInterface* mutex);
22-
2320

2421
#endif // __USB_INTERFACE_H__

src/coreLib/ScreenData.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#include "ScreenData.hpp"
22
#include <string.h>
33
#include <assert.h>
4-
#include <mutex>
4+
#include "hal/System/LockGuard.hpp"
5+
#include "utils.h"
56

67
ScreenData::ScreenData(MutexInterface& mutex) :
78
mMutex(mutex),
@@ -21,9 +22,16 @@ ScreenData::ScreenData(MutexInterface& mutex) :
2122
void ScreenData::setData(uint32_t* data, uint32_t startIndex, uint32_t numWords)
2223
{
2324
assert(startIndex + numWords <= sizeof(mScreenData));
24-
std::lock_guard<MutexInterface> lockGuard(mMutex);
25-
memcpy(mScreenData + startIndex, data, numWords);
26-
mNewDataAvailable = true;
25+
LockGuard lockGuard(mMutex);
26+
if (lockGuard.isLocked())
27+
{
28+
memcpy(mScreenData + startIndex, data, numWords);
29+
mNewDataAvailable = true;
30+
}
31+
else
32+
{
33+
DEBUG_PRINT("FAULT: failed to set screen data\n");
34+
}
2735
}
2836

2937
bool ScreenData::isNewDataAvailable() const
@@ -33,7 +41,15 @@ bool ScreenData::isNewDataAvailable() const
3341

3442
void ScreenData::readData(uint32_t* out)
3543
{
36-
std::lock_guard<MutexInterface> lockGuard(mMutex);
37-
mNewDataAvailable = false;
44+
LockGuard lockGuard(mMutex);
45+
if (lockGuard.isLocked())
46+
{
47+
mNewDataAvailable = false;
48+
}
49+
else
50+
{
51+
DEBUG_PRINT("FAULT: failed to set screen data\n");
52+
}
53+
// Allow this to happen, even if locking failed
3854
memcpy(out, mScreenData, sizeof(mScreenData));
3955
}

src/coreLib/ScreenData.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class ScreenData
99
{
1010
public:
1111
//! Constructor
12-
//! @param[in] mutex Reference to the mutex to use
12+
//! @param[in] mutex Reference to the mutex to use (critical section mutex recommended)
1313
ScreenData(MutexInterface& mutex);
1414

1515
//! Set the screen bits

src/hal/System/CriticalSectionMutex.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,10 @@ void CriticalSectionMutex::unlock()
1616
{
1717
critical_section_exit(&mCriticalSection);
1818
}
19+
20+
int8_t CriticalSectionMutex::tryLock()
21+
{
22+
// "try" isn't valid for critical section mutex, but taking will never cause deadlock since
23+
// interrupts are disabled as part of the locking mechanism.
24+
return 0;
25+
}

src/hal/System/CriticalSectionMutex.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44
#include "hal/System/MutexInterface.hpp"
55
#include "pico/critical_section.h"
66

7+
// From raspberry-pi-pico-c-sdk.pdf:
8+
// Critical Section API for short-lived mutual exclusion safe for IRQ and multi-core.
9+
// A critical section is non-reentrant, and provides mutual exclusion using a spin-lock to prevent
10+
// access from the other core, and from (higher priority) interrupts on the same core. It does the
11+
// former using a spin lock and the latter by disabling interrupts on the calling core. Because
12+
// interrupts are disabled when a critical_section is owned, uses of the critical_section should be
13+
// as short as possible.
714
class CriticalSectionMutex : public MutexInterface
815
{
916
public:
@@ -15,6 +22,8 @@ class CriticalSectionMutex : public MutexInterface
1522

1623
virtual void unlock() final;
1724

25+
virtual int8_t tryLock() final;
26+
1827
private:
1928
critical_section_t mCriticalSection;
2029
};

src/hal/System/Mutex.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#include "Mutex.hpp"
2+
3+
Mutex::Mutex()
4+
{
5+
mutex_init(&mMutex);
6+
}
7+
8+
Mutex::~Mutex() {}
9+
10+
void Mutex::lock()
11+
{
12+
mutex_enter_blocking(&mMutex);
13+
}
14+
15+
void Mutex::unlock()
16+
{
17+
mutex_exit(&mMutex);
18+
}
19+
20+
int8_t Mutex::tryLock()
21+
{
22+
uint32_t owner;
23+
if (!mutex_try_enter(&mMutex, &owner))
24+
{
25+
if (owner == get_core_num())
26+
{
27+
return -1; // would deadlock
28+
}
29+
else
30+
{
31+
return 0;
32+
}
33+
}
34+
return 1;
35+
}

src/hal/System/Mutex.hpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#ifndef __MUTEX_H__
2+
#define __MUTEX_H__
3+
4+
#include "hal/System/MutexInterface.hpp"
5+
#include "pico/mutex.h"
6+
7+
// From raspberry-pi-pico-c-sdk.pdf:
8+
// Mutex API for non IRQ mutual exclusion between cores.
9+
// Unlike critical sections, the mutex protected code is not necessarily required/expected to
10+
// complete quickly as no other sytem wide locks are held on account of an acquired mutex.
11+
class Mutex : public MutexInterface
12+
{
13+
public:
14+
Mutex();
15+
16+
virtual ~Mutex();
17+
18+
virtual void lock() final;
19+
20+
virtual void unlock() final;
21+
22+
virtual int8_t tryLock() final;
23+
24+
private:
25+
mutex_t mMutex;
26+
};
27+
28+
#endif // __MUTEX_H__

0 commit comments

Comments
 (0)