うおーるぼっとをWiiリモコンでコントロールする新しいプログラムです。 以前のものより、Wiiリモコンが早く繋がる様になりました。 It is a program which controls A with the Wii remote. ※ A Bluetooth dongle and a Wii remote control are needed.
Dependencies: USBHost mbed FATFileSystem mbed-rtos
Revision 0:fccb789424fc, committed 2013-06-10
- Comitter:
- jksoft
- Date:
- Mon Jun 10 16:01:50 2013 +0000
- Commit message:
- 1.0
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TB6612FNG2/TB6612.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,55 @@ +/** + * Motor Driver TB6612 Control Library + * + * -- TB6612 is a device of the TOSHIBA. + * + * Copyright (C) 2012 Junichi Katsu (JKSOFT) + */ + + +#include "TB6612.h" + +// TB6612 Class Constructor +TB6612::TB6612(PinName pwm, PinName fwd, PinName rev): + _pwm(pwm), _fwd(fwd), _rev(rev) { + + _fwd = 0; + _rev = 0; + _pwm = 0.0; + _pwm.period(0.001); +} + +// Speed Control +// arg +// int speed -100 -- 0 -- 100 +void TB6612::speed(int speed) { + + if( speed > 0 ) + { + _pwm = ((float)speed) / 100.0; + _fwd = 1; + _rev = 0; + } + else if( speed < 0 ) + { + _pwm = -((float)speed) / 100.0; + _fwd = 0; + _rev = 1; + } + else + { + _fwd = 1; + _rev = 1; + } +} + + +// Speed Control with time-out +// arg +// int speed -100 -- 0 -- 100 +// int time 0 +void TB6612::move(int sspeed , int time) +{ + speed(sspeed); + wait_ms(time); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TB6612FNG2/TB6612.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,30 @@ +/** + * Motor Driver TB6612 Control Library + * + * -- TB6612 is a device of the rohm. + * + * Copyright (C) 2012 Junichi Katsu (JKSOFT) + */ + +#ifndef MBED_TB6612_H +#define MBED_TB6612_H + +#include "mbed.h" + +class TB6612 { +public: + TB6612(PinName pwm, PinName fwd, PinName rev); + void speed(int speed); + void move(int speed , int time); + void operator= ( int value ) + { + speed(value); + } + +protected: + PwmOut _pwm; + DigitalOut _fwd; + DigitalOut _rev; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost.lib Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/USBHost/#0c11cf1cc218
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/FATFileSystem.lib Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/FATFileSystem/#b6669c987c8e
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/FATFileSystem/ChaN/ccsbcs.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,540 @@ +/*------------------------------------------------------------------------*/ +/* Unicode - Local code bidirectional converter (C)ChaN, 2009 */ +/* (SBCS code pages) */ +/*------------------------------------------------------------------------*/ +/* 437 U.S. (OEM) +/ 720 Arabic (OEM) +/ 1256 Arabic (Windows) +/ 737 Greek (OEM) +/ 1253 Greek (Windows) +/ 1250 Central Europe (Windows) +/ 775 Baltic (OEM) +/ 1257 Baltic (Windows) +/ 850 Multilingual Latin 1 (OEM) +/ 852 Latin 2 (OEM) +/ 1252 Latin 1 (Windows) +/ 855 Cyrillic (OEM) +/ 1251 Cyrillic (Windows) +/ 866 Russian (OEM) +/ 857 Turkish (OEM) +/ 1254 Turkish (Windows) +/ 858 Multilingual Latin 1 + Euro (OEM) +/ 862 Hebrew (OEM) +/ 1255 Hebrew (Windows) +/ 874 Thai (OEM, Windows) +/ 1258 Vietnam (OEM, Windows) +*/ + +#include "ff.h" + + +#if _CODE_PAGE == 437 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP437(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, + 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 720 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP720(0x80-0xFF) to Unicode conversion table */ + 0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, + 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627, + 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, + 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, + 0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0xO650, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 737 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP737(0x80-0xFF) to Unicode conversion table */ + 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, + 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, + 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, + 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, + 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, + 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, + 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E, + 0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 775 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP775(0x80-0xFF) to Unicode conversion table */ + 0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, + 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, + 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4, + 0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, + 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, + 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D, + 0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, + 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, + 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019, + 0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, + 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 850 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP850(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, + 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, + 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, + 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 852 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP852(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, + 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106, + 0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, + 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, + 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, + 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, + 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, + 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4, + 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 855 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP855(0x80-0xFF) to Unicode conversion table */ + 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, + 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408, + 0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, + 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A, + 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, + 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, + 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, + 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580, + 0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, + 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116, + 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, + 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 857 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP857(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, + 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, + 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, + 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, + 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 858 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP858(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, + 0x00A9, 0x2563, 0x2551, 0x2557, 0x2550, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE, + 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00C6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, + 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 862 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP862(0x80-0xFF) to Unicode conversion table */ + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, + 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, + 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, + 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 866 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP866(0x80-0xFF) to Unicode conversion table */ + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 874 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP874(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x2026, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07, + 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F, + 0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17, + 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F, + 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27, + 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F, + 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, + 0x0E38, 0x0E39, 0x0E3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0E3F, + 0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, + 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F, + 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, + 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +#elif _CODE_PAGE == 1250 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1250(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, + 0x0000, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A, + 0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B, + 0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C, + 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, + 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, + 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, + 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, + 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, + 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, + 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, + 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9 +}; + +#elif _CODE_PAGE == 1251 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1251(0x80-0xFF) to Unicode conversion table */ + 0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, + 0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F, + 0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2111, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F, + 0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, + 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407, + 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, + 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F +}; + +#elif _CODE_PAGE == 1252 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1252(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF +}; + +#elif _CODE_PAGE == 1253 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1253(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x0000, 0x2030, 0x0000, 0x2039, 0x000C, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x0000, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x2015, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7, + 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F, + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, + 0x03A0, 0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, + 0x03A8, 0x03A9, 0x03AA, 0x03AD, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, + 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x0000 +}; + +#elif _CODE_PAGE == 1254 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1254(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x210A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00BD, 0x00DC, 0x0130, 0x015E, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF +}; + +#elif _CODE_PAGE == 1255 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1255(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, + 0x05B8, 0x05B9, 0x0000, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF, + 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3, + 0x05F4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, + 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, + 0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E, 0x200F, 0x0000 +}; + +#elif _CODE_PAGE == 1256 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1256(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688, + 0x06AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x06A9, 0x2122, 0x0691, 0x203A, 0x0153, 0x200C, 0x200D, 0x06BA, + 0x00A0, 0x060C, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x061F, + 0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, + 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7, + 0x0637, 0x0638, 0x0639, 0x063A, 0x0640, 0x0640, 0x0642, 0x0643, + 0x00E0, 0x0644, 0x00E2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0649, 0x064A, 0x00EE, 0x00EF, + 0x064B, 0x064C, 0x064D, 0x064E, 0x00F4, 0x064F, 0x0650, 0x00F7, + 0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E, 0x200F, 0x06D2 +} + +#elif _CODE_PAGE == 1257 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1257(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, + 0x0000, 0x2030, 0x0000, 0x2039, 0x0000, 0x00A8, 0x02C7, 0x00B8, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x00AF, 0x02DB, 0x0000, + 0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x0000, 0x00A6, 0x00A7, + 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6, + 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, + 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B, + 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, + 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF, + 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, + 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C, + 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, + 0x0173, 0x014E, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x02D9 +}; + +#elif _CODE_PAGE == 1258 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1258(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0000, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0000, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0300, 0x00CD, 0x00CE, 0x00CF, + 0x0110, 0x00D1, 0x0309, 0x00D3, 0x00D4, 0x01A0, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x01AF, 0x0303, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0301, 0x00ED, 0x00EE, 0x00EF, + 0x0111, 0x00F1, 0x0323, 0x00F3, 0x00F4, 0x01A1, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x01B0, 0x20AB, 0x00FF +}; + +#endif + + +#if !_TBLDEF || !_USE_LFN +#error This file is not needed in current configuration. Remove from the project. +#endif + + +WCHAR ff_convert ( /* Converted character, Returns zero on error */ + WCHAR src, /* Character code to be converted */ + UINT dir /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */ +) +{ + WCHAR c; + + + if (src < 0x80) { /* ASCII */ + c = src; + + } else { + if (dir) { /* OEMCP to Unicode */ + c = (src >= 0x100) ? 0 : Tbl[src - 0x80]; + + } else { /* Unicode to OEMCP */ + for (c = 0; c < 0x80; c++) { + if (src == Tbl[c]) break; + } + c = (c + 0x80) & 0xFF; + } + } + + return c; +} + + +WCHAR ff_wtoupper ( /* Upper converted character */ + WCHAR chr /* Input character */ +) +{ + static const WCHAR tbl_lower[] = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xA1, 0x00A2, 0x00A3, 0x00A5, 0x00AC, 0x00AF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0x0FF, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10B, 0x10D, 0x10F, 0x111, 0x113, 0x115, 0x117, 0x119, 0x11B, 0x11D, 0x11F, 0x121, 0x123, 0x125, 0x127, 0x129, 0x12B, 0x12D, 0x12F, 0x131, 0x133, 0x135, 0x137, 0x13A, 0x13C, 0x13E, 0x140, 0x142, 0x144, 0x146, 0x148, 0x14B, 0x14D, 0x14F, 0x151, 0x153, 0x155, 0x157, 0x159, 0x15B, 0x15D, 0x15F, 0x161, 0x163, 0x165, 0x167, 0x169, 0x16B, 0x16D, 0x16F, 0x171, 0x173, 0x175, 0x177, 0x17A, 0x17C, 0x17E, 0x192, 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7, 0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE, 0x3BF, 0x3C0, 0x3C1, 0x3C3, 0x3C4, 0x3C5, 0x3C6, 0x3C7, 0x3C8, 0x3C9, 0x3CA, 0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, 0x457, 0x458, 0x459, 0x45A, 0x45B, 0x45C, 0x45E, 0x45F, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0 }; + static const WCHAR tbl_upper[] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x21, 0xFFE0, 0xFFE1, 0xFFE5, 0xFFE2, 0xFFE3, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0x178, 0x100, 0x102, 0x104, 0x106, 0x108, 0x10A, 0x10C, 0x10E, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11A, 0x11C, 0x11E, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12A, 0x12C, 0x12E, 0x130, 0x132, 0x134, 0x136, 0x139, 0x13B, 0x13D, 0x13F, 0x141, 0x143, 0x145, 0x147, 0x14A, 0x14C, 0x14E, 0x150, 0x152, 0x154, 0x156, 0x158, 0x15A, 0x15C, 0x15E, 0x160, 0x162, 0x164, 0x166, 0x168, 0x16A, 0x16C, 0x16E, 0x170, 0x172, 0x174, 0x176, 0x179, 0x17B, 0x17D, 0x191, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F, 0x3A0, 0x3A1, 0x3A3, 0x3A4, 0x3A5, 0x3A6, 0x3A7, 0x3A8, 0x3A9, 0x3AA, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B, 0x42C, 0x42D, 0x42E, 0x42F, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40A, 0x40B, 0x40C, 0x40E, 0x40F, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216A, 0x216B, 0x216C, 0x216D, 0x216E, 0x216F, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A, 0 }; + int i; + + + for (i = 0; tbl_lower[i] && chr != tbl_lower[i]; i++) ; + + return tbl_lower[i] ? tbl_upper[i] : chr; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/FATFileSystem/ChaN/diskio.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,104 @@ +/*-----------------------------------------------------------------------*/ +/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2007 */ +/*-----------------------------------------------------------------------*/ +/* This is a stub disk I/O module that acts as front end of the existing */ +/* disk I/O modules and attach it to FatFs module with common interface. */ +/*-----------------------------------------------------------------------*/ +#include "ffconf.h" +#include "diskio.h" + +#include "mbed_debug.h" +#include "FATFileSystem.h" + +using namespace mbed; + +DSTATUS disk_initialize ( + BYTE drv /* Physical drive nmuber (0..) */ +) +{ + debug_if(FFS_DBG, "disk_initialize on drv [%d]\n", drv); + return (DSTATUS)FATFileSystem::_ffs[drv]->disk_initialize(); +} + +DSTATUS disk_status ( + BYTE drv /* Physical drive nmuber (0..) */ +) +{ + debug_if(FFS_DBG, "disk_status on drv [%d]\n", drv); + return (DSTATUS)FATFileSystem::_ffs[drv]->disk_status(); +} + +DRESULT disk_read ( + BYTE drv, /* Physical drive nmuber (0..) */ + BYTE *buff, /* Data buffer to store read data */ + DWORD sector, /* Sector address (LBA) */ + BYTE count /* Number of sectors to read (1..255) */ +) +{ + debug_if(FFS_DBG, "disk_read(sector %d, count %d) on drv [%d]\n", sector, count, drv); + for(DWORD s=sector; s<sector+count; s++) { + debug_if(FFS_DBG, " disk_read(sector %d)\n", s); + int res = FATFileSystem::_ffs[drv]->disk_read((uint8_t*)buff, s); + if(res) { + return RES_PARERR; + } + buff += 512; + } + return RES_OK; +} + +#if _READONLY == 0 +DRESULT disk_write ( + BYTE drv, /* Physical drive nmuber (0..) */ + const BYTE *buff, /* Data to be written */ + DWORD sector, /* Sector address (LBA) */ + BYTE count /* Number of sectors to write (1..255) */ +) +{ + debug_if(FFS_DBG, "disk_write(sector %d, count %d) on drv [%d]\n", sector, count, drv); + for(DWORD s = sector; s < sector + count; s++) { + debug_if(FFS_DBG, " disk_write(sector %d)\n", s); + int res = FATFileSystem::_ffs[drv]->disk_write((uint8_t*)buff, s); + if(res) { + return RES_PARERR; + } + buff += 512; + } + return RES_OK; +} +#endif /* _READONLY */ + +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0..) */ + BYTE ctrl, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + debug_if(FFS_DBG, "disk_ioctl(%d)\n", ctrl); + switch(ctrl) { + case CTRL_SYNC: + if(FATFileSystem::_ffs[drv] == NULL) { + return RES_NOTRDY; + } else if(FATFileSystem::_ffs[drv]->disk_sync()) { + return RES_ERROR; + } + return RES_OK; + case GET_SECTOR_COUNT: + if(FATFileSystem::_ffs[drv] == NULL) { + return RES_NOTRDY; + } else { + DWORD res = FATFileSystem::_ffs[drv]->disk_sectors(); + if(res > 0) { + *((DWORD*)buff) = res; // minimum allowed + return RES_OK; + } else { + return RES_ERROR; + } + } + case GET_BLOCK_SIZE: + *((DWORD*)buff) = 1; // default when not known + return RES_OK; + + } + return RES_PARERR; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/FATFileSystem/ChaN/diskio.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------- +// Low level disk interface modlue include file +//----------------------------------------------------------------------- + +#ifndef _DISKIO + +#define _READONLY 0 // 1: Remove write functions +#define _USE_IOCTL 1 // 1: Use disk_ioctl fucntion + +#include "integer.h" + + +// Status of Disk Functions +typedef BYTE DSTATUS; + +// Results of Disk Functions +typedef enum { + RES_OK = 0, // 0: Successful + RES_ERROR, // 1: R/W Error + RES_WRPRT, // 2: Write Protected + RES_NOTRDY, // 3: Not Ready + RES_PARERR // 4: Invalid Parameter +} DRESULT; + + +// Prototypes for disk control functions + +int assign_drives (int, int); +DSTATUS disk_initialize (BYTE); +DSTATUS disk_status (BYTE); +DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE); +#if _READONLY == 0 +DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE); +#endif +DRESULT disk_ioctl (BYTE, BYTE, void*); + + + +// Disk Status Bits (DSTATUS) +#define STA_NOINIT 0x01 // Drive not initialized +#define STA_NODISK 0x02 // No medium in the drive +#define STA_PROTECT 0x04 // Write protected + + +// Command code for disk_ioctrl fucntion + +// Generic command (defined for FatFs) +#define CTRL_SYNC 0 // Flush disk cache (for write functions) +#define GET_SECTOR_COUNT 1 // Get media size (for only f_mkfs()) +#define GET_SECTOR_SIZE 2 // Get sector size (for multiple sector size (_MAX_SS >= 1024)) +#define GET_BLOCK_SIZE 3 // Get erase block size (for only f_mkfs()) +#define CTRL_ERASE_SECTOR 4 // Force erased a block of sectors (for only _USE_ERASE) + +// Generic command +#define CTRL_POWER 5 // Get/Set power status +#define CTRL_LOCK 6 // Lock/Unlock media removal +#define CTRL_EJECT 7 // Eject media + +// MMC/SDC specific ioctl command +#define MMC_GET_TYPE 10 // Get card type +#define MMC_GET_CSD 11 // Get CSD +#define MMC_GET_CID 12 // Get CID +#define MMC_GET_OCR 13 // Get OCR +#define MMC_GET_SDSTAT 14 // Get SD status + +// ATA/CF specific ioctl command +#define ATA_GET_REV 20 // Get F/W revision +#define ATA_GET_MODEL 21 // Get model name +#define ATA_GET_SN 22 // Get serial number + +// NAND specific ioctl command +#define NAND_FORMAT 30 // Create physical format + + +#define _DISKIO +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/FATFileSystem/ChaN/ff.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,4139 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - FAT file system module R0.09a (C)ChaN, 2012 +/-----------------------------------------------------------------------------/ +/ FatFs module is a generic FAT file system module for small embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following terms. +/ +/ Copyright (C) 2012, ChaN, all right reserved. +/ +/ * The FatFs module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/ +/-----------------------------------------------------------------------------/ +/ Feb 26,'06 R0.00 Prototype. +/ +/ Apr 29,'06 R0.01 First stable version. +/ +/ Jun 01,'06 R0.02 Added FAT12 support. +/ Removed unbuffered mode. +/ Fixed a problem on small (<32M) partition. +/ Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM). +/ +/ Sep 22,'06 R0.03 Added f_rename(). +/ Changed option _FS_MINIMUM to _FS_MINIMIZE. +/ Dec 11,'06 R0.03a Improved cluster scan algorithm to write files fast. +/ Fixed f_mkdir() creates incorrect directory on FAT32. +/ +/ Feb 04,'07 R0.04 Supported multiple drive system. +/ Changed some interfaces for multiple drive system. +/ Changed f_mountdrv() to f_mount(). +/ Added f_mkfs(). +/ Apr 01,'07 R0.04a Supported multiple partitions on a physical drive. +/ Added a capability of extending file size to f_lseek(). +/ Added minimization level 3. +/ Fixed an endian sensitive code in f_mkfs(). +/ May 05,'07 R0.04b Added a configuration option _USE_NTFLAG. +/ Added FSInfo support. +/ Fixed DBCS name can result FR_INVALID_NAME. +/ Fixed short seek (<= csize) collapses the file object. +/ +/ Aug 25,'07 R0.05 Changed arguments of f_read(), f_write() and f_mkfs(). +/ Fixed f_mkfs() on FAT32 creates incorrect FSInfo. +/ Fixed f_mkdir() on FAT32 creates incorrect directory. +/ Feb 03,'08 R0.05a Added f_truncate() and f_utime(). +/ Fixed off by one error at FAT sub-type determination. +/ Fixed btr in f_read() can be mistruncated. +/ Fixed cached sector is not flushed when create and close without write. +/ +/ Apr 01,'08 R0.06 Added fputc(), fputs(), fprintf() and fgets(). +/ Improved performance of f_lseek() on moving to the same or following cluster. +/ +/ Apr 01,'09 R0.07 Merged Tiny-FatFs as a configuration option. (_FS_TINY) +/ Added long file name feature. +/ Added multiple code page feature. +/ Added re-entrancy for multitask operation. +/ Added auto cluster size selection to f_mkfs(). +/ Added rewind option to f_readdir(). +/ Changed result code of critical errors. +/ Renamed string functions to avoid name collision. +/ Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg. +/ Added multiple sector size feature. +/ Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error. +/ Fixed wrong cache control in f_lseek(). +/ Added relative path feature. +/ Added f_chdir() and f_chdrive(). +/ Added proper case conversion to extended char. +/ Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h. +/ Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH. +/ Fixed name matching error on the 13 char boundary. +/ Added a configuration option, _LFN_UNICODE. +/ Changed f_readdir() to return the SFN with always upper case on non-LFN cfg. +/ +/ May 15,'10 R0.08 Added a memory configuration option. (_USE_LFN = 3) +/ Added file lock feature. (_FS_SHARE) +/ Added fast seek feature. (_USE_FASTSEEK) +/ Changed some types on the API, XCHAR->TCHAR. +/ Changed fname member in the FILINFO structure on Unicode cfg. +/ String functions support UTF-8 encoding files on Unicode cfg. +/ Aug 16,'10 R0.08a Added f_getcwd(). (_FS_RPATH = 2) +/ Added sector erase feature. (_USE_ERASE) +/ Moved file lock semaphore table from fs object to the bss. +/ Fixed a wrong directory entry is created on non-LFN cfg when the given name contains ';'. +/ Fixed f_mkfs() creates wrong FAT32 volume. +/ Jan 15,'11 R0.08b Fast seek feature is also applied to f_read() and f_write(). +/ f_lseek() reports required table size on creating CLMP. +/ Extended format syntax of f_printf function. +/ Ignores duplicated directory separators in given path name. +/ +/ Sep 06,'11 R0.09 f_mkfs() supports multiple partition to finish the multiple partition feature. +/ Added f_fdisk(). (_MULTI_PARTITION = 2) +/ Aug 27,'12 R0.09a Fixed assertion failure due to OS/2 EA on FAT12/16 volume. +/ Changed f_open() and f_opendir reject null object pointer to avoid crash. +/ Changed option name _FS_SHARE to _FS_LOCK. +/---------------------------------------------------------------------------*/ + +#include "ff.h" /* FatFs configurations and declarations */ +#include "diskio.h" /* Declarations of low level disk I/O functions */ + + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if _FATFS != 4004 /* Revision ID */ +#error Wrong include file (ff.h). +#endif + + +/* Definitions on sector size */ +#if _MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096 +#error Wrong sector size. +#endif +#if _MAX_SS != 512 +#define SS(fs) ((fs)->ssize) /* Variable sector size */ +#else +#define SS(fs) 512U /* Fixed sector size */ +#endif + + +/* Reentrancy related */ +#if _FS_REENTRANT +#if _USE_LFN == 1 +#error Static LFN work area must not be used in re-entrant configuration. +#endif +#define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; } +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } +#else +#define ENTER_FF(fs) +#define LEAVE_FF(fs, res) return res +#endif + +#define ABORT(fs, res) { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); } + + +/* File access control feature */ +#if _FS_LOCK +#if _FS_READONLY +#error _FS_LOCK must be 0 on read-only cfg. +#endif +typedef struct { + FATFS *fs; /* File ID 1, volume (NULL:blank entry) */ + DWORD clu; /* File ID 2, directory */ + WORD idx; /* File ID 3, directory index */ + WORD ctr; /* File open counter, 0:none, 0x01..0xFF:read open count, 0x100:write mode */ +} FILESEM; +#endif + + + +/* DBCS code ranges and SBCS extend char conversion table */ + +#if _CODE_PAGE == 932 /* Japanese Shift-JIS */ +#define _DF1S 0x81 /* DBC 1st byte range 1 start */ +#define _DF1E 0x9F /* DBC 1st byte range 1 end */ +#define _DF2S 0xE0 /* DBC 1st byte range 2 start */ +#define _DF2E 0xFC /* DBC 1st byte range 2 end */ +#define _DS1S 0x40 /* DBC 2nd byte range 1 start */ +#define _DS1E 0x7E /* DBC 2nd byte range 1 end */ +#define _DS2S 0x80 /* DBC 2nd byte range 2 start */ +#define _DS2E 0xFC /* DBC 2nd byte range 2 end */ + +#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0x80 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 949 /* Korean */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x41 +#define _DS1E 0x5A +#define _DS2S 0x61 +#define _DS2E 0x7A +#define _DS3S 0x81 +#define _DS3E 0xFE + +#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0xA1 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 437 /* U.S. (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 720 /* Arabic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 737 /* Greek (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 775 /* Baltic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 852 /* Latin 2 (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 855 /* Cyrillic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ + 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ + 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 857 /* Turkish (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 862 /* Hebrew (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 866 /* Russian (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF} + +#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1253 /* Greek (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \ + 0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF} + +#elif _CODE_PAGE == 1254 /* Turkish (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1256 /* Arabic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1257 /* Baltic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F} + +#elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ +#if _USE_LFN +#error Cannot use LFN feature without valid code page. +#endif +#define _DF1S 0 + +#else +#error Unknown code page + +#endif + + +/* Character code support macros */ +#define IsUpper(c) (((c)>='A')&&((c)<='Z')) +#define IsLower(c) (((c)>='a')&&((c)<='z')) +#define IsDigit(c) (((c)>='0')&&((c)<='9')) + +#if _DF1S /* Code page is DBCS */ + +#ifdef _DF2S /* Two 1st byte areas */ +#define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) +#else /* One 1st byte area */ +#define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) +#endif + +#ifdef _DS3S /* Three 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) +#else /* Two 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) +#endif + +#else /* Code page is SBCS */ + +#define IsDBCS1(c) 0 +#define IsDBCS2(c) 0 + +#endif /* _DF1S */ + + +/* Name status flags */ +#define NS 11 /* Index of name status byte in fn[] */ +#define NS_LOSS 0x01 /* Out of 8.3 format */ +#define NS_LFN 0x02 /* Force to create LFN entry */ +#define NS_LAST 0x04 /* Last segment */ +#define NS_BODY 0x08 /* Lower case flag (body) */ +#define NS_EXT 0x10 /* Lower case flag (ext) */ +#define NS_DOT 0x20 /* Dot entry */ + + +/* FAT sub-type boundaries */ +/* Note that the FAT spec by Microsoft says 4085 but Windows works with 4087! */ +#define MIN_FAT16 4086 /* Minimum number of clusters for FAT16 */ +#define MIN_FAT32 65526 /* Minimum number of clusters for FAT32 */ + + +/* FatFs refers the members in the FAT structures as byte array instead of +/ structure member because the structure is not binary compatible between +/ different platforms */ + +#define BS_jmpBoot 0 /* Jump instruction (3) */ +#define BS_OEMName 3 /* OEM name (8) */ +#define BPB_BytsPerSec 11 /* Sector size [byte] (2) */ +#define BPB_SecPerClus 13 /* Cluster size [sector] (1) */ +#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (2) */ +#define BPB_NumFATs 16 /* Number of FAT copies (1) */ +#define BPB_RootEntCnt 17 /* Number of root dir entries for FAT12/16 (2) */ +#define BPB_TotSec16 19 /* Volume size [sector] (2) */ +#define BPB_Media 21 /* Media descriptor (1) */ +#define BPB_FATSz16 22 /* FAT size [sector] (2) */ +#define BPB_SecPerTrk 24 /* Track size [sector] (2) */ +#define BPB_NumHeads 26 /* Number of heads (2) */ +#define BPB_HiddSec 28 /* Number of special hidden sectors (4) */ +#define BPB_TotSec32 32 /* Volume size [sector] (4) */ +#define BS_DrvNum 36 /* Physical drive number (2) */ +#define BS_BootSig 38 /* Extended boot signature (1) */ +#define BS_VolID 39 /* Volume serial number (4) */ +#define BS_VolLab 43 /* Volume label (8) */ +#define BS_FilSysType 54 /* File system type (1) */ +#define BPB_FATSz32 36 /* FAT size [sector] (4) */ +#define BPB_ExtFlags 40 /* Extended flags (2) */ +#define BPB_FSVer 42 /* File system version (2) */ +#define BPB_RootClus 44 /* Root dir first cluster (4) */ +#define BPB_FSInfo 48 /* Offset of FSInfo sector (2) */ +#define BPB_BkBootSec 50 /* Offset of backup boot sector (2) */ +#define BS_DrvNum32 64 /* Physical drive number (2) */ +#define BS_BootSig32 66 /* Extended boot signature (1) */ +#define BS_VolID32 67 /* Volume serial number (4) */ +#define BS_VolLab32 71 /* Volume label (8) */ +#define BS_FilSysType32 82 /* File system type (1) */ +#define FSI_LeadSig 0 /* FSI: Leading signature (4) */ +#define FSI_StrucSig 484 /* FSI: Structure signature (4) */ +#define FSI_Free_Count 488 /* FSI: Number of free clusters (4) */ +#define FSI_Nxt_Free 492 /* FSI: Last allocated cluster (4) */ +#define MBR_Table 446 /* MBR: Partition table offset (2) */ +#define SZ_PTE 16 /* MBR: Size of a partition table entry */ +#define BS_55AA 510 /* Boot sector signature (2) */ + +#define DIR_Name 0 /* Short file name (11) */ +#define DIR_Attr 11 /* Attribute (1) */ +#define DIR_NTres 12 /* NT flag (1) */ +#define DIR_CrtTimeTenth 13 /* Created time sub-second (1) */ +#define DIR_CrtTime 14 /* Created time (2) */ +#define DIR_CrtDate 16 /* Created date (2) */ +#define DIR_LstAccDate 18 /* Last accessed date (2) */ +#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (2) */ +#define DIR_WrtTime 22 /* Modified time (2) */ +#define DIR_WrtDate 24 /* Modified date (2) */ +#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (2) */ +#define DIR_FileSize 28 /* File size (4) */ +#define LDIR_Ord 0 /* LFN entry order and LLE flag (1) */ +#define LDIR_Attr 11 /* LFN attribute (1) */ +#define LDIR_Type 12 /* LFN type (1) */ +#define LDIR_Chksum 13 /* Sum of corresponding SFN entry */ +#define LDIR_FstClusLO 26 /* Filled by zero (0) */ +#define SZ_DIR 32 /* Size of a directory entry */ +#define LLE 0x40 /* Last long entry flag in LDIR_Ord */ +#define DDE 0xE5 /* Deleted directory entry mark in DIR_Name[0] */ +#define NDDE 0x05 /* Replacement of the character collides with DDE */ + + +/*------------------------------------------------------------*/ +/* Module private work area */ +/*------------------------------------------------------------*/ +/* Note that uninitialized variables with static duration are +/ zeroed/nulled at start-up. If not, the compiler or start-up +/ routine is out of ANSI-C standard. +*/ + +#if _VOLUMES +static +FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */ +#else +#error Number of volumes must not be 0. +#endif + +static +WORD Fsid; /* File system mount ID */ + +#if _FS_RPATH +static +BYTE CurrVol; /* Current drive */ +#endif + +#if _FS_LOCK +static +FILESEM Files[_FS_LOCK]; /* File lock semaphores */ +#endif + +#if _USE_LFN == 0 /* No LFN feature */ +#define DEF_NAMEBUF BYTE sfn[12] +#define INIT_BUF(dobj) (dobj).fn = sfn +#define FREE_BUF() + +#elif _USE_LFN == 1 /* LFN feature with static working buffer */ +static WCHAR LfnBuf[_MAX_LFN+1]; +#define DEF_NAMEBUF BYTE sfn[12] +#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = LfnBuf; } +#define FREE_BUF() + +#elif _USE_LFN == 2 /* LFN feature with dynamic working buffer on the stack */ +#define DEF_NAMEBUF BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1] +#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = lbuf; } +#define FREE_BUF() + +#elif _USE_LFN == 3 /* LFN feature with dynamic working buffer on the heap */ +#define DEF_NAMEBUF BYTE sfn[12]; WCHAR *lfn +#define INIT_BUF(dobj) { lfn = ff_memalloc((_MAX_LFN + 1) * 2); \ + if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); \ + (dobj).lfn = lfn; (dobj).fn = sfn; } +#define FREE_BUF() ff_memfree(lfn) + +#else +#error Wrong LFN configuration. +#endif + + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* String functions */ +/*-----------------------------------------------------------------------*/ + +/* Copy memory to memory */ +static +void mem_cpy (void* dst, const void* src, UINT cnt) { + BYTE *d = (BYTE*)dst; + const BYTE *s = (const BYTE*)src; + +#if _WORD_ACCESS == 1 + while (cnt >= sizeof (int)) { + *(int*)d = *(int*)s; + d += sizeof (int); s += sizeof (int); + cnt -= sizeof (int); + } +#endif + while (cnt--) + *d++ = *s++; +} + +/* Fill memory */ +static +void mem_set (void* dst, int val, UINT cnt) { + BYTE *d = (BYTE*)dst; + + while (cnt--) + *d++ = (BYTE)val; +} + +/* Compare memory to memory */ +static +int mem_cmp (const void* dst, const void* src, UINT cnt) { + const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; + int r = 0; + + while (cnt-- && (r = *d++ - *s++) == 0) ; + return r; +} + +/* Check if chr is contained in the string */ +static +int chk_chr (const char* str, int chr) { + while (*str && *str != chr) str++; + return *str; +} + + + +/*-----------------------------------------------------------------------*/ +/* Request/Release grant to access the volume */ +/*-----------------------------------------------------------------------*/ +#if _FS_REENTRANT + +static +int lock_fs ( + FATFS *fs /* File system object */ +) +{ + return ff_req_grant(fs->sobj); +} + + +static +void unlock_fs ( + FATFS *fs, /* File system object */ + FRESULT res /* Result code to be returned */ +) +{ + if (fs && + res != FR_NOT_ENABLED && + res != FR_INVALID_DRIVE && + res != FR_INVALID_OBJECT && + res != FR_TIMEOUT) { + ff_rel_grant(fs->sobj); + } +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* File lock control functions */ +/*-----------------------------------------------------------------------*/ +#if _FS_LOCK + +static +FRESULT chk_lock ( /* Check if the file can be accessed */ + DIR* dj, /* Directory object pointing the file to be checked */ + int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i, be; + + /* Search file semaphore table */ + for (i = be = 0; i < _FS_LOCK; i++) { + if (Files[i].fs) { /* Existing entry */ + if (Files[i].fs == dj->fs && /* Check if the file matched with an open file */ + Files[i].clu == dj->sclust && + Files[i].idx == dj->index) break; + } else { /* Blank entry */ + be++; + } + } + if (i == _FS_LOCK) /* The file is not opened */ + return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new file? */ + + /* The file has been opened. Reject any open against writing file and all write mode open */ + return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; +} + + +static +int enq_lock (void) /* Check if an entry is available for a new file */ +{ + UINT i; + + for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; + return (i == _FS_LOCK) ? 0 : 1; +} + + +static +UINT inc_lock ( /* Increment file open counter and returns its index (0:int error) */ + DIR* dj, /* Directory object pointing the file to register or increment */ + int acc /* Desired access mode (0:Read, !0:Write) */ +) +{ + UINT i; + + + for (i = 0; i < _FS_LOCK; i++) { /* Find the file */ + if (Files[i].fs == dj->fs && + Files[i].clu == dj->sclust && + Files[i].idx == dj->index) break; + } + + if (i == _FS_LOCK) { /* Not opened. Register it as new. */ + for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; + if (i == _FS_LOCK) return 0; /* No space to register (int err) */ + Files[i].fs = dj->fs; + Files[i].clu = dj->sclust; + Files[i].idx = dj->index; + Files[i].ctr = 0; + } + + if (acc && Files[i].ctr) return 0; /* Access violation (int err) */ + + Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */ + + return i + 1; +} + + +static +FRESULT dec_lock ( /* Decrement file open counter */ + UINT i /* Semaphore index */ +) +{ + WORD n; + FRESULT res; + + + if (--i < _FS_LOCK) { + n = Files[i].ctr; + if (n == 0x100) n = 0; + if (n) n--; + Files[i].ctr = n; + if (!n) Files[i].fs = 0; + res = FR_OK; + } else { + res = FR_INT_ERR; + } + return res; +} + + +static +void clear_lock ( /* Clear lock entries of the volume */ + FATFS *fs +) +{ + UINT i; + + for (i = 0; i < _FS_LOCK; i++) { + if (Files[i].fs == fs) Files[i].fs = 0; + } +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Change window offset */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT move_window ( + FATFS *fs, /* File system object */ + DWORD sector /* Sector number to make appearance in the fs->win[] */ +) /* Move to zero only writes back dirty window */ +{ + DWORD wsect; + + + wsect = fs->winsect; + if (wsect != sector) { /* Changed current window */ +#if !_FS_READONLY + if (fs->wflag) { /* Write back dirty window if needed */ + if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK) + return FR_DISK_ERR; + fs->wflag = 0; + if (wsect < (fs->fatbase + fs->fsize)) { /* In FAT area */ + BYTE nf; + for (nf = fs->n_fats; nf > 1; nf--) { /* Reflect the change to all FAT copies */ + wsect += fs->fsize; + disk_write(fs->drv, fs->win, wsect, 1); + } + } + } +#endif + if (sector) { + if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK) + return FR_DISK_ERR; + fs->winsect = sector; + } + } + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Clean-up cached data */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT sync ( /* FR_OK: successful, FR_DISK_ERR: failed */ + FATFS *fs /* File system object */ +) +{ + FRESULT res; + + + res = move_window(fs, 0); + if (res == FR_OK) { + /* Update FSInfo sector if needed */ + if (fs->fs_type == FS_FAT32 && fs->fsi_flag) { + fs->winsect = 0; + /* Create FSInfo structure */ + mem_set(fs->win, 0, 512); + ST_WORD(fs->win+BS_55AA, 0xAA55); + ST_DWORD(fs->win+FSI_LeadSig, 0x41615252); + ST_DWORD(fs->win+FSI_StrucSig, 0x61417272); + ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust); + ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust); + /* Write it into the FSInfo sector */ + disk_write(fs->drv, fs->win, fs->fsi_sector, 1); + fs->fsi_flag = 0; + } + /* Make sure that no pending write process in the physical drive */ + if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK) + res = FR_DISK_ERR; + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Get sector# from cluster# */ +/*-----------------------------------------------------------------------*/ + + +DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to be converted */ +) +{ + clst -= 2; + if (clst >= (fs->n_fatent - 2)) return 0; /* Invalid cluster# */ + return clst * fs->csize + fs->database; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Read value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + + +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to get the link information */ +) +{ + UINT wc, bc; + BYTE *p; + + + if (clst < 2 || clst >= fs->n_fatent) /* Check range */ + return 1; + + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break; + wc = fs->win[bc % SS(fs)]; bc++; + if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break; + wc |= fs->win[bc % SS(fs)] << 8; + return (clst & 1) ? (wc >> 4) : (wc & 0xFFF); + + case FS_FAT16 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)))) break; + p = &fs->win[clst * 2 % SS(fs)]; + return LD_WORD(p); + + case FS_FAT32 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)))) break; + p = &fs->win[clst * 4 % SS(fs)]; + return LD_DWORD(p) & 0x0FFFFFFF; + } + + return 0xFFFFFFFF; /* An error occurred at the disk I/O layer */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Change value of a FAT entry */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY + +FRESULT put_fat ( + FATFS *fs, /* File system object */ + DWORD clst, /* Cluster# to be changed in range of 2 to fs->n_fatent - 1 */ + DWORD val /* New value to mark the cluster */ +) +{ + UINT bc; + BYTE *p; + FRESULT res; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + res = FR_INT_ERR; + + } else { + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc % SS(fs)]; + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + bc++; + fs->wflag = 1; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc % SS(fs)]; + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + break; + + case FS_FAT16 : + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + p = &fs->win[clst * 2 % SS(fs)]; + ST_WORD(p, (WORD)val); + break; + + case FS_FAT32 : + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + p = &fs->win[clst * 4 % SS(fs)]; + val |= LD_DWORD(p) & 0xF0000000; + ST_DWORD(p, val); + break; + + default : + res = FR_INT_ERR; + } + fs->wflag = 1; + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT remove_chain ( + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to remove a chain from */ +) +{ + FRESULT res; + DWORD nxt; +#if _USE_ERASE + DWORD scl = clst, ecl = clst, rt[2]; +#endif + + if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + res = FR_INT_ERR; + + } else { + res = FR_OK; + while (clst < fs->n_fatent) { /* Not a last link? */ + nxt = get_fat(fs, clst); /* Get cluster status */ + if (nxt == 0) break; /* Empty cluster? */ + if (nxt == 1) { res = FR_INT_ERR; break; } /* Internal error? */ + if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } /* Disk error? */ + res = put_fat(fs, clst, 0); /* Mark the cluster "empty" */ + if (res != FR_OK) break; + if (fs->free_clust != 0xFFFFFFFF) { /* Update FSInfo */ + fs->free_clust++; + fs->fsi_flag = 1; + } +#if _USE_ERASE + if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ + ecl = nxt; + } else { /* End of contiguous clusters */ + rt[0] = clust2sect(fs, scl); /* Start sector */ + rt[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ + disk_ioctl(fs->drv, CTRL_ERASE_SECTOR, rt); /* Erase the block */ + scl = ecl = nxt; + } +#endif + clst = nxt; /* Next cluster */ + } + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Stretch or Create a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to stretch. 0 means create a new chain. */ +) +{ + DWORD cs, ncl, scl; + FRESULT res; + + + if (clst == 0) { /* Create a new chain */ + scl = fs->last_clust; /* Get suggested start point */ + if (!scl || scl >= fs->n_fatent) scl = 1; + } + else { /* Stretch the current chain */ + cs = get_fat(fs, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* It is an invalid cluster */ + if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */ + scl = clst; + } + + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= fs->n_fatent) { /* Wrap around */ + ncl = 2; + if (ncl > scl) return 0; /* No free cluster */ + } + cs = get_fat(fs, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster */ + if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */ + return cs; + if (ncl == scl) return 0; /* No free cluster */ + } + + res = put_fat(fs, ncl, 0x0FFFFFFF); /* Mark the new cluster "last link" */ + if (res == FR_OK && clst != 0) { + res = put_fat(fs, clst, ncl); /* Link it to the previous one if needed */ + } + if (res == FR_OK) { + fs->last_clust = ncl; /* Update FSINFO */ + if (fs->free_clust != 0xFFFFFFFF) { + fs->free_clust--; + fs->fsi_flag = 1; + } + } else { + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; + } + + return ncl; /* Return new cluster number or error code */ +} +#endif /* !_FS_READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Convert offset into cluster with link map table */ +/*-----------------------------------------------------------------------*/ + +#if _USE_FASTSEEK +static +DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ + FIL* fp, /* Pointer to the file object */ + DWORD ofs /* File offset to be converted to cluster# */ +) +{ + DWORD cl, ncl, *tbl; + + + tbl = fp->cltbl + 1; /* Top of CLMT */ + cl = ofs / SS(fp->fs) / fp->fs->csize; /* Cluster order from top of the file */ + for (;;) { + ncl = *tbl++; /* Number of cluters in the fragment */ + if (!ncl) return 0; /* End of table? (error) */ + if (cl < ncl) break; /* In this fragment? */ + cl -= ncl; tbl++; /* Next fragment */ + } + return cl + *tbl; /* Return the cluster number */ +} +#endif /* _USE_FASTSEEK */ + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Set directory index */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_sdi ( + FATFS_DIR *dj, /* Pointer to directory object */ + WORD idx /* Index of directory table */ +) +{ + DWORD clst; + WORD ic; + + + dj->index = idx; + clst = dj->sclust; + if (clst == 1 || clst >= dj->fs->n_fatent) /* Check start cluster range */ + return FR_INT_ERR; + if (!clst && dj->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */ + clst = dj->fs->dirbase; + + if (clst == 0) { /* Static table (root-dir in FAT12/16) */ + dj->clust = clst; + if (idx >= dj->fs->n_rootdir) /* Index is out of range */ + return FR_INT_ERR; + dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / SZ_DIR); /* Sector# */ + } + else { /* Dynamic table (sub-dirs or root-dir in FAT32) */ + ic = SS(dj->fs) / SZ_DIR * dj->fs->csize; /* Entries per cluster */ + while (idx >= ic) { /* Follow cluster chain */ + clst = get_fat(dj->fs, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= dj->fs->n_fatent) /* Reached to end of table or int error */ + return FR_INT_ERR; + idx -= ic; + } + dj->clust = clst; + dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / SZ_DIR); /* Sector# */ + } + + dj->dir = dj->fs->win + (idx % (SS(dj->fs) / SZ_DIR)) * SZ_DIR; /* Ptr to the entry in the sector */ + + return FR_OK; /* Seek succeeded */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Move directory table index next */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not stretch */ + FATFS_DIR *dj, /* Pointer to directory object */ + int stretch /* 0: Do not stretch table, 1: Stretch table if needed */ +) +{ + DWORD clst; + WORD i; + + + stretch = stretch; /* To suppress warning on read-only cfg. */ + i = dj->index + 1; + if (!i || !dj->sect) /* Report EOT when index has reached 65535 */ + return FR_NO_FILE; + + if (!(i % (SS(dj->fs) / SZ_DIR))) { /* Sector changed? */ + dj->sect++; /* Next sector */ + + if (dj->clust == 0) { /* Static table */ + if (i >= dj->fs->n_rootdir) /* Report EOT when end of table */ + return FR_NO_FILE; + } + else { /* Dynamic table */ + if (((i / (SS(dj->fs) / SZ_DIR)) & (dj->fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(dj->fs, dj->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + if (clst >= dj->fs->n_fatent) { /* When it reached end of dynamic table */ +#if !_FS_READONLY + BYTE c; + if (!stretch) return FR_NO_FILE; /* When do not stretch, report EOT */ + clst = create_chain(dj->fs, dj->clust); /* Stretch cluster chain */ + if (clst == 0) return FR_DENIED; /* No free cluster */ + if (clst == 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + /* Clean-up stretched table */ + if (move_window(dj->fs, 0)) return FR_DISK_ERR; /* Flush active window */ + mem_set(dj->fs->win, 0, SS(dj->fs)); /* Clear window buffer */ + dj->fs->winsect = clust2sect(dj->fs, clst); /* Cluster start sector */ + for (c = 0; c < dj->fs->csize; c++) { /* Fill the new cluster with 0 */ + dj->fs->wflag = 1; + if (move_window(dj->fs, 0)) return FR_DISK_ERR; + dj->fs->winsect++; + } + dj->fs->winsect -= c; /* Rewind window address */ +#else + return FR_NO_FILE; /* Report EOT */ +#endif + } + dj->clust = clst; /* Initialize data for new cluster */ + dj->sect = clust2sect(dj->fs, clst); + } + } + } + + dj->index = i; + dj->dir = dj->fs->win + (i % (SS(dj->fs) / SZ_DIR)) * SZ_DIR; + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Load/Store start cluster number */ +/*-----------------------------------------------------------------------*/ + +static +DWORD ld_clust ( + FATFS *fs, /* Pointer to the fs object */ + BYTE *dir /* Pointer to the directory entry */ +) +{ + DWORD cl; + + cl = LD_WORD(dir+DIR_FstClusLO); + if (fs->fs_type == FS_FAT32) + cl |= (DWORD)LD_WORD(dir+DIR_FstClusHI) << 16; + + return cl; +} + + +#if !_FS_READONLY +static +void st_clust ( + BYTE *dir, /* Pointer to the directory entry */ + DWORD cl /* Value to be set */ +) +{ + ST_WORD(dir+DIR_FstClusLO, cl); + ST_WORD(dir+DIR_FstClusHI, cl >> 16); +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN chars in the directory entry */ + + +static +int cmp_lfn ( /* 1:Matched, 0:Not matched */ + WCHAR *lfnbuf, /* Pointer to the LFN to be compared */ + BYTE *dir /* Pointer to the directory entry containing a part of LFN */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + i = ((dir[LDIR_Ord] & ~LLE) - 1) * 13; /* Get offset in the LFN buffer */ + s = 0; wc = 1; + do { + uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */ + if (wc) { /* Last char has not been processed */ + wc = ff_wtoupper(uc); /* Convert it to upper case */ + if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++])) /* Compare it */ + return 0; /* Not matched */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } while (++s < 13); /* Repeat until all chars in the entry are checked */ + + if ((dir[LDIR_Ord] & LLE) && wc && lfnbuf[i]) /* Last segment matched but different length */ + return 0; + + return 1; /* The part of LFN matched */ +} + + + +static +int pick_lfn ( /* 1:Succeeded, 0:Buffer overflow */ + WCHAR *lfnbuf, /* Pointer to the Unicode-LFN buffer */ + BYTE *dir /* Pointer to the directory entry */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + s = 0; wc = 1; + do { + uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */ + if (wc) { /* Last char has not been processed */ + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } while (++s < 13); /* Read all character in the entry */ + + if (dir[LDIR_Ord] & LLE) { /* Put terminator if it is the last LFN part */ + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return 1; +} + + +#if !_FS_READONLY +static +void fit_lfn ( + const WCHAR *lfnbuf, /* Pointer to the LFN buffer */ + BYTE *dir, /* Pointer to the directory entry */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* SFN sum */ +) +{ + UINT i, s; + WCHAR wc; + + + dir[LDIR_Chksum] = sum; /* Set check sum */ + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ + dir[LDIR_Type] = 0; + ST_WORD(dir+LDIR_FstClusLO, 0); + + i = (ord - 1) * 13; /* Get offset in the LFN buffer */ + s = wc = 0; + do { + if (wc != 0xFFFF) wc = lfnbuf[i++]; /* Get an effective char */ + ST_WORD(dir+LfnOfs[s], wc); /* Put it */ + if (!wc) wc = 0xFFFF; /* Padding chars following last char */ + } while (++s < 13); + if (wc == 0xFFFF || !lfnbuf[i]) ord |= LLE; /* Bottom LFN part is the start of LFN sequence */ + dir[LDIR_Ord] = ord; /* Set the LFN order */ +} + +#endif +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Create numbered name */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +void gen_numname ( + BYTE *dst, /* Pointer to generated SFN */ + const BYTE *src, /* Pointer to source SFN to be modified */ + const WCHAR *lfn, /* Pointer to LFN */ + WORD seq /* Sequence number */ +) +{ + BYTE ns[8], c; + UINT i, j; + + + mem_cpy(dst, src, 11); + + if (seq > 5) { /* On many collisions, generate a hash number instead of sequential number */ + do seq = (seq >> 1) + (seq << 15) + (WORD)*lfn++; while (*lfn); + } + + /* itoa (hexdecimal) */ + i = 7; + do { + c = (seq % 16) + '0'; + if (c > '9') c += 7; + ns[i--] = c; + seq /= 16; + } while (seq); + ns[i] = '~'; + + /* Append the number */ + for (j = 0; j < i && dst[j] != ' '; j++) { + if (IsDBCS1(dst[j])) { + if (j == i - 1) break; + j++; + } + } + do { + dst[j++] = (i < 8) ? ns[i++] : ' '; + } while (j < 8); +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Calculate sum of an SFN */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +BYTE sum_sfn ( + const BYTE *dir /* Ptr to directory entry */ +) +{ + BYTE sum = 0; + UINT n = 11; + + do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); + return sum; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Find an object in the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_find ( + FATFS_DIR *dj /* Pointer to the directory object linked to the file name */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN + BYTE a, ord, sum; +#endif + + res = dir_sdi(dj, 0); /* Rewind directory object */ + if (res != FR_OK) return res; + +#if _USE_LFN + ord = sum = 0xFF; +#endif + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + dir = dj->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if _USE_LFN /* LFN configuration */ + a = dir[DIR_Attr] & AM_MASK; + if (c == DDE || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (dj->lfn) { + if (c & LLE) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= ~LLE; ord = c; /* LFN start order */ + dj->lfn_idx = dj->index; + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */ + ord = 0xFF; dj->lfn_idx = 0xFFFF; /* Reset LFN sequence */ + if (!(dj->fn[NS] & NS_LOSS) && !mem_cmp(dir, dj->fn, 11)) break; /* SFN matched? */ + } + } +#else /* Non LFN configuration */ + if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, 0); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 +static +FRESULT dir_read ( + FATFS_DIR *dj /* Pointer to the directory object that pointing the entry to be read */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN + BYTE a, ord = 0xFF, sum = 0xFF; +#endif + + res = FR_NO_FILE; + while (dj->sect) { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + dir = dj->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if _USE_LFN /* LFN configuration */ + a = dir[DIR_Attr] & AM_MASK; + if (c == DDE || (!_FS_RPATH && c == '.') || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (c & LLE) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= ~LLE; ord = c; + dj->lfn_idx = dj->index; + } + /* Check LFN validity and capture it */ + ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */ + dj->lfn_idx = 0xFFFF; /* It has no LFN. */ + break; + } + } +#else /* Non LFN configuration */ + if (c != DDE && (_FS_RPATH || c != '.') && !(dir[DIR_Attr] & AM_VOL)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, 0); /* Next entry */ + if (res != FR_OK) break; + } + + if (res != FR_OK) dj->sect = 0; + + return res; +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Register an object to the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT dir_register ( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */ + FATFS_DIR *dj /* Target directory with object name to be created */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN /* LFN configuration */ + WORD n, ne, is; + BYTE sn[12], *fn, sum; + WCHAR *lfn; + + + fn = dj->fn; lfn = dj->lfn; + mem_cpy(sn, fn, 12); + + if (_FS_RPATH && (sn[NS] & NS_DOT)) /* Cannot create dot entry */ + return FR_INVALID_NAME; + + if (sn[NS] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ + fn[NS] = 0; dj->lfn = 0; /* Find only SFN */ + for (n = 1; n < 100; n++) { + gen_numname(fn, sn, lfn, n); /* Generate a numbered name */ + res = dir_find(dj); /* Check if the name collides with existing SFN */ + if (res != FR_OK) break; + } + if (n == 100) return FR_DENIED; /* Abort if too many collisions */ + if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ + fn[NS] = sn[NS]; dj->lfn = lfn; + } + + if (sn[NS] & NS_LFN) { /* When LFN is to be created, reserve an SFN + LFN entries. */ + for (ne = 0; lfn[ne]; ne++) ; + ne = (ne + 25) / 13; + } else { /* Otherwise reserve only an SFN entry. */ + ne = 1; + } + + /* Reserve contiguous entries */ + res = dir_sdi(dj, 0); + if (res != FR_OK) return res; + n = is = 0; + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + c = *dj->dir; /* Check the entry status */ + if (c == DDE || c == 0) { /* Is it a blank entry? */ + if (n == 0) is = dj->index; /* First index of the contiguous entry */ + if (++n == ne) break; /* A contiguous entry that required count is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dj, 1); /* Next entry with table stretch */ + } while (res == FR_OK); + + if (res == FR_OK && ne > 1) { /* Initialize LFN entry if needed */ + res = dir_sdi(dj, is); + if (res == FR_OK) { + sum = sum_sfn(dj->fn); /* Sum of the SFN tied to the LFN */ + ne--; + do { /* Store LFN entries in bottom first */ + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum); + dj->fs->wflag = 1; + res = dir_next(dj, 0); /* Next entry */ + } while (res == FR_OK && --ne); + } + } + +#else /* Non LFN configuration */ + res = dir_sdi(dj, 0); + if (res == FR_OK) { + do { /* Find a blank entry for the SFN */ + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + c = *dj->dir; + if (c == DDE || c == 0) break; /* Is it a blank entry? */ + res = dir_next(dj, 1); /* Next entry with table stretch */ + } while (res == FR_OK); + } +#endif + + if (res == FR_OK) { /* Initialize the SFN entry */ + res = move_window(dj->fs, dj->sect); + if (res == FR_OK) { + dir = dj->dir; + mem_set(dir, 0, SZ_DIR); /* Clean the entry */ + mem_cpy(dir, dj->fn, 11); /* Put SFN */ +#if _USE_LFN + dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT); /* Put NT flag */ +#endif + dj->fs->wflag = 1; + } + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Remove an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY && !_FS_MINIMIZE +static +FRESULT dir_remove ( /* FR_OK: Successful, FR_DISK_ERR: A disk error */ + FATFS_DIR *dj /* Directory object pointing the entry to be removed */ +) +{ + FRESULT res; +#if _USE_LFN /* LFN configuration */ + WORD i; + + i = dj->index; /* SFN index */ + res = dir_sdi(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx)); /* Goto the SFN or top of the LFN entries */ + if (res == FR_OK) { + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + *dj->dir = DDE; /* Mark the entry "deleted" */ + dj->fs->wflag = 1; + if (dj->index >= i) break; /* When reached SFN, all entries of the object has been deleted. */ + res = dir_next(dj, 0); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } + +#else /* Non LFN configuration */ + res = dir_sdi(dj, dj->index); + if (res == FR_OK) { + res = move_window(dj->fs, dj->sect); + if (res == FR_OK) { + *dj->dir = DDE; /* Mark the entry "deleted" */ + dj->fs->wflag = 1; + } + } +#endif + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Pick a segment and create the object name in directory form */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT create_name ( + FATFS_DIR *dj, /* Pointer to the directory object */ + const TCHAR **path /* Pointer to pointer to the segment in the path string */ +) +{ +#ifdef _EXCVT + static const BYTE excvt[] = _EXCVT; /* Upper conversion table for extended chars */ +#endif + +#if _USE_LFN /* LFN configuration */ + BYTE b, cf; + WCHAR w, *lfn; + UINT i, ni, si, di; + const TCHAR *p; + + /* Create LFN in Unicode */ + for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */ + lfn = dj->lfn; + si = di = 0; + for (;;) { + w = p[si++]; /* Get a character */ + if (w < ' ' || w == '/' || w == '\\') break; /* Break on end of segment */ + if (di >= _MAX_LFN) /* Reject too long name */ + return FR_INVALID_NAME; +#if !_LFN_UNICODE + w &= 0xFF; + if (IsDBCS1(w)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + b = (BYTE)p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(b)) + return FR_INVALID_NAME; /* Reject invalid sequence */ + w = (w << 8) + b; /* Create a DBC */ + } + w = ff_convert(w, 1); /* Convert ANSI/OEM to Unicode */ + if (!w) return FR_INVALID_NAME; /* Reject invalid code */ +#endif + if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal chars for LFN */ + return FR_INVALID_NAME; + lfn[di++] = w; /* Store the Unicode char */ + } + *path = &p[si]; /* Return pointer to the next segment */ + cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ +#if _FS_RPATH + if ((di == 1 && lfn[di-1] == '.') || /* Is this a dot entry? */ + (di == 2 && lfn[di-1] == '.' && lfn[di-2] == '.')) { + lfn[di] = 0; + for (i = 0; i < 11; i++) + dj->fn[i] = (i < di) ? '.' : ' '; + dj->fn[i] = cf | NS_DOT; /* This is a dot entry */ + return FR_OK; + } +#endif + while (di) { /* Strip trailing spaces and dots */ + w = lfn[di-1]; + if (w != ' ' && w != '.') break; + di--; + } + if (!di) return FR_INVALID_NAME; /* Reject nul string */ + + lfn[di] = 0; /* LFN is created */ + + /* Create SFN in directory form */ + mem_set(dj->fn, ' ', 11); + for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ + if (si) cf |= NS_LOSS | NS_LFN; + while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ + + b = i = 0; ni = 8; + for (;;) { + w = lfn[si++]; /* Get an LFN char */ + if (!w) break; /* Break on end of the LFN */ + if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ + cf |= NS_LOSS | NS_LFN; continue; + } + + if (i >= ni || si == di) { /* Extension or end of SFN */ + if (ni == 11) { /* Long extension */ + cf |= NS_LOSS | NS_LFN; break; + } + if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */ + if (si > di) break; /* No extension */ + si = di; i = 8; ni = 11; /* Enter extension section */ + b <<= 2; continue; + } + + if (w >= 0x80) { /* Non ASCII char */ +#ifdef _EXCVT + w = ff_convert(w, 0); /* Unicode -> OEM code */ + if (w) w = excvt[w - 0x80]; /* Convert extended char to upper (SBCS) */ +#else + w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */ +#endif + cf |= NS_LFN; /* Force create LFN entry */ + } + + if (_DF1S && w >= 0x100) { /* Double byte char (always false on SBCS cfg) */ + if (i >= ni - 1) { + cf |= NS_LOSS | NS_LFN; i = ni; continue; + } + dj->fn[i++] = (BYTE)(w >> 8); + } else { /* Single byte char */ + if (!w || chk_chr("+,;=[]", w)) { /* Replace illegal chars for SFN */ + w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ + } else { + if (IsUpper(w)) { /* ASCII large capital */ + b |= 2; + } else { + if (IsLower(w)) { /* ASCII small capital */ + b |= 1; w -= 0x20; + } + } + } + } + dj->fn[i++] = (BYTE)w; + } + + if (dj->fn[0] == DDE) dj->fn[0] = NDDE; /* If the first char collides with deleted mark, replace it with 0x05 */ + + if (ni == 8) b <<= 2; + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) /* Create LFN entry when there are composite capitals */ + cf |= NS_LFN; + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended char, NT flags are created */ + if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */ + } + + dj->fn[NS] = cf; /* SFN is created */ + + return FR_OK; + + +#else /* Non-LFN configuration */ + BYTE b, c, d, *sfn; + UINT ni, si, i; + const char *p; + + /* Create file name in directory form */ + for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */ + sfn = dj->fn; + mem_set(sfn, ' ', 11); + si = i = b = 0; ni = 8; +#if _FS_RPATH + if (p[si] == '.') { /* Is this a dot entry? */ + for (;;) { + c = (BYTE)p[si++]; + if (c != '.' || si >= 3) break; + sfn[i++] = c; + } + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; + *path = &p[si]; /* Return pointer to the next segment */ + sfn[NS] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */ + return FR_OK; + } +#endif + for (;;) { + c = (BYTE)p[si++]; + if (c <= ' ' || c == '/' || c == '\\') break; /* Break on end of segment */ + if (c == '.' || i >= ni) { + if (ni != 8 || c != '.') return FR_INVALID_NAME; + i = 8; ni = 11; + b <<= 2; continue; + } + if (c >= 0x80) { /* Extended char? */ + b |= 3; /* Eliminate NT flag */ +#ifdef _EXCVT + c = excvt[c - 0x80]; /* Upper conversion (SBCS) */ +#else +#if !_DF1S /* ASCII only cfg */ + return FR_INVALID_NAME; +#endif +#endif + } + if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + d = (BYTE)p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */ + return FR_INVALID_NAME; + sfn[i++] = c; + sfn[i++] = d; + } else { /* Single byte code */ + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) /* Reject illegal chrs for SFN */ + return FR_INVALID_NAME; + if (IsUpper(c)) { /* ASCII large capital? */ + b |= 2; + } else { + if (IsLower(c)) { /* ASCII small capital? */ + b |= 1; c -= 0x20; + } + } + sfn[i++] = c; + } + } + *path = &p[si]; /* Return pointer to the next segment */ + c = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ + + if (!i) return FR_INVALID_NAME; /* Reject nul string */ + if (sfn[0] == DDE) sfn[0] = NDDE; /* When first char collides with DDE, replace it with 0x05 */ + + if (ni == 8) b <<= 2; + if ((b & 0x03) == 0x01) c |= NS_EXT; /* NT flag (Name extension has only small capital) */ + if ((b & 0x0C) == 0x04) c |= NS_BODY; /* NT flag (Name body has only small capital) */ + + sfn[NS] = c; /* Store NT flag, File name is created */ + + return FR_OK; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get file information from directory entry */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 +static +void get_fileinfo ( /* No return code */ + FATFS_DIR *dj, /* Pointer to the directory object */ + FILINFO *fno /* Pointer to the file information to be filled */ +) +{ + UINT i; + BYTE nt, *dir; + TCHAR *p, c; + + + p = fno->fname; + if (dj->sect) { + dir = dj->dir; + nt = dir[DIR_NTres]; /* NT flag */ + for (i = 0; i < 8; i++) { /* Copy name body */ + c = dir[i]; + if (c == ' ') break; + if (c == NDDE) c = (TCHAR)DDE; + if (_USE_LFN && (nt & NS_BODY) && IsUpper(c)) c += 0x20; +#if _LFN_UNICODE + if (IsDBCS1(c) && i < 7 && IsDBCS2(dir[i+1])) + c = (c << 8) | dir[++i]; + c = ff_convert(c, 1); + if (!c) c = '?'; +#endif + *p++ = c; + } + if (dir[8] != ' ') { /* Copy name extension */ + *p++ = '.'; + for (i = 8; i < 11; i++) { + c = dir[i]; + if (c == ' ') break; + if (_USE_LFN && (nt & NS_EXT) && IsUpper(c)) c += 0x20; +#if _LFN_UNICODE + if (IsDBCS1(c) && i < 10 && IsDBCS2(dir[i+1])) + c = (c << 8) | dir[++i]; + c = ff_convert(c, 1); + if (!c) c = '?'; +#endif + *p++ = c; + } + } + fno->fattrib = dir[DIR_Attr]; /* Attribute */ + fno->fsize = LD_DWORD(dir+DIR_FileSize); /* Size */ + fno->fdate = LD_WORD(dir+DIR_WrtDate); /* Date */ + fno->ftime = LD_WORD(dir+DIR_WrtTime); /* Time */ + } + *p = 0; /* Terminate SFN str by a \0 */ + +#if _USE_LFN + if (fno->lfname && fno->lfsize) { + TCHAR *tp = fno->lfname; + WCHAR w, *lfn; + + i = 0; + if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */ + lfn = dj->lfn; + while ((w = *lfn++) != 0) { /* Get an LFN char */ +#if !_LFN_UNICODE + w = ff_convert(w, 0); /* Unicode -> OEM conversion */ + if (!w) { i = 0; break; } /* Could not convert, no LFN */ + if (_DF1S && w >= 0x100) /* Put 1st byte if it is a DBC (always false on SBCS cfg) */ + tp[i++] = (TCHAR)(w >> 8); +#endif + if (i >= fno->lfsize - 1) { i = 0; break; } /* Buffer overflow, no LFN */ + tp[i++] = (TCHAR)w; + } + } + tp[i] = 0; /* Terminate the LFN str by a \0 */ + } +#endif +} +#endif /* _FS_MINIMIZE <= 1 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Follow a file path */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + FATFS_DIR *dj, /* Directory object to return last directory and found object */ + const TCHAR *path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + BYTE *dir, ns; + + +#if _FS_RPATH + if (*path == '/' || *path == '\\') { /* There is a heading separator */ + path++; dj->sclust = 0; /* Strip it and start from the root dir */ + } else { /* No heading separator */ + dj->sclust = dj->fs->cdir; /* Start from the current dir */ + } +#else + if (*path == '/' || *path == '\\') /* Strip heading separator if exist */ + path++; + dj->sclust = 0; /* Start from the root dir */ +#endif + + if ((UINT)*path < ' ') { /* Nul path means the start directory itself */ + res = dir_sdi(dj, 0); + dj->dir = 0; + } else { /* Follow path */ + for (;;) { + res = create_name(dj, &path); /* Get a segment */ + if (res != FR_OK) break; + res = dir_find(dj); /* Find it */ + ns = *(dj->fn+NS); + if (res != FR_OK) { /* Failed to find the object */ + if (res != FR_NO_FILE) break; /* Abort if any hard error occurred */ + /* Object not found */ + if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exit */ + dj->sclust = 0; dj->dir = 0; /* It is the root dir */ + res = FR_OK; + if (!(ns & NS_LAST)) continue; + } else { /* Could not find the object */ + if (!(ns & NS_LAST)) res = FR_NO_PATH; + } + break; + } + if (ns & NS_LAST) break; /* Last segment match. Function completed. */ + dir = dj->dir; /* There is next segment. Follow the sub directory */ + if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */ + res = FR_NO_PATH; break; + } + dj->sclust = ld_clust(dj->fs, dir); + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load a sector and check if it is an FAT Volume Boot Record */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:FAT-VBR, 1:Any BR but not FAT, 2:Not a BR, 3:Disk error */ + FATFS *fs, /* File system object */ + DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ +) +{ + if (disk_read(fs->drv, fs->win, sect, 1) != RES_OK) /* Load boot record */ + return 3; + if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check record signature (always placed at offset 510 even if the sector size is >512) */ + return 2; + + if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ + return 0; + if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) + return 0; + + return 1; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file system object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT chk_mounted ( /* FR_OK(0): successful, !=0: any error occurred */ + const TCHAR **path, /* Pointer to pointer to the path name (drive number) */ + FATFS **rfs, /* Pointer to pointer to the found file system object */ + BYTE wmode /* !=0: Check write protection for write access */ +) +{ + BYTE fmt, b, pi, *tbl; + UINT vol; + DSTATUS stat; + DWORD bsect, fasize, tsect, sysect, nclst, szbfat; + WORD nrsv; + const TCHAR *p = *path; + FATFS *fs; + + + /* Get logical drive number from the path name */ + vol = p[0] - '0'; /* Is there a drive number? */ + if (vol <= 9 && p[1] == ':') { /* Found a drive number, get and strip it */ + p += 2; *path = p; /* Return pointer to the path name */ + } else { /* No drive number is given */ +#if _FS_RPATH + vol = CurrVol; /* Use current drive */ +#else + vol = 0; /* Use drive 0 */ +#endif + } + + /* Check if the file system object is valid or not */ + *rfs = 0; + if (vol >= _VOLUMES) /* Is the drive number valid? */ + return FR_INVALID_DRIVE; + fs = FatFs[vol]; /* Get corresponding file system object */ + if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */ + + ENTER_FF(fs); /* Lock file system */ + + *rfs = fs; /* Return pointer to the corresponding file system object */ + if (fs->fs_type) { /* If the volume has been mounted */ + stat = disk_status(fs->drv); + if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized (has not been changed), */ + if (!_FS_READONLY && wmode && (stat & STA_PROTECT)) /* Check write protection if needed */ + return FR_WRITE_PROTECTED; + return FR_OK; /* The file system object is valid */ + } + } + + /* The file system object is not valid. */ + /* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */ + + fs->fs_type = 0; /* Clear the file system object */ + fs->drv = LD2PD(vol); /* Bind the logical drive and a physical drive */ + stat = disk_initialize(fs->drv); /* Initialize the physical drive */ + if (stat & STA_NOINIT) /* Check if the initialization succeeded */ + return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */ + if (!_FS_READONLY && wmode && (stat & STA_PROTECT)) /* Check disk write protection if needed */ + return FR_WRITE_PROTECTED; +#if _MAX_SS != 512 /* Get disk sector size (variable sector size cfg only) */ + if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &fs->ssize) != RES_OK) + return FR_DISK_ERR; +#endif + /* Search FAT partition on the drive. Supports only generic partitions, FDISK and SFD. */ + fmt = check_fs(fs, bsect = 0); /* Load sector 0 and check if it is an FAT-VBR (in SFD) */ + if (LD2PT(vol) && !fmt) fmt = 1; /* Force non-SFD if the volume is forced partition */ + if (fmt == 1) { /* Not an FAT-VBR, the physical drive can be partitioned */ + /* Check the partition listed in the partition table */ + pi = LD2PT(vol); + if (pi) pi--; + tbl = &fs->win[MBR_Table + pi * SZ_PTE];/* Partition table */ + if (tbl[4]) { /* Is the partition existing? */ + bsect = LD_DWORD(&tbl[8]); /* Partition offset in LBA */ + fmt = check_fs(fs, bsect); /* Check the partition */ + } + } + if (fmt == 3) return FR_DISK_ERR; + if (fmt) return FR_NO_FILESYSTEM; /* No FAT volume is found */ + + /* An FAT volume is found. Following code initializes the file system object */ + + if (LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs)) /* (BPB_BytsPerSec must be equal to the physical sector size) */ + return FR_NO_FILESYSTEM; + + fasize = LD_WORD(fs->win+BPB_FATSz16); /* Number of sectors per FAT */ + if (!fasize) fasize = LD_DWORD(fs->win+BPB_FATSz32); + fs->fsize = fasize; + + fs->n_fats = b = fs->win[BPB_NumFATs]; /* Number of FAT copies */ + if (b != 1 && b != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ + fasize *= b; /* Number of sectors for FAT area */ + + fs->csize = b = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */ + if (!b || (b & (b - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */ + + fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt); /* Number of root directory entries */ + if (fs->n_rootdir % (SS(fs) / SZ_DIR)) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be sector aligned) */ + + tsect = LD_WORD(fs->win+BPB_TotSec16); /* Number of sectors on the volume */ + if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32); + + nrsv = LD_WORD(fs->win+BPB_RsvdSecCnt); /* Number of reserved sectors */ + if (!nrsv) return FR_NO_FILESYSTEM; /* (BPB_RsvdSecCnt must not be 0) */ + + /* Determine the FAT sub type */ + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZ_DIR); /* RSV+FAT+DIR */ + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ + if (!nclst) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + fmt = FS_FAT12; + if (nclst >= MIN_FAT16) fmt = FS_FAT16; + if (nclst >= MIN_FAT32) fmt = FS_FAT32; + + /* Boundaries and Limits */ + fs->n_fatent = nclst + 2; /* Number of FAT entries */ + fs->database = bsect + sysect; /* Data start sector */ + fs->fatbase = bsect + nrsv; /* FAT start sector */ + if (fmt == FS_FAT32) { + if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ + fs->dirbase = LD_DWORD(fs->win+BPB_RootClus); /* Root directory start cluster */ + szbfat = fs->n_fatent * 4; /* (Required FAT size) */ + } else { + if (!fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */ + fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ + szbfat = (fmt == FS_FAT16) ? /* (Required FAT size) */ + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); + } + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) /* (BPB_FATSz must not be less than required) */ + return FR_NO_FILESYSTEM; + +#if !_FS_READONLY + /* Initialize cluster allocation information */ + fs->free_clust = 0xFFFFFFFF; + fs->last_clust = 0; + + /* Get fsinfo if available */ + if (fmt == FS_FAT32) { + fs->fsi_flag = 0; + fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo); + if (disk_read(fs->drv, fs->win, fs->fsi_sector, 1) == RES_OK && + LD_WORD(fs->win+BS_55AA) == 0xAA55 && + LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252 && + LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272) { + fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free); + fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count); + } + } +#endif + fs->fs_type = fmt; /* FAT sub-type */ + fs->id = ++Fsid; /* File system mount ID */ + fs->winsect = 0; /* Invalidate sector cache */ + fs->wflag = 0; +#if _FS_RPATH + fs->cdir = 0; /* Current directory (root dir) */ +#endif +#if _FS_LOCK /* Clear file lock semaphores */ + clear_lock(fs); +#endif + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/dir object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ + void* obj /* Pointer to the object FIL/DIR to check validity */ +) +{ + FIL *fil; + + + fil = (FIL*)obj; /* Assuming offset of fs and id in the FIL/DIR is identical */ + if (!fil->fs || !fil->fs->fs_type || fil->fs->id != fil->id) + return FR_INVALID_OBJECT; + + ENTER_FF(fil->fs); /* Lock file system */ + + if (disk_status(fil->fs->drv) & STA_NOINIT) + return FR_NOT_READY; + + return FR_OK; +} + + + + +/*-------------------------------------------------------------------------- + + Public Functions + +--------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Logical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mount ( + BYTE vol, /* Logical drive number to be mounted/unmounted */ + FATFS *fs /* Pointer to new file system object (NULL for unmount)*/ +) +{ + FATFS *rfs; + + + if (vol >= _VOLUMES) /* Check if the drive number is valid */ + return FR_INVALID_DRIVE; + rfs = FatFs[vol]; /* Get current fs object */ + + if (rfs) { +#if _FS_LOCK + clear_lock(rfs); +#endif +#if _FS_REENTRANT /* Discard sync object of the current volume */ + if (!ff_del_syncobj(rfs->sobj)) return FR_INT_ERR; +#endif + rfs->fs_type = 0; /* Clear old fs object */ + } + + if (fs) { + fs->fs_type = 0; /* Clear new fs object */ +#if _FS_REENTRANT /* Create sync object for the new volume */ + if (!ff_cre_syncobj(vol, &fs->sobj)) return FR_INT_ERR; +#endif + } + FatFs[vol] = fs; /* Register new fs object */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( + FIL *fp, /* Pointer to the blank file object */ + const TCHAR *path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + FATFS_DIR dj; + BYTE *dir; + DEF_NAMEBUF; + + + if (!fp) return FR_INVALID_OBJECT; + fp->fs = 0; /* Clear file object */ + +#if !_FS_READONLY + mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW; + res = chk_mounted(&path, &dj.fs, (BYTE)(mode & ~FA_READ)); +#else + mode &= FA_READ; + res = chk_mounted(&path, &dj.fs, 0); +#endif + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + dir = dj.dir; +#if !_FS_READONLY /* R/W configuration */ + if (res == FR_OK) { + if (!dir) /* Current dir itself */ + res = FR_INVALID_NAME; +#if _FS_LOCK + else + res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); +#endif + } + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + DWORD dw, cl; + + if (res != FR_OK) { /* No file, create new */ + if (res == FR_NO_FILE) /* There is no file to open, create a new entry */ +#if _FS_LOCK + res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES; +#else + res = dir_register(&dj); +#endif + mode |= FA_CREATE_ALWAYS; /* File is created */ + dir = dj.dir; /* New entry */ + } + else { /* Any object is already existing */ + if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */ + res = FR_DENIED; + } else { + if (mode & FA_CREATE_NEW) /* Cannot create as new file */ + res = FR_EXIST; + } + } + if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */ + dw = get_fattime(); /* Created time */ + ST_DWORD(dir+DIR_CrtTime, dw); + dir[DIR_Attr] = 0; /* Reset attribute */ + ST_DWORD(dir+DIR_FileSize, 0); /* size = 0 */ + cl = ld_clust(dj.fs, dir); /* Get start cluster */ + st_clust(dir, 0); /* cluster = 0 */ + dj.fs->wflag = 1; + if (cl) { /* Remove the cluster chain if exist */ + dw = dj.fs->winsect; + res = remove_chain(dj.fs, cl); + if (res == FR_OK) { + dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */ + res = move_window(dj.fs, dw); + } + } + } + } + else { /* Open an existing file */ + if (res == FR_OK) { /* Follow succeeded */ + if (dir[DIR_Attr] & AM_DIR) { /* It is a directory */ + res = FR_NO_FILE; + } else { + if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ + res = FR_DENIED; + } + } + } + if (res == FR_OK) { + if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */ + mode |= FA__WRITTEN; + fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dir; +#if _FS_LOCK + fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); + if (!fp->lockid) res = FR_INT_ERR; +#endif + } + +#else /* R/O configuration */ + if (res == FR_OK) { /* Follow succeeded */ + dir = dj.dir; + if (!dir) { /* Current dir itself */ + res = FR_INVALID_NAME; + } else { + if (dir[DIR_Attr] & AM_DIR) /* It is a directory */ + res = FR_NO_FILE; + } + } +#endif + FREE_BUF(); + + if (res == FR_OK) { + fp->flag = mode; /* File access mode */ + fp->sclust = ld_clust(dj.fs, dir); /* File start cluster */ + fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */ + fp->fptr = 0; /* File pointer */ + fp->dsect = 0; +#if _USE_FASTSEEK + fp->cltbl = 0; /* Normal seek mode */ +#endif + fp->fs = dj.fs; fp->id = dj.fs->id; /* Validate file object */ + } + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL *fp, /* Pointer to the file object */ + void *buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT *br /* Pointer to number of bytes read */ +) +{ + FRESULT res; + DWORD clst, sect, remain; + UINT rcnt, cc; + BYTE csect, *rbuff = (BYTE *)buff; + + + *br = 0; /* Clear read byte counter */ + + res = validate(fp); /* Check validity */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Aborted file? */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + remain = fp->fsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr; /* Repeat until all data read */ + rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if (!csect) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->sclust; /* Follow from the origin */ + } else { /* Middle or end of the file */ +#if _USE_FASTSEEK + if (fp->cltbl) + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + else +#endif + clst = get_fat(fp->fs, fp->clust); /* Follow cluster chain on the FAT */ + } + if (clst < 2) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - csect; + if (disk_read(fp->fs->drv, rbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ +#if _FS_TINY + if (fp->fs->wflag && fp->fs->winsect - sect < cc) + mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs)); +#else + if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc) + mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs)); +#endif +#endif + rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } +#if !_FS_TINY + if (fp->dsect != sect) { /* Load data sector if not in cache */ +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) /* Fill sector cache */ + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + } + rcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */ + if (rcnt > btr) rcnt = btr; +#if _FS_TINY + if (move_window(fp->fs, fp->dsect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ +#else + mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ +#endif + } + + LEAVE_FF(fp->fs, FR_OK); +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_write ( + FIL *fp, /* Pointer to the file object */ + const void *buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write */ + UINT *bw /* Pointer to number of bytes written */ +) +{ + FRESULT res; + DWORD clst, sect; + UINT wcnt, cc; + const BYTE *wbuff = (const BYTE *)buff; + BYTE csect; + + + *bw = 0; /* Clear write byte counter */ + + res = validate(fp); /* Check validity */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Aborted file? */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + if ((DWORD)(fp->fsize + btw) < fp->fsize) btw = 0; /* File size cannot reach 4GB */ + + for ( ; btw; /* Repeat until all data written */ + wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if (!csect) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->sclust; /* Follow from the origin */ + if (clst == 0) /* When no cluster is allocated, */ + fp->sclust = clst = create_chain(fp->fs, 0); /* Create a new cluster chain */ + } else { /* Middle or end of the file */ +#if _USE_FASTSEEK + if (fp->cltbl) + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + else +#endif + clst = create_chain(fp->fs, fp->clust); /* Follow or stretch cluster chain on the FAT */ + } + if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ + if (clst == 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } +#if _FS_TINY + if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0)) /* Write-back sector cache */ + ABORT(fp->fs, FR_DISK_ERR); +#else + if (fp->flag & FA__DIRTY) { /* Write-back sector cache */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Write maximum contiguous sectors directly */ + if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - csect; + if (disk_write(fp->fs->drv, wbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if _FS_TINY + if (fp->fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->fs->wflag = 0; + } +#else + if (fp->dsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->flag &= ~FA__DIRTY; + } +#endif + wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } +#if _FS_TINY + if (fp->fptr >= fp->fsize) { /* Avoid silly cache filling at growing edge */ + if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR); + fp->fs->winsect = sect; + } +#else + if (fp->dsect != sect) { /* Fill sector cache with file data */ + if (fp->fptr < fp->fsize && + disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + } + wcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));/* Put partial sector into file I/O buffer */ + if (wcnt > btw) wcnt = btw; +#if _FS_TINY + if (move_window(fp->fs, fp->dsect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ + fp->fs->wflag = 1; +#else + mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ + fp->flag |= FA__DIRTY; +#endif + } + + if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */ + fp->flag |= FA__WRITTEN; /* Set file change flag */ + + LEAVE_FF(fp->fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize the File Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL *fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD tim; + BYTE *dir; + + + res = validate(fp); /* Check validity of the object */ + if (res == FR_OK) { + if (fp->flag & FA__WRITTEN) { /* Has the file been written? */ +#if !_FS_TINY /* Write-back dirty buffer */ + if (fp->flag & FA__DIRTY) { + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + LEAVE_FF(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + /* Update the directory entry */ + res = move_window(fp->fs, fp->dir_sect); + if (res == FR_OK) { + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ + ST_DWORD(dir+DIR_FileSize, fp->fsize); /* Update file size */ + st_clust(dir, fp->sclust); /* Update start cluster */ + tim = get_fattime(); /* Update updated time */ + ST_DWORD(dir+DIR_WrtTime, tim); + ST_WORD(dir+DIR_LstAccDate, 0); + fp->flag &= ~FA__WRITTEN; + fp->fs->wflag = 1; + res = sync(fp->fs); + } + } + } + + LEAVE_FF(fp->fs, res); +} + +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Close File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_close ( + FIL *fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + + +#if _FS_READONLY + res = validate(fp); + { +#if _FS_REENTRANT + FATFS *fs = fp->fs; +#endif + if (res == FR_OK) fp->fs = 0; /* Discard file object */ + LEAVE_FF(fs, res); + } +#else + res = f_sync(fp); /* Flush cached data */ +#if _FS_LOCK + if (res == FR_OK) { /* Decrement open counter */ +#if _FS_REENTRANT + FATFS *fs = fp->fs;; + res = validate(fp); + if (res == FR_OK) { + res = dec_lock(fp->lockid); + unlock_fs(fs, FR_OK); + } +#else + res = dec_lock(fp->lockid); +#endif + } +#endif + if (res == FR_OK) fp->fs = 0; /* Discard file object */ + return res; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Current Drive/Directory Handlings */ +/*-----------------------------------------------------------------------*/ + +#if _FS_RPATH >= 1 + +FRESULT f_chdrive ( + BYTE drv /* Drive number */ +) +{ + if (drv >= _VOLUMES) return FR_INVALID_DRIVE; + + CurrVol = drv; + + return FR_OK; +} + + + +FRESULT f_chdir ( + const TCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 0); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the path */ + FREE_BUF(); + if (res == FR_OK) { /* Follow completed */ + if (!dj.dir) { + dj.fs->cdir = dj.sclust; /* Start directory itself */ + } else { + if (dj.dir[DIR_Attr] & AM_DIR) /* Reached to the directory */ + dj.fs->cdir = ld_clust(dj.fs, dj.dir); + else + res = FR_NO_PATH; /* Reached but a file */ + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(dj.fs, res); +} + + +#if _FS_RPATH >= 2 +FRESULT f_getcwd ( + TCHAR *path, /* Pointer to the directory path */ + UINT sz_path /* Size of path */ +) +{ + FRESULT res; + DIR dj; + UINT i, n; + DWORD ccl; + TCHAR *tp; + FILINFO fno; + DEF_NAMEBUF; + + + *path = 0; + res = chk_mounted((const TCHAR**)&path, &dj.fs, 0); /* Get current volume */ + if (res == FR_OK) { + INIT_BUF(dj); + i = sz_path; /* Bottom of buffer (dir stack base) */ + dj.sclust = dj.fs->cdir; /* Start to follow upper dir from current dir */ + while ((ccl = dj.sclust) != 0) { /* Repeat while current dir is a sub-dir */ + res = dir_sdi(&dj, 1); /* Get parent dir */ + if (res != FR_OK) break; + res = dir_read(&dj); + if (res != FR_OK) break; + dj.sclust = ld_clust(dj.fs, dj.dir); /* Goto parent dir */ + res = dir_sdi(&dj, 0); + if (res != FR_OK) break; + do { /* Find the entry links to the child dir */ + res = dir_read(&dj); + if (res != FR_OK) break; + if (ccl == ld_clust(dj.fs, dj.dir)) break; /* Found the entry */ + res = dir_next(&dj, 0); + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ + if (res != FR_OK) break; +#if _USE_LFN + fno.lfname = path; + fno.lfsize = i; +#endif + get_fileinfo(&dj, &fno); /* Get the dir name and push it to the buffer */ + tp = fno.fname; + if (_USE_LFN && *path) tp = path; + for (n = 0; tp[n]; n++) ; + if (i < n + 3) { + res = FR_NOT_ENOUGH_CORE; break; + } + while (n) path[--i] = tp[--n]; + path[--i] = '/'; + } + tp = path; + if (res == FR_OK) { + *tp++ = '0' + CurrVol; /* Put drive number */ + *tp++ = ':'; + if (i == sz_path) { /* Root-dir */ + *tp++ = '/'; + } else { /* Sub-dir */ + do /* Add stacked path str */ + *tp++ = path[i++]; + while (i < sz_path); + } + } + *tp = 0; + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} +#endif /* _FS_RPATH >= 2 */ +#endif /* _FS_RPATH >= 1 */ + + + +#if _FS_MINIMIZE <= 2 +/*-----------------------------------------------------------------------*/ +/* Seek File R/W Pointer */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_lseek ( + FIL *fp, /* Pointer to the file object */ + DWORD ofs /* File pointer from top of file */ +) +{ + FRESULT res; + + + res = validate(fp); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + +#if _USE_FASTSEEK + if (fp->cltbl) { /* Fast seek */ + DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl; + + if (ofs == CREATE_LINKMAP) { /* Create CLMT */ + tbl = fp->cltbl; + tlen = *tbl++; ulen = 2; /* Given table size and required table size */ + cl = fp->sclust; /* Top of the chain */ + if (cl) { + do { + /* Get a fragment */ + tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */ + do { + pcl = cl; ncl++; + cl = get_fat(fp->fs, cl); + if (cl <= 1) ABORT(fp->fs, FR_INT_ERR); + if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + } while (cl == pcl + 1); + if (ulen <= tlen) { /* Store the length and top of the fragment */ + *tbl++ = ncl; *tbl++ = tcl; + } + } while (cl < fp->fs->n_fatent); /* Repeat until end of chain */ + } + *fp->cltbl = ulen; /* Number of items used */ + if (ulen <= tlen) + *tbl = 0; /* Terminate table */ + else + res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */ + + } else { /* Fast seek */ + if (ofs > fp->fsize) /* Clip offset at the file size */ + ofs = fp->fsize; + fp->fptr = ofs; /* Set file pointer */ + if (ofs) { + fp->clust = clmt_clust(fp, ofs - 1); + dsc = clust2sect(fp->fs, fp->clust); + if (!dsc) ABORT(fp->fs, FR_INT_ERR); + dsc += (ofs - 1) / SS(fp->fs) & (fp->fs->csize - 1); + if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) { /* Refill sector cache if needed */ +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK) /* Load current sector */ + ABORT(fp->fs, FR_DISK_ERR); +#endif + fp->dsect = dsc; + } + } + } + } else +#endif + + /* Normal Seek */ + { + DWORD clst, bcs, nsect, ifptr; + + if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ +#if !_FS_READONLY + && !(fp->flag & FA_WRITE) +#endif + ) ofs = fp->fsize; + + ifptr = fp->fptr; + fp->fptr = nsect = 0; + if (ofs) { + bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ + if (ifptr > 0 && + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ + fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */ + ofs -= fp->fptr; + clst = fp->clust; + } else { /* When seek to back cluster, */ + clst = fp->sclust; /* start from the first cluster */ +#if !_FS_READONLY + if (clst == 0) { /* If no cluster chain, create a new chain */ + clst = create_chain(fp->fs, 0); + if (clst == 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->sclust = clst; + } +#endif + fp->clust = clst; + } + if (clst != 0) { + while (ofs > bcs) { /* Cluster following loop */ +#if !_FS_READONLY + if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ + clst = create_chain(fp->fs, clst); /* Force stretch if in write mode */ + if (clst == 0) { /* When disk gets full, clip file size */ + ofs = bcs; break; + } + } else +#endif + clst = get_fat(fp->fs, clst); /* Follow cluster chain if not in write mode */ + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR); + fp->clust = clst; + fp->fptr += bcs; + ofs -= bcs; + } + fp->fptr += ofs; + if (ofs % SS(fp->fs)) { + nsect = clust2sect(fp->fs, clst); /* Current sector */ + if (!nsect) ABORT(fp->fs, FR_INT_ERR); + nsect += ofs / SS(fp->fs); + } + } + } + if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { /* Fill sector cache if needed */ +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK) /* Fill sector cache */ + ABORT(fp->fs, FR_DISK_ERR); +#endif + fp->dsect = nsect; + } +#if !_FS_READONLY + if (fp->fptr > fp->fsize) { /* Set file change flag if the file size is extended */ + fp->fsize = fp->fptr; + fp->flag |= FA__WRITTEN; + } +#endif + } + + LEAVE_FF(fp->fs, res); +} + + + +#if _FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Create a Directory Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_opendir ( + FATFS_DIR *dj, /* Pointer to directory object to create */ + const TCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + FATFS *fs; + DEF_NAMEBUF; + + + if (!dj) return FR_INVALID_OBJECT; + + res = chk_mounted(&path, &dj->fs, 0); + fs = dj->fs; + if (res == FR_OK) { + INIT_BUF(*dj); + res = follow_path(dj, path); /* Follow the path to the directory */ + FREE_BUF(); + if (res == FR_OK) { /* Follow completed */ + if (dj->dir) { /* It is not the root dir */ + if (dj->dir[DIR_Attr] & AM_DIR) { /* The object is a directory */ + dj->sclust = ld_clust(fs, dj->dir); + } else { /* The object is not a directory */ + res = FR_NO_PATH; + } + } + if (res == FR_OK) { + dj->id = fs->id; + res = dir_sdi(dj, 0); /* Rewind dir */ + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + if (res != FR_OK) dj->fs = 0; /* Invalidate the dir object if function faild */ + } else { + dj->fs = 0; + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entry in Sequence */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_readdir ( + FATFS_DIR *dj, /* Pointer to the open directory object */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DEF_NAMEBUF; + + + res = validate(dj); /* Check validity of the object */ + if (res == FR_OK) { + if (!fno) { + res = dir_sdi(dj, 0); /* Rewind the directory object */ + } else { + INIT_BUF(*dj); + res = dir_read(dj); /* Read an directory item */ + if (res == FR_NO_FILE) { /* Reached end of dir */ + dj->sect = 0; + res = FR_OK; + } + if (res == FR_OK) { /* A valid entry is found */ + get_fileinfo(dj, fno); /* Get the object information */ + res = dir_next(dj, 0); /* Increment index for next */ + if (res == FR_NO_FILE) { + dj->sect = 0; + res = FR_OK; + } + } + FREE_BUF(); + } + } + + LEAVE_FF(dj->fs, res); +} + + + +#if _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const TCHAR *path, /* Pointer to the file path */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + FRESULT res; + FATFS_DIR dj; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 0); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.dir) /* Found an object */ + get_fileinfo(&dj, fno); + else /* It is root dir */ + res = FR_INVALID_NAME; + } + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const TCHAR *path, /* Pointer to the logical drive number (root dir) */ + DWORD *nclst, /* Pointer to the variable to return number of free clusters */ + FATFS **fatfs /* Pointer to pointer to corresponding file system object to return */ +) +{ + FRESULT res; + FATFS *fs; + DWORD n, clst, sect, stat; + UINT i; + BYTE fat, *p; + + + /* Get drive number */ + res = chk_mounted(&path, fatfs, 0); + fs = *fatfs; + if (res == FR_OK) { + /* If free_clust is valid, return it without full cluster scan */ + if (fs->free_clust <= fs->n_fatent - 2) { + *nclst = fs->free_clust; + } else { + /* Get number of free clusters */ + fat = fs->fs_type; + n = 0; + if (fat == FS_FAT12) { + clst = 2; + do { + stat = get_fat(fs, clst); + if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (stat == 1) { res = FR_INT_ERR; break; } + if (stat == 0) n++; + } while (++clst < fs->n_fatent); + } else { + clst = fs->n_fatent; + sect = fs->fatbase; + i = 0; p = 0; + do { + if (!i) { + res = move_window(fs, sect++); + if (res != FR_OK) break; + p = fs->win; + i = SS(fs); + } + if (fat == FS_FAT16) { + if (LD_WORD(p) == 0) n++; + p += 2; i -= 2; + } else { + if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++; + p += 4; i -= 4; + } + } while (--clst); + } + fs->free_clust = n; + if (fat == FS_FAT32) fs->fsi_flag = 1; + *nclst = n; + } + } + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Truncate File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_truncate ( + FIL *fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD ncl; + + + if (!fp) return FR_INVALID_OBJECT; + + res = validate(fp); /* Check validity of the object */ + if (res == FR_OK) { + if (fp->flag & FA__ERROR) { /* Check abort flag */ + res = FR_INT_ERR; + } else { + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + res = FR_DENIED; + } + } + if (res == FR_OK) { + if (fp->fsize > fp->fptr) { + fp->fsize = fp->fptr; /* Set file size to current R/W point */ + fp->flag |= FA__WRITTEN; + if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ + res = remove_chain(fp->fs, fp->sclust); + fp->sclust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(fp->fs, fp->clust); + res = FR_OK; + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (ncl == 1) res = FR_INT_ERR; + if (res == FR_OK && ncl < fp->fs->n_fatent) { + res = put_fat(fp->fs, fp->clust, 0x0FFFFFFF); + if (res == FR_OK) res = remove_chain(fp->fs, ncl); + } + } + } + if (res != FR_OK) fp->flag |= FA__ERROR; + } + + LEAVE_FF(fp->fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File or Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const TCHAR *path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + FATFS_DIR dj, sdj; + BYTE *dir; + DWORD dclst; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; /* Cannot remove dot entry */ +#if _FS_LOCK + if (res == FR_OK) res = chk_lock(&dj, 2); /* Cannot remove open file */ +#endif + if (res == FR_OK) { /* The object is accessible */ + dir = dj.dir; + if (!dir) { + res = FR_INVALID_NAME; /* Cannot remove the start directory */ + } else { + if (dir[DIR_Attr] & AM_RDO) + res = FR_DENIED; /* Cannot remove R/O object */ + } + dclst = ld_clust(dj.fs, dir); + if (res == FR_OK && (dir[DIR_Attr] & AM_DIR)) { /* Is it a sub-dir? */ + if (dclst < 2) { + res = FR_INT_ERR; + } else { + mem_cpy(&sdj, &dj, sizeof (FATFS_DIR)); /* Check if the sub-dir is empty or not */ + sdj.sclust = dclst; + res = dir_sdi(&sdj, 2); /* Exclude dot entries */ + if (res == FR_OK) { + res = dir_read(&sdj); + if (res == FR_OK /* Not empty dir */ +#if _FS_RPATH + || dclst == dj.fs->cdir /* Current dir */ +#endif + ) res = FR_DENIED; + if (res == FR_NO_FILE) res = FR_OK; /* Empty */ + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj); /* Remove the directory entry */ + if (res == FR_OK) { + if (dclst) /* Remove the cluster chain if exist */ + res = remove_chain(dj.fs, dclst); + if (res == FR_OK) res = sync(dj.fs); + } + } + } + FREE_BUF(); + } + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const TCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + FATFS_DIR dj; + BYTE *dir, n; + DWORD dsc, dcl, pcl, tim = get_fattime(); + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */ + if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_NO_FILE) { /* Can create a new directory */ + dcl = create_chain(dj.fs, 0); /* Allocate a cluster for the new directory table */ + res = FR_OK; + if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */ + if (dcl == 1) res = FR_INT_ERR; + if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) /* Flush FAT */ + res = move_window(dj.fs, 0); + if (res == FR_OK) { /* Initialize the new directory table */ + dsc = clust2sect(dj.fs, dcl); + dir = dj.fs->win; + mem_set(dir, 0, SS(dj.fs)); + mem_set(dir+DIR_Name, ' ', 8+3); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + ST_DWORD(dir+DIR_WrtTime, tim); + st_clust(dir, dcl); + mem_cpy(dir+SZ_DIR, dir, SZ_DIR); /* Create ".." entry */ + dir[33] = '.'; pcl = dj.sclust; + if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase) + pcl = 0; + st_clust(dir+SZ_DIR, pcl); + for (n = dj.fs->csize; n; n--) { /* Write dot entries and clear following sectors */ + dj.fs->winsect = dsc++; + dj.fs->wflag = 1; + res = move_window(dj.fs, 0); + if (res != FR_OK) break; + mem_set(dir, 0, SS(dj.fs)); + } + } + if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */ + if (res != FR_OK) { + remove_chain(dj.fs, dcl); /* Could not register, remove cluster chain */ + } else { + dir = dj.dir; + dir[DIR_Attr] = AM_DIR; /* Attribute */ + ST_DWORD(dir+DIR_WrtTime, tim); /* Created time */ + st_clust(dir, dcl); /* Table start cluster */ + dj.fs->wflag = 1; + res = sync(dj.fs); + } + } + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const TCHAR *path, /* Pointer to the file path */ + BYTE value, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + FATFS_DIR dj; + BYTE *dir; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + FREE_BUF(); + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_OK) { + dir = dj.dir; + if (!dir) { /* Is it a root directory? */ + res = FR_INVALID_NAME; + } else { /* File or sub directory */ + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ + dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + dj.fs->wflag = 1; + res = sync(dj.fs); + } + } + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Timestamp */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_utime ( + const TCHAR *path, /* Pointer to the file/directory name */ + const FILINFO *fno /* Pointer to the time stamp to be set */ +) +{ + FRESULT res; + FATFS_DIR dj; + BYTE *dir; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + FREE_BUF(); + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_OK) { + dir = dj.dir; + if (!dir) { /* Root directory */ + res = FR_INVALID_NAME; + } else { /* File or sub-directory */ + ST_WORD(dir+DIR_WrtTime, fno->ftime); + ST_WORD(dir+DIR_WrtDate, fno->fdate); + dj.fs->wflag = 1; + res = sync(dj.fs); + } + } + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Rename File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const TCHAR *path_old, /* Pointer to the old name */ + const TCHAR *path_new /* Pointer to the new name */ +) +{ + FRESULT res; + FATFS_DIR djo, djn; + BYTE buf[21], *dir; + DWORD dw; + DEF_NAMEBUF; + + + res = chk_mounted(&path_old, &djo.fs, 1); + if (res == FR_OK) { + djn.fs = djo.fs; + INIT_BUF(djo); + res = follow_path(&djo, path_old); /* Check old object */ + if (_FS_RPATH && res == FR_OK && (djo.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; +#if _FS_LOCK + if (res == FR_OK) res = chk_lock(&djo, 2); +#endif + if (res == FR_OK) { /* Old object is found */ + if (!djo.dir) { /* Is root dir? */ + res = FR_NO_FILE; + } else { + mem_cpy(buf, djo.dir+DIR_Attr, 21); /* Save the object information except for name */ + mem_cpy(&djn, &djo, sizeof (FATFS_DIR)); /* Check new object */ + res = follow_path(&djn, path_new); + if (res == FR_OK) res = FR_EXIST; /* The new object name is already existing */ + if (res == FR_NO_FILE) { /* Is it a valid path and no name collision? */ +/* Start critical section that an interruption or error can cause cross-link */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + dir = djn.dir; /* Copy object information except for name */ + mem_cpy(dir+13, buf+2, 19); + dir[DIR_Attr] = buf[0] | AM_ARC; + djo.fs->wflag = 1; + if (djo.sclust != djn.sclust && (dir[DIR_Attr] & AM_DIR)) { /* Update .. entry in the directory if needed */ + dw = clust2sect(djo.fs, ld_clust(djo.fs, dir)); + if (!dw) { + res = FR_INT_ERR; + } else { + res = move_window(djo.fs, dw); + dir = djo.fs->win+SZ_DIR; /* .. entry */ + if (res == FR_OK && dir[1] == '.') { + dw = (djo.fs->fs_type == FS_FAT32 && djn.sclust == djo.fs->dirbase) ? 0 : djn.sclust; + st_clust(dir, dw); + djo.fs->wflag = 1; + } + } + } + if (res == FR_OK) { + res = dir_remove(&djo); /* Remove old entry */ + if (res == FR_OK) + res = sync(djo.fs); + } + } +/* End critical section */ + } + } + } + FREE_BUF(); + } + LEAVE_FF(djo.fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_MINIMIZE == 0 */ +#endif /* _FS_MINIMIZE <= 1 */ +#endif /* _FS_MINIMIZE <= 2 */ + + + +/*-----------------------------------------------------------------------*/ +/* Forward data to the stream directly (available on only tiny cfg) */ +/*-----------------------------------------------------------------------*/ +#if _USE_FORWARD && _FS_TINY + +FRESULT f_forward ( + FIL *fp, /* Pointer to the file object */ + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ + UINT btr, /* Number of bytes to forward */ + UINT *bf /* Pointer to number of bytes forwarded */ +) +{ + FRESULT res; + DWORD remain, clst, sect; + UINT rcnt; + BYTE csect; + + + *bf = 0; /* Clear transfer byte counter */ + + if (!fp) return FR_INVALID_OBJECT; + + res = validate(fp); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check error flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + + remain = fp->fsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr && (*func)(0, 0); /* Repeat until all data transferred or stream becomes busy */ + fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) { + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (!csect) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->sclust : get_fat(fp->fs, fp->clust); + if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + } + sect = clust2sect(fp->fs, fp->clust); /* Get current data sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + if (move_window(fp->fs, sect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + fp->dsect = sect; + rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs)); /* Forward data from sector window */ + if (rcnt > btr) rcnt = btr; + rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt); + if (!rcnt) ABORT(fp->fs, FR_INT_ERR); + } + + LEAVE_FF(fp->fs, FR_OK); +} +#endif /* _USE_FORWARD */ + + + +#if _USE_MKFS && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Create File System on the Drive */ +/*-----------------------------------------------------------------------*/ +#define N_ROOTDIR 512 /* Number of root dir entries for FAT12/16 */ +#define N_FATS 1 /* Number of FAT copies (1 or 2) */ + + +FRESULT f_mkfs ( + BYTE drv, /* Logical drive number */ + BYTE sfd, /* Partitioning rule 0:FDISK, 1:SFD */ + UINT au /* Allocation unit size [bytes] */ +) +{ + static const WORD vst[] = { 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 0}; + static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512}; + BYTE fmt, md, sys, *tbl, pdrv, part; + DWORD n_clst, vs, n, wsect; + UINT i; + DWORD b_vol, b_fat, b_dir, b_data; /* LBA */ + DWORD n_vol, n_rsv, n_fat, n_dir; /* Size */ + FATFS *fs; + DSTATUS stat; + + + /* Check mounted drive and clear work area */ + if (drv >= _VOLUMES) return FR_INVALID_DRIVE; + if (sfd > 1) return FR_INVALID_PARAMETER; + if (au & (au - 1)) return FR_INVALID_PARAMETER; + fs = FatFs[drv]; + if (!fs) return FR_NOT_ENABLED; + fs->fs_type = 0; + pdrv = LD2PD(drv); /* Physical drive */ + part = LD2PT(drv); /* Partition (0:auto detect, 1-4:get from partition table)*/ + + /* Get disk statics */ + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; +#if _MAX_SS != 512 /* Get disk sector size */ + if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS) + return FR_DISK_ERR; +#endif + if (_MULTI_PARTITION && part) { + /* Get partition information from partition table in the MBR */ + if (disk_read(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR; + if (LD_WORD(fs->win+BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; + tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE]; + if (!tbl[4]) return FR_MKFS_ABORTED; /* No partition? */ + b_vol = LD_DWORD(tbl+8); /* Volume start sector */ + n_vol = LD_DWORD(tbl+12); /* Volume size */ + } else { + /* Create a partition in this function */ + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128) + return FR_DISK_ERR; + b_vol = (sfd) ? 0 : 63; /* Volume start sector */ + n_vol -= b_vol; /* Volume size */ + } + + if (!au) { /* AU auto selection */ + vs = n_vol / (2000 / (SS(fs) / 512)); + for (i = 0; vs < vst[i]; i++) ; + au = cst[i]; + } + au /= SS(fs); /* Number of sectors per cluster */ + if (au == 0) au = 1; + if (au > 128) au = 128; + + /* Pre-compute number of clusters and FAT sub-type */ + n_clst = n_vol / au; + fmt = FS_FAT12; + if (n_clst >= MIN_FAT16) fmt = FS_FAT16; + if (n_clst >= MIN_FAT32) fmt = FS_FAT32; + + /* Determine offset and size of FAT structure */ + if (fmt == FS_FAT32) { + n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs); + n_rsv = 32; + n_dir = 0; + } else { + n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4; + n_fat = (n_fat + SS(fs) - 1) / SS(fs); + n_rsv = 1; + n_dir = (DWORD)N_ROOTDIR * SZ_DIR / SS(fs); + } + b_fat = b_vol + n_rsv; /* FAT area start sector */ + b_dir = b_fat + n_fat * N_FATS; /* Directory area start sector */ + b_data = b_dir + n_dir; /* Data area start sector */ + if (n_vol < b_data + au - b_vol) return FR_MKFS_ABORTED; /* Too small volume */ + + /* Align data start sector to erase block boundary (for flash memory media) */ + if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1; + n = (b_data + n - 1) & ~(n - 1); /* Next nearest erase block from current data start */ + n = (n - b_data) / N_FATS; + if (fmt == FS_FAT32) { /* FAT32: Move FAT offset */ + n_rsv += n; + b_fat += n; + } else { /* FAT12/16: Expand FAT size */ + n_fat += n; + } + + /* Determine number of clusters and final check of validity of the FAT sub-type */ + n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au; + if ( (fmt == FS_FAT16 && n_clst < MIN_FAT16) + || (fmt == FS_FAT32 && n_clst < MIN_FAT32)) + return FR_MKFS_ABORTED; + + switch (fmt) { /* Determine system ID for partition table */ + case FS_FAT12: sys = 0x01; break; + case FS_FAT16: sys = (n_vol < 0x10000) ? 0x04 : 0x06; break; + default: sys = 0x0C; + } + + if (_MULTI_PARTITION && part) { + /* Update system ID in the partition table */ + tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE]; + tbl[4] = sys; + if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR; + md = 0xF8; + } else { + if (sfd) { /* No partition table (SFD) */ + md = 0xF0; + } else { /* Create partition table (FDISK) */ + mem_set(fs->win, 0, SS(fs)); + tbl = fs->win+MBR_Table; /* Create partition table for single partition in the drive */ + tbl[1] = 1; /* Partition start head */ + tbl[2] = 1; /* Partition start sector */ + tbl[3] = 0; /* Partition start cylinder */ + tbl[4] = sys; /* System type */ + tbl[5] = 254; /* Partition end head */ + n = (b_vol + n_vol) / 63 / 255; + tbl[6] = (BYTE)((n >> 2) | 63); /* Partition end sector */ + tbl[7] = (BYTE)n; /* End cylinder */ + ST_DWORD(tbl+8, 63); /* Partition start in LBA */ + ST_DWORD(tbl+12, n_vol); /* Partition size in LBA */ + ST_WORD(fs->win+BS_55AA, 0xAA55); /* MBR signature */ + if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) /* Write it to the MBR sector */ + return FR_DISK_ERR; + md = 0xF8; + } + } + + /* Create BPB in the VBR */ + tbl = fs->win; /* Clear sector */ + mem_set(tbl, 0, SS(fs)); + mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */ + i = SS(fs); /* Sector size */ + ST_WORD(tbl+BPB_BytsPerSec, i); + tbl[BPB_SecPerClus] = (BYTE)au; /* Sectors per cluster */ + ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */ + tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ + i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR; /* Number of rootdir entries */ + ST_WORD(tbl+BPB_RootEntCnt, i); + if (n_vol < 0x10000) { /* Number of total sectors */ + ST_WORD(tbl+BPB_TotSec16, n_vol); + } else { + ST_DWORD(tbl+BPB_TotSec32, n_vol); + } + tbl[BPB_Media] = md; /* Media descriptor */ + ST_WORD(tbl+BPB_SecPerTrk, 63); /* Number of sectors per track */ + ST_WORD(tbl+BPB_NumHeads, 255); /* Number of heads */ + ST_DWORD(tbl+BPB_HiddSec, b_vol); /* Hidden sectors */ + n = get_fattime(); /* Use current time as VSN */ + if (fmt == FS_FAT32) { + ST_DWORD(tbl+BS_VolID32, n); /* VSN */ + ST_DWORD(tbl+BPB_FATSz32, n_fat); /* Number of sectors per FAT */ + ST_DWORD(tbl+BPB_RootClus, 2); /* Root directory start cluster (2) */ + ST_WORD(tbl+BPB_FSInfo, 1); /* FSInfo record offset (VBR+1) */ + ST_WORD(tbl+BPB_BkBootSec, 6); /* Backup boot record offset (VBR+6) */ + tbl[BS_DrvNum32] = 0x80; /* Drive number */ + tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(tbl+BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + } else { + ST_DWORD(tbl+BS_VolID, n); /* VSN */ + ST_WORD(tbl+BPB_FATSz16, n_fat); /* Number of sectors per FAT */ + tbl[BS_DrvNum] = 0x80; /* Drive number */ + tbl[BS_BootSig] = 0x29; /* Extended boot signature */ + mem_cpy(tbl+BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ + } + ST_WORD(tbl+BS_55AA, 0xAA55); /* Signature (Offset is fixed here regardless of sector size) */ + if (disk_write(pdrv, tbl, b_vol, 1) != RES_OK) /* Write it to the VBR sector */ + return FR_DISK_ERR; + if (fmt == FS_FAT32) /* Write backup VBR if needed (VBR+6) */ + disk_write(pdrv, tbl, b_vol + 6, 1); + + /* Initialize FAT area */ + wsect = b_fat; + for (i = 0; i < N_FATS; i++) { /* Initialize each FAT copy */ + mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */ + n = md; /* Media descriptor byte */ + if (fmt != FS_FAT32) { + n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00; + ST_DWORD(tbl+0, n); /* Reserve cluster #0-1 (FAT12/16) */ + } else { + n |= 0xFFFFFF00; + ST_DWORD(tbl+0, n); /* Reserve cluster #0-1 (FAT32) */ + ST_DWORD(tbl+4, 0xFFFFFFFF); + ST_DWORD(tbl+8, 0x0FFFFFFF); /* Reserve cluster #2 for root dir */ + } + if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + mem_set(tbl, 0, SS(fs)); /* Fill following FAT entries with zero */ + for (n = 1; n < n_fat; n++) { /* This loop may take a time on FAT32 volume due to many single sector writes */ + if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + } + } + + /* Initialize root directory */ + i = (fmt == FS_FAT32) ? au : n_dir; + do { + if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + } while (--i); + +#if _USE_ERASE /* Erase data area if needed */ + { + DWORD eb[2]; + + eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1; + disk_ioctl(pdrv, CTRL_ERASE_SECTOR, eb); + } +#endif + + /* Create FSInfo if needed */ + if (fmt == FS_FAT32) { + ST_DWORD(tbl+FSI_LeadSig, 0x41615252); + ST_DWORD(tbl+FSI_StrucSig, 0x61417272); + ST_DWORD(tbl+FSI_Free_Count, n_clst - 1); /* Number of free clusters */ + ST_DWORD(tbl+FSI_Nxt_Free, 2); /* Last allocated cluster# */ + ST_WORD(tbl+BS_55AA, 0xAA55); + disk_write(pdrv, tbl, b_vol + 1, 1); /* Write original (VBR+1) */ + disk_write(pdrv, tbl, b_vol + 7, 1); /* Write backup (VBR+7) */ + } + + return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR; +} + + +#if _MULTI_PARTITION == 2 +/*-----------------------------------------------------------------------*/ +/* Divide Physical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_fdisk ( + BYTE pdrv, /* Physical drive number */ + const DWORD szt[], /* Pointer to the size table for each partitions */ + void* work /* Pointer to the working buffer */ +) +{ + UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl; + BYTE s_hd, e_hd, *p, *buf = (BYTE*)work; + DSTATUS stat; + DWORD sz_disk, sz_part, s_part; + + + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR; + + /* Determine CHS in the table regardless of the drive geometry */ + for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ; + if (n == 256) n--; + e_hd = n - 1; + sz_cyl = 63 * n; + tot_cyl = sz_disk / sz_cyl; + + /* Create partition table */ + mem_set(buf, 0, _MAX_SS); + p = buf + MBR_Table; b_cyl = 0; + for (i = 0; i < 4; i++, p += SZ_PTE) { + p_cyl = (szt[i] <= 100) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl; + if (!p_cyl) continue; + s_part = (DWORD)sz_cyl * b_cyl; + sz_part = (DWORD)sz_cyl * p_cyl; + if (i == 0) { /* Exclude first track of cylinder 0 */ + s_hd = 1; + s_part += 63; sz_part -= 63; + } else { + s_hd = 0; + } + e_cyl = b_cyl + p_cyl - 1; + if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER; + + /* Set partition table */ + p[1] = s_hd; /* Start head */ + p[2] = (BYTE)((b_cyl >> 2) + 1); /* Start sector */ + p[3] = (BYTE)b_cyl; /* Start cylinder */ + p[4] = 0x06; /* System type (temporary setting) */ + p[5] = e_hd; /* End head */ + p[6] = (BYTE)((e_cyl >> 2) + 63); /* End sector */ + p[7] = (BYTE)e_cyl; /* End cylinder */ + ST_DWORD(p + 8, s_part); /* Start sector in LBA */ + ST_DWORD(p + 12, sz_part); /* Partition size */ + + /* Next partition */ + b_cyl += p_cyl; + } + ST_WORD(p, 0xAA55); + + /* Write it to the MBR */ + return (disk_write(pdrv, buf, 0, 1) || disk_ioctl(pdrv, CTRL_SYNC, 0)) ? FR_DISK_ERR : FR_OK; +} + + +#endif /* _MULTI_PARTITION == 2 */ +#endif /* _USE_MKFS && !_FS_READONLY */ + + + + +#if _USE_STRFUNC +/*-----------------------------------------------------------------------*/ +/* Get a string from the file */ +/*-----------------------------------------------------------------------*/ +TCHAR* f_gets ( + TCHAR* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer (characters) */ + FIL* fil /* Pointer to the file object */ +) +{ + int n = 0; + TCHAR c, *p = buff; + BYTE s[2]; + UINT rc; + + + while (n < len - 1) { /* Read bytes until buffer gets filled */ + f_read(fil, s, 1, &rc); + if (rc != 1) break; /* Break on EOF or error */ + c = s[0]; +#if _LFN_UNICODE /* Read a character in UTF-8 encoding */ + if (c >= 0x80) { + if (c < 0xC0) continue; /* Skip stray trailer */ + if (c < 0xE0) { /* Two-byte sequence */ + f_read(fil, s, 1, &rc); + if (rc != 1) break; + c = ((c & 0x1F) << 6) | (s[0] & 0x3F); + if (c < 0x80) c = '?'; + } else { + if (c < 0xF0) { /* Three-byte sequence */ + f_read(fil, s, 2, &rc); + if (rc != 2) break; + c = (c << 12) | ((s[0] & 0x3F) << 6) | (s[1] & 0x3F); + if (c < 0x800) c = '?'; + } else { /* Reject four-byte sequence */ + c = '?'; + } + } + } +#endif +#if _USE_STRFUNC >= 2 + if (c == '\r') continue; /* Strip '\r' */ +#endif + *p++ = c; + n++; + if (c == '\n') break; /* Break on EOL */ + } + *p = 0; + return n ? buff : 0; /* When no data read (eof or error), return with error. */ +} + + + +#if !_FS_READONLY +#include <stdarg.h> +/*-----------------------------------------------------------------------*/ +/* Put a character to the file */ +/*-----------------------------------------------------------------------*/ +int f_putc ( + TCHAR c, /* A character to be output */ + FIL* fil /* Pointer to the file object */ +) +{ + UINT bw, btw; + BYTE s[3]; + + +#if _USE_STRFUNC >= 2 + if (c == '\n') f_putc ('\r', fil); /* LF -> CRLF conversion */ +#endif + +#if _LFN_UNICODE /* Write the character in UTF-8 encoding */ + if (c < 0x80) { /* 7-bit */ + s[0] = (BYTE)c; + btw = 1; + } else { + if (c < 0x800) { /* 11-bit */ + s[0] = (BYTE)(0xC0 | (c >> 6)); + s[1] = (BYTE)(0x80 | (c & 0x3F)); + btw = 2; + } else { /* 16-bit */ + s[0] = (BYTE)(0xE0 | (c >> 12)); + s[1] = (BYTE)(0x80 | ((c >> 6) & 0x3F)); + s[2] = (BYTE)(0x80 | (c & 0x3F)); + btw = 3; + } + } +#else /* Write the character without conversion */ + s[0] = (BYTE)c; + btw = 1; +#endif + f_write(fil, s, btw, &bw); /* Write the char to the file */ + return (bw == btw) ? 1 : EOF; /* Return the result */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a string to the file */ +/*-----------------------------------------------------------------------*/ +int f_puts ( + const TCHAR* str, /* Pointer to the string to be output */ + FIL* fil /* Pointer to the file object */ +) +{ + int n; + + + for (n = 0; *str; str++, n++) { + if (f_putc(*str, fil) == EOF) return EOF; + } + return n; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a formatted string to the file */ +/*-----------------------------------------------------------------------*/ +int f_printf ( + FIL* fil, /* Pointer to the file object */ + const TCHAR* str, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + BYTE f, r; + UINT i, j, w; + ULONG v; + TCHAR c, d, s[16], *p; + int res, chc, cc; + + + va_start(arp, str); + + for (cc = res = 0; cc != EOF; res += cc) { + c = *str++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape character */ + cc = f_putc(c, fil); + if (cc != EOF) cc = 1; + continue; + } + w = f = 0; + c = *str++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *str++; + } else { + if (c == '-') { /* Flag: left justified */ + f = 2; c = *str++; + } + } + while (IsDigit(c)) { /* Precision */ + w = w * 10 + c - '0'; + c = *str++; + } + if (c == 'l' || c == 'L') { /* Prefix: Size is long int */ + f |= 4; c = *str++; + } + if (!c) break; + d = c; + if (IsLower(d)) d -= 0x20; + switch (d) { /* Type is... */ + case 'S' : /* String */ + p = va_arg(arp, TCHAR*); + for (j = 0; p[j]; j++) ; + chc = 0; + if (!(f & 2)) { + while (j++ < w) chc += (cc = f_putc(' ', fil)); + } + chc += (cc = f_puts(p, fil)); + while (j++ < w) chc += (cc = f_putc(' ', fil)); + if (cc != EOF) cc = chc; + continue; + case 'C' : /* Character */ + cc = f_putc((TCHAR)va_arg(arp, int), fil); continue; + case 'B' : /* Binary */ + r = 2; break; + case 'O' : /* Octal */ + r = 8; break; + case 'D' : /* Signed decimal */ + case 'U' : /* Unsigned decimal */ + r = 10; break; + case 'X' : /* Hexdecimal */ + r = 16; break; + default: /* Unknown type (pass-through) */ + cc = f_putc(c, fil); continue; + } + + /* Get an argument and put it in numeral */ + v = (f & 4) ? (ULONG)va_arg(arp, long) : ((d == 'D') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int)); + if (d == 'D' && (v & 0x80000000)) { + v = 0 - v; + f |= 8; + } + i = 0; + do { + d = (TCHAR)(v % r); v /= r; + if (d > 9) d += (c == 'x') ? 0x27 : 0x07; + s[i++] = d + '0'; + } while (v && i < sizeof s / sizeof s[0]); + if (f & 8) s[i++] = '-'; + j = i; d = (f & 1) ? '0' : ' '; + res = 0; + while (!(f & 2) && j++ < w) res += (cc = f_putc(d, fil)); + do res += (cc = f_putc(s[--i], fil)); while(i); + while (j++ < w) res += (cc = f_putc(' ', fil)); + if (cc != EOF) cc = res; + } + + va_end(arp); + return (cc == EOF) ? cc : res; +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_STRFUNC */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/FATFileSystem/ChaN/ff.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,337 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module include file R0.09a (C)ChaN, 2012 +/----------------------------------------------------------------------------/ +/ FatFs module is a generic FAT file system module for small embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following terms. +/ +/ Copyright (C) 2012, ChaN, all right reserved. +/ +/ * The FatFs module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/ +/----------------------------------------------------------------------------*/ + +#ifndef _FATFS +#define _FATFS 4004 /* Revision ID */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "integer.h" /* Basic integer types */ +#include "ffconf.h" /* FatFs configuration options */ + +#if _FATFS != _FFCONF +#error Wrong configuration file (ffconf.h). +#endif + + + +/* Definitions of volume management */ + +#if _MULTI_PARTITION /* Multiple partition configuration */ +typedef struct { + BYTE pd; /* Physical drive number */ + BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ +} PARTITION; +extern PARTITION VolToPart[]; /* Volume - Partition resolution table */ +#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */ +#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */ + +#else /* Single partition configuration */ +#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */ +#define LD2PT(vol) 0 /* Always mounts the 1st partition or in SFD */ + +#endif + + + +/* Type of path name strings on FatFs API */ + +#if _LFN_UNICODE /* Unicode string */ +#if !_USE_LFN +#error _LFN_UNICODE must be 0 in non-LFN cfg. +#endif +#ifndef _INC_TCHAR +typedef WCHAR TCHAR; +#define _T(x) L ## x +#define _TEXT(x) L ## x +#endif + +#else /* ANSI/OEM string */ +#ifndef _INC_TCHAR +typedef char TCHAR; +#define _T(x) x +#define _TEXT(x) x +#endif + +#endif + + + +/* File system object structure (FATFS) */ + +typedef struct { + BYTE fs_type; /* FAT sub-type (0:Not mounted) */ + BYTE drv; /* Physical drive number */ + BYTE csize; /* Sectors per cluster (1,2,4...128) */ + BYTE n_fats; /* Number of FAT copies (1,2) */ + BYTE wflag; /* win[] dirty flag (1:must be written back) */ + BYTE fsi_flag; /* fsinfo dirty flag (1:must be written back) */ + WORD id; /* File system mount ID */ + WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ +#if _MAX_SS != 512 + WORD ssize; /* Bytes per sector (512, 1024, 2048 or 4096) */ +#endif +#if _FS_REENTRANT + _SYNC_t sobj; /* Identifier of sync object */ +#endif +#if !_FS_READONLY + DWORD last_clust; /* Last allocated cluster */ + DWORD free_clust; /* Number of free clusters */ + DWORD fsi_sector; /* fsinfo sector (FAT32) */ +#endif +#if _FS_RPATH + DWORD cdir; /* Current directory start cluster (0:root) */ +#endif + DWORD n_fatent; /* Number of FAT entries (= number of clusters + 2) */ + DWORD fsize; /* Sectors per FAT */ + DWORD fatbase; /* FAT start sector */ + DWORD dirbase; /* Root directory start sector (FAT32:Cluster#) */ + DWORD database; /* Data start sector */ + DWORD winsect; /* Current sector appearing in the win[] */ + BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and Data on tiny cfg) */ +} FATFS; + + + +/* File object structure (FIL) */ + +typedef struct { + FATFS* fs; /* Pointer to the related file system object */ + WORD id; /* File system mount ID of the related file system object */ + BYTE flag; /* File status flags */ + BYTE pad1; + DWORD fptr; /* File read/write pointer (0ed on file open) */ + DWORD fsize; /* File size */ + DWORD sclust; /* File data start cluster (0:no data cluster, always 0 when fsize is 0) */ + DWORD clust; /* Current cluster of fpter */ + DWORD dsect; /* Current data sector of fpter */ +#if !_FS_READONLY + DWORD dir_sect; /* Sector containing the directory entry */ + BYTE* dir_ptr; /* Pointer to the directory entry in the window */ +#endif +#if _USE_FASTSEEK + DWORD* cltbl; /* Pointer to the cluster link map table (null on file open) */ +#endif +#if _FS_LOCK + UINT lockid; /* File lock ID (index of file semaphore table Files[]) */ +#endif +#if !_FS_TINY + BYTE buf[_MAX_SS]; /* File data read/write buffer */ +#endif +} FIL; + + + +/* Directory object structure (DIR) */ + +typedef struct { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + WORD index; /* Current read/write index number */ + DWORD sclust; /* Table start cluster (0:Root dir) */ + DWORD clust; /* Current cluster */ + DWORD sect; /* Current sector */ + BYTE* dir; /* Pointer to the current SFN entry in the win[] */ + BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */ +#if _USE_LFN + WCHAR* lfn; /* Pointer to the LFN working buffer */ + WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */ +#endif +} FATFS_DIR; + + + +/* File status structure (FILINFO) */ + +typedef struct { + DWORD fsize; /* File size */ + WORD fdate; /* Last modified date */ + WORD ftime; /* Last modified time */ + BYTE fattrib; /* Attribute */ + TCHAR fname[13]; /* Short file name (8.3 format) */ +#if _USE_LFN + TCHAR* lfname; /* Pointer to the LFN buffer */ + UINT lfsize; /* Size of LFN buffer in TCHAR */ +#endif +} FILINFO; + + + +/* File function return code (FRESULT) */ + +typedef enum { + FR_OK = 0, /* (0) Succeeded */ + FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ + FR_INT_ERR, /* (2) Assertion failed */ + FR_NOT_READY, /* (3) The physical drive cannot work */ + FR_NO_FILE, /* (4) Could not find the file */ + FR_NO_PATH, /* (5) Could not find the path */ + FR_INVALID_NAME, /* (6) The path name format is invalid */ + FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ + FR_EXIST, /* (8) Access denied due to prohibited access */ + FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ + FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ + FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ + FR_NOT_ENABLED, /* (12) The volume has no work area */ + FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ + FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */ + FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ + FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ + FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ + FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_SHARE */ + FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ +} FRESULT; + + + +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ + +FRESULT f_mount (BYTE, FATFS*); /* Mount/Unmount a logical drive */ +FRESULT f_open (FIL*, const TCHAR*, BYTE); /* Open or create a file */ +FRESULT f_read (FIL*, void*, UINT, UINT*); /* Read data from a file */ +FRESULT f_lseek (FIL*, DWORD); /* Move file pointer of a file object */ +FRESULT f_close (FIL*); /* Close an open file object */ +FRESULT f_opendir (FATFS_DIR*, const TCHAR*); /* Open an existing directory */ +FRESULT f_readdir (FATFS_DIR*, FILINFO*); /* Read a directory item */ +FRESULT f_stat (const TCHAR*, FILINFO*); /* Get file status */ +FRESULT f_write (FIL*, const void*, UINT, UINT*); /* Write data to a file */ +FRESULT f_getfree (const TCHAR*, DWORD*, FATFS**); /* Get number of free clusters on the drive */ +FRESULT f_truncate (FIL*); /* Truncate file */ +FRESULT f_sync (FIL*); /* Flush cached data of a writing file */ +FRESULT f_unlink (const TCHAR*); /* Delete an existing file or directory */ +FRESULT f_mkdir (const TCHAR*); /* Create a new directory */ +FRESULT f_chmod (const TCHAR*, BYTE, BYTE); /* Change attribute of the file/dir */ +FRESULT f_utime (const TCHAR*, const FILINFO*); /* Change times-tamp of the file/dir */ +FRESULT f_rename (const TCHAR*, const TCHAR*); /* Rename/Move a file or directory */ +FRESULT f_chdrive (BYTE); /* Change current drive */ +FRESULT f_chdir (const TCHAR*); /* Change current directory */ +FRESULT f_getcwd (TCHAR*, UINT); /* Get current directory */ +FRESULT f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*); /* Forward data to the stream */ +FRESULT f_mkfs (BYTE, BYTE, UINT); /* Create a file system on the drive */ +FRESULT f_fdisk (BYTE, const DWORD[], void*); /* Divide a physical drive into some partitions */ +int f_putc (TCHAR, FIL*); /* Put a character to the file */ +int f_puts (const TCHAR*, FIL*); /* Put a string to the file */ +int f_printf (FIL*, const TCHAR*, ...); /* Put a formatted string to the file */ +TCHAR* f_gets (TCHAR*, int, FIL*); /* Get a string from the file */ + +#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0) +#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0) +#define f_tell(fp) ((fp)->fptr) +#define f_size(fp) ((fp)->fsize) + +#ifndef EOF +#define EOF (-1) +#endif + + + + +/*--------------------------------------------------------------*/ +/* Additional user defined functions */ + +/* RTC function */ +#if !_FS_READONLY +DWORD get_fattime (void); +#endif + +/* Unicode support functions */ +#if _USE_LFN /* Unicode - OEM code conversion */ +WCHAR ff_convert (WCHAR, UINT); /* OEM-Unicode bidirectional conversion */ +WCHAR ff_wtoupper (WCHAR); /* Unicode upper-case conversion */ +#if _USE_LFN == 3 /* Memory functions */ +void* ff_memalloc (UINT); /* Allocate memory block */ +void ff_memfree (void*); /* Free memory block */ +#endif +#endif + +/* Sync functions */ +#if _FS_REENTRANT +int ff_cre_syncobj (BYTE, _SYNC_t*);/* Create a sync object */ +int ff_req_grant (_SYNC_t); /* Lock sync object */ +void ff_rel_grant (_SYNC_t); /* Unlock sync object */ +int ff_del_syncobj (_SYNC_t); /* Delete a sync object */ +#endif + + + + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ + + +/* File access control and file status flags (FIL.flag) */ + +#define FA_READ 0x01 +#define FA_OPEN_EXISTING 0x00 +#define FA__ERROR 0x80 + +#if !_FS_READONLY +#define FA_WRITE 0x02 +#define FA_CREATE_NEW 0x04 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA__WRITTEN 0x20 +#define FA__DIRTY 0x40 +#endif + + +/* FAT sub type (FATFS.fs_type) */ + +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 + + +/* File attribute bits for directory entry */ + +#define AM_RDO 0x01 /* Read only */ +#define AM_HID 0x02 /* Hidden */ +#define AM_SYS 0x04 /* System */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_DIR 0x10 /* Directory */ +#define AM_ARC 0x20 /* Archive */ +#define AM_MASK 0x3F /* Mask of defined bits */ + + +/* Fast seek feature */ +#define CREATE_LINKMAP 0xFFFFFFFF + + + +/*--------------------------------*/ +/* Multi-byte word access macros */ + +#if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */ +#define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr)) +#define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val) +#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val) +#else /* Use byte-by-byte access to the FAT structure */ +#define LD_WORD(ptr) (WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr)) +#define LD_DWORD(ptr) (DWORD)(((DWORD)*((BYTE*)(ptr)+3)<<24)|((DWORD)*((BYTE*)(ptr)+2)<<16)|((WORD)*((BYTE*)(ptr)+1)<<8)|*(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8) +#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8); *((BYTE*)(ptr)+2)=(BYTE)((DWORD)(val)>>16); *((BYTE*)(ptr)+3)=(BYTE)((DWORD)(val)>>24) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _FATFS */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/FATFileSystem/ChaN/ffconf.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,191 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module configuration file R0.09a (C)ChaN, 2012 +/----------------------------------------------------------------------------/ +/ +/ CAUTION! Do not forget to make clean the project after any changes to +/ the configuration options. +/ +/----------------------------------------------------------------------------*/ +#ifndef _FFCONF +#define _FFCONF 4004 /* Revision ID */ + +#define FFS_DBG 0 + +/*---------------------------------------------------------------------------/ +/ Functions and Buffer Configurations +/----------------------------------------------------------------------------*/ + +#define _FS_TINY 0 /* 0:Normal or 1:Tiny */ +/* When _FS_TINY is set to 1, FatFs uses the sector buffer in the file system +/ object instead of the sector buffer in the individual file object for file +/ data transfer. This reduces memory consumption 512 bytes each file object. */ + + +#define _FS_READONLY 0 /* 0:Read/Write or 1:Read only */ +/* Setting _FS_READONLY to 1 defines read only configuration. This removes +/ writing functions, f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename, +/ f_truncate and useless f_getfree. */ + + +#define _FS_MINIMIZE 0 /* 0 to 3 */ +/* The _FS_MINIMIZE option defines minimization level to remove some functions. +/ +/ 0: Full function. +/ 1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod, f_truncate and f_rename +/ are removed. +/ 2: f_opendir and f_readdir are removed in addition to 1. +/ 3: f_lseek is removed in addition to 2. */ + + +#define _USE_STRFUNC 0 /* 0:Disable or 1-2:Enable */ +/* To enable string functions, set _USE_STRFUNC to 1 or 2. */ + + +#define _USE_MKFS 1 /* 0:Disable or 1:Enable */ +/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */ + + +#define _USE_FORWARD 0 /* 0:Disable or 1:Enable */ +/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */ + + +#define _USE_FASTSEEK 0 /* 0:Disable or 1:Enable */ +/* To enable fast seek feature, set _USE_FASTSEEK to 1. */ + + + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/----------------------------------------------------------------------------*/ + +#define _CODE_PAGE 858 +/* The _CODE_PAGE specifies the OEM code page to be used on the target system. +/ Incorrect setting of the code page can cause a file open failure. +/ +/ 932 - Japanese Shift-JIS (DBCS, OEM, Windows) +/ 936 - Simplified Chinese GBK (DBCS, OEM, Windows) +/ 949 - Korean (DBCS, OEM, Windows) +/ 950 - Traditional Chinese Big5 (DBCS, OEM, Windows) +/ 1250 - Central Europe (Windows) +/ 1251 - Cyrillic (Windows) +/ 1252 - Latin 1 (Windows) +/ 1253 - Greek (Windows) +/ 1254 - Turkish (Windows) +/ 1255 - Hebrew (Windows) +/ 1256 - Arabic (Windows) +/ 1257 - Baltic (Windows) +/ 1258 - Vietnam (OEM, Windows) +/ 437 - U.S. (OEM) +/ 720 - Arabic (OEM) +/ 737 - Greek (OEM) +/ 775 - Baltic (OEM) +/ 850 - Multilingual Latin 1 (OEM) +/ 858 - Multilingual Latin 1 + Euro (OEM) +/ 852 - Latin 2 (OEM) +/ 855 - Cyrillic (OEM) +/ 866 - Russian (OEM) +/ 857 - Turkish (OEM) +/ 862 - Hebrew (OEM) +/ 874 - Thai (OEM, Windows) +/ 1 - ASCII only (Valid for non LFN cfg.) +*/ + + +#define _USE_LFN 1 /* 0 to 3 */ +#define _MAX_LFN 255 /* Maximum LFN length to handle (12 to 255) */ +/* The _USE_LFN option switches the LFN support. +/ +/ 0: Disable LFN feature. _MAX_LFN and _LFN_UNICODE have no effect. +/ 1: Enable LFN with static working buffer on the BSS. Always NOT reentrant. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ 3: Enable LFN with dynamic working buffer on the HEAP. +/ +/ The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. To enable LFN, +/ Unicode handling functions ff_convert() and ff_wtoupper() must be added +/ to the project. When enable to use heap, memory control functions +/ ff_memalloc() and ff_memfree() must be added to the project. */ + + +#define _LFN_UNICODE 0 /* 0:ANSI/OEM or 1:Unicode */ +/* To switch the character code set on FatFs API to Unicode, +/ enable LFN feature and set _LFN_UNICODE to 1. */ + + +#define _FS_RPATH 0 /* 0 to 2 */ +/* The _FS_RPATH option configures relative path feature. +/ +/ 0: Disable relative path feature and remove related functions. +/ 1: Enable relative path. f_chdrive() and f_chdir() are available. +/ 2: f_getcwd() is available in addition to 1. +/ +/ Note that output of the f_readdir fnction is affected by this option. */ + + + +/*---------------------------------------------------------------------------/ +/ Physical Drive Configurations +/----------------------------------------------------------------------------*/ + +#define _VOLUMES 1 +/* Number of volumes (logical drives) to be used. */ + + +#define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */ +/* Maximum sector size to be handled. +/ Always set 512 for memory card and hard disk but a larger value may be +/ required for on-board flash memory, floppy disk and optical disk. +/ When _MAX_SS is larger than 512, it configures FatFs to variable sector size +/ and GET_SECTOR_SIZE command must be implememted to the disk_ioctl function. */ + + +#define _MULTI_PARTITION 0 /* 0:Single partition, 1/2:Enable multiple partition */ +/* When set to 0, each volume is bound to the same physical drive number and +/ it can mount only first primaly partition. When it is set to 1, each volume +/ is tied to the partitions listed in VolToPart[]. */ + + +#define _USE_ERASE 0 /* 0:Disable or 1:Enable */ +/* To enable sector erase feature, set _USE_ERASE to 1. CTRL_ERASE_SECTOR command +/ should be added to the disk_ioctl functio. */ + + + +/*---------------------------------------------------------------------------/ +/ System Configurations +/----------------------------------------------------------------------------*/ + +#define _WORD_ACCESS 0 /* 0 or 1 */ +/* Set 0 first and it is always compatible with all platforms. The _WORD_ACCESS +/ option defines which access method is used to the word data on the FAT volume. +/ +/ 0: Byte-by-byte access. +/ 1: Word access. Do not choose this unless following condition is met. +/ +/ When the byte order on the memory is big-endian or address miss-aligned word +/ access results incorrect behavior, the _WORD_ACCESS must be set to 0. +/ If it is not the case, the value can also be set to 1 to improve the +/ performance and code size. +*/ + + +/* A header file that defines sync object types on the O/S, such as +/ windows.h, ucos_ii.h and semphr.h, must be included prior to ff.h. */ + +#define _FS_REENTRANT 0 /* 0:Disable or 1:Enable */ +#define _FS_TIMEOUT 1000 /* Timeout period in unit of time ticks */ +#define _SYNC_t HANDLE /* O/S dependent type of sync object. e.g. HANDLE, OS_EVENT*, ID and etc.. */ + +/* The _FS_REENTRANT option switches the reentrancy (thread safe) of the FatFs module. +/ +/ 0: Disable reentrancy. _SYNC_t and _FS_TIMEOUT have no effect. +/ 1: Enable reentrancy. Also user provided synchronization handlers, +/ ff_req_grant, ff_rel_grant, ff_del_syncobj and ff_cre_syncobj +/ function must be added to the project. */ + + +#define _FS_LOCK 0 /* 0:Disable or >=1:Enable */ +/* To enable file lock control feature, set _FS_LOCK to 1 or greater. + The value defines how many files can be opened simultaneously. */ + + +#endif /* _FFCONFIG */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/FATFileSystem/ChaN/integer.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,37 @@ +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef _INTEGER +#define _INTEGER + +#ifdef _WIN32 /* FatFs development platform */ + +#include <windows.h> +#include <tchar.h> + +#else /* Embedded platform */ + +/* These types must be 16-bit, 32-bit or larger integer */ +typedef int INT; +typedef unsigned int UINT; + +/* These types must be 8-bit integer */ +typedef char CHAR; +typedef unsigned char UCHAR; +typedef unsigned char BYTE; + +/* These types must be 16-bit integer */ +typedef short SHORT; +typedef unsigned short USHORT; +typedef unsigned short WORD; +typedef unsigned short WCHAR; + +/* These types must be 32-bit integer */ +typedef long LONG; +typedef unsigned long ULONG; +typedef unsigned long DWORD; + +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/FATFileSystem/FATDirHandle.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,78 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include <string.h> +#include "ff.h" +#include "FATDirHandle.h" + +using namespace mbed; + +FATDirHandle::FATDirHandle(const FATFS_DIR &the_dir) { + dir = the_dir; +} + +int FATDirHandle::closedir() { + delete this; + return 0; +} + +struct dirent *FATDirHandle::readdir() { + FILINFO finfo; + +#if _USE_LFN + finfo.lfname = cur_entry.d_name; + finfo.lfsize = sizeof(cur_entry.d_name); +#endif // _USE_LFN + + FRESULT res = f_readdir(&dir, &finfo); + +#if _USE_LFN + if(res != 0 || finfo.fname[0]==0) { + return NULL; + } else { + if(cur_entry.d_name[0]==0) { + // No long filename so use short filename. + memcpy(cur_entry.d_name, finfo.fname, sizeof(finfo.fname)); + } + return &cur_entry; + } +#else + if(res != 0 || finfo.fname[0]==0) { + return NULL; + } else { + memcpy(cur_entry.d_name, finfo.fname, sizeof(finfo.fname)); + return &cur_entry; + } +#endif /* _USE_LFN */ +} + +void FATDirHandle::rewinddir() { + dir.index = 0; +} + +off_t FATDirHandle::telldir() { + return dir.index; +} + +void FATDirHandle::seekdir(off_t location) { + dir.index = location; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/FATFileSystem/FATDirHandle.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,45 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef MBED_FATDIRHANDLE_H +#define MBED_FATDIRHANDLE_H + +#include "DirHandle.h" + +using namespace mbed; + +class FATDirHandle : public DirHandle { + + public: + FATDirHandle(const FATFS_DIR &the_dir); + virtual int closedir(); + virtual struct dirent *readdir(); + virtual void rewinddir(); + virtual off_t telldir(); + virtual void seekdir(off_t location); + + private: + FATFS_DIR dir; + struct dirent cur_entry; + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/FATFileSystem/FATFileHandle.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,90 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "ff.h" +#include "ffconf.h" +#include "mbed_debug.h" + +#include "FATFileHandle.h" + +FATFileHandle::FATFileHandle(FIL fh) { + _fh = fh; +} + +int FATFileHandle::close() { + int retval = f_close(&_fh); + delete this; + return retval; +} + +ssize_t FATFileHandle::write(const void* buffer, size_t length) { + UINT n; + FRESULT res = f_write(&_fh, buffer, length, &n); + if (res) { + debug_if(FFS_DBG, "f_write() failed: %d", res); + return -1; + } + return n; +} + +ssize_t FATFileHandle::read(void* buffer, size_t length) { + debug_if(FFS_DBG, "read(%d)\n", length); + UINT n; + FRESULT res = f_read(&_fh, buffer, length, &n); + if (res) { + debug_if(FFS_DBG, "f_read() failed: %d\n", res); + return -1; + } + return n; +} + +int FATFileHandle::isatty() { + return 0; +} + +off_t FATFileHandle::lseek(off_t position, int whence) { + if (whence == SEEK_END) { + position += _fh.fsize; + } else if(whence==SEEK_CUR) { + position += _fh.fptr; + } + FRESULT res = f_lseek(&_fh, position); + if (res) { + debug_if(FFS_DBG, "lseek failed: %d\n", res); + return -1; + } else { + debug_if(FFS_DBG, "lseek OK, returning %i\n", _fh.fptr); + return _fh.fptr; + } +} + +int FATFileHandle::fsync() { + FRESULT res = f_sync(&_fh); + if (res) { + debug_if(FFS_DBG, "f_sync() failed: %d\n", res); + return -1; + } + return 0; +} + +off_t FATFileHandle::flen() { + return _fh.fsize; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/FATFileSystem/FATFileHandle.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,47 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef MBED_FATFILEHANDLE_H +#define MBED_FATFILEHANDLE_H + +#include "FileHandle.h" + +using namespace mbed; + +class FATFileHandle : public FileHandle { +public: + + FATFileHandle(FIL fh); + virtual int close(); + virtual ssize_t write(const void* buffer, size_t length); + virtual ssize_t read(void* buffer, size_t length); + virtual int isatty(); + virtual off_t lseek(off_t position, int whence); + virtual int fsync(); + virtual off_t flen(); + +protected: + + FIL _fh; + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/FATFileSystem/FATFileSystem.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,132 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "mbed.h" + +#include "ffconf.h" +#include "mbed_debug.h" + +#include "FATFileSystem.h" +#include "FATFileHandle.h" +#include "FATDirHandle.h" + +DWORD get_fattime(void) { + time_t rawtime; + time(&rawtime); + struct tm *ptm = localtime(&rawtime); + return (DWORD)(ptm->tm_year - 80) << 25 + | (DWORD)(ptm->tm_mon + 1 ) << 21 + | (DWORD)(ptm->tm_mday ) << 16 + | (DWORD)(ptm->tm_hour ) << 11 + | (DWORD)(ptm->tm_min ) << 5 + | (DWORD)(ptm->tm_sec/2 ); +} + +FATFileSystem *FATFileSystem::_ffs[_VOLUMES] = {0}; + +FATFileSystem::FATFileSystem(const char* n) : FileSystemLike(n) { + debug_if(FFS_DBG, "FATFileSystem(%s)\n", n); + for(int i=0; i<_VOLUMES; i++) { + if(_ffs[i] == 0) { + _ffs[i] = this; + _fsid = i; + debug_if(FFS_DBG, "Mounting [%s] on ffs drive [%d]\n", _name, _fsid); + f_mount(i, &_fs); + return; + } + } + error("Couldn't create %s in FATFileSystem::FATFileSystem\n", n); +} + +FATFileSystem::~FATFileSystem() { + for (int i=0; i<_VOLUMES; i++) { + if (_ffs[i] == this) { + _ffs[i] = 0; + f_mount(i, NULL); + } + } +} + +FileHandle *FATFileSystem::open(const char* name, int flags) { + debug_if(FFS_DBG, "open(%s) on filesystem [%s], drv [%d]\n", name, _name, _fsid); + char n[64]; + sprintf(n, "%d:/%s", _fsid, name); + + /* POSIX flags -> FatFS open mode */ + BYTE openmode; + if (flags & O_RDWR) { + openmode = FA_READ|FA_WRITE; + } else if(flags & O_WRONLY) { + openmode = FA_WRITE; + } else { + openmode = FA_READ; + } + if(flags & O_CREAT) { + if(flags & O_TRUNC) { + openmode |= FA_CREATE_ALWAYS; + } else { + openmode |= FA_OPEN_ALWAYS; + } + } + + FIL fh; + FRESULT res = f_open(&fh, n, openmode); + if (res) { + debug_if(FFS_DBG, "f_open('w') failed: %d\n", res); + return NULL; + } + if (flags & O_APPEND) { + f_lseek(&fh, fh.fsize); + } + return new FATFileHandle(fh); +} + +int FATFileSystem::remove(const char *filename) { + FRESULT res = f_unlink(filename); + if (res) { + debug_if(FFS_DBG, "f_unlink() failed: %d\n", res); + return -1; + } + return 0; +} + +int FATFileSystem::format() { + FRESULT res = f_mkfs(_fsid, 0, 512); // Logical drive number, Partitioning rule, Allocation unit size (bytes per cluster) + if (res) { + debug_if(FFS_DBG, "f_mkfs() failed: %d\n", res); + return -1; + } + return 0; +} + +DirHandle *FATFileSystem::opendir(const char *name) { + FATFS_DIR dir; + FRESULT res = f_opendir(&dir, name); + if (res != 0) { + return NULL; + } + return new FATDirHandle(dir); +} + +int FATFileSystem::mkdir(const char *name, mode_t mode) { + FRESULT res = f_mkdir(name); + return res == 0 ? 0 : -1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/FATFileSystem/FATFileSystem.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,57 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef MBED_FATFILESYSTEM_H +#define MBED_FATFILESYSTEM_H + +#include "FileSystemLike.h" +#include "FileHandle.h" +#include "ff.h" +#include <stdint.h> + +using namespace mbed; + +class FATFileSystem : public FileSystemLike { +public: + + FATFileSystem(const char* n); + virtual ~FATFileSystem(); + + static FATFileSystem * _ffs[_VOLUMES]; // FATFileSystem objects, as parallel to FatFs drives array + FATFS _fs; // Work area (file system object) for logical drive + int _fsid; + + virtual FileHandle *open(const char* name, int flags); + virtual int remove(const char *filename); + virtual int format(); + virtual DirHandle *opendir(const char *name); + virtual int mkdir(const char *name, mode_t mode); + + virtual int disk_initialize() { return 0; } + virtual int disk_status() { return 0; } + virtual int disk_read(uint8_t * buffer, uint64_t sector) = 0; + virtual int disk_write(const uint8_t * buffer, uint64_t sector) = 0; + virtual int disk_sync() { return 0; } + virtual uint64_t disk_sectors() = 0; + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHost/IUSBEnumerator.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,36 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IUSBENUMERATOR_H_ +#define IUSBENUMERATOR_H_ + +#include "stdint.h" +#include "USBEndpoint.h" + +/* +Generic interface to implement for "smart" USB enumeration +*/ + +class IUSBEnumerator +{ +public: + virtual void setVidPid(uint16_t vid, uint16_t pid) = 0; + virtual bool parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) = 0; //Must return true if the interface should be parsed + virtual bool useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) = 0; //Must return true if the endpoint will be used +}; + +#endif /*IUSBENUMERATOR_H_*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHost/USBDeviceConnected.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,124 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "USBDeviceConnected.h" +#include "dbg.h" + +USBDeviceConnected::USBDeviceConnected() { + init(); +} + +void USBDeviceConnected::init() { + hub_nb = 0; + port = 0; + vid = 0; + pid = 0; + nb_interf = 0; + enumerated = false; + activeAddr = false; + sizeControlEndpoint = 8; + device_class = 0; + device_subclass = 0; + proto = 0; + speed = false; + for (int i = 0; i < MAX_INTF; i++) { + memset((void *)&intf[i], 0, sizeof(INTERFACE)); + intf[i].in_use = false; + for (int j = 0; j < MAX_ENDPOINT_PER_INTERFACE; j++) { + intf[i].ep[j] = NULL; + strcpy(intf[i].name, "Unknown"); + } + } + hub_parent = NULL; + hub = NULL; + nb_interf = 0; +} + +INTERFACE * USBDeviceConnected::getInterface(uint8_t index) { + if (index >= MAX_INTF) + return NULL; + + if (intf[index].in_use) + return &intf[index]; + + return NULL; +} + +bool USBDeviceConnected::addInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) { + if ((intf_nb >= MAX_INTF) || (intf[intf_nb].in_use)) { + return false; + } + intf[intf_nb].in_use = true; + intf[intf_nb].intf_class = intf_class; + intf[intf_nb].intf_subclass = intf_subclass; + intf[intf_nb].intf_protocol = intf_protocol; + intf[intf_nb].nb_endpoint = 0; + return true; +} + +bool USBDeviceConnected::addEndpoint(uint8_t intf_nb, USBEndpoint * ept) { + if ((intf_nb >= MAX_INTF) || (intf[intf_nb].in_use == false) || (intf[intf_nb].nb_endpoint >= MAX_ENDPOINT_PER_INTERFACE)) { + return false; + } + intf[intf_nb].nb_endpoint++; + + for (int i = 0; i < MAX_ENDPOINT_PER_INTERFACE; i++) { + if (intf[intf_nb].ep[i] == NULL) { + intf[intf_nb].ep[i] = ept; + return true; + } + } + return false; +} + +void USBDeviceConnected::init(uint8_t hub_, uint8_t port_, bool lowSpeed_) { + USB_DBG("init dev: %p", this); + init(); + hub_nb = hub_; + port = port_; + speed = lowSpeed_; +} + +void USBDeviceConnected::disconnect() { + for(int i = 0; i < MAX_INTF; i++) { + intf[i].detach.call(); + } + init(); +} + + +USBEndpoint * USBDeviceConnected::getEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir, uint8_t index) { + if (intf_nb >= MAX_INTF) { + return NULL; + } + for (int i = 0; i < MAX_ENDPOINT_PER_INTERFACE; i++) { + if ((intf[intf_nb].ep[i]->getType() == type) && (intf[intf_nb].ep[i]->getDir() == dir)) { + if(index) { + index--; + } else { + return intf[intf_nb].ep[i]; + } + } + } + return NULL; +} + +USBEndpoint * USBDeviceConnected::getEndpoint(uint8_t intf_nb, uint8_t index) { + if ((intf_nb >= MAX_INTF) || (index >= MAX_ENDPOINT_PER_INTERFACE)) { + return NULL; + } + return intf[intf_nb].ep[index]; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHost/USBDeviceConnected.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,185 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USBDEVICECONNECTED_H +#define USBDEVICECONNECTED_H + +#include "stdint.h" +#include "USBEndpoint.h" +#include "USBHostConf.h" +#include "rtos.h" + +class USBHostHub; + +typedef struct { + bool in_use; + uint8_t nb_endpoint; + uint8_t intf_class; + uint8_t intf_subclass; + uint8_t intf_protocol; + USBEndpoint * ep[MAX_ENDPOINT_PER_INTERFACE]; + FunctionPointer detach; + char name[10]; +} INTERFACE; + +/** +* USBDeviceConnected class +*/ +class USBDeviceConnected +{ +public: + + /** + * Constructor + */ + USBDeviceConnected(); + + /** + * Attach an USBEndpoint to this device + * + * @param intf_nb interface number + * @param ep pointeur on the USBEndpoint which will be attached + * @returns true if successful, false otherwise + */ + bool addEndpoint(uint8_t intf_nb, USBEndpoint * ep); + + /** + * Retrieve an USBEndpoint by its TYPE and DIRECTION + * + * @param intf_nb the interface on which to lookup the USBEndpoint + * @param type type of the USBEndpoint looked for + * @param dir direction of the USBEndpoint looked for + * @param index the index of the USBEndpoint whitin the interface + * @returns pointer on the USBEndpoint if found, NULL otherwise + */ + USBEndpoint * getEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir, uint8_t index = 0); + + /** + * Retrieve an USBEndpoint by its index + * + * @param intf_nb interface number + * @param index index of the USBEndpoint + * @returns pointer on the USBEndpoint if found, NULL otherwise + */ + USBEndpoint * getEndpoint(uint8_t intf_nb, uint8_t index); + + /** + * Add a new interface to this device + * + * @param intf_nb interface number + * @param intf_class interface class + * @param intf_subclass interface subclass + * @param intf_protocol interface protocol + * @returns true if successful, false otherwise + */ + bool addInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol); + + /** + * Get a specific interface + * + * @param index index of the interface to be fetched + * @returns interface + */ + INTERFACE * getInterface(uint8_t index); + + /** + * Attach a member function to call when a the device has been disconnected + * + * @param intf_nb interface number + * @param tptr pointer to the object to call the member function on + * @param mptr pointer to the member function to be called + */ + template<typename T> + inline void onDisconnect(uint8_t intf_nb, T* tptr, void (T::*mptr)(void)) { + if ((mptr != NULL) && (tptr != NULL)) { + intf[intf_nb].detach.attach(tptr, mptr); + } + } + + /** + * Attach a callback called when the device has been disconnected + * + * @param intf_nb interface number + * @param fn function pointer + */ + inline void onDisconnect(uint8_t intf_nb, void (*fn)(void)) { + if (fn != NULL) { + intf[intf_nb].detach.attach(fn); + } + } + + /** + * Disconnect the device by calling a callback function registered by a driver + */ + void disconnect(); + + // setters + void init(uint8_t hub, uint8_t port, bool lowSpeed); + inline void setAddress(uint8_t addr_) { addr = addr_; }; + inline void setVid(uint16_t vid_) { vid = vid_; }; + inline void setPid(uint16_t pid_) { pid = pid_; }; + inline void setClass(uint8_t device_class_) { device_class = device_class_; }; + inline void setSubClass(uint8_t device_subclass_) { device_subclass = device_subclass_; }; + inline void setProtocol(uint8_t pr) { proto = pr; }; + inline void setSizeControlEndpoint(uint32_t size) { sizeControlEndpoint = size; }; + inline void activeAddress(bool active) { activeAddr = active; }; + inline void setEnumerated() { enumerated = true; }; + inline void setNbIntf(uint8_t nb_intf) {nb_interf = nb_intf; }; + inline void setHubParent(USBHostHub * hub) { hub_parent = hub; }; + inline void setName(const char * name_, uint8_t intf_nb) { strcpy(intf[intf_nb].name, name_); }; + + //getters + inline uint8_t getPort() { return port; }; + inline uint8_t getHub() { return hub_nb; }; + inline uint8_t getAddress() { return addr; }; + inline uint16_t getVid() { return vid; }; + inline uint16_t getPid() { return pid; }; + inline uint8_t getClass() { return device_class; }; + inline uint8_t getSubClass() { return device_subclass; }; + inline uint8_t getProtocol() { return proto; }; + inline bool getSpeed() { return speed; }; + inline uint32_t getSizeControlEndpoint() { return sizeControlEndpoint; }; + inline bool isActiveAddress() { return activeAddr; }; + inline bool isEnumerated() { return enumerated; }; + inline USBHostHub * getHubParent() { return hub_parent; }; + inline uint8_t getNbIntf() { return nb_interf; }; + inline const char * getName(uint8_t intf_nb) { return intf[intf_nb].name; }; + + // in case this device is a hub + USBHostHub * hub; + +private: + USBHostHub * hub_parent; + + INTERFACE intf[MAX_INTF]; + uint32_t sizeControlEndpoint; + uint8_t hub_nb; + uint8_t port; + uint16_t vid; + uint16_t pid; + uint8_t addr; + uint8_t device_class; + uint8_t device_subclass; + uint8_t proto; + bool speed; + volatile bool activeAddr; + volatile bool enumerated; + uint8_t nb_interf; + + void init(); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHost/USBEndpoint.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,162 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "dbg.h" +#include "USBEndpoint.h" + +void USBEndpoint::init(HCED * hced_, ENDPOINT_TYPE type_, ENDPOINT_DIRECTION dir_, uint32_t size, uint8_t ep_number, HCTD* td_list_[2]) +{ + hced = hced_; + type = type_; + dir = dir_; + setup = (type == CONTROL_ENDPOINT) ? true : false; + + //TDs have been allocated by the host + memcpy((HCTD**)td_list, td_list_, sizeof(HCTD*)*2); //TODO: Maybe should add a param for td_list size... at least a define + memcpy(td_list_[0], 0, sizeof(HCTD)); + memcpy(td_list_[1], 0, sizeof(HCTD)); + + td_list[0]->ep = this; + td_list[1]->ep = this; + + hced->control = 0; + //Empty queue + hced->tailTD = td_list[0]; + hced->headTD = td_list[0]; + hced->nextED = 0; + + address = (ep_number & 0x7F) | ((dir - 1) << 7); + + hced->control = ((ep_number & 0x7F) << 7) // Endpoint address + | (type != CONTROL_ENDPOINT ? ( dir << 11) : 0 ) // direction : Out = 1, 2 = In + | ((size & 0x3ff) << 16); // MaxPkt Size + + transfer_len = 0; + transferred = 0; + buf_start = 0; + nextEp = NULL; + + td_current = td_list[0]; + td_next = td_list[1]; + + intf_nb = 0; + + state = USB_TYPE_IDLE; +} + +void USBEndpoint::setSize(uint32_t size) +{ + hced->control &= ~(0x3ff << 16); + hced->control |= (size << 16); +} + + +void USBEndpoint::setDeviceAddress(uint8_t addr) +{ + hced->control &= ~(0x7f); + hced->control |= (addr & 0x7F); +} + +void USBEndpoint::setSpeed(uint8_t speed) +{ + hced->control &= ~(1 << 13); + hced->control |= (speed << 13); +} + +//Only for control Eps +void USBEndpoint::setNextToken(uint32_t token) +{ + switch (token) { + case TD_SETUP: + dir = OUT; + setup = true; + break; + case TD_IN: + dir = IN; + setup = false; + break; + case TD_OUT: + dir = OUT; + setup = false; + break; + } +} + +struct { + USB_TYPE type; + const char * str; +} static type_string[] = { +/*0*/ {USB_TYPE_OK, "USB_TYPE_OK"}, + {USB_TYPE_CRC_ERROR, "USB_TYPE_CRC_ERROR"}, + {USB_TYPE_BIT_STUFFING_ERROR, "USB_TYPE_BIT_STUFFING_ERROR"}, + {USB_TYPE_DATA_TOGGLE_MISMATCH_ERROR, "USB_TYPE_DATA_TOGGLE_MISMATCH_ERROR"}, + {USB_TYPE_STALL_ERROR, "USB_TYPE_STALL_ERROR"}, +/*5*/ {USB_TYPE_DEVICE_NOT_RESPONDING_ERROR, "USB_TYPE_DEVICE_NOT_RESPONDING_ERROR"}, + {USB_TYPE_PID_CHECK_FAILURE_ERROR, "USB_TYPE_PID_CHECK_FAILURE_ERROR"}, + {USB_TYPE_UNEXPECTED_PID_ERROR, "USB_TYPE_UNEXPECTED_PID_ERROR"}, + {USB_TYPE_DATA_OVERRUN_ERROR, "USB_TYPE_DATA_OVERRUN_ERROR"}, + {USB_TYPE_DATA_UNDERRUN_ERROR, "USB_TYPE_DATA_UNDERRUN_ERROR"}, +/*10*/ {USB_TYPE_ERROR, "USB_TYPE_ERROR"}, + {USB_TYPE_ERROR, "USB_TYPE_ERROR"}, + {USB_TYPE_BUFFER_OVERRUN_ERROR, "USB_TYPE_BUFFER_OVERRUN_ERROR"}, + {USB_TYPE_BUFFER_UNDERRUN_ERROR, "USB_TYPE_BUFFER_UNDERRUN_ERROR"}, + {USB_TYPE_DISCONNECTED, "USB_TYPE_DISCONNECTED"}, +/*15*/ {USB_TYPE_FREE, "USB_TYPE_FREE"}, + {USB_TYPE_IDLE, "USB_TYPE_IDLE"}, + {USB_TYPE_PROCESSING, "USB_TYPE_PROCESSING"}, + {USB_TYPE_ERROR, "USB_TYPE_ERROR"} +}; + +void USBEndpoint::setState(uint8_t st) { + if (st > 18) + return; + state = type_string[st].type; +} + + +const char * USBEndpoint::getStateString() { + return type_string[state].str; +} + +void USBEndpoint::queueTransfer() +{ + transfer_len = (uint32_t)td_current->bufEnd - (uint32_t)td_current->currBufPtr + 1; + transferred = transfer_len; + buf_start = (uint8_t *)td_current->currBufPtr; + + //Now add this free TD at this end of the queue + state = USB_TYPE_PROCESSING; + td_current->nextTD = td_next; + hced->tailTD = td_next; +} + +void USBEndpoint::unqueueTransfer(volatile HCTD * td) +{ + td->control=0; + td->currBufPtr=0; + td->bufEnd=0; + td->nextTD=0; + hced->headTD = (HCTD *)((uint32_t)hced->tailTD | ((uint32_t)hced->headTD & 0x2)); //Carry bit + td_current = td_next; + td_next = td; +} + +void USBEndpoint::queueEndpoint(USBEndpoint * ed) +{ + nextEp = ed; + hced->nextED = (ed == NULL) ? 0 : ed->getHCED(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHost/USBEndpoint.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,171 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USBENDPOINT_H +#define USBENDPOINT_H + +#include "FunctionPointer.h" +#include "USBHostTypes.h" +#include "rtos.h" + +class USBDeviceConnected; + +/** +* USBEndpoint class +*/ +class USBEndpoint +{ +public: + /** + * Constructor + */ + USBEndpoint() { + state = USB_TYPE_FREE; + nextEp = NULL; + }; + + /** + * Initialize an endpoint + * + * @param hced hced associated to the endpoint + * @param type endpoint type + * @param dir endpoint direction + * @param size endpoint size + * @param ep_number endpoint number + * @param td_list array of two allocated transfer descriptors + */ + void init(HCED * hced, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir, uint32_t size, uint8_t ep_number, HCTD* td_list[2]); + + /** + * Set next token. Warning: only useful for the control endpoint + * + * @param token IN, OUT or SETUP token + */ + void setNextToken(uint32_t token); + + /** + * Queue an endpoint + * + * @param endpoint endpoint which will be queued in the linked list + */ + void queueEndpoint(USBEndpoint * endpoint); + + + /** + * Queue a transfer on the endpoint + */ + void queueTransfer(); + + /** + * Unqueue a transfer from the endpoint + * + * @param td hctd which will be unqueued + */ + void unqueueTransfer(volatile HCTD * td); + + /** + * Attach a member function to call when a transfer is finished + * + * @param tptr pointer to the object to call the member function on + * @param mptr pointer to the member function to be called + */ + template<typename T> + inline void attach(T* tptr, void (T::*mptr)(void)) { + if((mptr != NULL) && (tptr != NULL)) { + rx.attach(tptr, mptr); + } + } + + /** + * Attach a callback called when a transfer is finished + * + * @param fptr function pointer + */ + inline void attach(void (*fptr)(void)) { + if(fptr != NULL) { + rx.attach(fptr); + } + } + + /** + * Call the handler associted to the end of a transfer + */ + inline void call() { + rx.call(); + }; + + + // setters + inline void setState(USB_TYPE st) { state = st; } + void setState(uint8_t st); + void setDeviceAddress(uint8_t addr); + inline void setLengthTransferred(int len) { transferred = len; }; + void setSpeed(uint8_t speed); + void setSize(uint32_t size); + inline void setDir(ENDPOINT_DIRECTION d) { dir = d; } + inline void setIntfNb(uint8_t intf_nb_) { intf_nb = intf_nb_; }; + + // getters + const char * getStateString(); + inline USB_TYPE getState() { return state; } + inline ENDPOINT_TYPE getType() { return type; }; + inline uint8_t getDeviceAddress() { return hced->control & 0x7f; }; + inline int getLengthTransferred() { return transferred; } + inline uint8_t * getBufStart() { return buf_start; } + inline uint8_t getAddress(){ return address; }; + inline uint32_t getSize() { return (hced->control >> 16) & 0x3ff; }; + inline volatile HCTD * getHeadTD() { return (volatile HCTD*) ((uint32_t)hced->headTD & ~0xF); }; + inline volatile HCTD** getTDList() { return td_list; }; + inline volatile HCED * getHCED() { return hced; }; + inline ENDPOINT_DIRECTION getDir() { return dir; } + inline volatile HCTD * getProcessedTD() { return td_current; }; + inline volatile HCTD* getNextTD() { return td_current; }; + inline bool isSetup() { return setup; } + inline USBEndpoint * nextEndpoint() { return (USBEndpoint*)nextEp; }; + inline uint8_t getIntfNb() { return intf_nb; }; + + USBDeviceConnected * dev; + + Queue<uint8_t, 1> ep_queue; + +private: + ENDPOINT_TYPE type; + volatile USB_TYPE state; + ENDPOINT_DIRECTION dir; + bool setup; + + uint8_t address; + + int transfer_len; + int transferred; + uint8_t * buf_start; + + FunctionPointer rx; + + USBEndpoint* nextEp; + + // USBEndpoint descriptor + volatile HCED * hced; + + volatile HCTD * td_list[2]; + volatile HCTD * td_current; + volatile HCTD * td_next; + + uint8_t intf_nb; + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHost/USBHALHost.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,322 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mbed.h" +#include "USBHALHost.h" +#include "dbg.h" + +// bits of the USB/OTG clock control register +#define HOST_CLK_EN (1<<0) +#define DEV_CLK_EN (1<<1) +#define PORTSEL_CLK_EN (1<<3) +#define AHB_CLK_EN (1<<4) + +// bits of the USB/OTG clock status register +#define HOST_CLK_ON (1<<0) +#define DEV_CLK_ON (1<<1) +#define PORTSEL_CLK_ON (1<<3) +#define AHB_CLK_ON (1<<4) + +// we need host clock, OTG/portsel clock and AHB clock +#define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN) + +#define HCCA_SIZE sizeof(HCCA) +#define ED_SIZE sizeof(HCED) +#define TD_SIZE sizeof(HCTD) + +#define TOTAL_SIZE (HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE) + (MAX_TD*TD_SIZE)) + +static volatile __align(256) uint8_t usb_buf[TOTAL_SIZE] __attribute((section("AHBSRAM1"),aligned)); //256 bytes aligned! + +USBHALHost * USBHALHost::instHost; + +USBHALHost::USBHALHost() { + instHost = this; + memInit(); + memset((void*)usb_hcca, 0, HCCA_SIZE); + for (int i = 0; i < MAX_ENDPOINT; i++) { + edBufAlloc[i] = false; + } + for (int i = 0; i < MAX_TD; i++) { + tdBufAlloc[i] = false; + } +} + +void USBHALHost::init() { + NVIC_DisableIRQ(USB_IRQn); + + //Cut power + LPC_SC->PCONP &= ~(1UL<<31); + wait_ms(100); + + // turn on power for USB + LPC_SC->PCONP |= (1UL<<31); + + // Enable USB host clock, port selection and AHB clock + LPC_USB->USBClkCtrl |= CLOCK_MASK; + + // Wait for clocks to become available + while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK); + + // it seems the bits[0:1] mean the following + // 0: U1=device, U2=host + // 1: U1=host, U2=host + // 2: reserved + // 3: U1=host, U2=device + // NB: this register is only available if OTG clock (aka "port select") is enabled!! + // since we don't care about port 2, set just bit 0 to 1 (U1=host) + LPC_USB->OTGStCtrl |= 1; + + // now that we've configured the ports, we can turn off the portsel clock + LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN; + + // configure USB D+/D- pins + // P0[29] = USB_D+, 01 + // P0[30] = USB_D-, 01 + LPC_PINCON->PINSEL1 &= ~((3<<26) | (3<<28)); + LPC_PINCON->PINSEL1 |= ((1<<26) | (1<<28)); + + LPC_USB->HcControl = 0; // HARDWARE RESET + LPC_USB->HcControlHeadED = 0; // Initialize Control list head to Zero + LPC_USB->HcBulkHeadED = 0; // Initialize Bulk list head to Zero + + // Wait 100 ms before apply reset + wait_ms(100); + + // software reset + LPC_USB->HcCommandStatus = OR_CMD_STATUS_HCR; + + // Write Fm Interval and Largest Data Packet Counter + LPC_USB->HcFmInterval = DEFAULT_FMINTERVAL; + LPC_USB->HcPeriodicStart = FI * 90 / 100; + + // Put HC in operational state + LPC_USB->HcControl = (LPC_USB->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER; + // Set Global Power + LPC_USB->HcRhStatus = OR_RH_STATUS_LPSC; + + LPC_USB->HcHCCA = (uint32_t)(usb_hcca); + + // Clear Interrrupt Status + LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus; + + LPC_USB->HcInterruptEnable = OR_INTR_ENABLE_MIE | OR_INTR_ENABLE_WDH | OR_INTR_ENABLE_RHSC; + + // Enable the USB Interrupt + NVIC_SetVector(USB_IRQn, (uint32_t)(_usbisr)); + LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC; + LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; + + NVIC_EnableIRQ(USB_IRQn); + + // Check for any connected devices + if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS) { + //Device connected + wait_ms(150); + USB_DBG("Device connected (%08x)\n\r", LPC_USB->HcRhPortStatus1); + deviceConnected(0, 1, LPC_USB->HcRhPortStatus1 & OR_RH_PORT_LSDA); + } +} + +uint32_t USBHALHost::controlHeadED() { + return LPC_USB->HcControlHeadED; +} + +uint32_t USBHALHost::bulkHeadED() { + return LPC_USB->HcBulkHeadED; +} + +uint32_t USBHALHost::interruptHeadED() { + return usb_hcca->IntTable[0]; +} + +void USBHALHost::updateBulkHeadED(uint32_t addr) { + LPC_USB->HcBulkHeadED = addr; +} + + +void USBHALHost::updateControlHeadED(uint32_t addr) { + LPC_USB->HcControlHeadED = addr; +} + +void USBHALHost::updateInterruptHeadED(uint32_t addr) { + usb_hcca->IntTable[0] = addr; +} + + +void USBHALHost::enableList(ENDPOINT_TYPE type) { + switch(type) { + case CONTROL_ENDPOINT: + LPC_USB->HcCommandStatus = OR_CMD_STATUS_CLF; + LPC_USB->HcControl |= OR_CONTROL_CLE; + break; + case ISOCHRONOUS_ENDPOINT: + break; + case BULK_ENDPOINT: + LPC_USB->HcCommandStatus = OR_CMD_STATUS_BLF; + LPC_USB->HcControl |= OR_CONTROL_BLE; + break; + case INTERRUPT_ENDPOINT: + LPC_USB->HcControl |= OR_CONTROL_PLE; + break; + } +} + + +bool USBHALHost::disableList(ENDPOINT_TYPE type) { + switch(type) { + case CONTROL_ENDPOINT: + if(LPC_USB->HcControl & OR_CONTROL_CLE) { + LPC_USB->HcControl &= ~OR_CONTROL_CLE; + return true; + } + return false; + case ISOCHRONOUS_ENDPOINT: + return false; + case BULK_ENDPOINT: + if(LPC_USB->HcControl & OR_CONTROL_BLE){ + LPC_USB->HcControl &= ~OR_CONTROL_BLE; + return true; + } + return false; + case INTERRUPT_ENDPOINT: + if(LPC_USB->HcControl & OR_CONTROL_PLE) { + LPC_USB->HcControl &= ~OR_CONTROL_PLE; + return true; + } + return false; + } + return false; +} + + +void USBHALHost::memInit() { + usb_hcca = (volatile HCCA *)usb_buf; + usb_edBuf = usb_buf + HCCA_SIZE; + usb_tdBuf = usb_buf + HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE); +} + +volatile uint8_t * USBHALHost::getED() { + for (int i = 0; i < MAX_ENDPOINT; i++) { + if ( !edBufAlloc[i] ) { + edBufAlloc[i] = true; + return (volatile uint8_t *)(usb_edBuf + i*ED_SIZE); + } + } + perror("Could not allocate ED\r\n"); + return NULL; //Could not alloc ED +} + +volatile uint8_t * USBHALHost::getTD() { + int i; + for (i = 0; i < MAX_TD; i++) { + if ( !tdBufAlloc[i] ) { + tdBufAlloc[i] = true; + return (volatile uint8_t *)(usb_tdBuf + i*TD_SIZE); + } + } + perror("Could not allocate TD\r\n"); + return NULL; //Could not alloc TD +} + + +void USBHALHost::freeED(volatile uint8_t * ed) { + int i; + i = (ed - usb_edBuf) / ED_SIZE; + edBufAlloc[i] = false; +} + +void USBHALHost::freeTD(volatile uint8_t * td) { + int i; + i = (td - usb_tdBuf) / TD_SIZE; + tdBufAlloc[i] = false; +} + + +void USBHALHost::resetRootHub() { + // Initiate port reset + LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS; + + while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS); + + // ...and clear port reset signal + LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; +} + + +void USBHALHost::_usbisr(void) { + if (instHost) { + instHost->UsbIrqhandler(); + } +} + +void USBHALHost::UsbIrqhandler() { + if( LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable ) //Is there something to actually process? + { + + uint32_t int_status = LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable; + + // Root hub status change interrupt + if (int_status & OR_INTR_STATUS_RHSC) { + if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CSC) { + if (LPC_USB->HcRhStatus & OR_RH_STATUS_DRWE) { + // When DRWE is on, Connect Status Change + // means a remote wakeup event. + } else { + + //Root device connected + if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS) { + + // wait 150ms to avoid bounce + wait_ms(150); + + //Hub 0 (root hub), Port 1 (count starts at 1), Low or High speed + deviceConnected(0, 1, LPC_USB->HcRhPortStatus1 & OR_RH_PORT_LSDA); + } + + //Root device disconnected + else { + + if (!(int_status & OR_INTR_STATUS_WDH)) { + usb_hcca->DoneHead = 0; + } + + // wait 200ms to avoid bounce + wait_ms(200); + + deviceDisconnected(0, 1, NULL, usb_hcca->DoneHead & 0xFFFFFFFE); + + if (int_status & OR_INTR_STATUS_WDH) { + usb_hcca->DoneHead = 0; + LPC_USB->HcInterruptStatus = OR_INTR_STATUS_WDH; + } + } + } + LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC; + } + if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRSC) { + LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; + } + LPC_USB->HcInterruptStatus = OR_INTR_STATUS_RHSC; + } + + // Writeback Done Head interrupt + if (int_status & OR_INTR_STATUS_WDH) { + transferCompleted(usb_hcca->DoneHead & 0xFFFFFFFE); + LPC_USB->HcInterruptStatus = OR_INTR_STATUS_WDH; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHost/USBHALHost.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,169 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USBHALHOST_H +#define USBHALHOST_H + +#include "USBHostTypes.h" +#include "USBHostConf.h" + +class USBHostHub; + +/** +* USBHALHost class +*/ +class USBHALHost { +protected: + + /** + * Constructor + * init variables and memory where will be stored HCCA, ED and TD + */ + USBHALHost(); + + /** + * Initialize host controller. Enable USB interrupts. This part is not in the constructor because, + * this function calls a virtual method if a device is already connected + */ + void init(); + + /** + * reset the root hub + */ + void resetRootHub(); + + /** + * return the value contained in the control HEAD ED register + * + * @returns address of the control Head ED + */ + uint32_t controlHeadED(); + + /** + * return the value contained in the bulk HEAD ED register + * + * @returns address of the bulk head ED + */ + uint32_t bulkHeadED(); + + /** + * return the value of the head interrupt ED contained in the HCCA + * + * @returns address of the head interrupt ED contained in the HCCA + */ + uint32_t interruptHeadED(); + + /** + * Update the head ED for control transfers + */ + void updateControlHeadED(uint32_t addr); + + /** + * Update the head ED for bulk transfers + */ + void updateBulkHeadED(uint32_t addr); + + /** + * Update the head ED for interrupt transfers + */ + void updateInterruptHeadED(uint32_t addr); + + /** + * Enable List for the specified endpoint type + * + * @param type enable the list of ENDPOINT_TYPE type + */ + void enableList(ENDPOINT_TYPE type); + + /** + * Disable List for the specified endpoint type + * + * @param type disable the list of ENDPOINT_TYPE type + */ + bool disableList(ENDPOINT_TYPE type); + + /** + * Virtual method called when a device has been connected + * + * @param hub hub number of the device + * @param port port number of the device + * @param lowSpeed 1 if low speed, 0 otherwise + * @param hub_parent reference to the hub where the device is connected (NULL if the hub parent is the root hub) + */ + virtual void deviceConnected(int hub, int port, bool lowSpeed, USBHostHub * hub_parent = NULL) = 0; + + /** + * Virtual method called when a device has been disconnected + * + * @param hub hub number of the device + * @param port port number of the device + * @param hub_parent reference to the hub where the device is connected (NULL if the hub parent is the root hub) + * @param addr list of the TDs which have been completed to dequeue freed TDs + */ + virtual void deviceDisconnected(int hub, int port, USBHostHub * hub_parent, volatile uint32_t addr) = 0; + + /** + * Virtual method called when a transfer has been completed + * + * @param addr list of the TDs which have been completed + */ + virtual void transferCompleted(volatile uint32_t addr) = 0; + + /** + * Find a memory section for a new ED + * + * @returns the address of the new ED + */ + volatile uint8_t * getED(); + + /** + * Find a memory section for a new TD + * + * @returns the address of the new TD + */ + volatile uint8_t * getTD(); + + /** + * Release a previous memory section reserved for an ED + * + * @param ed address of the ED + */ + void freeED(volatile uint8_t * ed); + + /** + * Release a previous memory section reserved for an TD + * + * @param td address of the TD + */ + void freeTD(volatile uint8_t * td); + +private: + static void _usbisr(void); + void UsbIrqhandler(); + + void memInit(); + + HCCA volatile * usb_hcca; //256 bytes aligned + uint8_t volatile * usb_edBuf; //4 bytes aligned + uint8_t volatile * usb_tdBuf; //4 bytes aligned + + static USBHALHost * instHost; + + bool volatile edBufAlloc[MAX_ENDPOINT]; + bool volatile tdBufAlloc[MAX_TD]; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHost/USBHost.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,1161 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "USBHost.h" +#include "USBHostHub.h" + +USBHost * USBHost::instHost = NULL; + +#define DEVICE_CONNECTED_EVENT (1 << 0) +#define DEVICE_DISCONNECTED_EVENT (1 << 1) +#define TD_PROCESSED_EVENT (1 << 2) + +#define MAX_TRY_ENUMERATE_HUB 3 + +#define MIN(a, b) ((a > b) ? b : a) + +/** +* How interrupts are processed: +* - new device connected: +* - a message is queued in queue_usb_event with the id DEVICE_CONNECTED_EVENT +* - when the usb_thread receives the event, it: +* - resets the device +* - reads the device descriptor +* - sets the address of the device +* - if it is a hub, enumerates it +* - device disconnected: +* - a message is queued in queue_usb_event with the id DEVICE_DISCONNECTED_EVENT +* - when the usb_thread receives the event, it: +* - free the device and all its children (hub) +* - td processed +* - a message is queued in queue_usb_event with the id TD_PROCESSED_EVENT +* - when the usb_thread receives the event, it: +* - call the callback attached to the endpoint where the td is attached +*/ +void USBHost::usb_process() { + + bool controlListState; + bool bulkListState; + bool interruptListState; + USBEndpoint * ep; + uint8_t i, j, res, timeout_set_addr = 10; + uint8_t buf[8]; + bool too_many_hub; + int idx; + +#if DEBUG_TRANSFER + uint8_t * buf_transfer; +#endif + +#if MAX_HUB_NB + uint8_t k; +#endif + + while(1) { + osEvent evt = mail_usb_event.get(); + + if (evt.status == osEventMail) { + + message_t * usb_msg = (message_t*)evt.value.p; + + switch (usb_msg->event_id) { + + // a new device has been connected + case DEVICE_CONNECTED_EVENT: + too_many_hub = false; + buf[4] = 0; + + usb_mutex.lock(); + + for (i = 0; i < MAX_DEVICE_CONNECTED; i++) { + if (!deviceInUse[i]) { + USB_DBG_EVENT("new device connected: %p\r\n", &devices[i]); + devices[i].init(usb_msg->hub, usb_msg->port, usb_msg->lowSpeed); + deviceReset[i] = false; + deviceInited[i] = true; + break; + } + } + + if (i == MAX_DEVICE_CONNECTED) { + USB_ERR("Too many device connected!!\r\n"); + deviceInited[i] = false; + usb_mutex.unlock(); + continue; + } + + if (!controlEndpointAllocated) { + control = newEndpoint(CONTROL_ENDPOINT, OUT, 0x08, 0x00); + addEndpoint(NULL, 0, (USBEndpoint*)control); + controlEndpointAllocated = true; + } + +#if MAX_HUB_NB + if (usb_msg->hub_parent) + devices[i].setHubParent((USBHostHub *)(usb_msg->hub_parent)); +#endif + + for (j = 0; j < timeout_set_addr; j++) { + + resetDevice(&devices[i]); + + // set size of control endpoint + devices[i].setSizeControlEndpoint(8); + + devices[i].activeAddress(false); + + // get first 8 bit of device descriptor + // and check if we deal with a hub + USB_DBG("usb_thread read device descriptor on dev: %p\r\n", &devices[i]); + res = getDeviceDescriptor(&devices[i], buf, 8); + + if (res != USB_TYPE_OK) { + USB_ERR("usb_thread could not read dev descr"); + continue; + } + + // set size of control endpoint + devices[i].setSizeControlEndpoint(buf[7]); + + // second step: set an address to the device + res = setAddress(&devices[i], devices[i].getAddress()); + + if (res != USB_TYPE_OK) { + USB_ERR("SET ADDR FAILED"); + continue; + } + devices[i].activeAddress(true); + USB_DBG("Address of %p: %d", &devices[i], devices[i].getAddress()); + + // try to read again the device descriptor to check if the device + // answers to its new address + res = getDeviceDescriptor(&devices[i], buf, 8); + + if (res == USB_TYPE_OK) { + break; + } + + Thread::wait(100); + } + + USB_INFO("New device connected: %p [hub: %d - port: %d]", &devices[i], usb_msg->hub, usb_msg->port); + +#if MAX_HUB_NB + if (buf[4] == HUB_CLASS) { + for (k = 0; k < MAX_HUB_NB; k++) { + if (hub_in_use[k] == false) { + for (uint8_t j = 0; j < MAX_TRY_ENUMERATE_HUB; j++) { + if (hubs[k].connect(&devices[i])) { + devices[i].hub = &hubs[k]; + hub_in_use[k] = true; + break; + } + } + if (hub_in_use[k] == true) + break; + } + } + + if (k == MAX_HUB_NB) { + USB_ERR("Too many hubs connected!!\r\n"); + too_many_hub = true; + } + } + + if (usb_msg->hub_parent) + ((USBHostHub *)(usb_msg->hub_parent))->deviceConnected(&devices[i]); +#endif + + if ((i < MAX_DEVICE_CONNECTED) && !too_many_hub) { + deviceInUse[i] = true; + } + + usb_mutex.unlock(); + + break; + + // a device has been disconnected + case DEVICE_DISCONNECTED_EVENT: + + usb_mutex.lock(); + + controlListState = disableList(CONTROL_ENDPOINT); + bulkListState = disableList(BULK_ENDPOINT); + interruptListState = disableList(INTERRUPT_ENDPOINT); + + idx = findDevice(usb_msg->hub, usb_msg->port, (USBHostHub *)(usb_msg->hub_parent)); + if (idx != -1) { + freeDevice((USBDeviceConnected*)&devices[idx]); + } + + if (controlListState) enableList(CONTROL_ENDPOINT); + if (bulkListState) enableList(BULK_ENDPOINT); + if (interruptListState) enableList(INTERRUPT_ENDPOINT); + + usb_mutex.unlock(); + + break; + + // a td has been processed + // call callback on the ed associated to the td + // we are not in ISR -> users can use printf in their callback method + case TD_PROCESSED_EVENT: + ep = (USBEndpoint *) ((HCTD *)usb_msg->td_addr)->ep; + if (usb_msg->td_state == USB_TYPE_IDLE) { + USB_DBG_EVENT("call callback on td %p [ep: %p state: %s - dev: %p - %s]", usb_msg->td_addr, ep, ep->getStateString(), ep->dev, ep->dev->getName(ep->getIntfNb())); + +#if DEBUG_TRANSFER + if (ep->getDir() == IN) { + buf_transfer = ep->getBufStart(); + printf("READ SUCCESS [%d bytes transferred - td: 0x%08X] on ep: [%p - addr: %02X]: ", ep->getLengthTransferred(), usb_msg->td_addr, ep, ep->getAddress()); + for (int i = 0; i < ep->getLengthTransferred(); i++) + printf("%02X ", buf_transfer[i]); + printf("\r\n\r\n"); + } +#endif + ep->call(); + } else { + idx = findDevice(ep->dev); + if (idx != -1) { + if (deviceInUse[idx]) { + USB_WARN("td %p processed but not in idle state: %s [ep: %p - dev: %p - %s]", usb_msg->td_addr, ep->getStateString(), ep, ep->dev, ep->dev->getName(ep->getIntfNb())); + ep->setState(USB_TYPE_IDLE); + } + } + } + break; + } + + mail_usb_event.free(usb_msg); + } + } +} + +/* static */void USBHost::usb_process_static(void const * arg) { + ((USBHost *)arg)->usb_process(); +} + +USBHost::USBHost() : usbThread(USBHost::usb_process_static, (void *)this, osPriorityNormal, USB_THREAD_STACK) +{ + headControlEndpoint = NULL; + headBulkEndpoint = NULL; + headInterruptEndpoint = NULL; + tailControlEndpoint = NULL; + tailBulkEndpoint = NULL; + tailInterruptEndpoint = NULL; + + lenReportDescr = 0; + + controlEndpointAllocated = false; + + for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; i++) { + deviceInUse[i] = false; + devices[i].setAddress(i + 1); + deviceReset[i] = false; + deviceInited[i] = false; + for (uint8_t j = 0; j < MAX_INTF; j++) + deviceAttachedDriver[i][j] = false; + } + +#if MAX_HUB_NB + for (uint8_t i = 0; i < MAX_HUB_NB; i++) { + hubs[i].setHost(this); + hub_in_use[i] = false; + } +#endif +} + + +void USBHost::transferCompleted(volatile uint32_t addr) +{ + uint8_t state; + + if(addr == NULL) + return; + + volatile HCTD* tdList = NULL; + + //First we must reverse the list order and dequeue each TD + do { + volatile HCTD* td = (volatile HCTD*)addr; + addr = (uint32_t)td->nextTD; //Dequeue from physical list + td->nextTD = tdList; //Enqueue into reversed list + tdList = td; + } while(addr); + + while(tdList != NULL) { + volatile HCTD* td = tdList; + tdList = (volatile HCTD*)td->nextTD; //Dequeue element now as it could be modified below + if (td->ep != NULL) { + USBEndpoint * ep = (USBEndpoint *)(td->ep); + + if (((HCTD *)td)->control >> 28) { + state = ((HCTD *)td)->control >> 28; + } else { + if (td->currBufPtr) + ep->setLengthTransferred((uint32_t)td->currBufPtr - (uint32_t)ep->getBufStart()); + state = 16 /*USB_TYPE_IDLE*/; + } + + ep->unqueueTransfer(td); + + if (ep->getType() != CONTROL_ENDPOINT) { + // callback on the processed td will be called from the usb_thread (not in ISR) + message_t * usb_msg = mail_usb_event.alloc(); + usb_msg->event_id = TD_PROCESSED_EVENT; + usb_msg->td_addr = (void *)td; + usb_msg->td_state = state; + mail_usb_event.put(usb_msg); + } + ep->setState(state); + ep->ep_queue.put((uint8_t*)1); + } + } +} + +USBHost * USBHost::getHostInst() +{ + if (instHost == NULL) { + instHost = new USBHost(); + instHost->init(); + } + return instHost; +} + + +/* + * Called when a device has been connected + * Called in ISR!!!! (no printf) + */ +/* virtual */ void USBHost::deviceConnected(int hub, int port, bool lowSpeed, USBHostHub * hub_parent) +{ + // be sure that the new device connected is not already connected... + int idx = findDevice(hub, port, hub_parent); + if (idx != -1) { + if (deviceInited[idx]) + return; + } + + message_t * usb_msg = mail_usb_event.alloc(); + usb_msg->event_id = DEVICE_CONNECTED_EVENT; + usb_msg->hub = hub; + usb_msg->port = port; + usb_msg->lowSpeed = lowSpeed; + usb_msg->hub_parent = hub_parent; + mail_usb_event.put(usb_msg); +} + +/* + * Called when a device has been disconnected + * Called in ISR!!!! (no printf) + */ +/* virtual */ void USBHost::deviceDisconnected(int hub, int port, USBHostHub * hub_parent, volatile uint32_t addr) +{ + // be sure that the device disconnected is connected... + int idx = findDevice(hub, port, hub_parent); + if (idx != -1) { + if (!deviceInUse[idx]) + return; + } else { + return; + } + + message_t * usb_msg = mail_usb_event.alloc(); + usb_msg->event_id = DEVICE_DISCONNECTED_EVENT; + usb_msg->hub = hub; + usb_msg->port = port; + usb_msg->hub_parent = hub_parent; + mail_usb_event.put(usb_msg); +} + +void USBHost::freeDevice(USBDeviceConnected * dev) +{ + USBEndpoint * ep = NULL; + HCED * ed = NULL; + +#if MAX_HUB_NB + if (dev->getClass() == HUB_CLASS) { + if (dev->hub == NULL) { + USB_ERR("HUB NULL!!!!!\r\n"); + } else { + dev->hub->hubDisconnected(); + for (uint8_t i = 0; i < MAX_HUB_NB; i++) { + if (dev->hub == &hubs[i]) { + hub_in_use[i] = false; + break; + } + } + } + } + + // notify hub parent that this device has been disconnected + if (dev->getHubParent()) + dev->getHubParent()->deviceDisconnected(dev); + +#endif + + int idx = findDevice(dev); + if (idx != -1) { + deviceInUse[idx] = false; + deviceReset[idx] = false; + + for (uint8_t j = 0; j < MAX_INTF; j++) { + deviceAttachedDriver[idx][j] = false; + if (dev->getInterface(j) != NULL) { + USB_DBG("FREE INTF %d on dev: %p, %p, nb_endpot: %d, %s", j, (void *)dev->getInterface(j), dev, dev->getInterface(j)->nb_endpoint, dev->getName(j)); + for (int i = 0; i < dev->getInterface(j)->nb_endpoint; i++) { + if ((ep = dev->getEndpoint(j, i)) != NULL) { + ed = (HCED *)ep->getHCED(); + ed->control |= (1 << 14); //sKip bit + unqueueEndpoint(ep); + + freeTD((volatile uint8_t*)ep->getTDList()[0]); + freeTD((volatile uint8_t*)ep->getTDList()[1]); + + freeED((uint8_t *)ep->getHCED()); + } + printList(BULK_ENDPOINT); + printList(INTERRUPT_ENDPOINT); + } + USB_INFO("Device disconnected [%p - %s - hub: %d - port: %d]", dev, dev->getName(j), dev->getHub(), dev->getPort()); + } + } + dev->disconnect(); + } +} + + +void USBHost::unqueueEndpoint(USBEndpoint * ep) +{ + USBEndpoint * prec = NULL; + USBEndpoint * current = NULL; + + for (int i = 0; i < 2; i++) { + current = (i == 0) ? (USBEndpoint*)headBulkEndpoint : (USBEndpoint*)headInterruptEndpoint; + prec = current; + while (current != NULL) { + if (current == ep) { + if (current->nextEndpoint() != NULL) { + prec->queueEndpoint(current->nextEndpoint()); + if (current == headBulkEndpoint) { + updateBulkHeadED((uint32_t)current->nextEndpoint()->getHCED()); + headBulkEndpoint = current->nextEndpoint(); + } else if (current == headInterruptEndpoint) { + updateInterruptHeadED((uint32_t)current->nextEndpoint()->getHCED()); + headInterruptEndpoint = current->nextEndpoint(); + } + } + // here we are dequeuing the queue of ed + // we need to update the tail pointer + else { + prec->queueEndpoint(NULL); + if (current == headBulkEndpoint) { + updateBulkHeadED(0); + headBulkEndpoint = current->nextEndpoint(); + } else if (current == headInterruptEndpoint) { + updateInterruptHeadED(0); + headInterruptEndpoint = current->nextEndpoint(); + } + + // modify tail + switch (current->getType()) { + case BULK_ENDPOINT: + tailBulkEndpoint = prec; + break; + case INTERRUPT_ENDPOINT: + tailInterruptEndpoint = prec; + break; + } + } + current->setState(USB_TYPE_FREE); + return; + } + prec = current; + current = current->nextEndpoint(); + } + } +} + + +USBDeviceConnected * USBHost::getDevice(uint8_t index) +{ + if ((index >= MAX_DEVICE_CONNECTED) || (!deviceInUse[index])) { + return NULL; + } + return (USBDeviceConnected*)&devices[index]; +} + +// create an USBEndpoint descriptor. the USBEndpoint is not linked +USBEndpoint * USBHost::newEndpoint(ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir, uint32_t size, uint8_t addr) +{ + int i = 0; + HCED * ed = (HCED *)getED(); + HCTD* td_list[2] = { (HCTD*)getTD(), (HCTD*)getTD() }; + + memset((void *)td_list[0], 0x00, sizeof(HCTD)); + memset((void *)td_list[1], 0x00, sizeof(HCTD)); + + // search a free USBEndpoint + for (i = 0; i < MAX_ENDPOINT; i++) { + if (endpoints[i].getState() == USB_TYPE_FREE) { + endpoints[i].init(ed, type, dir, size, addr, td_list); + USB_DBG("USBEndpoint created (%p): type: %d, dir: %d, size: %d, addr: %d, state: %s", &endpoints[i], type, dir, size, addr, endpoints[i].getStateString()); + return &endpoints[i]; + } + } + USB_ERR("could not allocate more endpoints!!!!"); + return NULL; +} + + +USB_TYPE USBHost::resetDevice(USBDeviceConnected * dev) +{ + int index = findDevice(dev); + if (index != -1) { + USB_DBG("Resetting hub %d, port %d\n", dev->getHub(), dev->getPort()); + Thread::wait(100); + if (dev->getHub() == 0) { + resetRootHub(); + } +#if MAX_HUB_NB + else { + dev->getHubParent()->portReset(dev->getPort()); + } +#endif + Thread::wait(100); + deviceReset[index] = true; + return USB_TYPE_OK; + } + + return USB_TYPE_ERROR; +} + +// link the USBEndpoint to the linked list and attach an USBEndpoint to a device +bool USBHost::addEndpoint(USBDeviceConnected * dev, uint8_t intf_nb, USBEndpoint * ep) +{ + + if (ep == NULL) { + return false; + } + + HCED * prevEd; + + // set device address in the USBEndpoint descriptor + if (dev == NULL) { + ep->setDeviceAddress(0); + } else { + ep->setDeviceAddress(dev->getAddress()); + } + + if ((dev != NULL) && dev->getSpeed()) { + ep->setSpeed(dev->getSpeed()); + } + + ep->setIntfNb(intf_nb); + + // queue the new USBEndpoint on the ED list + switch (ep->getType()) { + + case CONTROL_ENDPOINT: + prevEd = ( HCED*) controlHeadED(); + if (!prevEd) { + updateControlHeadED((uint32_t) ep->getHCED()); + USB_DBG_TRANSFER("First control USBEndpoint: %08X", (uint32_t) ep->getHCED()); + headControlEndpoint = ep; + tailControlEndpoint = ep; + return true; + } + tailControlEndpoint->queueEndpoint(ep); + tailControlEndpoint = ep; + return true; + + case BULK_ENDPOINT: + prevEd = ( HCED*) bulkHeadED(); + if (!prevEd) { + updateBulkHeadED((uint32_t) ep->getHCED()); + USB_DBG_TRANSFER("First bulk USBEndpoint: %08X\r\n", (uint32_t) ep->getHCED()); + headBulkEndpoint = ep; + tailBulkEndpoint = ep; + break; + } + USB_DBG_TRANSFER("Queue BULK Ed %p after %p\r\n",ep->getHCED(), prevEd); + tailBulkEndpoint->queueEndpoint(ep); + tailBulkEndpoint = ep; + break; + + case INTERRUPT_ENDPOINT: + prevEd = ( HCED*) interruptHeadED(); + if (!prevEd) { + updateInterruptHeadED((uint32_t) ep->getHCED()); + USB_DBG_TRANSFER("First interrupt USBEndpoint: %08X\r\n", (uint32_t) ep->getHCED()); + headInterruptEndpoint = ep; + tailInterruptEndpoint = ep; + break; + } + USB_DBG_TRANSFER("Queue INTERRUPT Ed %p after %p\r\n",ep->getHCED(), prevEd); + tailInterruptEndpoint->queueEndpoint(ep); + tailInterruptEndpoint = ep; + break; + default: + return false; + } + + ep->dev = dev; + dev->addEndpoint(intf_nb, ep); + + return true; +} + + +int USBHost::findDevice(USBDeviceConnected * dev) +{ + for (int i = 0; i < MAX_DEVICE_CONNECTED; i++) { + if (dev == &devices[i]) { + return i; + } + } + return -1; +} + +int USBHost::findDevice(uint8_t hub, uint8_t port, USBHostHub * hub_parent) +{ + for (int i = 0; i < MAX_DEVICE_CONNECTED; i++) { + if (devices[i].getHub() == hub && devices[i].getPort() == port) { + if (hub_parent != NULL) { + if (hub_parent == devices[i].getHubParent()) + return i; + } else { + return i; + } + } + } + return -1; +} + +void USBHost::printList(ENDPOINT_TYPE type) +{ +#if DEBUG_EP_STATE + volatile HCED * hced; + switch(type) { + case CONTROL_ENDPOINT: + hced = (HCED *)controlHeadED(); + break; + case BULK_ENDPOINT: + hced = (HCED *)bulkHeadED(); + break; + case INTERRUPT_ENDPOINT: + hced = (HCED *)interruptHeadED(); + break; + } + volatile HCTD * hctd = NULL; + const char * type_str = (type == BULK_ENDPOINT) ? "BULK" : + ((type == INTERRUPT_ENDPOINT) ? "INTERRUPT" : + ((type == CONTROL_ENDPOINT) ? "CONTROL" : "ISOCHRONOUS")); + printf("State of %s:\r\n", type_str); + while (hced != NULL) { + uint8_t dir = ((hced->control & (3 << 11)) >> 11); + printf("hced: %p [ADDR: %d, DIR: %s, EP_NB: 0x%X]\r\n", hced, + hced->control & 0x7f, + (dir == 1) ? "OUT" : ((dir == 0) ? "FROM_TD":"IN"), + (hced->control & (0xf << 7)) >> 7); + hctd = (HCTD *)((uint32_t)(hced->headTD) & ~(0xf)); + while (hctd != hced->tailTD) { + printf("\thctd: %p [DIR: %s]\r\n", hctd, ((hctd->control & (3 << 19)) >> 19) == 1 ? "OUT" : "IN"); + hctd = hctd->nextTD; + } + printf("\thctd: %p\r\n", hctd); + hced = hced->nextED; + } + printf("\r\n\r\n"); +#endif +} + + +// add a transfer on the TD linked list +USB_TYPE USBHost::addTransfer(USBEndpoint * ed, uint8_t * buf, uint32_t len) +{ + td_mutex.lock(); + + // allocate a TD which will be freed in TDcompletion + volatile HCTD * td = ed->getNextTD(); + if (td == NULL) { + return USB_TYPE_ERROR; + } + + uint32_t token = (ed->isSetup() ? TD_SETUP : ( (ed->getDir() == IN) ? TD_IN : TD_OUT )); + + uint32_t td_toggle; + + if (ed->getType() == CONTROL_ENDPOINT) { + if (ed->isSetup()) { + td_toggle = TD_TOGGLE_0; + } else { + td_toggle = TD_TOGGLE_1; + } + } else { + td_toggle = 0; + } + + td->control = (TD_ROUNDING | token | TD_DELAY_INT(0) | td_toggle | TD_CC); + td->currBufPtr = buf; + td->bufEnd = (buf + (len - 1)); + + ENDPOINT_TYPE type = ed->getType(); + + disableList(type); + ed->queueTransfer(); + printList(type); + enableList(type); + + td_mutex.unlock(); + + return USB_TYPE_PROCESSING; +} + + + +USB_TYPE USBHost::getDeviceDescriptor(USBDeviceConnected * dev, uint8_t * buf, uint16_t max_len_buf, uint16_t * len_dev_descr) +{ + USB_TYPE t = controlRead( dev, + USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, + GET_DESCRIPTOR, + (DEVICE_DESCRIPTOR << 8) | (0), + 0, buf, MIN(DEVICE_DESCRIPTOR_LENGTH, max_len_buf)); + if (len_dev_descr) + *len_dev_descr = MIN(DEVICE_DESCRIPTOR_LENGTH, max_len_buf); + + return t; +} + +USB_TYPE USBHost::getConfigurationDescriptor(USBDeviceConnected * dev, uint8_t * buf, uint16_t max_len_buf, uint16_t * len_conf_descr) +{ + USB_TYPE res; + uint16_t total_conf_descr_length = 0; + + // fourth step: get the beginning of the configuration descriptor to have the total length of the conf descr + res = controlRead( dev, + USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, + GET_DESCRIPTOR, + (CONFIGURATION_DESCRIPTOR << 8) | (0), + 0, buf, CONFIGURATION_DESCRIPTOR_LENGTH); + + if (res != USB_TYPE_OK) { + USB_ERR("GET CONF 1 DESCR FAILED"); + return res; + } + total_conf_descr_length = buf[2] | (buf[3] << 8); + total_conf_descr_length = MIN(max_len_buf, total_conf_descr_length); + + if (len_conf_descr) + *len_conf_descr = total_conf_descr_length; + + USB_DBG("TOTAL_LENGTH: %d \t NUM_INTERF: %d", total_conf_descr_length, buf[4]); + + return controlRead( dev, + USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, + GET_DESCRIPTOR, + (CONFIGURATION_DESCRIPTOR << 8) | (0), + 0, buf, total_conf_descr_length); +} + + +USB_TYPE USBHost::setAddress(USBDeviceConnected * dev, uint8_t address) { + return controlWrite( dev, + USB_HOST_TO_DEVICE | USB_RECIPIENT_DEVICE, + SET_ADDRESS, + address, + 0, NULL, 0); + +} + +USB_TYPE USBHost::setConfiguration(USBDeviceConnected * dev, uint8_t conf) +{ + return controlWrite( dev, + USB_HOST_TO_DEVICE | USB_RECIPIENT_DEVICE, + SET_CONFIGURATION, + conf, + 0, NULL, 0); +} + +uint8_t USBHost::numberDriverAttached(USBDeviceConnected * dev) { + int index = findDevice(dev); + uint8_t cnt = 0; + if (index == -1) + return 0; + for (uint8_t i = 0; i < MAX_INTF; i++) { + if (deviceAttachedDriver[index][i]) + cnt++; + } + return cnt; +} + +// enumerate a device with the control USBEndpoint +USB_TYPE USBHost::enumerate(USBDeviceConnected * dev, IUSBEnumerator* pEnumerator) +{ + uint16_t total_conf_descr_length = 0; + USB_TYPE res; + + usb_mutex.lock(); + + // don't enumerate a device which all interfaces are registered to a specific driver + int index = findDevice(dev); + + if (index == -1) { + usb_mutex.unlock(); + return USB_TYPE_ERROR; + } + + uint8_t nb_intf_attached = numberDriverAttached(dev); + USB_DBG("dev: %p nb_intf: %d", dev, dev->getNbIntf()); + USB_DBG("dev: %p nb_intf_attached: %d", dev, nb_intf_attached); + if ((nb_intf_attached != 0) && (dev->getNbIntf() == nb_intf_attached)) { + USB_DBG("Don't enumerate dev: %p because all intf are registered with a driver", dev); + usb_mutex.unlock(); + return USB_TYPE_OK; + } + + USB_DBG("Enumerate dev: %p", dev); + + // third step: get the whole device descriptor to see vid, pid + res = getDeviceDescriptor(dev, data, DEVICE_DESCRIPTOR_LENGTH); + + if (res != USB_TYPE_OK) { + USB_DBG("GET DEV DESCR FAILED"); + usb_mutex.unlock(); + return res; + } + + dev->setClass(data[4]); + dev->setSubClass(data[5]); + dev->setProtocol(data[6]); + dev->setVid(data[8] | (data[9] << 8)); + dev->setPid(data[10] | (data[11] << 8)); + USB_DBG("CLASS: %02X \t VID: %04X \t PID: %04X", data[4], data[8] | (data[9] << 8), data[10] | (data[11] << 8)); + + pEnumerator->setVidPid( data[8] | (data[9] << 8), data[10] | (data[11] << 8) ); + + res = getConfigurationDescriptor(dev, data, 300, &total_conf_descr_length); + if (res != USB_TYPE_OK) { + usb_mutex.unlock(); + return res; + } + +#if DEBUG + USB_DBG("CONFIGURATION DESCRIPTOR:\r\n"); + for (int i = 0; i < total_conf_descr_length; i++) + printf("%02X ", data[i]); + printf("\r\n\r\n"); +#endif + + // Parse the configuration descriptor + parseConfDescr(dev, data, total_conf_descr_length, pEnumerator); + + // only set configuration if not enumerated before + if (!dev->isEnumerated()) { + + USB_DBG("Set configuration 1 on dev: %p", dev); + // sixth step: set configuration (only 1 supported) + res = setConfiguration(dev, 1); + + if (res != USB_TYPE_OK) { + USB_DBG("SET CONF FAILED"); + usb_mutex.unlock(); + return res; + } + } + + dev->setEnumerated(); + + // Now the device is enumerated! + USB_DBG("dev %p is enumerated\r\n", dev); + usb_mutex.unlock(); + + // Some devices may require this delay + wait_ms(100); + + return USB_TYPE_OK; +} +// this method fills the USBDeviceConnected object: class,.... . It also add endpoints found in the descriptor. +void USBHost::parseConfDescr(USBDeviceConnected * dev, uint8_t * conf_descr, uint32_t len, IUSBEnumerator* pEnumerator) +{ + uint32_t index = 0; + uint32_t len_desc = 0; + uint8_t id = 0; + int nb_endpoints_used = 0; + USBEndpoint * ep = NULL; + uint8_t intf_nb = 0; + bool parsing_intf = false; + uint8_t current_intf = 0; + + while (index < len) { + len_desc = conf_descr[index]; + id = conf_descr[index+1]; + switch (id) { + case CONFIGURATION_DESCRIPTOR: + USB_DBG("dev: %p has %d intf", dev, conf_descr[4]); + dev->setNbIntf(conf_descr[4]); + break; + case INTERFACE_DESCRIPTOR: + if(pEnumerator->parseInterface(conf_descr[index + 2], conf_descr[index + 5], conf_descr[index + 6], conf_descr[index + 7])) { + if (intf_nb++ <= MAX_INTF) { + current_intf = conf_descr[index + 2]; + dev->addInterface(current_intf, conf_descr[index + 5], conf_descr[index + 6], conf_descr[index + 7]); + nb_endpoints_used = 0; + USB_DBG("ADD INTF %d on device %p: class: %d, subclass: %d, proto: %d", current_intf, dev, conf_descr[index + 5],conf_descr[index + 6],conf_descr[index + 7]); + } else { + USB_DBG("Drop intf..."); + } + parsing_intf = true; + } else { + parsing_intf = false; + } + break; + case ENDPOINT_DESCRIPTOR: + if (parsing_intf && (intf_nb <= MAX_INTF) ) { + if (nb_endpoints_used < MAX_ENDPOINT_PER_INTERFACE) { + if( pEnumerator->useEndpoint(current_intf, (ENDPOINT_TYPE)(conf_descr[index + 3] & 0x03), (ENDPOINT_DIRECTION)((conf_descr[index + 2] >> 7) + 1)) ) { + // if the USBEndpoint is isochronous -> skip it (TODO: fix this) + if ((conf_descr[index + 3] & 0x03) != ISOCHRONOUS_ENDPOINT) { + ep = newEndpoint((ENDPOINT_TYPE)(conf_descr[index+3] & 0x03), + (ENDPOINT_DIRECTION)((conf_descr[index + 2] >> 7) + 1), + conf_descr[index + 4] | (conf_descr[index + 5] << 8), + conf_descr[index + 2] & 0x0f); + USB_DBG("ADD USBEndpoint %p, on interf %d on device %p", ep, current_intf, dev); + if (ep != NULL && dev != NULL) { + addEndpoint(dev, current_intf, ep); + } else { + USB_DBG("EP NULL"); + } + nb_endpoints_used++; + } else { + USB_DBG("ISO USBEndpoint NOT SUPPORTED"); + } + } + } + } + break; + case HID_DESCRIPTOR: + lenReportDescr = conf_descr[index + 7] | (conf_descr[index + 8] << 8); + break; + default: + break; + } + index += len_desc; + } +} + + +USB_TYPE USBHost::bulkWrite(USBDeviceConnected * dev, USBEndpoint * ep, uint8_t * buf, uint32_t len, bool blocking) +{ + return generalTransfer(dev, ep, buf, len, blocking, BULK_ENDPOINT, true); +} + +USB_TYPE USBHost::bulkRead(USBDeviceConnected * dev, USBEndpoint * ep, uint8_t * buf, uint32_t len, bool blocking) +{ + return generalTransfer(dev, ep, buf, len, blocking, BULK_ENDPOINT, false); +} + +USB_TYPE USBHost::interruptWrite(USBDeviceConnected * dev, USBEndpoint * ep, uint8_t * buf, uint32_t len, bool blocking) +{ + return generalTransfer(dev, ep, buf, len, blocking, INTERRUPT_ENDPOINT, true); +} + +USB_TYPE USBHost::interruptRead(USBDeviceConnected * dev, USBEndpoint * ep, uint8_t * buf, uint32_t len, bool blocking) +{ + return generalTransfer(dev, ep, buf, len, blocking, INTERRUPT_ENDPOINT, false); +} + +USB_TYPE USBHost::generalTransfer(USBDeviceConnected * dev, USBEndpoint * ep, uint8_t * buf, uint32_t len, bool blocking, ENDPOINT_TYPE type, bool write) { + +#if DEBUG_TRANSFER + const char * type_str = (type == BULK_ENDPOINT) ? "BULK" : ((type == INTERRUPT_ENDPOINT) ? "INTERRUPT" : "ISOCHRONOUS"); + USB_DBG_TRANSFER("----- %s %s [dev: %p - %s - hub: %d - port: %d - addr: %d - ep: %02X]------", type_str, (write) ? "WRITE" : "READ", dev, dev->getName(ep->getIntfNb()), dev->getHub(), dev->getPort(), dev->getAddress(), ep->getAddress()); +#endif + + usb_mutex.lock(); + + USB_TYPE res; + ENDPOINT_DIRECTION dir = (write) ? OUT : IN; + + if (dev == NULL) { + USB_ERR("dev NULL"); + usb_mutex.unlock(); + return USB_TYPE_ERROR; + } + + if (ep == NULL) { + USB_ERR("ep NULL"); + usb_mutex.unlock(); + return USB_TYPE_ERROR; + } + + if (ep->getState() != USB_TYPE_IDLE) { + USB_WARN("[ep: %p - dev: %p - %s] NOT IDLE: %s", ep, ep->dev, ep->dev->getName(ep->getIntfNb()), ep->getStateString()); + usb_mutex.unlock(); + return ep->getState(); + } + + if ((ep->getDir() != dir) || (ep->getType() != type)) { + USB_ERR("[ep: %p - dev: %p] wrong dir or bad USBEndpoint type", ep, ep->dev); + usb_mutex.unlock(); + return USB_TYPE_ERROR; + } + + if (dev->getAddress() != ep->getDeviceAddress()) { + USB_ERR("[ep: %p - dev: %p] USBEndpoint addr and device addr don't match", ep, ep->dev); + usb_mutex.unlock(); + return USB_TYPE_ERROR; + } + +#if DEBUG_TRANSFER + if (write) { + USB_DBG_TRANSFER("%s WRITE buffer", type_str); + for (int i = 0; i < ep->getLengthTransferred(); i++) + printf("%02X ", buf[i]); + printf("\r\n\r\n"); + } +#endif + addTransfer(ep, buf, len); + + if (blocking) { + + ep->ep_queue.get(); + res = ep->getState(); + + USB_DBG_TRANSFER("%s TRANSFER res: %s on ep: %p\r\n", type_str, ep->getStateString(), ep); + + if (res != USB_TYPE_IDLE) { + usb_mutex.unlock(); + return res; + } + + usb_mutex.unlock(); + return USB_TYPE_OK; + } + + usb_mutex.unlock(); + return USB_TYPE_PROCESSING; + +} + + +USB_TYPE USBHost::controlRead(USBDeviceConnected * dev, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t * buf, uint32_t len) { + return controlTransfer(dev, requestType, request, value, index, buf, len, false); +} + +USB_TYPE USBHost::controlWrite(USBDeviceConnected * dev, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t * buf, uint32_t len) { + return controlTransfer(dev, requestType, request, value, index, buf, len, true); +} + +USB_TYPE USBHost::controlTransfer(USBDeviceConnected * dev, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t * buf, uint32_t len, bool write) +{ + usb_mutex.lock(); + USB_DBG_TRANSFER("----- CONTROL %s [dev: %p - hub: %d - port: %d] ------", (write) ? "WRITE" : "READ", dev, dev->getHub(), dev->getPort()); + + int length_transfer = len; + USB_TYPE res; + uint32_t token; + + control->setSpeed(dev->getSpeed()); + control->setSize(dev->getSizeControlEndpoint()); + if (dev->isActiveAddress()) { + control->setDeviceAddress(dev->getAddress()); + } else { + control->setDeviceAddress(0); + } + + USB_DBG_TRANSFER("Control transfer on device: %d\r\n", control->getDeviceAddress()); + fillControlBuf(requestType, request, value, index, len); + +#if DEBUG_TRANSFER + USB_DBG_TRANSFER("SETUP PACKET: "); + for (int i = 0; i < 8; i++) + printf("%01X ", setupPacket[i]); + printf("\r\n"); +#endif + + control->setNextToken(TD_SETUP); + addTransfer(control, (uint8_t*)setupPacket, 8); + + control->ep_queue.get(); + res = control->getState(); + + USB_DBG_TRANSFER("CONTROL setup stage %s", control->getStateString()); + + if (res != USB_TYPE_IDLE) { + usb_mutex.unlock(); + return res; + } + + if (length_transfer) { + token = (write) ? TD_OUT : TD_IN; + control->setNextToken(token); + addTransfer(control, (uint8_t *)buf, length_transfer); + + control->ep_queue.get(); + res = control->getState(); + +#if DEBUG_TRANSFER + USB_DBG_TRANSFER("CONTROL %s stage %s", (write) ? "WRITE" : "READ", control->getStateString()); + if (write) { + USB_DBG_TRANSFER("CONTROL WRITE buffer"); + for (int i = 0; i < control->getLengthTransferred(); i++) + printf("%02X ", buf[i]); + printf("\r\n\r\n"); + } else { + USB_DBG_TRANSFER("CONTROL READ SUCCESS [%d bytes transferred]", control->getLengthTransferred()); + for (int i = 0; i < control->getLengthTransferred(); i++) + printf("%02X ", buf[i]); + printf("\r\n\r\n"); + } +#endif + + if (res != USB_TYPE_IDLE) { + usb_mutex.unlock(); + return res; + } + } + + token = (write) ? TD_IN : TD_OUT; + control->setNextToken(token); + addTransfer(control, NULL, 0); + + control->ep_queue.get(); + res = control->getState(); + + USB_DBG_TRANSFER("CONTROL ack stage %s", control->getStateString()); + usb_mutex.unlock(); + + if (res != USB_TYPE_IDLE) + return res; + + return USB_TYPE_OK; +} + + +void USBHost::fillControlBuf(uint8_t requestType, uint8_t request, uint16_t value, uint16_t index, int len) +{ +#ifdef __BIG_ENDIAN +#error "Must implement BE to LE conv here" +#endif + setupPacket[0] = requestType; + setupPacket[1] = request; + //We are in LE so it's fine + *((uint16_t*)&setupPacket[2]) = value; + *((uint16_t*)&setupPacket[4]) = index; + *((uint16_t*)&setupPacket[6]) = (uint32_t) len; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHost/USBHost.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,383 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USBHOST_H +#define USBHOST_H + +#include "USBHALHost.h" +#include "USBDeviceConnected.h" +#include "IUSBEnumerator.h" +#include "USBHostConf.h" +#include "rtos.h" +#include "dbg.h" +#include "USBHostHub.h" + +/** +* USBHost class +* This class is a singleton. All drivers have a reference on the static USBHost instance +*/ +class USBHost : public USBHALHost { +public: + /** + * Static method to create or retrieve the single USBHost instance + */ + static USBHost * getHostInst(); + + /** + * Control read: setup stage, data stage and status stage + * + * @param dev the control read will be done for this device + * @param requestType request type + * @param request request + * @param value value + * @param index index + * @param buf pointer on a buffer where will be store the data received + * @param len length of the transfer + * + * @returns status of the control read + */ + USB_TYPE controlRead(USBDeviceConnected * dev, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t * buf, uint32_t len); + + /** + * Control write: setup stage, data stage and status stage + * + * @param dev the control write will be done for this device + * @param requestType request type + * @param request request + * @param value value + * @param index index + * @param buf pointer on a buffer which will be written + * @param len length of the transfer + * + * @returns status of the control write + */ + USB_TYPE controlWrite(USBDeviceConnected * dev, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t * buf, uint32_t len); + + /** + * Bulk read + * + * @param dev the bulk transfer will be done for this device + * @param ep USBEndpoint which will be used to read a packet + * @param buf pointer on a buffer where will be store the data received + * @param len length of the transfer + * @param blocking if true, the read is blocking (wait for completion) + * + * @returns status of the bulk read + */ + USB_TYPE bulkRead(USBDeviceConnected * dev, USBEndpoint * ep, uint8_t * buf, uint32_t len, bool blocking = true); + + /** + * Bulk write + * + * @param dev the bulk transfer will be done for this device + * @param ep USBEndpoint which will be used to write a packet + * @param buf pointer on a buffer which will be written + * @param len length of the transfer + * @param blocking if true, the write is blocking (wait for completion) + * + * @returns status of the bulk write + */ + USB_TYPE bulkWrite(USBDeviceConnected * dev, USBEndpoint * ep, uint8_t * buf, uint32_t len, bool blocking = true); + + /** + * Interrupt read + * + * @param dev the bulk transfer will be done for this device + * @param ep USBEndpoint which will be used to write a packet + * @param buf pointer on a buffer which will be written + * @param len length of the transfer + * @param blocking if true, the read is blocking (wait for completion) + * + * @returns status of the interrupt read + */ + USB_TYPE interruptRead(USBDeviceConnected * dev, USBEndpoint * ep, uint8_t * buf, uint32_t len, bool blocking = true); + + /** + * Interrupt write + * + * @param dev the bulk transfer will be done for this device + * @param ep USBEndpoint which will be used to write a packet + * @param buf pointer on a buffer which will be written + * @param len length of the transfer + * @param blocking if true, the write is blocking (wait for completion) + * + * @returns status of the interrupt write + */ + USB_TYPE interruptWrite(USBDeviceConnected * dev, USBEndpoint * ep, uint8_t * buf, uint32_t len, bool blocking = true); + + /** + * Enumerate a device. + * + * @param dev device which will be enumerated + * + * @returns status of the enumeration + */ + USB_TYPE enumerate(USBDeviceConnected * dev, IUSBEnumerator* pEnumerator); + + /** + * reset a specific device + * + * @param dev device which will be resetted + */ + USB_TYPE resetDevice(USBDeviceConnected * dev); + + /** + * Get a device + * + * @param index index of the device which will be returned + * + * @returns pointer on the "index" device + */ + USBDeviceConnected * getDevice(uint8_t index); + + /* + * If there is a HID device connected, the host stores the length of the report descriptor. + * This avoid to the driver to re-ask the configuration descriptor to request the report descriptor + * + * @returns length of the report descriptor + */ + inline uint16_t getLengthReportDescr() { + return lenReportDescr; + }; + + /** + * register a driver into the host associated with a callback function called when the device is disconnected + * + * @param dev device + * @param intf interface number + * @param tptr pointer to the object to call the member function on + * @param mptr pointer to the member function to be called + */ + template<typename T> + inline void registerDriver(USBDeviceConnected * dev, uint8_t intf, T* tptr, void (T::*mptr)(void)) { + int index = findDevice(dev); + if ((index != -1) && (mptr != NULL) && (tptr != NULL)) { + USB_DBG("register driver for dev: %p on intf: %d", dev, intf); + deviceAttachedDriver[index][intf] = true; + dev->onDisconnect(intf, tptr, mptr); + } + } + + /** + * register a driver into the host associated with a callback function called when the device is disconnected + * + * @param dev device + * @param intf interface number + * @param fn callback called when the specified device has been disconnected + */ + inline void registerDriver(USBDeviceConnected * dev, uint8_t intf, void (*fn)(void)) { + int index = findDevice(dev); + if ((index != -1) && (fn != NULL)) { + USB_DBG("register driver for dev: %p on intf: %d", dev, intf); + deviceAttachedDriver[index][intf] = true; + dev->onDisconnect(intf, fn); + } + } + + friend class USBHostHub; + +protected: + + /** + * Virtual method called when a transfer has been completed + * + * @param addr list of the TDs which have been completed + */ + virtual void transferCompleted(volatile uint32_t addr); + + /** + * Virtual method called when a device has been connected + * + * @param hub hub number of the device + * @param port port number of the device + * @param lowSpeed 1 if low speed, 0 otherwise + * @param hub_parent reference on the parent hub + */ + virtual void deviceConnected(int hub, int port, bool lowSpeed, USBHostHub * hub_parent = NULL); + + /** + * Virtuel method called when a device has been disconnected + * + * @param hub hub number of the device + * @param port port number of the device + * @param addr list of the TDs which have been completed to dequeue freed TDs + */ + virtual void deviceDisconnected(int hub, int port, USBHostHub * hub_parent, volatile uint32_t addr); + + +private: + // singleton class -> constructor is private + USBHost(); + static USBHost * instHost; + uint16_t lenReportDescr; + + // endpoints + void unqueueEndpoint(USBEndpoint * ep) ; + USBEndpoint endpoints[MAX_ENDPOINT]; + USBEndpoint* volatile control; + + USBEndpoint* volatile headControlEndpoint; + USBEndpoint* volatile headBulkEndpoint; + USBEndpoint* volatile headInterruptEndpoint; + + USBEndpoint* volatile tailControlEndpoint; + USBEndpoint* volatile tailBulkEndpoint; + USBEndpoint* volatile tailInterruptEndpoint; + + bool controlEndpointAllocated; + + // devices connected + USBDeviceConnected devices[MAX_DEVICE_CONNECTED]; + bool deviceInUse[MAX_DEVICE_CONNECTED]; + bool deviceAttachedDriver[MAX_DEVICE_CONNECTED][MAX_INTF]; + bool deviceReset[MAX_DEVICE_CONNECTED]; + bool deviceInited[MAX_DEVICE_CONNECTED]; + +#if MAX_HUB_NB + USBHostHub hubs[MAX_HUB_NB]; + bool hub_in_use[MAX_HUB_NB]; +#endif + + // to store a setup packet + uint8_t setupPacket[8]; + + typedef struct { + uint8_t event_id; + void * td_addr; + uint8_t hub; + uint8_t port; + uint8_t lowSpeed; + uint8_t td_state; + void * hub_parent; + } message_t; + + Thread usbThread; + void usb_process(); + static void usb_process_static(void const * arg); + Mail<message_t, 10> mail_usb_event; + Mutex usb_mutex; + Mutex td_mutex; + + // buffer for conf descriptor + uint8_t data[300]; + + /** + * Add a transfer on the TD linked list associated to an ED + * + * @param ed the transfer is associated to this ed + * @param buf pointer on a buffer where will be read/write data to send or receive + * @param len transfer length + * + * @return status of the transfer + */ + USB_TYPE addTransfer(USBEndpoint * ed, uint8_t * buf, uint32_t len) ; + + /** + * Link the USBEndpoint to the linked list and attach an USBEndpoint this USBEndpoint to a device + * + * @param dev pointer on a USBDeviceConnected object + * @param ep pointer on the USBEndpoint which will be added + * + * return true if successful + */ + bool addEndpoint(USBDeviceConnected * dev, uint8_t intf_nb, USBEndpoint * ep) ; + + /** + * Create an USBEndpoint descriptor. Warning: the USBEndpoint is not linked. + * + * @param type USBEndpoint type (CONTROL_ENDPOINT, BULK_ENDPOINT, INTERRUPT_ENDPOINT) + * @param dir USBEndpoint direction (no meaning for CONTROL_ENDPOINT) + * @param size USBEndpoint max packet size + * @param addr USBEndpoint address + * + * @returns pointer on the USBEndpoint created + */ + USBEndpoint * newEndpoint(ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir, uint32_t size, uint8_t addr) ; + + /** + * Request the device descriptor + * + * @param dev request the device descriptor on this device + * @param buf buffer to store the device descriptor + * @param max_len_buf maximum size of buf + * @param len_dev_descr pointer to store the length of the packet transferred + */ + USB_TYPE getDeviceDescriptor(USBDeviceConnected * dev, uint8_t * buf, uint16_t max_len_buf, uint16_t * len_dev_descr = NULL); + + /** + * Request the configuration descriptor + * + * @param dev request the configuration descriptor on this device + * @param buf buffer to store the configuration descriptor + * @param max_len_buf maximum size of buf + * @param len_conf_descr pointer to store the length of the packet transferred + */ + USB_TYPE getConfigurationDescriptor(USBDeviceConnected * dev, uint8_t * buf, uint16_t max_len_buf, uint16_t * len_conf_descr = NULL); + + /** + * Set the address of a specific device + * + * @param dev device to set the address + * @param address address + */ + USB_TYPE setAddress(USBDeviceConnected * dev, uint8_t address); + + /** + * Set the configuration of a device + * + * @param dev device on which the specified configuration will be activated + * @param conf configuration number to activate (usually 1) + */ + USB_TYPE setConfiguration(USBDeviceConnected * dev, uint8_t conf); + + /** + * Free a specific device + * + * @param dev device to be freed + */ + void freeDevice(USBDeviceConnected * dev); + + USB_TYPE controlTransfer( USBDeviceConnected * dev, + uint8_t requestType, + uint8_t request, + uint32_t value, + uint32_t index, + uint8_t * buf, + uint32_t len, + bool write); + + USB_TYPE generalTransfer( USBDeviceConnected * dev, + USBEndpoint * ep, + uint8_t * buf, + uint32_t len, + bool blocking, + ENDPOINT_TYPE type, + bool write) ; + + void fillControlBuf(uint8_t requestType, uint8_t request, uint16_t value, uint16_t index, int len) ; + void parseConfDescr(USBDeviceConnected * dev, uint8_t * conf_descr, uint32_t len, IUSBEnumerator* pEnumerator) ; + int findDevice(USBDeviceConnected * dev) ; + int findDevice(uint8_t hub, uint8_t port, USBHostHub * hub_parent = NULL) ; + uint8_t numberDriverAttached(USBDeviceConnected * dev); + + ///////////////////////// + /// FOR DEBUG + ///////////////////////// + void printList(ENDPOINT_TYPE type); + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHost/USBHostConf.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,81 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USBHOST_CONF_H +#define USBHOST_CONF_H + +/* +* Maximum number of devices that can be connected +* to the usb host +*/ +#define MAX_DEVICE_CONNECTED 5 + +/* +* Maximum of Hub connected to the usb host +*/ +#define MAX_HUB_NB 2 + +/* +* Maximum number of ports on a USB hub +*/ +#define MAX_HUB_PORT 4 + +/* +* Enable USBHostMSD +*/ +#define USBHOST_MSD 1 + +/* +* Enable USBHostKeyboard +*/ +#define USBHOST_KEYBOARD 1 + +/* +* Enable USBHostMouse +*/ +#define USBHOST_MOUSE 1 + +/* +* Enable USBHostSerial +*/ +#define USBHOST_SERIAL 1 + +/* +* Maximum number of interfaces of a usb device +*/ +#define MAX_INTF 3 + +/* +* Maximum number of endpoints on each interface +*/ +#define MAX_ENDPOINT_PER_INTERFACE 3 + +/* +* Maximum number of endpoint descriptors that can be allocated +*/ +#define MAX_ENDPOINT (MAX_DEVICE_CONNECTED * MAX_INTF * MAX_ENDPOINT_PER_INTERFACE) + +/* +* Maximum number of transfer descriptors that can be allocated +*/ +#define MAX_TD (MAX_ENDPOINT*2) + +/* +* usb_thread stack size +*/ +#define USB_THREAD_STACK (256*4 + MAX_HUB_NB*256*4) + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHost/USBHostTypes.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,225 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USB_INC_H +#define USB_INC_H + +#include "mbed.h" + +enum USB_TYPE { + USB_TYPE_OK = 0, + + // completion code + USB_TYPE_CRC_ERROR = 1, + USB_TYPE_BIT_STUFFING_ERROR = 2, + USB_TYPE_DATA_TOGGLE_MISMATCH_ERROR = 3, + USB_TYPE_STALL_ERROR = 4, + USB_TYPE_DEVICE_NOT_RESPONDING_ERROR = 5, + USB_TYPE_PID_CHECK_FAILURE_ERROR = 6, + USB_TYPE_UNEXPECTED_PID_ERROR = 7, + USB_TYPE_DATA_OVERRUN_ERROR = 8, + USB_TYPE_DATA_UNDERRUN_ERROR = 9, + USB_TYPE_RESERVED = 9, + USB_TYPE_RESERVED_ = 10, + USB_TYPE_BUFFER_OVERRUN_ERROR = 12, + USB_TYPE_BUFFER_UNDERRUN_ERROR = 13, + + // general usb state + USB_TYPE_DISCONNECTED = 14, + USB_TYPE_FREE = 15, + USB_TYPE_IDLE = 16, + USB_TYPE_PROCESSING = 17, + + USB_TYPE_ERROR = 18, +}; + + +enum ENDPOINT_DIRECTION { + OUT = 1, + IN +}; + +enum ENDPOINT_TYPE { + CONTROL_ENDPOINT = 0, + ISOCHRONOUS_ENDPOINT, + BULK_ENDPOINT, + INTERRUPT_ENDPOINT +}; + +#define AUDIO_CLASS 0x01 +#define CDC_CLASS 0x02 +#define HID_CLASS 0x03 +#define MSD_CLASS 0x08 +#define HUB_CLASS 0x09 +#define SERIAL_CLASS 0x0A + +// ------------------ HcControl Register --------------------- +#define OR_CONTROL_PLE 0x00000004 +#define OR_CONTROL_CLE 0x00000010 +#define OR_CONTROL_BLE 0x00000020 +#define OR_CONTROL_HCFS 0x000000C0 +#define OR_CONTROL_HC_OPER 0x00000080 +// ----------------- HcCommandStatus Register ----------------- +#define OR_CMD_STATUS_HCR 0x00000001 +#define OR_CMD_STATUS_CLF 0x00000002 +#define OR_CMD_STATUS_BLF 0x00000004 +// --------------- HcInterruptStatus Register ----------------- +#define OR_INTR_STATUS_WDH 0x00000002 +#define OR_INTR_STATUS_RHSC 0x00000040 +#define OR_INTR_STATUS_UE 0x00000010 +// --------------- HcInterruptEnable Register ----------------- +#define OR_INTR_ENABLE_WDH 0x00000002 +#define OR_INTR_ENABLE_RHSC 0x00000040 +#define OR_INTR_ENABLE_MIE 0x80000000 +// ---------------- HcRhDescriptorA Register ------------------ +#define OR_RH_STATUS_LPSC 0x00010000 +#define OR_RH_STATUS_DRWE 0x00008000 +// -------------- HcRhPortStatus[1:NDP] Register -------------- +#define OR_RH_PORT_CCS 0x00000001 +#define OR_RH_PORT_PRS 0x00000010 +#define OR_RH_PORT_CSC 0x00010000 +#define OR_RH_PORT_PRSC 0x00100000 +#define OR_RH_PORT_LSDA 0x00000200 + +#define FI 0x2EDF // 12000 bits per frame (-1) +#define DEFAULT_FMINTERVAL ((((6 * (FI - 210)) / 7) << 16) | FI) + +#define ED_SKIP (uint32_t) (0x00001000) // Skip this ep in queue + +#define TD_ROUNDING (uint32_t) (0x00040000) // Buffer Rounding +#define TD_SETUP (uint32_t)(0) // Direction of Setup Packet +#define TD_IN (uint32_t)(0x00100000) // Direction In +#define TD_OUT (uint32_t)(0x00080000) // Direction Out +#define TD_DELAY_INT(x) (uint32_t)((x) << 21) // Delay Interrupt +#define TD_TOGGLE_0 (uint32_t)(0x02000000) // Toggle 0 +#define TD_TOGGLE_1 (uint32_t)(0x03000000) // Toggle 1 +#define TD_CC (uint32_t)(0xF0000000) // Completion Code + +#define DEVICE_DESCRIPTOR (1) +#define CONFIGURATION_DESCRIPTOR (2) +#define INTERFACE_DESCRIPTOR (4) +#define ENDPOINT_DESCRIPTOR (5) +#define HID_DESCRIPTOR (33) + +// ----------- Control RequestType Fields ----------- +#define USB_DEVICE_TO_HOST 0x80 +#define USB_HOST_TO_DEVICE 0x00 +#define USB_REQUEST_TYPE_CLASS 0x20 +#define USB_REQUEST_TYPE_STANDARD 0x00 +#define USB_RECIPIENT_DEVICE 0x00 +#define USB_RECIPIENT_INTERFACE 0x01 +#define USB_RECIPIENT_ENDPOINT 0x02 + +// -------------- USB Standard Requests -------------- +#define SET_ADDRESS 0x05 +#define GET_DESCRIPTOR 0x06 +#define SET_CONFIGURATION 0x09 +#define SET_INTERFACE 0x0b +#define CLEAR_FEATURE 0x01 + +// -------------- USB Descriptor Length -------------- +#define DEVICE_DESCRIPTOR_LENGTH 0x12 +#define CONFIGURATION_DESCRIPTOR_LENGTH 0x09 + +// ------------ HostController Transfer Descriptor ------------ +typedef __packed struct HCTD { + __IO uint32_t control; // Transfer descriptor control + __IO uint8_t * currBufPtr; // Physical address of current buffer pointer + __IO HCTD * nextTD; // Physical pointer to next Transfer Descriptor + __IO uint8_t * bufEnd; // Physical address of end of buffer + void * ep; // ep address where a td is linked in + uint32_t dummy[3]; // padding +} HCTD; + +// ----------- HostController EndPoint Descriptor ------------- +typedef __packed struct hcEd { + __IO uint32_t control; // Endpoint descriptor control + __IO HCTD * tailTD; // Physical address of tail in Transfer descriptor list + __IO HCTD * headTD; // Physcial address of head in Transfer descriptor list + __IO hcEd * nextED; // Physical address of next Endpoint descriptor +} HCED; + + +// ----------- Host Controller Communication Area ------------ +typedef __packed struct hcca { + __IO uint32_t IntTable[32]; // Interrupt Table + __IO uint32_t FrameNumber; // Frame Number + __IO uint32_t DoneHead; // Done Head + volatile uint8_t Reserved[116]; // Reserved for future use + volatile uint8_t Unknown[4]; // Unused +} HCCA; + +typedef __packed struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} DeviceDescriptor; + +typedef __packed struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +} ConfigurationDescriptor; + +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; +} InterfaceDescriptor; + +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; +} EndpointDescriptor; + +typedef struct { + uint8_t bDescLength; + uint8_t bDescriptorType; + uint8_t bNbrPorts; + uint16_t wHubCharacteristics; + uint8_t bPwrOn2PwrGood; + uint8_t bHubContrCurrent; + uint8_t DeviceRemovable; + uint8_t PortPweCtrlMak; +} HubDescriptor; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHost/dbg.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,51 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USB_DEBUG_H +#define USB_DEBUG_H + +//Debug is disabled by default +#define DEBUG 0 +#define DEBUG_TRANSFER 0 +#define DEBUG_EP_STATE 0 +#define DEBUG_EVENT 0 + +#if (DEBUG) +#define USB_DBG(x, ...) std::printf("[USB_DBG: %s:%d]"x"\r\n", __FILE__, __LINE__, ##__VA_ARGS__); +#else +#define USB_DBG(x, ...) +#endif + +#if (DEBUG_TRANSFER) +#define USB_DBG_TRANSFER(x, ...) std::printf("[USB_TRANSFER: %s:%d]"x"\r\n", __FILE__, __LINE__, ##__VA_ARGS__); +#else +#define USB_DBG_TRANSFER(x, ...) +#endif + +#if (DEBUG_EVENT) +#define USB_DBG_EVENT(x, ...) std::printf("[USB_EVENT: %s:%d]"x"\r\n", __FILE__, __LINE__, ##__VA_ARGS__); +#else +#define USB_DBG_EVENT(x, ...) +#endif + +#define USB_INFO(x, ...) std::printf("[USB_INFO: %s:%d]"x"\r\n", __FILE__, __LINE__, ##__VA_ARGS__); +#define USB_WARN(x, ...) std::printf("[USB_WARNING: %s:%d]"x"\r\n", __FILE__, __LINE__, ##__VA_ARGS__); +#define USB_ERR(x, ...) std::printf("[USB_ERR: %s:%d]"x"\r\n", __FILE__, __LINE__, ##__VA_ARGS__); + +#endif + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHostHID/USBHostKeyboard.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,184 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "USBHostKeyboard.h" + +#if USBHOST_KEYBOARD + +static uint8_t keymap[4][0x39] = { + { 0, 0, 0, 0, 'a', 'b' /*0x05*/, + 'c', 'd', 'e', 'f', 'g' /*0x0a*/, + 'h', 'i', 'j', 'k', 'l'/*0x0f*/, + 'm', 'n', 'o', 'p', 'q'/*0x14*/, + 'r', 's', 't', 'u', 'v'/*0x19*/, + 'w', 'x', 'y', 'z', '1'/*0x1E*/, + '2', '3', '4', '5', '6'/*0x23*/, + '7', '8', '9', '0', 0x0A /*enter*/, /*0x28*/ + 0x1B /*escape*/, 0x08 /*backspace*/, 0x09/*tab*/, 0x20/*space*/, '-', /*0x2d*/ + '=', '[', ']', '\\', '#', /*0x32*/ + ';', '\'', 0, ',', '.', /*0x37*/ + '/'}, + + /* CTRL MODIFIER */ + { 0, 0, 0, 0, 0, 0 /*0x05*/, + 0, 0, 0, 0, 0 /*0x0a*/, + 0, 0, 0, 0, 0/*0x0f*/, + 0, 0, 0, 0, 0/*0x14*/, + 0, 0, 0, 0, 0/*0x19*/, + 0, 0, 0, 0, 0/*0x1E*/, + 0, 0, 0, 0, 0/*0x23*/, + 0, 0, 0, 0, 0 /*enter*/, /*0x28*/ + 0, 0, 0, 0, 0, /*0x2d*/ + 0, 0, 0, 0, 0, /*0x32*/ + 0, 0, 0, 0, 0, /*0x37*/ + 0}, + + /* SHIFT MODIFIER */ + { 0, 0, 0, 0, 'A', 'B' /*0x05*/, + 'C', 'D', 'E', 'F', 'G' /*0x0a*/, + 'H', 'I', 'J', 'K', 'L'/*0x0f*/, + 'M', 'N', 'O', 'P', 'Q'/*0x14*/, + 'R', 'S', 'T', 'U', 'V'/*0x19*/, + 'W', 'X', 'Y', 'Z', '!'/*0x1E*/, + '@', '#', '$', '%', '^'/*0x23*/, + '&', '*', '(', ')', 0, /*0x28*/ + 0, 0, 0, 0, 0, /*0x2d*/ + '+', '{', '}', '|', '~', /*0x32*/ + ':', '"', 0, '<', '>', /*0x37*/ + '?'}, + + /* ALT MODIFIER */ + { 0, 0, 0, 0, 0, 0 /*0x05*/, + 0, 0, 0, 0, 0 /*0x0a*/, + 0, 0, 0, 0, 0/*0x0f*/, + 0, 0, 0, 0, 0/*0x14*/, + 0, 0, 0, 0, 0/*0x19*/, + 0, 0, 0, 0, 0/*0x1E*/, + 0, 0, 0, 0, 0/*0x23*/, + 0, 0, 0, 0, 0 /*enter*/, /*0x28*/ + 0, 0, 0, 0, 0, /*0x2d*/ + 0, 0, 0, 0, 0, /*0x32*/ + 0, 0, 0, 0, 0, /*0x37*/ + 0} + +}; + + +USBHostKeyboard::USBHostKeyboard() { + host = USBHost::getHostInst(); + init(); +} + + +void USBHostKeyboard::init() { + dev = NULL; + int_in = NULL; + report_id = 0; + onKey = NULL; + onKeyCode = NULL; + dev_connected = false; + keyboard_intf = -1; + keyboard_device_found = false; +} + +bool USBHostKeyboard::connected() { + return dev_connected; +} + + +bool USBHostKeyboard::connect() { + + if (dev_connected) { + return true; + } + + for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; i++) { + if ((dev = host->getDevice(i)) != NULL) { + + if (host->enumerate(dev, this)) + break; + + if (keyboard_device_found) { + int_in = dev->getEndpoint(keyboard_intf, INTERRUPT_ENDPOINT, IN); + + if (!int_in) + break; + + USB_INFO("New Keyboard device: VID:%04x PID:%04x [dev: %p - intf: %d]", dev->getVid(), dev->getPid(), dev, keyboard_intf); + dev->setName("Keyboard", keyboard_intf); + host->registerDriver(dev, keyboard_intf, this, &USBHostKeyboard::init); + + int_in->attach(this, &USBHostKeyboard::rxHandler); + host->interruptRead(dev, int_in, report, int_in->getSize(), false); + + dev_connected = true; + return true; + } + } + } + init(); + return false; +} + +void USBHostKeyboard::rxHandler() { + int len = int_in->getLengthTransferred(); + int index = (len == 9) ? 1 : 0; + int len_listen = int_in->getSize(); + uint8_t key = 0; + if (len == 8 || len == 9) { + uint8_t modifier = (report[index] == 4) ? 3 : report[index]; + len_listen = len; + key = keymap[modifier][report[index + 2]]; + if (key && onKey) { + (*onKey)(key); + } + if ((report[index + 2] || modifier) && onKeyCode) { + (*onKeyCode)(report[index + 2], modifier); + } + } + if (dev && int_in) + host->interruptRead(dev, int_in, report, len_listen, false); +} + +/*virtual*/ void USBHostKeyboard::setVidPid(uint16_t vid, uint16_t pid) +{ + // we don't check VID/PID for keyboard driver +} + +/*virtual*/ bool USBHostKeyboard::parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) //Must return true if the interface should be parsed +{ + if ((keyboard_intf == -1) && + (intf_class == HID_CLASS) && + (intf_subclass == 0x01) && + (intf_protocol == 0x01)) { + keyboard_intf = intf_nb; + return true; + } + return false; +} + +/*virtual*/ bool USBHostKeyboard::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) //Must return true if the endpoint will be used +{ + if (intf_nb == keyboard_intf) { + if (type == INTERRUPT_ENDPOINT && dir == IN) { + keyboard_device_found = true; + return true; + } + } + return false; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHostHID/USBHostKeyboard.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,102 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USBHOSTKEYBOARD_H +#define USBHOSTKEYBOARD_H + +#include "USBHostConf.h" + +#if USBHOST_KEYBOARD + +#include "USBHost.h" + +/** + * A class to communicate a USB keyboard + */ +class USBHostKeyboard : public IUSBEnumerator { +public: + + /** + * Constructor + */ + USBHostKeyboard(); + + /** + * Try to connect a keyboard device + * + * @return true if connection was successful + */ + bool connect(); + + /** + * Check if a keyboard is connected + * + * @returns true if a keyboard is connected + */ + bool connected(); + + /** + * Attach a callback called when a keyboard event is received + * + * @param ptr function pointer + */ + inline void attach(void (*ptr)(uint8_t key)) { + if (ptr != NULL) { + onKey = ptr; + } + } + + /** + * Attach a callback called when a keyboard event is received + * + * @param ptr function pointer + */ + inline void attach(void (*ptr)(uint8_t keyCode, uint8_t modifier)) { + if (ptr != NULL) { + onKeyCode = ptr; + } + } + +protected: + //From IUSBEnumerator + virtual void setVidPid(uint16_t vid, uint16_t pid); + virtual bool parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol); //Must return true if the interface should be parsed + virtual bool useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir); //Must return true if the endpoint will be used + +private: + USBHost * host; + USBDeviceConnected * dev; + USBEndpoint * int_in; + uint8_t report[9]; + int keyboard_intf; + bool keyboard_device_found; + + bool dev_connected; + + void rxHandler(); + + void (*onKey)(uint8_t key); + void (*onKeyCode)(uint8_t key, uint8_t modifier); + + int report_id; + + void init(); + +}; + +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHostHID/USBHostMouse.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,145 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "USBHostMouse.h" + +#if USBHOST_MOUSE + +USBHostMouse::USBHostMouse() { + host = USBHost::getHostInst(); + init(); +} + +void USBHostMouse::init() { + dev = NULL; + int_in = NULL; + onUpdate = NULL; + onButtonUpdate = NULL; + onXUpdate = NULL; + onYUpdate = NULL; + onZUpdate = NULL; + report_id = 0; + dev_connected = false; + mouse_device_found = false; + mouse_intf = -1; + + buttons = 0; + x = 0; + y = 0; + z = 0; +} + +bool USBHostMouse::connected() { + return dev_connected; +} + +bool USBHostMouse::connect() { + + if (dev_connected) { + return true; + } + + for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; i++) { + if ((dev = host->getDevice(i)) != NULL) { + + if(host->enumerate(dev, this)) + break; + + if (mouse_device_found) { + + int_in = dev->getEndpoint(mouse_intf, INTERRUPT_ENDPOINT, IN); + if (!int_in) + break; + + USB_INFO("New Mouse device: VID:%04x PID:%04x [dev: %p - intf: %d]", dev->getVid(), dev->getPid(), dev, mouse_intf); + dev->setName("Mouse", mouse_intf); + host->registerDriver(dev, mouse_intf, this, &USBHostMouse::init); + + int_in->attach(this, &USBHostMouse::rxHandler); + host->interruptRead(dev, int_in, report, int_in->getSize(), false); + + dev_connected = true; + return true; + } + } + } + init(); + return false; +} + +void USBHostMouse::rxHandler() { + int len_listen = int_in->getSize(); + int len = int_in->getLengthTransferred(); + + if (onUpdate) { + (*onUpdate)(report[0] & 0x07, report[1], report[2], report[3]); + } + + if (onButtonUpdate && (buttons != (report[0] & 0x07))) { + (*onButtonUpdate)(report[0] & 0x07); + } + + if (onXUpdate && (x != report[1])) { + (*onXUpdate)(report[1]); + } + + if (onYUpdate && (y != report[2])) { + (*onYUpdate)(report[2]); + } + + if (onZUpdate && (z != report[3])) { + (*onZUpdate)(report[3]); + } + + // update mouse state + buttons = report[0] & 0x07; + x = report[1]; + y = report[2]; + z = report[3]; + + if (dev) + host->interruptRead(dev, int_in, report, len_listen, false); +} + +/*virtual*/ void USBHostMouse::setVidPid(uint16_t vid, uint16_t pid) +{ + // we don't check VID/PID for mouse driver +} + +/*virtual*/ bool USBHostMouse::parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) //Must return true if the interface should be parsed +{ + if ((mouse_intf == -1) && + (intf_class == HID_CLASS) && + (intf_subclass == 0x01) && + (intf_protocol == 0x02)) { + mouse_intf = intf_nb; + return true; + } + return false; +} + +/*virtual*/ bool USBHostMouse::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) //Must return true if the endpoint will be used +{ + if (intf_nb == mouse_intf) { + if (type == INTERRUPT_ENDPOINT && dir == IN) { + mouse_device_found = true; + return true; + } + } + return false; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHostHID/USBHostMouse.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,139 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USBHOSTMOUSE_H +#define USBHOSTMOUSE_H + +#include "USBHostConf.h" + +#if USBHOST_MOUSE + +#include "USBHost.h" + +/** + * A class to communicate a USB mouse + */ +class USBHostMouse : public IUSBEnumerator { +public: + + /** + * Constructor + */ + USBHostMouse(); + + /** + * Try to connect a mouse device + * + * @return true if connection was successful + */ + bool connect(); + + /** + * Check if a mouse is connected + * + * @returns true if a mouse is connected + */ + bool connected(); + + /** + * Attach a callback called when a mouse event is received + * + * @param ptr function pointer + */ + inline void attachEvent(void (*ptr)(uint8_t buttons, int8_t x, int8_t y, int8_t z)) { + if (ptr != NULL) { + onUpdate = ptr; + } + } + + /** + * Attach a callback called when the button state changes + * + * @param ptr function pointer + */ + inline void attachButtonEvent(void (*ptr)(uint8_t buttons)) { + if (ptr != NULL) { + onButtonUpdate = ptr; + } + } + + /** + * Attach a callback called when the X axis value changes + * + * @param ptr function pointer + */ + inline void attachXEvent(void (*ptr)(int8_t x)) { + if (ptr != NULL) { + onXUpdate = ptr; + } + } + + /** + * Attach a callback called when the Y axis value changes + * + * @param ptr function pointer + */ + inline void attachYEvent(void (*ptr)(int8_t y)) { + if (ptr != NULL) { + onYUpdate = ptr; + } + } + + /** + * Attach a callback called when the Z axis value changes (scrolling) + * + * @param ptr function pointer + */ + inline void attachZEvent(void (*ptr)(int8_t z)) { + if (ptr != NULL) { + onZUpdate = ptr; + } + } + +protected: + //From IUSBEnumerator + virtual void setVidPid(uint16_t vid, uint16_t pid); + virtual bool parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol); //Must return true if the interface should be parsed + virtual bool useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir); //Must return true if the endpoint will be used + +private: + USBHost * host; + USBDeviceConnected * dev; + USBEndpoint * int_in; + uint8_t report[4]; + + bool dev_connected; + bool mouse_device_found; + int mouse_intf; + + uint8_t buttons; + int8_t x; + int8_t y; + int8_t z; + + void rxHandler(); + void (*onUpdate)(uint8_t buttons, int8_t x, int8_t y, int8_t z); + void (*onButtonUpdate)(uint8_t buttons); + void (*onXUpdate)(int8_t x); + void (*onYUpdate)(int8_t y); + void (*onZUpdate)(int8_t z); + int report_id; + void init(); +}; + +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHostHub/USBHostHub.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,271 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "USBHostHub.h" + +#if MAX_HUB_NB + +#include "USBHost.h" +#include "dbg.h" + +#define GET_STATUS 0x00 +#define CLEAR_FEATURE 0x01 +#define GET_STATE 0x02 +#define SET_FEATURE 0x03 +#define GET_DESCRIPTOR 0x06 + +#define PORT_CONNECTION_FEATURE (0x00) +#define PORT_ENABLE_FEATURE (0x01) +#define PORT_RESET_FEATURE (0x04) +#define PORT_POWER_FEATURE (0x08) + +#define C_PORT_CONNECTION_FEATURE (16) +#define C_PORT_ENABLE_FEATURE (17) +#define C_PORT_RESET_FEATURE (20) + +#define PORT_CONNECTION (1 << 0) +#define PORT_ENABLE (1 << 1) +#define PORT_SUSPEND (1 << 2) +#define PORT_OVER_CURRENT (1 << 3) +#define PORT_RESET (1 << 4) +#define PORT_POWER (1 << 8) +#define PORT_LOW_SPEED (1 << 9) + +#define C_PORT_CONNECTION (1 << 16) +#define C_PORT_ENABLE (1 << 17) +#define C_PORT_SUSPEND (1 << 18) +#define C_PORT_OVER_CURRENT (1 << 19) +#define C_PORT_RESET (1 << 20) + +USBHostHub::USBHostHub() { + host = NULL; + init(); +} + +void USBHostHub::init() { + dev_connected = false; + dev = NULL; + int_in = NULL; + dev_connected = false; + hub_intf = -1; + hub_device_found = false; + nb_port = 0; + hub_characteristics = 0; + + for (int i = 0; i < MAX_HUB_PORT; i++) { + device_children[i] = NULL; + } +} + +void USBHostHub::setHost(USBHost * host_) { + host = host_; +} + +bool USBHostHub::connected() +{ + return dev_connected; +} + +bool USBHostHub::connect(USBDeviceConnected * dev) +{ + if (dev_connected) { + return true; + } + + if(host->enumerate(dev, this)) { + init(); + return false; + } + + if (hub_device_found) { + this->dev = dev; + + int_in = dev->getEndpoint(hub_intf, INTERRUPT_ENDPOINT, IN); + + if (!int_in) { + init(); + return false; + } + + USB_INFO("New HUB: VID:%04x PID:%04x [dev: %p - intf: %d]", dev->getVid(), dev->getPid(), dev, hub_intf); + dev->setName("Hub", hub_intf); + host->registerDriver(dev, hub_intf, this, &USBHostHub::disconnect); + + int_in->attach(this, &USBHostHub::rxHandler); + + // get HUB descriptor + host->controlRead( dev, + USB_DEVICE_TO_HOST | USB_REQUEST_TYPE_CLASS, + GET_DESCRIPTOR, + 0x29 << 8, 0, buf, sizeof(HubDescriptor)); + nb_port = buf[2]; + hub_characteristics = buf[3]; + + USB_DBG("Hub has %d port", nb_port); + + for (uint8_t j = 1; j <= nb_port; j++) { + setPortFeature(PORT_POWER_FEATURE, j); + } + wait_ms(buf[5]*2); + + host->interruptRead(dev, int_in, buf, 1, false); + dev_connected = true; + return true; + } + + return false; +} + +void USBHostHub::disconnect() { + init(); +} + +/*virtual*/ void USBHostHub::setVidPid(uint16_t vid, uint16_t pid) +{ + // we don't check VID/PID for MSD driver +} + +/*virtual*/ bool USBHostHub::parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) //Must return true if the interface should be parsed +{ + if ((hub_intf == -1) && + (intf_class == HUB_CLASS) && + (intf_subclass == 0) && + (intf_protocol == 0)) { + hub_intf = intf_nb; + return true; + } + return false; +} + +/*virtual*/ bool USBHostHub::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) //Must return true if the endpoint will be used +{ + if (intf_nb == hub_intf) { + if ((type == INTERRUPT_ENDPOINT) && (dir == IN)) { + hub_device_found = true; + return true; + } + } + return false; +} + +void USBHostHub::deviceConnected(USBDeviceConnected * dev) { + device_children[dev->getPort() - 1] = dev; +} + +void USBHostHub::deviceDisconnected(USBDeviceConnected * dev) { + device_children[dev->getPort() - 1] = NULL; +} + +void USBHostHub::hubDisconnected() { + for (uint8_t i = 0; i < MAX_HUB_PORT; i++) { + if (device_children[i] != NULL) { + host->freeDevice(device_children[i]); + } + } +} + +void USBHostHub::rxHandler() { + uint32_t status; + if (int_in) { + if (int_in->getState() == USB_TYPE_IDLE) { + for (int port = 1; port <= nb_port; port++) { + status = getPortStatus(port); + USB_DBG("[hub handler hub: %d] status port %d [hub: %p]: 0x%X", dev->getHub(), port, dev, status); + + // if connection status has changed + if (status & C_PORT_CONNECTION) { + if (status & PORT_CONNECTION) { + USB_DBG("[hub handler hub: %d - port: %d] new device connected", dev->getHub(), port); + host->deviceConnected(dev->getHub() + 1, port, status & PORT_LOW_SPEED, this); + } else { + USB_DBG("[hub handler hub: %d - port: %d] device disconnected", dev->getHub(), port); + host->deviceDisconnected(dev->getHub() + 1, port, this, NULL); + } + + clearPortFeature(C_PORT_CONNECTION_FEATURE, port); + } + + if (status & C_PORT_RESET) { + clearPortFeature(C_PORT_RESET_FEATURE, port); + } + + if (status & C_PORT_ENABLE) { + clearPortFeature(C_PORT_ENABLE_FEATURE, port); + } + + if ((status & PORT_OVER_CURRENT)) { + USB_ERR("OVER CURRENT DETECTED\r\n"); + clearPortFeature(PORT_OVER_CURRENT, port); + host->deviceDisconnected(dev->getHub() + 1, port, this, NULL); + } + } + } + host->interruptRead(dev, int_in, buf, 1, false); + } +} + +void USBHostHub::portReset(uint8_t port) { + // reset port + uint32_t status; + USB_DBG("reset port %d on hub: %p [this: %p]", port, dev, this) + setPortFeature(PORT_RESET_FEATURE, port); + while(1) { + status = getPortStatus(port); + if (status & (PORT_ENABLE | PORT_RESET)) + break; + if (status & PORT_OVER_CURRENT) { + USB_ERR("OVER CURRENT DETECTED\r\n"); + clearPortFeature(PORT_OVER_CURRENT, port); + host->deviceDisconnected(dev->getHub() + 1, port, this, NULL); + break; + } + Thread::wait(10); + } +} + +void USBHostHub::setPortFeature(uint32_t feature, uint8_t port) { + host->controlWrite( dev, + USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_INTERFACE | USB_RECIPIENT_ENDPOINT, + SET_FEATURE, + feature, + port, + NULL, + 0); +} + +void USBHostHub::clearPortFeature(uint32_t feature, uint8_t port) { + host->controlWrite( dev, + USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_INTERFACE | USB_RECIPIENT_ENDPOINT, + CLEAR_FEATURE, + feature, + port, + NULL, + 0); +} + +uint32_t USBHostHub::getPortStatus(uint8_t port) { + uint32_t st; + host->controlRead( dev, + USB_DEVICE_TO_HOST | USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_INTERFACE | USB_RECIPIENT_ENDPOINT, + GET_STATUS, + 0, + port, + (uint8_t *)&st, + 4); + return st; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHostHub/USBHostHub.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,125 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USBHOSTHUB_H +#define USBHOSTHUB_H + +#include "USBHostConf.h" + +#if MAX_HUB_NB + +#include "USBHostTypes.h" +#include "IUSBEnumerator.h" + +class USBHost; +class USBDeviceConnected; +class USBEndpoint; + +/** + * A class to use a USB Hub + */ +class USBHostHub : public IUSBEnumerator { +public: + /** + * Constructor + */ + USBHostHub(); + + /** + * Check if a USB Hub is connected + * + * @return true if a serial device is connected + */ + bool connected(); + + /** + * Try to connect device + * + * @param dev device to connect + * @return true if connection was successful + */ + bool connect(USBDeviceConnected * dev); + + /** + * Automatically called by USBHost when a device + * has been enumerated by usb_thread + * + * @param dev device connected + */ + void deviceConnected(USBDeviceConnected * dev); + + /** + * Automatically called by USBHost when a device + * has been disconnected from this hub + * + * @param dev device disconnected + */ + void deviceDisconnected(USBDeviceConnected * dev); + + /** + * Rest a specific port + * + * @param port port number + */ + void portReset(uint8_t port); + + /* + * Called by USBHost to set the instance of USBHost + * + * @param host host instance + */ + void setHost(USBHost * host); + + /** + * Called by USBhost when a hub has been disconnected + */ + void hubDisconnected(); + +protected: + //From IUSBEnumerator + virtual void setVidPid(uint16_t vid, uint16_t pid); + virtual bool parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol); //Must return true if the interface should be parsed + virtual bool useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir); //Must return true if the endpoint will be used + +private: + USBHost * host; + USBDeviceConnected * dev; + bool dev_connected; + USBEndpoint * int_in; + uint8_t nb_port; + uint8_t hub_characteristics; + + void rxHandler(); + + uint8_t buf[sizeof(HubDescriptor)]; + + int hub_intf; + bool hub_device_found; + + void setPortFeature(uint32_t feature, uint8_t port); + void clearPortFeature(uint32_t feature, uint8_t port); + uint32_t getPortStatus(uint8_t port); + + USBDeviceConnected * device_children[MAX_HUB_PORT]; + + void init(); + void disconnect(); + +}; + +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHostMSD/USBHostMSD.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,356 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "USBHostMSD.h" + +#if USBHOST_MSD + +#include "dbg.h" + +#define CBW_SIGNATURE 0x43425355 +#define CSW_SIGNATURE 0x53425355 + +#define DEVICE_TO_HOST 0x80 +#define HOST_TO_DEVICE 0x00 + +#define GET_MAX_LUN (0xFE) +#define BO_MASS_STORAGE_RESET (0xFF) + +USBHostMSD::USBHostMSD(const char * rootdir) : FATFileSystem(rootdir) +{ + host = USBHost::getHostInst(); + init(); +} + +void USBHostMSD::init() { + dev_connected = false; + dev = NULL; + bulk_in = NULL; + bulk_out = NULL; + dev_connected = false; + blockSize = 0; + blockCount = 0; + msd_intf = -1; + msd_device_found = false; + disk_init = false; + dev_connected = false; + nb_ep = 0; +} + + +bool USBHostMSD::connected() +{ + return dev_connected; +} + +bool USBHostMSD::connect() +{ + + if (dev_connected) { + return true; + } + + for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; i++) { + if ((dev = host->getDevice(i)) != NULL) { + + USB_DBG("Trying to connect MSD device\r\n"); + + if(host->enumerate(dev, this)) + break; + + if (msd_device_found) { + bulk_in = dev->getEndpoint(msd_intf, BULK_ENDPOINT, IN); + bulk_out = dev->getEndpoint(msd_intf, BULK_ENDPOINT, OUT); + + if (!bulk_in || !bulk_out) + continue; + + USB_INFO("New MSD device: VID:%04x PID:%04x [dev: %p - intf: %d]", dev->getVid(), dev->getPid(), dev, msd_intf); + dev->setName("MSD", msd_intf); + host->registerDriver(dev, msd_intf, this, &USBHostMSD::init); + + dev_connected = true; + return true; + } + } //if() + } //for() + init(); + return false; +} + +/*virtual*/ void USBHostMSD::setVidPid(uint16_t vid, uint16_t pid) +{ + // we don't check VID/PID for MSD driver +} + +/*virtual*/ bool USBHostMSD::parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) //Must return true if the interface should be parsed +{ + if ((msd_intf == -1) && + (intf_class == MSD_CLASS) && + (intf_subclass == 0x06) && + (intf_protocol == 0x50)) { + msd_intf = intf_nb; + return true; + } + return false; +} + +/*virtual*/ bool USBHostMSD::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) //Must return true if the endpoint will be used +{ + if (intf_nb == msd_intf) { + if (type == BULK_ENDPOINT) { + nb_ep++; + if (nb_ep == 2) + msd_device_found = true; + return true; + } + } + return false; +} + + +int USBHostMSD::testUnitReady() { + USB_DBG("Test unit ready"); + return SCSITransfer(NULL, 6, DEVICE_TO_HOST, 0, 0); +} + + +int USBHostMSD::readCapacity() { + USB_DBG("Read capacity"); + uint8_t cmd[10] = {0x25,0,0,0,0,0,0,0,0,0}; + uint8_t result[8]; + int status = SCSITransfer(cmd, 10, DEVICE_TO_HOST, result, 8); + if (status == 0) { + blockCount = (result[0] << 24) | (result[1] << 16) | (result[2] << 8) | result[3]; + blockSize = (result[4] << 24) | (result[5] << 16) | (result[6] << 8) | result[7]; + USB_INFO("MSD [dev: %p] - blockCount: %lld, blockSize: %d, Capacity: %lld\r\n", dev, blockCount, blockSize, blockCount*blockSize); + } + return status; +} + + +int USBHostMSD::SCSIRequestSense() { + USB_DBG("Request sense"); + uint8_t cmd[6] = {0x03,0,0,0,18,0}; + uint8_t result[18]; + int status = SCSITransfer(cmd, 6, DEVICE_TO_HOST, result, 18); + return status; +} + + +int USBHostMSD::inquiry(uint8_t lun, uint8_t page_code) { + USB_DBG("Inquiry"); + uint8_t evpd = (page_code == 0) ? 0 : 1; + uint8_t cmd[6] = {0x12, (lun << 5) | evpd, page_code, 0, 36, 0}; + uint8_t result[36]; + int status = SCSITransfer(cmd, 6, DEVICE_TO_HOST, result, 36); + if (status == 0) { + char vid_pid[17]; + memcpy(vid_pid, &result[8], 8); + vid_pid[8] = 0; + USB_INFO("MSD [dev: %p] - Vendor ID: %s", dev, vid_pid); + + memcpy(vid_pid, &result[16], 16); + vid_pid[16] = 0; + USB_INFO("MSD [dev: %p] - Product ID: %s", dev, vid_pid); + + memcpy(vid_pid, &result[32], 4); + vid_pid[4] = 0; + USB_INFO("MSD [dev: %p] - Product rev: %s", dev, vid_pid); + } + return status; +} + +int USBHostMSD::checkResult(uint8_t res, USBEndpoint * ep) { + // if ep stalled: send clear feature + if (res == USB_TYPE_STALL_ERROR) { + res = host->controlWrite( dev, + USB_RECIPIENT_ENDPOINT | USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_STANDARD, + CLEAR_FEATURE, + 0, ep->getAddress(), NULL, 0); + // set state to IDLE if clear feature successful + if (res == USB_TYPE_OK) { + ep->setState(USB_TYPE_IDLE); + } + } + + if (res != USB_TYPE_OK) + return -1; + + return 0; +} + + +int USBHostMSD::SCSITransfer(uint8_t * cmd, uint8_t cmd_len, int flags, uint8_t * data, uint32_t transfer_len) { + + int res = 0; + + cbw.Signature = CBW_SIGNATURE; + cbw.Tag = 0; + cbw.DataLength = transfer_len; + cbw.Flags = flags; + cbw.LUN = 0; + cbw.CBLength = cmd_len; + memset(cbw.CB,0,sizeof(cbw.CB)); + if (cmd) { + memcpy(cbw.CB,cmd,cmd_len); + } + + // send the cbw + USB_DBG("Send CBW"); + res = host->bulkWrite(dev, bulk_out,(uint8_t *)&cbw, 31); + if (checkResult(res, bulk_out)) + return -1; + + // data stage if needed + if (data) { + USB_DBG("data stage"); + if (flags == HOST_TO_DEVICE) { + + res = host->bulkWrite(dev, bulk_out, data, transfer_len); + if (checkResult(res, bulk_out)) + return -1; + + } else if (flags == DEVICE_TO_HOST) { + + res = host->bulkRead(dev, bulk_in, data, transfer_len); + if (checkResult(res, bulk_in)) + return -1; + } + } + + // status stage + csw.Signature = 0; + USB_DBG("Read CSW"); + res = host->bulkRead(dev, bulk_in,(uint8_t *)&csw, 13); + if (checkResult(res, bulk_in)) + return -1; + + if (csw.Signature != CSW_SIGNATURE) { + return -1; + } + + USB_DBG("recv csw: status: %d", csw.Status); + + // ModeSense? + if ((csw.Status == 1) && (cmd[0] != 0x03)) { + USB_DBG("request mode sense"); + return SCSIRequestSense(); + } + + // perform reset recovery + if ((csw.Status == 2) && (cmd[0] != 0x03)) { + + // send Bulk-Only Mass Storage Reset request + res = host->controlWrite( dev, + USB_RECIPIENT_INTERFACE | USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS, + BO_MASS_STORAGE_RESET, + 0, msd_intf, NULL, 0); + + // unstall both endpoints + res = host->controlWrite( dev, + USB_RECIPIENT_ENDPOINT | USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_STANDARD, + CLEAR_FEATURE, + 0, bulk_in->getAddress(), NULL, 0); + + res = host->controlWrite( dev, + USB_RECIPIENT_ENDPOINT | USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_STANDARD, + CLEAR_FEATURE, + 0, bulk_out->getAddress(), NULL, 0); + + } + + return csw.Status; +} + + +int USBHostMSD::dataTransfer(uint8_t * buf, uint32_t block, uint8_t nbBlock, int direction) { + uint8_t cmd[10]; + memset(cmd,0,10); + cmd[0] = (direction == DEVICE_TO_HOST) ? 0x28 : 0x2A; + + cmd[2] = (block >> 24) & 0xff; + cmd[3] = (block >> 16) & 0xff; + cmd[4] = (block >> 8) & 0xff; + cmd[5] = block & 0xff; + + cmd[7] = (nbBlock >> 8) & 0xff; + cmd[8] = nbBlock & 0xff; + + return SCSITransfer(cmd, 10, direction, buf, blockSize*nbBlock); +} + +int USBHostMSD::getMaxLun() { + uint8_t buf[1], res; + res = host->controlRead( dev, USB_RECIPIENT_INTERFACE | USB_DEVICE_TO_HOST | USB_REQUEST_TYPE_CLASS, + 0xfe, 0, msd_intf, buf, 1); + USB_DBG("max lun: %d", buf[0]); + return res; +} + +int USBHostMSD::disk_initialize() { + USB_DBG("FILESYSTEM: init"); + U16 i, timeout = 10; + + getMaxLun(); + + for (i = 0; i < timeout; i++) { + Thread::wait(100); + if (!testUnitReady()) + break; + } + + if (i == timeout) { + disk_init = false; + return -1; + } + + inquiry(0, 0); + disk_init = 1; + return readCapacity(); +} + +int USBHostMSD::disk_write(const uint8_t *buffer, uint64_t block_number) { + USB_DBG("FILESYSTEM: write block: %lld", block_number); + if (!disk_init) { + disk_initialize(); + } + if (!disk_init) + return -1; + return dataTransfer((uint8_t *)buffer, block_number, 1, HOST_TO_DEVICE); +} + +int USBHostMSD::disk_read(uint8_t * buffer, uint64_t block_number) { + USB_DBG("FILESYSTEM: read block %lld", block_number); + if (!disk_init) { + disk_initialize(); + } + if (!disk_init) + return -1; + return dataTransfer((uint8_t *)buffer, block_number, 1, DEVICE_TO_HOST); +} + +uint64_t USBHostMSD::disk_sectors() { + USB_DBG("FILESYSTEM: sectors"); + if (!disk_init) { + disk_initialize(); + } + if (!disk_init) + return 0; + return blockCount; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHostMSD/USBHostMSD.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,119 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USBHOSTMSD_H +#define USBHOSTMSD_H + +#include "USBHostConf.h" + +#if USBHOST_MSD + +#include "USBHost.h" +#include "FATFileSystem.h" + +/** + * A class to communicate a USB flash disk + */ +class USBHostMSD : public IUSBEnumerator, public FATFileSystem { +public: + /** + * Constructor + * + * @param rootdir mount name + */ + USBHostMSD(const char * rootdir); + + /** + * Check if a MSD device is connected + * + * @return true if a MSD device is connected + */ + bool connected(); + + /** + * Try to connect to a MSD device + * + * @return true if connection was successful + */ + bool connect(); + +protected: + //From IUSBEnumerator + virtual void setVidPid(uint16_t vid, uint16_t pid); + virtual bool parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol); //Must return true if the interface should be parsed + virtual bool useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir); //Must return true if the endpoint will be used + + // From FATFileSystem + virtual int disk_initialize(); + virtual int disk_status() {return 0;}; + virtual int disk_read(uint8_t * buffer, uint64_t sector); + virtual int disk_write(const uint8_t * buffer, uint64_t sector); + virtual int disk_sync() {return 0;}; + virtual uint64_t disk_sectors(); + +private: + USBHost * host; + USBDeviceConnected * dev; + bool dev_connected; + USBEndpoint * bulk_in; + USBEndpoint * bulk_out; + uint8_t nb_ep; + + // Bulk-only CBW + typedef __packed struct { + uint32_t Signature; + uint32_t Tag; + uint32_t DataLength; + uint8_t Flags; + uint8_t LUN; + uint8_t CBLength; + uint8_t CB[16]; + } CBW; + + // Bulk-only CSW + typedef __packed struct { + uint32_t Signature; + uint32_t Tag; + uint32_t DataResidue; + uint8_t Status; + } CSW; + + CBW cbw; + CSW csw; + + int SCSITransfer(uint8_t * cmd, uint8_t cmd_len, int flags, uint8_t * data, uint32_t transfer_len); + int testUnitReady(); + int readCapacity(); + int inquiry(uint8_t lun, uint8_t page_code); + int SCSIRequestSense(); + int dataTransfer(uint8_t * buf, uint32_t block, uint8_t nbBlock, int direction); + int checkResult(uint8_t res, USBEndpoint * ep); + int getMaxLun(); + + int blockSize; + uint64_t blockCount; + + int msd_intf; + bool msd_device_found; + bool disk_init; + + void init(); + +}; + +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHostSerial/MtxCircBuffer.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,89 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MTXCIRCBUFFER_H +#define MTXCIRCBUFFER_H + +#include "stdint.h" +#include "rtos.h" + +//Mutex protected circular buffer +template<typename T, int size> +class MtxCircBuffer { +public: + + MtxCircBuffer() { + write = 0; + read = 0; + } + + bool isFull() { + mtx.lock(); + bool r = (((write + 1) % size) == read); + mtx.unlock(); + return r; + } + + bool isEmpty() { + mtx.lock(); + bool r = (read == write); + mtx.unlock(); + return r; + } + + void flush() { + write = 0; + read = 0; + } + + void queue(T k) { + mtx.lock(); + while (((write + 1) % size) == read) { + mtx.unlock(); + Thread::wait(10); + mtx.lock(); + } + buf[write++] = k; + write %= size; + mtx.unlock(); + } + + uint16_t available() { + mtx.lock(); + uint16_t a = (write >= read) ? (write - read) : (size - read + write); + mtx.unlock(); + return a; + } + + bool dequeue(T * c) { + mtx.lock(); + bool empty = (read == write); + if (!empty) { + *c = buf[read++]; + read %= size; + } + mtx.unlock(); + return (!empty); + } + +private: + volatile uint16_t write; + volatile uint16_t read; + volatile T buf[size]; + Mutex mtx; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHostSerial/USBHostSerial.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,184 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "USBHostSerial.h" + +#if USBHOST_SERIAL + +#include "dbg.h" + +#define SET_LINE_CODING 0x20 + +USBHostSerial::USBHostSerial(): circ_buf() { + host = USBHost::getHostInst(); + size_bulk_in = 0; + size_bulk_out = 0; + init(); +} + +void USBHostSerial::init() { + dev = NULL; + bulk_in = NULL; + bulk_out = NULL; + dev_connected = false; + serial_intf = -1; + serial_device_found = false; + line_coding.baudrate = 9600; + line_coding.data_bits = 8; + line_coding.parity = None; + line_coding.stop_bits = 1; + circ_buf.flush(); +} + +bool USBHostSerial::connected() +{ + return dev_connected; +} + +bool USBHostSerial::connect() { + + if (dev_connected) { + return true; + } + for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; i++) { + if ((dev = host->getDevice(i)) != NULL) { + + USB_DBG("Trying to connect serial device\r\n"); + + if(host->enumerate(dev, this)) + break; + + if (serial_device_found) { + bulk_in = dev->getEndpoint(serial_intf, BULK_ENDPOINT, IN); + bulk_out = dev->getEndpoint(serial_intf, BULK_ENDPOINT, OUT); + + if (!bulk_in || !bulk_out) + break; + + USB_INFO("New Serial device: VID:%04x PID:%04x [dev: %p - intf: %d]", dev->getVid(), dev->getPid(), dev, serial_intf); + dev->setName("Serial", serial_intf); + host->registerDriver(dev, serial_intf, this, &USBHostSerial::init); + + baud(9600); + + size_bulk_in = bulk_in->getSize(); + size_bulk_out = bulk_out->getSize(); + + bulk_in->attach(this, &USBHostSerial::rxHandler); + bulk_out->attach(this, &USBHostSerial::txHandler); + + host->bulkRead(dev, bulk_in, buf, size_bulk_in, false); + dev_connected = true; + return true; + } + } + } + init(); + return false; +} + +void USBHostSerial::rxHandler() { + if (bulk_in) { + int len = bulk_in->getLengthTransferred(); + if (bulk_in->getState() == USB_TYPE_IDLE) { + for (int i = 0; i < len; i++) { + circ_buf.queue(buf[i]); + } + rx.call(); + host->bulkRead(dev, bulk_in, buf, size_bulk_in, false); + } + } +} + +void USBHostSerial::txHandler() { + if (bulk_out) { + if (bulk_out->getState() == USB_TYPE_IDLE) { + tx.call(); + } + } +} + +int USBHostSerial::_putc(int c) { + if (bulk_out) { + if (host->bulkWrite(dev, bulk_out, (uint8_t *)&c, 1) == USB_TYPE_OK) { + return 1; + } + } + return -1; +} + +void USBHostSerial::baud(int baudrate) { + line_coding.baudrate = baudrate; + format(line_coding.data_bits, (Parity)line_coding.parity, line_coding.stop_bits); +} + +void USBHostSerial::format(int bits, Parity parity, int stop_bits) { + line_coding.data_bits = bits; + line_coding.parity = parity; + line_coding.stop_bits = (stop_bits == 1) ? 0 : 2; + + // set line coding + int res = host->controlWrite( dev, + USB_RECIPIENT_INTERFACE | USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS, + SET_LINE_CODING, + 0, serial_intf, (uint8_t *)&line_coding, 7); +} + +int USBHostSerial::_getc() { + uint8_t c = 0; + if (bulk_in == NULL) { + init(); + return -1; + } + while (circ_buf.isEmpty()); + circ_buf.dequeue(&c); + return c; +} + + +uint8_t USBHostSerial::available() { + return circ_buf.available(); +} + +/*virtual*/ void USBHostSerial::setVidPid(uint16_t vid, uint16_t pid) +{ + // we don't check VID/PID for MSD driver +} + +/*virtual*/ bool USBHostSerial::parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) //Must return true if the interface should be parsed +{ + if ((serial_intf == -1) && + (intf_class == SERIAL_CLASS) && + (intf_subclass == 0x00) && + (intf_protocol == 0x00)) { + serial_intf = intf_nb; + return true; + } + return false; +} + +/*virtual*/ bool USBHostSerial::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) //Must return true if the endpoint will be used +{ + if (intf_nb == serial_intf) { + if (type == BULK_ENDPOINT) { + serial_device_found = true; + return true; + } + } + return false; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHostSerial/USBHostSerial.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,166 @@ +/* mbed USBHost Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USBHOSTSERIAL_H +#define USBHOSTSERIAL_H + +#include "USBHostConf.h" + +#if USBHOST_SERIAL + +#include "USBHost.h" +#include "Stream.h" +#include "MtxCircBuffer.h" + +/** + * A class to communicate a USB virtual serial port + */ +class USBHostSerial : public IUSBEnumerator, public Stream { +public: + /** + * Constructor + */ + USBHostSerial(); + + enum IrqType { + RxIrq, + TxIrq + }; + + enum Parity { + None = 0, + Odd, + Even, + Mark, + Space + }; + + /** + * Check if a virtual serial port is connected + * + * @returns true if a serial device is connected + */ + bool connected(); + + /** + * Try to connect a serial device + * + * @return true if connection was successful + */ + bool connect(); + + /** + * Check the number of bytes available. + * + * @returns the number of bytes available + */ + uint8_t available(); + + /** + * Attach a member function to call when a packet is received. + * + * @param tptr pointer to the object to call the member function on + * @param mptr pointer to the member function to be called + * @param irq irq type + */ + template<typename T> + inline void attach(T* tptr, void (T::*mptr)(void), IrqType irq = RxIrq) { + if ((mptr != NULL) && (tptr != NULL)) { + if (irq == RxIrq) { + rx.attach(tptr, mptr); + } else { + tx.attach(tptr, mptr); + } + } + } + + /** + * Attach a callback called when a packet is received + * + * @param ptr function pointer + */ + inline void attach(void (*fn)(void), IrqType irq = RxIrq) { + if (fn != NULL) { + if (irq == RxIrq) { + rx.attach(fn); + } else { + tx.attach(fn); + } + } + } + + /** Set the baud rate of the serial port + * + * @param baudrate The baudrate of the serial port (default = 9600). + */ + void baud(int baudrate = 9600); + + /** Set the transmission format used by the Serial port + * + * @param bits The number of bits in a word (default = 8) + * @param parity The parity used (USBHostSerial::None, USBHostSerial::Odd, USBHostSerial::Even, USBHostSerial::Mark, USBHostSerial::Space; default = USBHostSerial::None) + * @param stop The number of stop bits (1 or 2; default = 1) + */ + void format(int bits = 8, Parity parity = USBHostSerial::None, int stop_bits = 1); + + +protected: + //From IUSBEnumerator + virtual void setVidPid(uint16_t vid, uint16_t pid); + virtual bool parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol); //Must return true if the interface should be parsed + virtual bool useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir); //Must return true if the endpoint will be used + + virtual int _getc(); + virtual int _putc(int c); + +private: + USBHost * host; + USBDeviceConnected * dev; + USBEndpoint * bulk_in; + USBEndpoint * bulk_out; + uint32_t size_bulk_in; + uint32_t size_bulk_out; + + bool dev_connected; + + void init(); + + MtxCircBuffer<uint8_t, 64> circ_buf; + + uint8_t buf[64]; + + typedef __packed struct { + uint32_t baudrate; + uint8_t stop_bits; + uint8_t parity; + uint8_t data_bits; + } LINE_CODING; + + LINE_CODING line_coding; + + void rxHandler(); + void txHandler(); + FunctionPointer rx; + FunctionPointer tx; + + int serial_intf; + bool serial_device_found; + +}; + +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos.lib Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed-rtos/#53e6cccd8782
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtos/Mail.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,109 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef MAIL_H +#define MAIL_H + +#include <stdint.h> +#include <string.h> + +#include "cmsis_os.h" + +namespace rtos { + +/** The Mail class allow to control, send, receive, or wait for mail. + A mail is a memory block that is send to a thread or interrupt service routine. + @tparam T data type of a single message element. + @tparam queue_sz maximum number of messages in queue. +*/ +template<typename T, uint32_t queue_sz> +class Mail { +public: + /** Create and Initialise Mail queue. */ + Mail() { + #ifdef CMSIS_OS_RTX + memset(_mail_q, 0, sizeof(_mail_q)); + _mail_p[0] = _mail_q; + + memset(_mail_m, 0, sizeof(_mail_m)); + _mail_p[1] = _mail_m; + + _mail_def.pool = _mail_p; + _mail_def.queue_sz = queue_sz; + _mail_def.item_sz = sizeof(T); + #endif + _mail_id = osMailCreate(&_mail_def, NULL); + } + + /** Allocate a memory block of type T + @param millisec timeout value or 0 in case of no time-out. (default: 0). + @return pointer to memory block that can be filled with mail or NULL in case error. + */ + T* alloc(uint32_t millisec=0) { + return (T*)osMailAlloc(_mail_id, millisec); + } + + /** Allocate a memory block of type T and set memory block to zero. + @param millisec timeout value or 0 in case of no time-out. (default: 0). + @return pointer to memory block that can be filled with mail or NULL in case error. + */ + T* calloc(uint32_t millisec=0) { + return (T*)osMailCAlloc(_mail_id, millisec); + } + + /** Put a mail in the queue. + @param mptr memory block previously allocated with Mail::alloc or Mail::calloc. + @return status code that indicates the execution status of the function. + */ + osStatus put(T *mptr) { + return osMailPut(_mail_id, (void*)mptr); + } + + /** Get a mail from a queue. + @param millisec timeout value or 0 in case of no time-out. (default: osWaitForever). + @return event that contains mail information or error code. + */ + osEvent get(uint32_t millisec=osWaitForever) { + return osMailGet(_mail_id, millisec); + } + + /** Free a memory block from a mail. + @param mptr pointer to the memory block that was obtained with Mail::get. + @return status code that indicates the execution status of the function. + */ + osStatus free(T *mptr) { + return osMailFree(_mail_id, (void*)mptr); + } + +private: + osMailQId _mail_id; + osMailQDef_t _mail_def; +#ifdef CMSIS_OS_RTX + uint32_t _mail_q[4+(queue_sz)]; + uint32_t _mail_m[3+((sizeof(T)+3)/4)*(queue_sz)]; + void *_mail_p[2]; +#endif +}; + +} + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtos/MemoryPool.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,82 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef MEMORYPOOL_H +#define MEMORYPOOL_H + +#include <stdint.h> +#include <string.h> + +#include "cmsis_os.h" + +namespace rtos { + +/** Define and manage fixed-size memory pools of objects of a given type. + @tparam T data type of a single object (element). + @tparam queue_sz maximum number of objects (elements) in the memory pool. +*/ +template<typename T, uint32_t pool_sz> +class MemoryPool { +public: + /** Create and Initialize a memory pool. */ + MemoryPool() { + #ifdef CMSIS_OS_RTX + memset(_pool_m, 0, sizeof(_pool_m)); + _pool_def.pool = _pool_m; + + _pool_def.pool_sz = pool_sz; + _pool_def.item_sz = sizeof(T); + #endif + _pool_id = osPoolCreate(&_pool_def); + } + + /** Allocate a memory block of type T from a memory pool. + @return address of the allocated memory block or NULL in case of no memory available. + */ + T* alloc(void) { + return (T*)osPoolAlloc(_pool_id); + } + + /** Allocate a memory block of type T from a memory pool and set memory block to zero. + @return address of the allocated memory block or NULL in case of no memory available. + */ + T* calloc(void) { + return (T*)osPoolCAlloc(_pool_id); + } + + /** Return an allocated memory block back to a specific memory pool. + @param address of the allocated memory block that is returned to the memory pool. + @return status code that indicates the execution status of the function. + */ + osStatus free(T *block) { + return osPoolFree(_pool_id, (void*)block); + } + +private: + osPoolId _pool_id; + osPoolDef_t _pool_def; +#ifdef CMSIS_OS_RTX + uint32_t _pool_m[3+((sizeof(T)+3)/4)*(pool_sz)]; +#endif +}; + +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtos/Mutex.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,56 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "Mutex.h" + +#include <string.h> +#include "error.h" + +namespace rtos { + +Mutex::Mutex() { +#ifdef CMSIS_OS_RTX + memset(_mutex_data, 0, sizeof(_mutex_data)); + _osMutexDef.mutex = _mutex_data; +#endif + _osMutexId = osMutexCreate(&_osMutexDef); + if (_osMutexId == NULL) { + error("Error initializing the mutex object\n"); + } +} + +osStatus Mutex::lock(uint32_t millisec) { + return osMutexWait(_osMutexId, millisec); +} + +bool Mutex::trylock() { + return (osMutexWait(_osMutexId, 0) == osOK); +} + +osStatus Mutex::unlock() { + return osMutexRelease(_osMutexId); +} + +Mutex::~Mutex() { + osMutexDelete(_osMutexId); +} + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtos/Mutex.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,65 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef MUTEX_H +#define MUTEX_H + +#include <stdint.h> +#include "cmsis_os.h" + +namespace rtos { + +/** The Mutex class is used to synchronise the execution of threads. + This is for example used to protect access to a shared resource. +*/ +class Mutex { +public: + /** Create and Initialize a Mutex object */ + Mutex(); + + /** Wait until a Mutex becomes available. + @param millisec timeout value or 0 in case of no time-out. (default: osWaitForever) + @return status code that indicates the execution status of the function. + */ + osStatus lock(uint32_t millisec=osWaitForever); + + /** Try to lock the mutex, and return immediately + @return true if the mutex was acquired, false otherwise. + */ + bool trylock(); + + /** Unlock the mutex that has previously been locked by the same thread + @return status code that indicates the execution status of the function. + */ + osStatus unlock(); + + ~Mutex(); + +private: + osMutexId _osMutexId; + osMutexDef_t _osMutexDef; +#ifdef CMSIS_OS_RTX + int32_t _mutex_data[3]; +#endif +}; + +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtos/Queue.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,81 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef QUEUE_H +#define QUEUE_H + +#include <stdint.h> +#include <string.h> + +#include "cmsis_os.h" +#include "error.h" + +namespace rtos { + +/** The Queue class allow to control, send, receive, or wait for messages. + A message can be a integer or pointer value to a certain type T that is send + to a thread or interrupt service routine. + @tparam T data type of a single message element. + @tparam queue_sz maximum number of messages in queue. +*/ +template<typename T, uint32_t queue_sz> +class Queue { +public: + /** Create and initialise a message Queue. */ + Queue() { + #ifdef CMSIS_OS_RTX + memset(_queue_q, 0, sizeof(_queue_q)); + _queue_def.pool = _queue_q; + _queue_def.queue_sz = queue_sz; + #endif + _queue_id = osMessageCreate(&_queue_def, NULL); + if (_queue_id == NULL) { + error("Error initialising the queue object\n"); + } + } + + /** Put a message in a Queue. + @param data message pointer. + @param millisec timeout value or 0 in case of no time-out. (default: 0) + @return status code that indicates the execution status of the function. + */ + osStatus put(T* data, uint32_t millisec=0) { + return osMessagePut(_queue_id, (uint32_t)data, millisec); + } + + /** Get a message or Wait for a message from a Queue. + @param millisec timeout value or 0 in case of no time-out. (default: osWaitForever). + @return event information that includes the message and the status code. + */ + osEvent get(uint32_t millisec=osWaitForever) { + return osMessageGet(_queue_id, millisec); + } + +private: + osMessageQId _queue_id; + osMessageQDef_t _queue_def; +#ifdef CMSIS_OS_RTX + uint32_t _queue_q[4+(queue_sz)]; +#endif +}; + +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtos/RtosTimer.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,53 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "RtosTimer.h" + +#include <string.h> + +#include "cmsis_os.h" +#include "error.h" + +namespace rtos { + +RtosTimer::RtosTimer(void (*periodic_task)(void const *argument), os_timer_type type, void *argument) { +#ifdef CMSIS_OS_RTX + _timer.ptimer = periodic_task; + + memset(_timer_data, 0, sizeof(_timer_data)); + _timer.timer = _timer_data; +#endif + _timer_id = osTimerCreate(&_timer, type, argument); +} + +osStatus RtosTimer::start(uint32_t millisec) { + return osTimerStart(_timer_id, millisec); +} + +osStatus RtosTimer::stop(void) { + return osTimerStop(_timer_id); +} + +RtosTimer::~RtosTimer() { + osTimerDelete(_timer_id); +} + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtos/RtosTimer.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,71 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef RTOS_TIMER_H +#define RTOS_TIMER_H + +#include <stdint.h> +#include "cmsis_os.h" + +namespace rtos { + +/** The RtosTimer class allow creating and and controlling of timer functions in the system. + A timer function is called when a time period expires whereby both on-shot and + periodic timers are possible. A timer can be started, restarted, or stopped. + + Timers are handled in the thread osTimerThread. + Callback functions run under control of this thread and may use CMSIS-RTOS API calls. +*/ +class RtosTimer { +public: + /** Create and Start timer. + @param task name of the timer call back function. + @param type osTimerOnce for one-shot or osTimerPeriodic for periodic behaviour. (default: osTimerPeriodic) + @param argument argument to the timer call back function. (default: NULL) + */ + RtosTimer(void (*task)(void const *argument), + os_timer_type type=osTimerPeriodic, + void *argument=NULL); + + /** Stop the timer. + @return status code that indicates the execution status of the function. + */ + osStatus stop(void); + + /** start a timer. + @param millisec time delay value of the timer. + @return status code that indicates the execution status of the function. + */ + osStatus start(uint32_t millisec); + + ~RtosTimer(); + +private: + osTimerId _timer_id; + osTimerDef_t _timer; +#ifdef CMSIS_OS_RTX + uint32_t _timer_data[5]; +#endif +}; + +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtos/Semaphore.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,49 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "Semaphore.h" + +#include <string.h> +#include "error.h" + +namespace rtos { + +Semaphore::Semaphore(int32_t count) { +#ifdef CMSIS_OS_RTX + memset(_semaphore_data, 0, sizeof(_semaphore_data)); + _osSemaphoreDef.semaphore = _semaphore_data; +#endif + _osSemaphoreId = osSemaphoreCreate(&_osSemaphoreDef, count); +} + +int32_t Semaphore::wait(uint32_t millisec) { + return osSemaphoreWait(_osSemaphoreId, millisec); +} + +osStatus Semaphore::release(void) { + return osSemaphoreRelease(_osSemaphoreId); +} + +Semaphore::~Semaphore() { + osSemaphoreDelete(_osSemaphoreId); +} + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtos/Semaphore.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,60 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef SEMAPHORE_H +#define SEMAPHORE_H + +#include <stdint.h> +#include "cmsis_os.h" + +namespace rtos { + +/** The Semaphore class is used to manage and protect access to a set of shared resources. */ +class Semaphore { +public: + /** Create and Initialize a Semaphore object used for managing resources. + @param number of available resources; maximum index value is (count-1). + */ + Semaphore(int32_t count); + + /** Wait until a Semaphore resource becomes available. + @param millisec timeout value or 0 in case of no time-out. (default: osWaitForever). + @return number of available tokens, or -1 in case of incorrect parameters + */ + int32_t wait(uint32_t millisec=osWaitForever); + + /** Release a Semaphore resource that was obtain with Semaphore::wait. + @return status code that indicates the execution status of the function. + */ + osStatus release(void); + + ~Semaphore(); + +private: + osSemaphoreId _osSemaphoreId; + osSemaphoreDef_t _osSemaphoreDef; +#ifdef CMSIS_OS_RTX + uint32_t _semaphore_data[2]; +#endif +}; + +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtos/Thread.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,90 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "Thread.h" + +#include "error.h" + +namespace rtos { + +Thread::Thread(void (*task)(void const *argument), void *argument, + osPriority priority, uint32_t stack_size, unsigned char *stack_pointer) { +#ifdef CMSIS_OS_RTX + _thread_def.pthread = task; + _thread_def.tpriority = priority; + _thread_def.stacksize = stack_size; + if (stack_pointer != NULL) { + _thread_def.stack_pointer = stack_pointer; + _dynamic_stack = false; + } else { + _thread_def.stack_pointer = new unsigned char[stack_size]; + if (_thread_def.stack_pointer == NULL) + error("Error allocating the stack memory"); + _dynamic_stack = true; + } +#endif + _tid = osThreadCreate(&_thread_def, argument); +} + +osStatus Thread::terminate() { + return osThreadTerminate(_tid); +} + +osStatus Thread::set_priority(osPriority priority) { + return osThreadSetPriority(_tid, priority); +} + +osPriority Thread::get_priority() { + return osThreadGetPriority(_tid); +} + +int32_t Thread::signal_set(int32_t signals) { + return osSignalSet(_tid, signals); +} + +Thread::State Thread::get_state() { + return ((State)_thread_def.tcb.state); +} + +osEvent Thread::signal_wait(int32_t signals, uint32_t millisec) { + return osSignalWait(signals, millisec); +} + +osStatus Thread::wait(uint32_t millisec) { + return osDelay(millisec); +} + +osStatus Thread::yield() { + return osThreadYield(); +} + +osThreadId Thread::gettid() { + return osThreadGetId(); +} + +Thread::~Thread() { + terminate(); + if (_dynamic_stack) { + delete[] (_thread_def.stack_pointer); + } +} + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtos/Thread.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,118 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef THREAD_H +#define THREAD_H + +#include <stdint.h> +#include "cmsis_os.h" + +namespace rtos { + +/** The Thread class allow defining, creating, and controlling thread functions in the system. */ +class Thread { +public: + /** Create a new thread, and start it executing the specified function. + @param task function to be executed by this thread. + @param argument pointer that is passed to the thread function as start argument. (default: NULL). + @param priority initial priority of the thread function. (default: osPriorityNormal). + @param stack_size stack size (in bytes) requirements for the thread function. (default: DEFAULT_STACK_SIZE). + @param stack_pointer pointer to the stack area to be used by this thread (default: NULL). + */ + Thread(void (*task)(void const *argument), void *argument=NULL, + osPriority priority=osPriorityNormal, + uint32_t stack_size=DEFAULT_STACK_SIZE, + unsigned char *stack_pointer=NULL); + + /** Terminate execution of a thread and remove it from Active Threads + @return status code that indicates the execution status of the function. + */ + osStatus terminate(); + + /** Set priority of an active thread + @param priority new priority value for the thread function. + @return status code that indicates the execution status of the function. + */ + osStatus set_priority(osPriority priority); + + /** Get priority of an active thread + @return current priority value of the thread function. + */ + osPriority get_priority(); + + /** Set the specified Signal Flags of an active thread. + @param signals specifies the signal flags of the thread that should be set. + @return previous signal flags of the specified thread or 0x80000000 in case of incorrect parameters. + */ + int32_t signal_set(int32_t signals); + + /** State of the Thread */ + enum State { + Inactive, /**< Not created or terminated */ + Ready, /**< Ready to run */ + Running, /**< Running */ + WaitingDelay, /**< Waiting for a delay to occur */ + WaitingInterval, /**< Waiting for an interval to occur */ + WaitingOr, /**< Waiting for one event in a set to occur */ + WaitingAnd, /**< Waiting for multiple events in a set to occur */ + WaitingSemaphore, /**< Waiting for a semaphore event to occur */ + WaitingMailbox, /**< Waiting for a mailbox event to occur */ + WaitingMutex, /**< Waiting for a mutex event to occur */ + }; + + /** State of this Thread + @return the State of this Thread + */ + State get_state(); + + /** Wait for one or more Signal Flags to become signaled for the current RUNNING thread. + @param signals wait until all specified signal flags set or 0 for any single signal flag. + @param millisec timeout value or 0 in case of no time-out. (default: osWaitForever). + @return event flag information or error code. + */ + static osEvent signal_wait(int32_t signals, uint32_t millisec=osWaitForever); + + /** Wait for a specified time period in millisec: + @param millisec time delay value + @return status code that indicates the execution status of the function. + */ + static osStatus wait(uint32_t millisec); + + /** Pass control to next thread that is in state READY. + @return status code that indicates the execution status of the function. + */ + static osStatus yield(); + + /** Get the thread id of the current running thread. + @return thread ID for reference by other functions or NULL in case of error. + */ + static osThreadId gettid(); + + virtual ~Thread(); + +private: + osThreadId _tid; + osThreadDef_t _thread_def; + bool _dynamic_stack; +}; + +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtos/rtos.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,35 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef RTOS_H +#define RTOS_H + +#include "Thread.h" +#include "Mutex.h" +#include "RtosTimer.h" +#include "Semaphore.h" +#include "Mail.h" +#include "MemoryPool.h" +#include "Queue.h" + +using namespace rtos; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/HAL_CM.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,170 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: HAL_CM.C + * Purpose: Hardware Abstraction Layer for Cortex-M + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +#include "rt_TypeDef.h" +#include "RTX_Config.h" +#include "rt_HAL_CM.h" + + +/*---------------------------------------------------------------------------- + * Global Variables + *---------------------------------------------------------------------------*/ + +#ifdef DBG_MSG +BIT dbg_msg; +#endif + +/*---------------------------------------------------------------------------- + * Functions + *---------------------------------------------------------------------------*/ + + +/*--------------------------- rt_init_stack ---------------------------------*/ + +void rt_init_stack (P_TCB p_TCB, FUNCP task_body) { + /* Prepare TCB and saved context for a first time start of a task. */ + U32 *stk,i,size; + + /* Prepare a complete interrupt frame for first task start */ + size = p_TCB->priv_stack >> 2; + + /* Write to the top of stack. */ + stk = &p_TCB->stack[size]; + + /* Auto correct to 8-byte ARM stack alignment. */ + if ((U32)stk & 0x04) { + stk--; + } + + stk -= 16; + + /* Default xPSR and initial PC */ + stk[15] = INITIAL_xPSR; + stk[14] = (U32)task_body; + + /* Clear R4-R11,R0-R3,R12,LR registers. */ + for (i = 0; i < 14; i++) { + stk[i] = 0; + } + + /* Assign a void pointer to R0. */ + stk[8] = (U32)p_TCB->msg; + + /* Initial Task stack pointer. */ + p_TCB->tsk_stack = (U32)stk; + + /* Task entry point. */ + p_TCB->ptask = task_body; + + /* Set a magic word for checking of stack overflow. + For the main thread (ID: 0x01) the stack is in a memory area shared with the + heap, therefore the last word of the stack is a moving target. + We want to do stack/heap collision detection instead. + */ + if (p_TCB->task_id != 0x01) + p_TCB->stack[0] = MAGIC_WORD; +} + + +/*--------------------------- rt_ret_val ----------------------------------*/ + +static __inline U32 *rt_ret_regs (P_TCB p_TCB) { + /* Get pointer to task return value registers (R0..R3) in Stack */ +#if (__TARGET_FPU_VFP) + if (p_TCB->stack_frame) { + /* Extended Stack Frame: R4-R11,S16-S31,R0-R3,R12,LR,PC,xPSR,S0-S15,FPSCR */ + return (U32 *)(p_TCB->tsk_stack + 8*4 + 16*4); + } else { + /* Basic Stack Frame: R4-R11,R0-R3,R12,LR,PC,xPSR */ + return (U32 *)(p_TCB->tsk_stack + 8*4); + } +#else + /* Stack Frame: R4-R11,R0-R3,R12,LR,PC,xPSR */ + return (U32 *)(p_TCB->tsk_stack + 8*4); +#endif +} + +void rt_ret_val (P_TCB p_TCB, U32 v0) { + U32 *ret; + + ret = rt_ret_regs(p_TCB); + ret[0] = v0; +} + +void rt_ret_val2(P_TCB p_TCB, U32 v0, U32 v1) { + U32 *ret; + + ret = rt_ret_regs(p_TCB); + ret[0] = v0; + ret[1] = v1; +} + + +/*--------------------------- dbg_init --------------------------------------*/ + +#ifdef DBG_MSG +void dbg_init (void) { + if ((DEMCR & DEMCR_TRCENA) && + (ITM_CONTROL & ITM_ITMENA) && + (ITM_ENABLE & (1UL << 31))) { + dbg_msg = __TRUE; + } +} +#endif + +/*--------------------------- dbg_task_notify -------------------------------*/ + +#ifdef DBG_MSG +void dbg_task_notify (P_TCB p_tcb, BOOL create) { + while (ITM_PORT31_U32 == 0); + ITM_PORT31_U32 = (U32)p_tcb->ptask; + while (ITM_PORT31_U32 == 0); + ITM_PORT31_U16 = (create << 8) | p_tcb->task_id; +} +#endif + +/*--------------------------- dbg_task_switch -------------------------------*/ + +#ifdef DBG_MSG +void dbg_task_switch (U32 task_id) { + while (ITM_PORT31_U32 == 0); + ITM_PORT31_U8 = task_id; +} +#endif + + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/LPC11U24/GCC_ARM/HAL_CM0.s Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,370 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: HAL_CM0.S + * Purpose: Hardware Abstraction Layer for Cortex-M0 + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + + .file "HAL_CM0.S" + .syntax unified + + .equ TCB_TSTACK, 36 + + +/*---------------------------------------------------------------------------- + * Functions + *---------------------------------------------------------------------------*/ + + .thumb + + .section ".text" + .align 2 + + +/*--------------------------- rt_set_PSP ------------------------------------*/ + +# void rt_set_PSP (U32 stack); + + .thumb_func + .type rt_set_PSP, %function + .global rt_set_PSP +rt_set_PSP: + .fnstart + .cantunwind + + MSR PSP,R0 + BX LR + + .fnend + .size rt_set_PSP, .-rt_set_PSP + + +/*--------------------------- rt_get_PSP ------------------------------------*/ + +# U32 rt_get_PSP (void); + + .thumb_func + .type rt_get_PSP, %function + .global rt_get_PSP +rt_get_PSP: + .fnstart + .cantunwind + + MRS R0,PSP + BX LR + + .fnend + .size rt_get_PSP, .-rt_get_PSP + + +/*--------------------------- os_set_env ------------------------------------*/ + +# void os_set_env (void); + /* Switch to Unprivileged/Privileged Thread mode, use PSP. */ + + .thumb_func + .type os_set_env, %function + .global os_set_env +os_set_env: + .fnstart + .cantunwind + + MOV R0,SP /* PSP = MSP */ + MSR PSP,R0 + LDR R0,=os_flags + LDRB R0,[R0] + LSLS R0,#31 + BNE PrivilegedE + MOVS R0,#0x03 /* Unprivileged Thread mode, use PSP */ + MSR CONTROL,R0 + BX LR +PrivilegedE: + MOVS R0,#0x02 /* Privileged Thread mode, use PSP */ + MSR CONTROL,R0 + BX LR + + .fnend + .size os_set_env, .-os_set_env + + +/*--------------------------- _alloc_box ------------------------------------*/ + +# void *_alloc_box (void *box_mem); + /* Function wrapper for Unprivileged/Privileged mode. */ + + .thumb_func + .type _alloc_box, %function + .global _alloc_box +_alloc_box: + .fnstart + .cantunwind + + LDR R3,=rt_alloc_box + MOV R12,R3 + MRS R3,IPSR + LSLS R3,#24 + BNE PrivilegedA + MRS R3,CONTROL + LSLS R3,#31 + BEQ PrivilegedA + SVC 0 + BX LR +PrivilegedA: + BX R12 + + .fnend + .size _alloc_box, .-_alloc_box + + +/*--------------------------- _free_box -------------------------------------*/ + +# int _free_box (void *box_mem, void *box); + /* Function wrapper for Unprivileged/Privileged mode. */ + + .thumb_func + .type _free_box, %function + .global _free_box +_free_box: + .fnstart + .cantunwind + + LDR R3,=rt_free_box + MOV R12,R3 + MRS R3,IPSR + LSLS R3,#24 + BNE PrivilegedF + MRS R3,CONTROL + LSLS R3,#31 + BEQ PrivilegedF + SVC 0 + BX LR +PrivilegedF: + BX R12 + + .fnend + .size _free_box, .-_free_box + + +/*-------------------------- SVC_Handler ------------------------------------*/ + +# void SVC_Handler (void); + + .thumb_func + .type SVC_Handler, %function + .global SVC_Handler +SVC_Handler: + .fnstart + .cantunwind + + MRS R0,PSP /* Read PSP */ + LDR R1,[R0,#24] /* Read Saved PC from Stack */ + SUBS R1,R1,#2 /* Point to SVC Instruction */ + LDRB R1,[R1] /* Load SVC Number */ + CMP R1,#0 + BNE SVC_User /* User SVC Number > 0 */ + + MOV LR,R4 + LDMIA R0,{R0-R3,R4} /* Read R0-R3,R12 from stack */ + MOV R12,R4 + MOV R4,LR + BLX R12 /* Call SVC Function */ + + MRS R3,PSP /* Read PSP */ + STMIA R3!,{R0-R2} /* Store return values */ + + LDR R3,=os_tsk + LDMIA R3!,{R1,R2} /* os_tsk.run, os_tsk.new */ + CMP R1,R2 + BEQ SVC_Exit /* no task switch */ + + SUBS R3,#8 + CMP R1,#0 /* Runtask deleted? */ + BEQ SVC_Next + + MRS R0,PSP /* Read PSP */ + SUBS R0,R0,#32 /* Adjust Start Address */ + STR R0,[R1,#TCB_TSTACK] /* Update os_tsk.run->tsk_stack */ + STMIA R0!,{R4-R7} /* Save old context (R4-R7) */ + MOV R4,R8 + MOV R5,R9 + MOV R6,R10 + MOV R7,R11 + STMIA R0!,{R4-R7} /* Save old context (R8-R11) */ + + PUSH {R2,R3} + BL rt_stk_check /* Check for Stack overflow */ + POP {R2,R3} + +SVC_Next: + STR R2,[R3] /* os_tsk.run = os_tsk.new */ + + LDR R0,[R2,#TCB_TSTACK] /* os_tsk.new->tsk_stack */ + ADDS R0,R0,#16 /* Adjust Start Address */ + LDMIA R0!,{R4-R7} /* Restore new Context (R8-R11) */ + MOV R8,R4 + MOV R9,R5 + MOV R10,R6 + MOV R11,R7 + MSR PSP,R0 /* Write PSP */ + SUBS R0,R0,#32 /* Adjust Start Address */ + LDMIA R0!,{R4-R7} /* Restore new Context (R4-R7) */ + +SVC_Exit: + MOVS R0,#~0xFFFFFFFD /* Set EXC_RETURN value */ + MVNS R0,R0 + BX R0 /* RETI to Thread Mode, use PSP */ + + /*------------------- User SVC ------------------------------*/ + +SVC_User: + PUSH {R4,LR} /* Save Registers */ + LDR R2,=SVC_Count + LDR R2,[R2] + CMP R1,R2 + BHI SVC_Done /* Overflow */ + + LDR R4,=SVC_Table-4 + LSLS R1,R1,#2 + LDR R4,[R4,R1] /* Load SVC Function Address */ + MOV LR,R4 + + LDMIA R0,{R0-R3,R4} /* Read R0-R3,R12 from stack */ + MOV R12,R4 + BLX LR /* Call SVC Function */ + + MRS R4,PSP /* Read PSP */ + STMIA R4!,{R0-R3} /* Function return values */ +SVC_Done: + POP {R4,PC} /* RETI */ + + .fnend + .size SVC_Handler, .-SVC_Handler + + +/*-------------------------- PendSV_Handler ---------------------------------*/ + +# void PendSV_Handler (void); + + .thumb_func + .type PendSV_Handler, %function + .global PendSV_Handler + .global Sys_Switch +PendSV_Handler: + .fnstart + .cantunwind + + BL rt_pop_req + +Sys_Switch: + LDR R3,=os_tsk + LDMIA R3!,{R1,R2} /* os_tsk.run, os_tsk.new */ + CMP R1,R2 + BEQ Sys_Exit /* no task switch */ + + SUBS R3,#8 + + MRS R0,PSP /* Read PSP */ + SUBS R0,R0,#32 /* Adjust Start Address */ + STR R0,[R1,#TCB_TSTACK] /* Update os_tsk.run->tsk_stack */ + STMIA R0!,{R4-R7} /* Save old context (R4-R7) */ + MOV R4,R8 + MOV R5,R9 + MOV R6,R10 + MOV R7,R11 + STMIA R0!,{R4-R7} /* Save old context (R8-R11) */ + + PUSH {R2,R3} + BL rt_stk_check /* Check for Stack overflow */ + POP {R2,R3} + + STR R2,[R3] /* os_tsk.run = os_tsk.new */ + + LDR R0,[R2,#TCB_TSTACK] /* os_tsk.new->tsk_stack */ + ADDS R0,R0,#16 /* Adjust Start Address */ + LDMIA R0!,{R4-R7} /* Restore new Context (R8-R11) */ + MOV R8,R4 + MOV R9,R5 + MOV R10,R6 + MOV R11,R7 + MSR PSP,R0 /* Write PSP */ + SUBS R0,R0,#32 /* Adjust Start Address */ + LDMIA R0!,{R4-R7} /* Restore new Context (R4-R7) */ + +Sys_Exit: + MOVS R0,#~0xFFFFFFFD /* Set EXC_RETURN value */ + MVNS R0,R0 + BX R0 /* RETI to Thread Mode, use PSP */ + + .fnend + .size PendSV_Handler, .-PendSV_Handler + + +/*-------------------------- SysTick_Handler --------------------------------*/ + +# void SysTick_Handler (void); + + .thumb_func + .type SysTick_Handler, %function + .global SysTick_Handler +SysTick_Handler: + .fnstart + .cantunwind + + BL rt_systick + B Sys_Switch + + .fnend + .size SysTick_Handler, .-SysTick_Handler + + +/*-------------------------- OS_Tick_Handler --------------------------------*/ + +# void OS_Tick_Handler (void); + + .thumb_func + .type OS_Tick_Handler, %function + .global OS_Tick_Handler +OS_Tick_Handler: + .fnstart + .cantunwind + + BL os_tick_irqack + BL rt_systick + B Sys_Switch + + .fnend + .size OS_Tick_Handler, .-OS_Tick_Handler + + + .end + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/LPC11U24/GCC_ARM/SVC_Table.s Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,56 @@ +;/*---------------------------------------------------------------------------- +; * RL-ARM - RTX +; *---------------------------------------------------------------------------- +; * Name: SVC_TABLE.S +; * Purpose: Pre-defined SVC Table for Cortex-M +; * Rev.: V4.60 +; *---------------------------------------------------------------------------- +; * +; * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH +; * All rights reserved. +; * Redistribution and use in source and binary forms, with or without +; * modification, are permitted provided that the following conditions are met: +; * - Redistributions of source code must retain the above copyright +; * notice, this list of conditions and the following disclaimer. +; * - Redistributions in binary form must reproduce the above copyright +; * notice, this list of conditions and the following disclaimer in the +; * documentation and/or other materials provided with the distribution. +; * - Neither the name of ARM nor the names of its contributors may be used +; * to endorse or promote products derived from this software without +; * specific prior written permission. +; * +; * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE +; * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; * POSSIBILITY OF SUCH DAMAGE. +; *---------------------------------------------------------------------------*/ + + + .file "SVC_Table.S" + + + .section ".svc_table" + + .global SVC_Table +SVC_Table: +/* Insert user SVC functions here. SVC 0 used by RTL Kernel. */ +# .long __SVC_1 /* user SVC function */ +SVC_End: + + .global SVC_Count +SVC_Count: + .long (SVC_End-SVC_Table)/4 + + + .end + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/LPC11U24/uARM/HAL_CM0.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,301 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: HAL_CM0.C + * Purpose: Hardware Abstraction Layer for Cortex-M0 + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +#include "rt_TypeDef.h" +#include "RTX_Config.h" +#include "rt_System.h" +#include "rt_HAL_CM.h" +#include "rt_Task.h" +#include "rt_MemBox.h" + + +/*---------------------------------------------------------------------------- + * Functions + *---------------------------------------------------------------------------*/ + + +/*--------------------------- rt_set_PSP ------------------------------------*/ + +__asm void rt_set_PSP (U32 stack) { + MSR PSP,R0 + BX LR +} + + +/*--------------------------- rt_get_PSP ------------------------------------*/ + +__asm U32 rt_get_PSP (void) { + MRS R0,PSP + BX LR +} + + +/*--------------------------- os_set_env ------------------------------------*/ + +__asm void os_set_env (void) { + /* Switch to Unprivileged/Privileged Thread mode, use PSP. */ + MOV R0,SP ; PSP = MSP + MSR PSP,R0 + LDR R0,=__cpp(&os_flags) + LDRB R0,[R0] + LSLS R0,#31 + BNE PrivilegedE + MOVS R0,#0x03 ; Unprivileged Thread mode, use PSP + MSR CONTROL,R0 + BX LR +PrivilegedE + MOVS R0,#0x02 ; Privileged Thread mode, use PSP + MSR CONTROL,R0 + BX LR + + ALIGN +} + + +/*--------------------------- _alloc_box ------------------------------------*/ + +__asm void *_alloc_box (void *box_mem) { + /* Function wrapper for Unprivileged/Privileged mode. */ + LDR R3,=__cpp(rt_alloc_box) + MOV R12,R3 + MRS R3,IPSR + LSLS R3,#24 + BNE PrivilegedA + MRS R3,CONTROL + LSLS R3,#31 + BEQ PrivilegedA + SVC 0 + BX LR +PrivilegedA + BX R12 + + ALIGN +} + + +/*--------------------------- _free_box -------------------------------------*/ + +__asm int _free_box (void *box_mem, void *box) { + /* Function wrapper for Unprivileged/Privileged mode. */ + LDR R3,=__cpp(rt_free_box) + MOV R12,R3 + MRS R3,IPSR + LSLS R3,#24 + BNE PrivilegedF + MRS R3,CONTROL + LSLS R3,#31 + BEQ PrivilegedF + SVC 0 + BX LR +PrivilegedF + BX R12 + + ALIGN +} + + +/*-------------------------- SVC_Handler ------------------------------------*/ + +__asm void SVC_Handler (void) { + PRESERVE8 + + IMPORT SVC_Count + IMPORT SVC_Table + IMPORT rt_stk_check + + MRS R0,PSP ; Read PSP + LDR R1,[R0,#24] ; Read Saved PC from Stack + SUBS R1,R1,#2 ; Point to SVC Instruction + LDRB R1,[R1] ; Load SVC Number + CMP R1,#0 + BNE SVC_User ; User SVC Number > 0 + + MOV LR,R4 + LDMIA R0,{R0-R3,R4} ; Read R0-R3,R12 from stack + MOV R12,R4 + MOV R4,LR + BLX R12 ; Call SVC Function + + MRS R3,PSP ; Read PSP + STMIA R3!,{R0-R2} ; Store return values + + LDR R3,=__cpp(&os_tsk) + LDMIA R3!,{R1,R2} ; os_tsk.run, os_tsk.new + CMP R1,R2 + BEQ SVC_Exit ; no task switch + + SUBS R3,#8 + CMP R1,#0 ; Runtask deleted? + BEQ SVC_Next + + MRS R0,PSP ; Read PSP + SUBS R0,R0,#32 ; Adjust Start Address + STR R0,[R1,#TCB_TSTACK] ; Update os_tsk.run->tsk_stack + STMIA R0!,{R4-R7} ; Save old context (R4-R7) + MOV R4,R8 + MOV R5,R9 + MOV R6,R10 + MOV R7,R11 + STMIA R0!,{R4-R7} ; Save old context (R8-R11) + + PUSH {R2,R3} + BL rt_stk_check ; Check for Stack overflow + POP {R2,R3} + +SVC_Next + STR R2,[R3] ; os_tsk.run = os_tsk.new + + LDR R0,[R2,#TCB_TSTACK] ; os_tsk.new->tsk_stack + ADDS R0,R0,#16 ; Adjust Start Address + LDMIA R0!,{R4-R7} ; Restore new Context (R8-R11) + MOV R8,R4 + MOV R9,R5 + MOV R10,R6 + MOV R11,R7 + MSR PSP,R0 ; Write PSP + SUBS R0,R0,#32 ; Adjust Start Address + LDMIA R0!,{R4-R7} ; Restore new Context (R4-R7) + +SVC_Exit + MOVS R0,#:NOT:0xFFFFFFFD ; Set EXC_RETURN value + MVNS R0,R0 + BX R0 ; RETI to Thread Mode, use PSP + + /*------------------- User SVC ------------------------------*/ + +SVC_User + PUSH {R4,LR} ; Save Registers + LDR R2,=SVC_Count + LDR R2,[R2] + CMP R1,R2 + BHI SVC_Done ; Overflow + + LDR R4,=SVC_Table-4 + LSLS R1,R1,#2 + LDR R4,[R4,R1] ; Load SVC Function Address + MOV LR,R4 + + LDMIA R0,{R0-R3,R4} ; Read R0-R3,R12 from stack + MOV R12,R4 + BLX LR ; Call SVC Function + + MRS R4,PSP ; Read PSP + STMIA R4!,{R0-R3} ; Function return values +SVC_Done + POP {R4,PC} ; RETI + + ALIGN +} + + +/*-------------------------- PendSV_Handler ---------------------------------*/ + +__asm void PendSV_Handler (void) { + PRESERVE8 + + BL __cpp(rt_pop_req) + +Sys_Switch + LDR R3,=__cpp(&os_tsk) + LDMIA R3!,{R1,R2} ; os_tsk.run, os_tsk.new + CMP R1,R2 + BEQ Sys_Exit ; no task switch + + SUBS R3,#8 + + MRS R0,PSP ; Read PSP + SUBS R0,R0,#32 ; Adjust Start Address + STR R0,[R1,#TCB_TSTACK] ; Update os_tsk.run->tsk_stack + STMIA R0!,{R4-R7} ; Save old context (R4-R7) + MOV R4,R8 + MOV R5,R9 + MOV R6,R10 + MOV R7,R11 + STMIA R0!,{R4-R7} ; Save old context (R8-R11) + + PUSH {R2,R3} + BL rt_stk_check ; Check for Stack overflow + POP {R2,R3} + + STR R2,[R3] ; os_tsk.run = os_tsk.new + + LDR R0,[R2,#TCB_TSTACK] ; os_tsk.new->tsk_stack + ADDS R0,R0,#16 ; Adjust Start Address + LDMIA R0!,{R4-R7} ; Restore new Context (R8-R11) + MOV R8,R4 + MOV R9,R5 + MOV R10,R6 + MOV R11,R7 + MSR PSP,R0 ; Write PSP + SUBS R0,R0,#32 ; Adjust Start Address + LDMIA R0!,{R4-R7} ; Restore new Context (R4-R7) + +Sys_Exit + MOVS R0,#:NOT:0xFFFFFFFD ; Set EXC_RETURN value + MVNS R0,R0 + BX R0 ; RETI to Thread Mode, use PSP + + ALIGN +} + + +/*-------------------------- SysTick_Handler --------------------------------*/ + +__asm void SysTick_Handler (void) { + PRESERVE8 + + BL __cpp(rt_systick) + B Sys_Switch + + ALIGN +} + + +/*-------------------------- OS_Tick_Handler --------------------------------*/ + +__asm void OS_Tick_Handler (void) { + PRESERVE8 + + BL __cpp(os_tick_irqack) + BL __cpp(rt_systick) + B Sys_Switch + + ALIGN +} + + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/LPC11U24/uARM/SVC_Table.s Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,57 @@ +;/*---------------------------------------------------------------------------- +; * RL-ARM - RTX +; *---------------------------------------------------------------------------- +; * Name: SVC_TABLE.S +; * Purpose: Pre-defined SVC Table for Cortex-M +; * Rev.: V4.60 +; *---------------------------------------------------------------------------- +; * +; * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH +; * All rights reserved. +; * Redistribution and use in source and binary forms, with or without +; * modification, are permitted provided that the following conditions are met: +; * - Redistributions of source code must retain the above copyright +; * notice, this list of conditions and the following disclaimer. +; * - Redistributions in binary form must reproduce the above copyright +; * notice, this list of conditions and the following disclaimer in the +; * documentation and/or other materials provided with the distribution. +; * - Neither the name of ARM nor the names of its contributors may be used +; * to endorse or promote products derived from this software without +; * specific prior written permission. +; * +; * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE +; * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; * POSSIBILITY OF SUCH DAMAGE. +; *---------------------------------------------------------------------------*/ + + + AREA SVC_TABLE, CODE, READONLY + + EXPORT SVC_Count + +SVC_Cnt EQU (SVC_End-SVC_Table)/4 +SVC_Count DCD SVC_Cnt + +; Import user SVC functions here. +; IMPORT __SVC_1 + + EXPORT SVC_Table +SVC_Table +; Insert user SVC functions here. SVC 0 used by RTL Kernel. +; DCD __SVC_1 ; user SVC function + +SVC_End + + END + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/LPC1768/ARM/HAL_CM3.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,249 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: HAL_CM3.C + * Purpose: Hardware Abstraction Layer for Cortex-M3 + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +#include "rt_TypeDef.h" +#include "RTX_Config.h" +#include "rt_System.h" +#include "rt_HAL_CM.h" +#include "rt_Task.h" +#include "rt_MemBox.h" + + +/*---------------------------------------------------------------------------- + * Functions + *---------------------------------------------------------------------------*/ + + +/*--------------------------- rt_set_PSP ------------------------------------*/ + +__asm void rt_set_PSP (U32 stack) { + MSR PSP,R0 + BX LR +} + + +/*--------------------------- rt_get_PSP ------------------------------------*/ + +__asm U32 rt_get_PSP (void) { + MRS R0,PSP + BX LR +} + + +/*--------------------------- os_set_env ------------------------------------*/ + +__asm void os_set_env (void) { + /* Switch to Unprivileged/Privileged Thread mode, use PSP. */ + MOV R0,SP ; PSP = MSP + MSR PSP,R0 + LDR R0,=__cpp(&os_flags) + LDRB R0,[R0] + LSLS R0,#31 + MOVNE R0,#0x02 ; Privileged Thread mode, use PSP + MOVEQ R0,#0x03 ; Unprivileged Thread mode, use PSP + MSR CONTROL,R0 + BX LR + + ALIGN +} + + +/*--------------------------- _alloc_box ------------------------------------*/ + +__asm void *_alloc_box (void *box_mem) { + /* Function wrapper for Unprivileged/Privileged mode. */ + LDR R12,=__cpp(rt_alloc_box) + MRS R3,IPSR + LSLS R3,#24 + BXNE R12 + MRS R3,CONTROL + LSLS R3,#31 + BXEQ R12 + SVC 0 + BX LR + + ALIGN +} + + +/*--------------------------- _free_box -------------------------------------*/ + +__asm int _free_box (void *box_mem, void *box) { + /* Function wrapper for Unprivileged/Privileged mode. */ + LDR R12,=__cpp(rt_free_box) + MRS R3,IPSR + LSLS R3,#24 + BXNE R12 + MRS R3,CONTROL + LSLS R3,#31 + BXEQ R12 + SVC 0 + BX LR + + ALIGN +} + + +/*-------------------------- SVC_Handler ------------------------------------*/ + +__asm void SVC_Handler (void) { + PRESERVE8 + + IMPORT SVC_Count + IMPORT SVC_Table + IMPORT rt_stk_check + + MRS R0,PSP ; Read PSP + LDR R1,[R0,#24] ; Read Saved PC from Stack + LDRB R1,[R1,#-2] ; Load SVC Number + CBNZ R1,SVC_User + + LDM R0,{R0-R3,R12} ; Read R0-R3,R12 from stack + BLX R12 ; Call SVC Function + + MRS R12,PSP ; Read PSP + STM R12,{R0-R2} ; Store return values + + LDR R3,=__cpp(&os_tsk) + LDM R3,{R1,R2} ; os_tsk.run, os_tsk.new + CMP R1,R2 + BEQ SVC_Exit ; no task switch + + CBZ R1,SVC_Next ; Runtask deleted? + STMDB R12!,{R4-R11} ; Save Old context + STR R12,[R1,#TCB_TSTACK] ; Update os_tsk.run->tsk_stack + + PUSH {R2,R3} + BL rt_stk_check ; Check for Stack overflow + POP {R2,R3} + +SVC_Next + STR R2,[R3] ; os_tsk.run = os_tsk.new + + LDR R12,[R2,#TCB_TSTACK] ; os_tsk.new->tsk_stack + LDMIA R12!,{R4-R11} ; Restore New Context + MSR PSP,R12 ; Write PSP + +SVC_Exit + MVN LR,#:NOT:0xFFFFFFFD ; set EXC_RETURN value + BX LR + + /*------------------- User SVC ------------------------------*/ + +SVC_User + PUSH {R4,LR} ; Save Registers + LDR R2,=SVC_Count + LDR R2,[R2] + CMP R1,R2 + BHI SVC_Done ; Overflow + + LDR R4,=SVC_Table-4 + LDR R4,[R4,R1,LSL #2] ; Load SVC Function Address + + LDM R0,{R0-R3,R12} ; Read R0-R3,R12 from stack + BLX R4 ; Call SVC Function + + MRS R12,PSP + STM R12,{R0-R3} ; Function return values +SVC_Done + POP {R4,PC} ; RETI + + ALIGN +} + + +/*-------------------------- PendSV_Handler ---------------------------------*/ + +__asm void PendSV_Handler (void) { + PRESERVE8 + + BL __cpp(rt_pop_req) + +Sys_Switch + LDR R3,=__cpp(&os_tsk) + LDM R3,{R1,R2} ; os_tsk.run, os_tsk.new + CMP R1,R2 + BEQ Sys_Exit + + MRS R12,PSP ; Read PSP + STMDB R12!,{R4-R11} ; Save Old context + STR R12,[R1,#TCB_TSTACK] ; Update os_tsk.run->tsk_stack + + PUSH {R2,R3} + BL rt_stk_check ; Check for Stack overflow + POP {R2,R3} + + STR R2,[R3] ; os_tsk.run = os_tsk.new + + LDR R12,[R2,#TCB_TSTACK] ; os_tsk.new->tsk_stack + LDMIA R12!,{R4-R11} ; Restore New Context + MSR PSP,R12 ; Write PSP + +Sys_Exit + MVN LR,#:NOT:0xFFFFFFFD ; set EXC_RETURN value + BX LR ; Return to Thread Mode + + ALIGN +} + + +/*-------------------------- SysTick_Handler --------------------------------*/ + +__asm void SysTick_Handler (void) { + PRESERVE8 + + BL __cpp(rt_systick) + B Sys_Switch + + ALIGN +} + + +/*-------------------------- OS_Tick_Handler --------------------------------*/ + +__asm void OS_Tick_Handler (void) { + PRESERVE8 + + BL __cpp(os_tick_irqack) + BL __cpp(rt_systick) + B Sys_Switch + + ALIGN +} + + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/LPC1768/ARM/SVC_Table.s Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,57 @@ +;/*---------------------------------------------------------------------------- +; * RL-ARM - RTX +; *---------------------------------------------------------------------------- +; * Name: SVC_TABLE.S +; * Purpose: Pre-defined SVC Table for Cortex-M +; * Rev.: V4.60 +; *---------------------------------------------------------------------------- +; * +; * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH +; * All rights reserved. +; * Redistribution and use in source and binary forms, with or without +; * modification, are permitted provided that the following conditions are met: +; * - Redistributions of source code must retain the above copyright +; * notice, this list of conditions and the following disclaimer. +; * - Redistributions in binary form must reproduce the above copyright +; * notice, this list of conditions and the following disclaimer in the +; * documentation and/or other materials provided with the distribution. +; * - Neither the name of ARM nor the names of its contributors may be used +; * to endorse or promote products derived from this software without +; * specific prior written permission. +; * +; * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE +; * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; * POSSIBILITY OF SUCH DAMAGE. +; *---------------------------------------------------------------------------*/ + + + AREA SVC_TABLE, CODE, READONLY + + EXPORT SVC_Count + +SVC_Cnt EQU (SVC_End-SVC_Table)/4 +SVC_Count DCD SVC_Cnt + +; Import user SVC functions here. +; IMPORT __SVC_1 + + EXPORT SVC_Table +SVC_Table +; Insert user SVC functions here. SVC 0 used by RTL Kernel. +; DCD __SVC_1 ; user SVC function + +SVC_End + + END + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/LPC1768/GCC_ARM/HAL_CM3.s Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,323 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: HAL_CM3.S + * Purpose: Hardware Abstraction Layer for Cortex-M3 + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + + .file "HAL_CM3.S" + .syntax unified + + .equ TCB_TSTACK, 36 + + +/*---------------------------------------------------------------------------- + * Functions + *---------------------------------------------------------------------------*/ + + .thumb + + .section ".text" + .align 2 + + +/*--------------------------- rt_set_PSP ------------------------------------*/ + +# void rt_set_PSP (U32 stack); + + .thumb_func + .type rt_set_PSP, %function + .global rt_set_PSP +rt_set_PSP: + .fnstart + .cantunwind + + MSR PSP,R0 + BX LR + + .fnend + .size rt_set_PSP, .-rt_set_PSP + + +/*--------------------------- rt_get_PSP ------------------------------------*/ + +# U32 rt_get_PSP (void); + + .thumb_func + .type rt_get_PSP, %function + .global rt_get_PSP +rt_get_PSP: + .fnstart + .cantunwind + + MRS R0,PSP + BX LR + + .fnend + .size rt_get_PSP, .-rt_get_PSP + + +/*--------------------------- os_set_env ------------------------------------*/ + +# void os_set_env (void); + /* Switch to Unprivileged/Privileged Thread mode, use PSP. */ + + .thumb_func + .type os_set_env, %function + .global os_set_env +os_set_env: + .fnstart + .cantunwind + + MOV R0,SP /* PSP = MSP */ + MSR PSP,R0 + LDR R0,=os_flags + LDRB R0,[R0] + LSLS R0,#31 + ITE NE + MOVNE R0,#0x02 /* Privileged Thread mode, use PSP */ + MOVEQ R0,#0x03 /* Unprivileged Thread mode, use PSP */ + MSR CONTROL,R0 + BX LR + + .fnend + .size os_set_env, .-os_set_env + + +/*--------------------------- _alloc_box ------------------------------------*/ + +# void *_alloc_box (void *box_mem); + /* Function wrapper for Unprivileged/Privileged mode. */ + + .thumb_func + .type _alloc_box, %function + .global _alloc_box +_alloc_box: + .fnstart + .cantunwind + + LDR R12,=rt_alloc_box + MRS R3,IPSR + LSLS R3,#24 + IT NE + BXNE R12 + MRS R3,CONTROL + LSLS R3,#31 + IT EQ + BXEQ R12 + SVC 0 + BX LR + + .fnend + .size _alloc_box, .-_alloc_box + + +/*--------------------------- _free_box -------------------------------------*/ + +# int _free_box (void *box_mem, void *box); + /* Function wrapper for Unprivileged/Privileged mode. */ + + .thumb_func + .type _free_box, %function + .global _free_box +_free_box: + .fnstart + .cantunwind + + LDR R12,=rt_free_box + MRS R3,IPSR + LSLS R3,#24 + IT NE + BXNE R12 + MRS R3,CONTROL + LSLS R3,#31 + IT EQ + BXEQ R12 + SVC 0 + BX LR + + .fnend + .size _free_box, .-_free_box + + +/*-------------------------- SVC_Handler ------------------------------------*/ + +# void SVC_Handler (void); + + .thumb_func + .type SVC_Handler, %function + .global SVC_Handler +SVC_Handler: + .fnstart + .cantunwind + + MRS R0,PSP /* Read PSP */ + LDR R1,[R0,#24] /* Read Saved PC from Stack */ + LDRB R1,[R1,#-2] /* Load SVC Number */ + CBNZ R1,SVC_User + + LDM R0,{R0-R3,R12} /* Read R0-R3,R12 from stack */ + BLX R12 /* Call SVC Function */ + + MRS R12,PSP /* Read PSP */ + STM R12,{R0-R2} /* Store return values */ + + LDR R3,=os_tsk + LDM R3,{R1,R2} /* os_tsk.run, os_tsk.new */ + CMP R1,R2 + BEQ SVC_Exit /* no task switch */ + + CBZ R1,SVC_Next /* Runtask deleted? */ + STMDB R12!,{R4-R11} /* Save Old context */ + STR R12,[R1,#TCB_TSTACK] /* Update os_tsk.run->tsk_stack */ + + PUSH {R2,R3} + BL rt_stk_check /* Check for Stack overflow */ + POP {R2,R3} + +SVC_Next: + STR R2,[R3] /* os_tsk.run = os_tsk.new */ + + LDR R12,[R2,#TCB_TSTACK] /* os_tsk.new->tsk_stack */ + LDMIA R12!,{R4-R11} /* Restore New Context */ + MSR PSP,R12 /* Write PSP */ + +SVC_Exit: + MVN LR,#~0xFFFFFFFD /* set EXC_RETURN value */ + BX LR + + /*------------------- User SVC ------------------------------*/ + +SVC_User: + PUSH {R4,LR} /* Save Registers */ + LDR R2,=SVC_Count + LDR R2,[R2] + CMP R1,R2 + BHI SVC_Done /* Overflow */ + + LDR R4,=SVC_Table-4 + LDR R4,[R4,R1,LSL #2] /* Load SVC Function Address */ + + LDM R0,{R0-R3,R12} /* Read R0-R3,R12 from stack */ + BLX R4 /* Call SVC Function */ + + MRS R12,PSP + STM R12,{R0-R3} /* Function return values */ +SVC_Done: + POP {R4,PC} /* RETI */ + + .fnend + .size SVC_Handler, .-SVC_Handler + + +/*-------------------------- PendSV_Handler ---------------------------------*/ + +# void PendSV_Handler (void); + + .thumb_func + .type PendSV_Handler, %function + .global PendSV_Handler + .global Sys_Switch +PendSV_Handler: + .fnstart + .cantunwind + + BL rt_pop_req + +Sys_Switch: + LDR R3,=os_tsk + LDM R3,{R1,R2} /* os_tsk.run, os_tsk.new */ + CMP R1,R2 + BEQ Sys_Exit + + MRS R12,PSP /* Read PSP */ + STMDB R12!,{R4-R11} /* Save Old context */ + STR R12,[R1,#TCB_TSTACK] /* Update os_tsk.run->tsk_stack */ + + PUSH {R2,R3} + BL rt_stk_check /* Check for Stack overflow */ + POP {R2,R3} + + STR R2,[R3] /* os_tsk.run = os_tsk.new */ + + LDR R12,[R2,#TCB_TSTACK] /* os_tsk.new->tsk_stack */ + LDMIA R12!,{R4-R11} /* Restore New Context */ + MSR PSP,R12 /* Write PSP */ + +Sys_Exit: + MVN LR,#~0xFFFFFFFD /* set EXC_RETURN value */ + BX LR /* Return to Thread Mode */ + + .fnend + .size PendSV_Handler, .-PendSV_Handler + + +/*-------------------------- SysTick_Handler --------------------------------*/ + +# void SysTick_Handler (void); + + .thumb_func + .type SysTick_Handler, %function + .global SysTick_Handler +SysTick_Handler: + .fnstart + .cantunwind + + BL rt_systick + B Sys_Switch + + .fnend + .size SysTick_Handler, .-SysTick_Handler + + +/*-------------------------- OS_Tick_Handler --------------------------------*/ + +# void OS_Tick_Handler (void); + + .thumb_func + .type OS_Tick_Handler, %function + .global OS_Tick_Handler +OS_Tick_Handler: + .fnstart + .cantunwind + + BL os_tick_irqack + BL rt_systick + B Sys_Switch + + .fnend + .size OS_Tick_Handler, .-OS_Tick_Handler + + + .end + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/LPC1768/GCC_ARM/SVC_Table.s Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,56 @@ +;/*---------------------------------------------------------------------------- +; * RL-ARM - RTX +; *---------------------------------------------------------------------------- +; * Name: SVC_TABLE.S +; * Purpose: Pre-defined SVC Table for Cortex-M +; * Rev.: V4.60 +; *---------------------------------------------------------------------------- +; * +; * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH +; * All rights reserved. +; * Redistribution and use in source and binary forms, with or without +; * modification, are permitted provided that the following conditions are met: +; * - Redistributions of source code must retain the above copyright +; * notice, this list of conditions and the following disclaimer. +; * - Redistributions in binary form must reproduce the above copyright +; * notice, this list of conditions and the following disclaimer in the +; * documentation and/or other materials provided with the distribution. +; * - Neither the name of ARM nor the names of its contributors may be used +; * to endorse or promote products derived from this software without +; * specific prior written permission. +; * +; * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE +; * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; * POSSIBILITY OF SUCH DAMAGE. +; *---------------------------------------------------------------------------*/ + + + .file "SVC_Table.S" + + + .section ".svc_table" + + .global SVC_Table +SVC_Table: +/* Insert user SVC functions here. SVC 0 used by RTL Kernel. */ +# .long __SVC_1 /* user SVC function */ +SVC_End: + + .global SVC_Count +SVC_Count: + .long (SVC_End-SVC_Table)/4 + + + .end + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/RTX_CM_lib.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,368 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RTX_CM_LIB.H + * Purpose: RTX Kernel System Configuration + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ +#include "error.h" + +#if defined (__CC_ARM) +#pragma O3 +#define __USED __attribute__((used)) +#elif defined (__GNUC__) +#pragma GCC optimize ("O3") +#define __USED __attribute__((used)) +#elif defined (__ICCARM__) +#define __USED __root +#endif + + +/*---------------------------------------------------------------------------- + * Definitions + *---------------------------------------------------------------------------*/ + +#define _declare_box(pool,size,cnt) uint32_t pool[(((size)+3)/4)*(cnt) + 3] +#define _declare_box8(pool,size,cnt) uint64_t pool[(((size)+7)/8)*(cnt) + 2] + +#define OS_TCB_SIZE 48 +#define OS_TMR_SIZE 8 + +#if defined (__CC_ARM) && !defined (__MICROLIB) + +typedef void *OS_ID; +typedef uint32_t OS_TID; +typedef uint32_t OS_MUT[3]; +typedef uint32_t OS_RESULT; + +#define runtask_id() rt_tsk_self() +#define mutex_init(m) rt_mut_init(m) +#define mutex_wait(m) os_mut_wait(m,0xFFFF) +#define mutex_rel(m) os_mut_release(m) + +extern OS_TID rt_tsk_self (void); +extern void rt_mut_init (OS_ID mutex); +extern OS_RESULT rt_mut_release (OS_ID mutex); +extern OS_RESULT rt_mut_wait (OS_ID mutex, uint16_t timeout); + +#define os_mut_wait(mutex,timeout) _os_mut_wait((uint32_t)rt_mut_wait,mutex,timeout) +#define os_mut_release(mutex) _os_mut_release((uint32_t)rt_mut_release,mutex) + +OS_RESULT _os_mut_release (uint32_t p, OS_ID mutex) __svc_indirect(0); +OS_RESULT _os_mut_wait (uint32_t p, OS_ID mutex, uint16_t timeout) __svc_indirect(0); + +#endif + + +/*---------------------------------------------------------------------------- + * Global Variables + *---------------------------------------------------------------------------*/ + +#if (OS_TIMERS != 0) +#define OS_TASK_CNT (OS_TASKCNT + 1) +#else +#define OS_TASK_CNT OS_TASKCNT +#endif + +uint16_t const os_maxtaskrun = OS_TASK_CNT; +uint32_t const os_rrobin = (OS_ROBIN << 16) | OS_ROBINTOUT; +uint32_t const os_trv = OS_TRV; +uint8_t const os_flags = OS_RUNPRIV; + +/* Export following defines to uVision debugger. */ +__USED uint32_t const os_clockrate = OS_TICK; +__USED uint32_t const os_timernum = 0; + +/* Stack for the os_idle_demon */ +unsigned int idle_task_stack[OS_IDLESTKSIZE]; +unsigned short const idle_task_stack_size = OS_IDLESTKSIZE; + +#ifndef OS_FIFOSZ + #define OS_FIFOSZ 16 +#endif + +/* Fifo Queue buffer for ISR requests.*/ +uint32_t os_fifo[OS_FIFOSZ*2+1]; +uint8_t const os_fifo_size = OS_FIFOSZ; + +/* An array of Active task pointers. */ +void *os_active_TCB[OS_TASK_CNT]; + +/* User Timers Resources */ +#if (OS_TIMERS != 0) +extern void osTimerThread (void const *argument); +osThreadDef(osTimerThread, (osPriority)(OS_TIMERPRIO-3), 4*OS_TIMERSTKSZ); +osThreadId osThreadId_osTimerThread; +osMessageQDef(osTimerMessageQ, OS_TIMERCBQS, void *); +osMessageQId osMessageQId_osTimerMessageQ; +#else +osThreadDef_t os_thread_def_osTimerThread = { NULL }; +osThreadId osThreadId_osTimerThread; +osMessageQDef(osTimerMessageQ, 0, void *); +osMessageQId osMessageQId_osTimerMessageQ; +#endif + + +/*---------------------------------------------------------------------------- + * RTX Optimizations (empty functions) + *---------------------------------------------------------------------------*/ + +#if OS_ROBIN == 0 + void rt_init_robin (void) {;} + void rt_chk_robin (void) {;} +#endif + +#if OS_STKCHECK == 0 + void rt_stk_check (void) {;} +#endif + + +/*---------------------------------------------------------------------------- + * Standard Library multithreading interface + *---------------------------------------------------------------------------*/ + +#if defined (__CC_ARM) && !defined (__MICROLIB) + static OS_MUT std_libmutex[OS_MUTEXCNT]; + static uint32_t nr_mutex; + + /*--------------------------- _mutex_initialize -----------------------------*/ + +int _mutex_initialize (OS_ID *mutex) { + /* Allocate and initialize a system mutex. */ + + if (nr_mutex >= OS_MUTEXCNT) { + /* If you are here, you need to increase the number OS_MUTEXCNT. */ + error("Not enough stdlib mutexes\n"); + } + *mutex = &std_libmutex[nr_mutex++]; + mutex_init (*mutex); + return (1); +} + + +/*--------------------------- _mutex_acquire --------------------------------*/ + +__attribute__((used)) void _mutex_acquire (OS_ID *mutex) { + /* Acquire a system mutex, lock stdlib resources. */ + if (runtask_id ()) { + /* RTX running, acquire a mutex. */ + mutex_wait (*mutex); + } +} + + +/*--------------------------- _mutex_release --------------------------------*/ + +__attribute__((used)) void _mutex_release (OS_ID *mutex) { + /* Release a system mutex, unlock stdlib resources. */ + if (runtask_id ()) { + /* RTX running, release a mutex. */ + mutex_rel (*mutex); + } +} + +#endif + + +/*---------------------------------------------------------------------------- + * RTX Startup + *---------------------------------------------------------------------------*/ + +/* Main Thread definition */ +extern int main (void); +osThreadDef_t os_thread_def_main = {(os_pthread)main, osPriorityNormal, 0, NULL}; + +#if defined (__CC_ARM) + +// This define should be probably moved to the CMSIS layer +#ifdef TARGET_LPC1768 +#define INITIAL_SP (0x10008000UL) + +#elif TARGET_LPC11U24 +#define INITIAL_SP (0x10001000UL) + +#endif + +extern unsigned char Image$$RW_IRAM1$$ZI$$Limit[]; + +void set_main_stack(void) { + // Leave OS_SCHEDULERSTKSIZE words for the scheduler and interrupts + os_thread_def_main.stack_pointer = Image$$RW_IRAM1$$ZI$$Limit; + os_thread_def_main.stacksize = (INITIAL_SP - (unsigned int)Image$$RW_IRAM1$$ZI$$Limit) - (OS_SCHEDULERSTKSIZE * 4); +} + +#ifdef __MICROLIB +void _main_init (void) __attribute__((section(".ARM.Collect$$$$000000FF"))); +void _main_init (void) { + osKernelInitialize(); + set_main_stack(); + osThreadCreate(&os_thread_def_main, NULL); + osKernelStart(); + for (;;); +} +#else + +/* The single memory model is checking for stack collision at run time, verifing + that the heap pointer is underneath the stack pointer. + + With the RTOS there is not only one stack above the heap, there are multiple + stacks and some of them are underneath the heap pointer. +*/ +#pragma import(__use_two_region_memory) + +__asm void __rt_entry (void) { + + IMPORT __user_setup_stackheap + IMPORT __rt_lib_init + IMPORT os_thread_def_main + IMPORT osKernelInitialize + IMPORT set_main_stack + IMPORT osKernelStart + IMPORT osThreadCreate + IMPORT exit + + BL __user_setup_stackheap + MOV R1,R2 + BL __rt_lib_init + BL osKernelInitialize + BL set_main_stack + LDR R0,=os_thread_def_main + MOVS R1,#0 + BL osThreadCreate + BL osKernelStart + BL exit + + ALIGN +} +#endif + +#elif defined (__GNUC__) + +#ifdef __CS3__ + +/* CS3 start_c routine. + * + * Copyright (c) 2006, 2007 CodeSourcery Inc + * + * The authors hereby grant permission to use, copy, modify, distribute, + * and license this software and its documentation for any purpose, provided + * that existing copyright notices are retained in all copies and that this + * notice is included verbatim in any distributions. No written agreement, + * license, or royalty fee is required for any of the authorized uses. + * Modifications to this software may be copyrighted by their authors + * and need not follow the licensing terms described here, provided that + * the new terms are clearly indicated on the first page of each file where + * they apply. + */ + +#include "cs3.h" + +extern void __libc_init_array (void); + +__attribute ((noreturn)) void __cs3_start_c (void){ + unsigned regions = __cs3_region_num; + const struct __cs3_region *rptr = __cs3_regions; + + /* Initialize memory */ + for (regions = __cs3_region_num, rptr = __cs3_regions; regions--; rptr++) { + long long *src = (long long *)rptr->init; + long long *dst = (long long *)rptr->data; + unsigned limit = rptr->init_size; + unsigned count; + + if (src != dst) + for (count = 0; count != limit; count += sizeof (long long)) + *dst++ = *src++; + else + dst = (long long *)((char *)dst + limit); + limit = rptr->zero_size; + for (count = 0; count != limit; count += sizeof (long long)) + *dst++ = 0; + } + + /* Run initializers. */ + __libc_init_array (); + + osKernelInitialize(); + osThreadCreate(&os_thread_def_main, NULL); + osKernelStart(); + for (;;); +} + +#else + +__attribute__((naked)) void software_init_hook (void) { + __asm ( + ".syntax unified\n" + ".thumb\n" + "movs r0,#0\n" + "movs r1,#0\n" + "mov r4,r0\n" + "mov r5,r1\n" + "ldr r0,= __libc_fini_array\n" + "bl atexit\n" + "bl __libc_init_array\n" + "mov r0,r4\n" + "mov r1,r5\n" + "bl osKernelInitialize\n" + "ldr r0,=os_thread_def_main\n" + "movs r1,#0\n" + "bl osThreadCreate\n" + "bl osKernelStart\n" + "bl exit\n" + ); +} + +#endif + +#elif defined (__ICCARM__) + +extern int __low_level_init(void); +extern void __iar_data_init3(void); +extern void exit(int arg); + +__noreturn __stackless void __cmain(void) { + int a; + + if (__low_level_init() != 0) { + __iar_data_init3(); + } + osKernelInitialize(); + osThreadCreate(&os_thread_def_main, NULL); + a = osKernelStart(); + exit(a); +} + +#endif + + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/RTX_Conf_CM.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,241 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RTX_Conf_CM.C + * Purpose: Configuration of CMSIS RTX Kernel for Cortex-M + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +#include "cmsis_os.h" + + +/*---------------------------------------------------------------------------- + * RTX User configuration part BEGIN + *---------------------------------------------------------------------------*/ + +//-------- <<< Use Configuration Wizard in Context Menu >>> ----------------- +// +// <h>Thread Configuration +// ======================= +// +// <o>Number of concurrent running threads <0-250> +// <i> Defines max. number of threads that will run at the same time. +// counting "main", but not counting "osTimerThread" +// <i> Default: 6 +#ifndef OS_TASKCNT +# if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) +# define OS_TASKCNT 14 +# elif defined(TARGET_LPC11U24) +# define OS_TASKCNT 6 +# endif +#endif + +// <o>Scheduler (+ interrupts) stack size [bytes] <64-4096:8><#/4> +#ifndef OS_SCHEDULERSTKSIZE +# if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) +# define OS_SCHEDULERSTKSIZE 256 +# elif defined(TARGET_LPC11U24) +# define OS_SCHEDULERSTKSIZE 128 +# endif +#endif + +// <o>Idle stack size [bytes] <64-4096:8><#/4> +// <i> Defines default stack size for the Idle thread. +#ifndef OS_IDLESTKSIZE + #define OS_IDLESTKSIZE 128 +#endif + +// <o>Timer Thread stack size [bytes] <64-4096:8><#/4> +// <i> Defines stack size for Timer thread. +// <i> Default: 200 +#ifndef OS_TIMERSTKSZ + #define OS_TIMERSTKSZ WORDS_STACK_SIZE +#endif + +// <q>Check for stack overflow +// <i> Includes the stack checking code for stack overflow. +// <i> Note that additional code reduces the Kernel performance. +#ifndef OS_STKCHECK + #define OS_STKCHECK 1 +#endif + +// <o>Processor mode for thread execution +// <0=> Unprivileged mode +// <1=> Privileged mode +// <i> Default: Privileged mode +#ifndef OS_RUNPRIV + #define OS_RUNPRIV 1 +#endif + +// </h> +// <h>SysTick Timer Configuration +// ============================== +// +// <o>Timer clock value [Hz] <1-1000000000> +// <i> Defines the timer clock value. +// <i> Default: 6000000 (6MHz) +#ifndef OS_CLOCK +# if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) +# define OS_CLOCK 96000000 +# elif defined(TARGET_LPC11U24) +# define OS_CLOCK 48000000 +# endif +#endif + +// <o>Timer tick value [us] <1-1000000> +// <i> Defines the timer tick value. +// <i> Default: 1000 (1ms) +#ifndef OS_TICK + #define OS_TICK 1000 +#endif + +// </h> + +// <h>System Configuration +// ======================= +// +// <e>Round-Robin Thread switching +// =============================== +// +// <i> Enables Round-Robin Thread switching. +#ifndef OS_ROBIN + #define OS_ROBIN 1 +#endif + +// <o>Round-Robin Timeout [ticks] <1-1000> +// <i> Defines how long a thread will execute before a thread switch. +// <i> Default: 5 +#ifndef OS_ROBINTOUT + #define OS_ROBINTOUT 5 +#endif + +// </e> + +// <e>User Timers +// ============== +// <i> Enables user Timers +#ifndef OS_TIMERS + #define OS_TIMERS 1 +#endif + +// <o>Timer Thread Priority +// <1=> Low +// <2=> Below Normal +// <3=> Normal +// <4=> Above Normal +// <5=> High +// <6=> Realtime (highest) +// <i> Defines priority for Timer Thread +// <i> Default: High +#ifndef OS_TIMERPRIO + #define OS_TIMERPRIO 5 +#endif + +// <o>Timer Callback Queue size <1-32> +// <i> Number of concurrent active timer callback functions. +// <i> Default: 4 +#ifndef OS_TIMERCBQSZ + #define OS_TIMERCBQS 4 +#endif + +// </e> + +// <o>ISR FIFO Queue size<4=> 4 entries <8=> 8 entries +// <12=> 12 entries <16=> 16 entries +// <24=> 24 entries <32=> 32 entries +// <48=> 48 entries <64=> 64 entries +// <96=> 96 entries +// <i> ISR functions store requests to this buffer, +// <i> when they are called from the interrupt handler. +// <i> Default: 16 entries +#ifndef OS_FIFOSZ + #define OS_FIFOSZ 16 +#endif + +// </h> + +//------------- <<< end of configuration section >>> ----------------------- + +// Standard library system mutexes +// =============================== +// Define max. number system mutexes that are used to protect +// the arm standard runtime library. For microlib they are not used. +#ifndef OS_MUTEXCNT + #define OS_MUTEXCNT 12 +#endif + +/*---------------------------------------------------------------------------- + * RTX User configuration part END + *---------------------------------------------------------------------------*/ + +#define OS_TRV ((uint32_t)(((double)OS_CLOCK*(double)OS_TICK)/1E6)-1) + + +/*---------------------------------------------------------------------------- + * OS Idle daemon + *---------------------------------------------------------------------------*/ +void os_idle_demon (void) { + /* The idle demon is a system thread, running when no other thread is */ + /* ready to run. */ + + /* Sleep: ideally, we should put the chip to sleep. + Unfortunately, this usually requires disconnecting the interface chip (debugger). + This can be done, but it would break the local file system. + */ + for (;;) { + // sleep(); + } +} + +/*---------------------------------------------------------------------------- + * RTX Errors + *---------------------------------------------------------------------------*/ +extern void mbed_die(void); + +void os_error (uint32_t err_code) { + /* This function is called when a runtime error is detected. Parameter */ + /* 'err_code' holds the runtime error code (defined in RTX_Config.h). */ + mbed_die(); +} + +void sysThreadError(osStatus status) { + if (status != osOK) { + mbed_die(); + } +} + +/*---------------------------------------------------------------------------- + * RTX Configuration Functions + *---------------------------------------------------------------------------*/ + +#include "RTX_CM_lib.h" + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/RTX_Config.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,72 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RTX_CONFIG.H + * Purpose: Exported functions of RTX_Config.c + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + + +/* Error Codes */ +#define OS_ERR_STK_OVF 1 +#define OS_ERR_FIFO_OVF 2 +#define OS_ERR_MBX_OVF 3 + +/* Definitions */ +#define BOX_ALIGN_8 0x80000000 +#define _declare_box(pool,size,cnt) U32 pool[(((size)+3)/4)*(cnt) + 3] +#define _declare_box8(pool,size,cnt) U64 pool[(((size)+7)/8)*(cnt) + 2] +#define _init_box8(pool,size,bsize) _init_box (pool,size,(bsize) | BOX_ALIGN_8) + +/* Variables */ +extern U32 idle_task_stack[]; +extern U32 os_fifo[]; +extern void *os_active_TCB[]; + +/* Constants */ +extern U16 const os_maxtaskrun; +extern U32 const os_trv; +extern U8 const os_flags; +extern U32 const os_rrobin; +extern U32 const os_clockrate; +extern U32 const os_timernum; +extern U16 const idle_task_stack_size; + +extern U8 const os_fifo_size; + +/* Functions */ +extern void os_idle_demon (void); +extern int os_tick_init (void); +extern void os_tick_irqack (void); +extern void os_tmr_call (U16 info); +extern void os_error (U32 err_code); + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/cmsis_os.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,774 @@ +/* ---------------------------------------------------------------------- + * Copyright (C) 2012 ARM Limited. All rights reserved. + * + * $Date: 5. June 2012 + * $Revision: V1.01 + * + * Project: CMSIS-RTOS API + * Title: cmsis_os.h RTX header file + * + * Version 0.02 + * Initial Proposal Phase + * Version 0.03 + * osKernelStart added, optional feature: main started as thread + * osSemaphores have standard behavior + * osTimerCreate does not start the timer, added osTimerStart + * osThreadPass is renamed to osThreadYield + * Version 1.01 + * Support for C++ interface + * - const attribute removed from the osXxxxDef_t typedef's + * - const attribute added to the osXxxxDef macros + * Added: osTimerDelete, osMutexDelete, osSemaphoreDelete + * Added: osKernelInitialize + * -------------------------------------------------------------------- */ + +/** +\page cmsis_os_h Header File Template: cmsis_os.h + +The file \b cmsis_os.h is a template header file for a CMSIS-RTOS compliant Real-Time Operating System (RTOS). +Each RTOS that is compliant with CMSIS-RTOS shall provide a specific \b cmsis_os.h header file that represents +its implementation. + +The file cmsis_os.h contains: + - CMSIS-RTOS API function definitions + - struct definitions for parameters and return types + - status and priority values used by CMSIS-RTOS API functions + - macros for defining threads and other kernel objects + + +<b>Name conventions and header file modifications</b> + +All definitions are prefixed with \b os to give an unique name space for CMSIS-RTOS functions. +Definitions that are prefixed \b os_ are not used in the application code but local to this header file. +All definitions and functions that belong to a module are grouped and have a common prefix, i.e. \b osThread. + +Definitions that are marked with <b>CAN BE CHANGED</b> can be adapted towards the needs of the actual CMSIS-RTOS implementation. +These definitions can be specific to the underlying RTOS kernel. + +Definitions that are marked with <b>MUST REMAIN UNCHANGED</b> cannot be altered. Otherwise the CMSIS-RTOS implementation is no longer +compliant to the standard. Note that some functions are optional and need not to be provided by every CMSIS-RTOS implementation. + + +<b>Function calls from interrupt service routines</b> + +The following CMSIS-RTOS functions can be called from threads and interrupt service routines (ISR): + - \ref osSignalSet + - \ref osSemaphoreRelease + - \ref osPoolAlloc, \ref osPoolCAlloc, \ref osPoolFree + - \ref osMessagePut, \ref osMessageGet + - \ref osMailAlloc, \ref osMailCAlloc, \ref osMailGet, \ref osMailPut, \ref osMailFree + +Functions that cannot be called from an ISR are verifying the interrupt status and return in case that they are called +from an ISR context the status code \b osErrorISR. In some implementations this condition might be caught using the HARD FAULT vector. + +Some CMSIS-RTOS implementations support CMSIS-RTOS function calls from multiple ISR at the same time. +If this is impossible, the CMSIS-RTOS rejects calls by nested ISR functions with the status code \b osErrorISRRecursive. + + +<b>Define and reference object definitions</b> + +With <b>\#define osObjectsExternal</b> objects are defined as external symbols. This allows to create a consistent header file +that is used throughout a project as shown below: + +<i>Header File</i> +\code +#include <cmsis_os.h> // CMSIS RTOS header file + +// Thread definition +extern void thread_sample (void const *argument); // function prototype +osThreadDef (thread_sample, osPriorityBelowNormal, 1, 100); + +// Pool definition +osPoolDef(MyPool, 10, long); +\endcode + + +This header file defines all objects when included in a C/C++ source file. When <b>\#define osObjectsExternal</b> is +present before the header file, the objects are defined as external symbols. A single consistent header file can therefore be +used throughout the whole project. + +<i>Example</i> +\code +#include "osObjects.h" // Definition of the CMSIS-RTOS objects +\endcode + +\code +#define osObjectExternal // Objects will be defined as external symbols +#include "osObjects.h" // Reference to the CMSIS-RTOS objects +\endcode + +*/ + +#ifndef _CMSIS_OS_H +#define _CMSIS_OS_H + +/// \note MUST REMAIN UNCHANGED: \b osCMSIS identifies the CMSIS-RTOS API version. +#define osCMSIS 0x10001 ///< API version (main [31:16] .sub [15:0]) + +/// \note CAN BE CHANGED: \b osCMSIS_KERNEL identifies the underlying RTOS kernel and version number. +#define osCMSIS_RTX ((4<<16)|61) ///< RTOS identification and version (main [31:16] .sub [15:0]) + +/// \note MUST REMAIN UNCHANGED: \b osKernelSystemId shall be consistent in every CMSIS-RTOS. +#define osKernelSystemId "RTX V4.61" ///< RTOS identification string + + +#define CMSIS_OS_RTX + +// The stack space occupied is mainly dependent on the underling C standard library +#if defined(TOOLCHAIN_GCC_ARM) || defined(TOOLCHAIN_CS_ARM) || defined(TOOLCHAIN_CR_ARM) +# define WORDS_STACK_SIZE 512 +#elif defined(TOOLCHAIN_ARM) +# define WORDS_STACK_SIZE 512 +#elif defined(TOOLCHAIN_uARM) +# define WORDS_STACK_SIZE 128 +#endif + +#define DEFAULT_STACK_SIZE (WORDS_STACK_SIZE*4) + + +/// \note MUST REMAIN UNCHANGED: \b osFeature_xxx shall be consistent in every CMSIS-RTOS. +#define osFeature_MainThread 1 ///< main thread 1=main can be thread, 0=not available +#define osFeature_Pool 1 ///< Memory Pools: 1=available, 0=not available +#define osFeature_MailQ 1 ///< Mail Queues: 1=available, 0=not available +#define osFeature_MessageQ 1 ///< Message Queues: 1=available, 0=not available +#define osFeature_Signals 16 ///< maximum number of Signal Flags available per thread +#define osFeature_Semaphore 65535 ///< maximum count for \ref osSemaphoreCreate function +#define osFeature_Wait 0 ///< osWait function: 1=available, 0=not available + +#if defined (__CC_ARM) +#define os_InRegs __value_in_regs // Compiler specific: force struct in registers +#else +#define os_InRegs +#endif + +#include <stdint.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "os_tcb.h" + +// ==== Enumeration, structures, defines ==== + +/// Priority used for thread control. +/// \note MUST REMAIN UNCHANGED: \b osPriority shall be consistent in every CMSIS-RTOS. +typedef enum { + osPriorityIdle = -3, ///< priority: idle (lowest) + osPriorityLow = -2, ///< priority: low + osPriorityBelowNormal = -1, ///< priority: below normal + osPriorityNormal = 0, ///< priority: normal (default) + osPriorityAboveNormal = +1, ///< priority: above normal + osPriorityHigh = +2, ///< priority: high + osPriorityRealtime = +3, ///< priority: realtime (highest) + osPriorityError = 0x84 ///< system cannot determine priority or thread has illegal priority +} osPriority; + +/// Timeout value. +/// \note MUST REMAIN UNCHANGED: \b osWaitForever shall be consistent in every CMSIS-RTOS. +#define osWaitForever 0xFFFFFFFF ///< wait forever timeout value + +/// Status code values returned by CMSIS-RTOS functions. +/// \note MUST REMAIN UNCHANGED: \b osStatus shall be consistent in every CMSIS-RTOS. +typedef enum { + osOK = 0, ///< function completed; no error or event occurred. + osEventSignal = 0x08, ///< function completed; signal event occurred. + osEventMessage = 0x10, ///< function completed; message event occurred. + osEventMail = 0x20, ///< function completed; mail event occurred. + osEventTimeout = 0x40, ///< function completed; timeout occurred. + osErrorParameter = 0x80, ///< parameter error: a mandatory parameter was missing or specified an incorrect object. + osErrorResource = 0x81, ///< resource not available: a specified resource was not available. + osErrorTimeoutResource = 0xC1, ///< resource not available within given time: a specified resource was not available within the timeout period. + osErrorISR = 0x82, ///< not allowed in ISR context: the function cannot be called from interrupt service routines. + osErrorISRRecursive = 0x83, ///< function called multiple times from ISR with same object. + osErrorPriority = 0x84, ///< system cannot determine priority or thread has illegal priority. + osErrorNoMemory = 0x85, ///< system is out of memory: it was impossible to allocate or reserve memory for the operation. + osErrorValue = 0x86, ///< value of a parameter is out of range. + osErrorOS = 0xFF, ///< unspecified RTOS error: run-time error but no other error message fits. + os_status_reserved = 0x7FFFFFFF ///< prevent from enum down-size compiler optimization. +} osStatus; + + +/// Timer type value for the timer definition. +/// \note MUST REMAIN UNCHANGED: \b os_timer_type shall be consistent in every CMSIS-RTOS. +typedef enum { + osTimerOnce = 0, ///< one-shot timer + osTimerPeriodic = 1 ///< repeating timer +} os_timer_type; + +/// Entry point of a thread. +/// \note MUST REMAIN UNCHANGED: \b os_pthread shall be consistent in every CMSIS-RTOS. +typedef void (*os_pthread) (void const *argument); + +/// Entry point of a timer call back function. +/// \note MUST REMAIN UNCHANGED: \b os_ptimer shall be consistent in every CMSIS-RTOS. +typedef void (*os_ptimer) (void const *argument); + +// >>> the following data type definitions may shall adapted towards a specific RTOS + +/// Thread ID identifies the thread (pointer to a thread control block). +/// \note CAN BE CHANGED: \b os_thread_cb is implementation specific in every CMSIS-RTOS. +typedef struct os_thread_cb *osThreadId; + +/// Timer ID identifies the timer (pointer to a timer control block). +/// \note CAN BE CHANGED: \b os_timer_cb is implementation specific in every CMSIS-RTOS. +typedef struct os_timer_cb *osTimerId; + +/// Mutex ID identifies the mutex (pointer to a mutex control block). +/// \note CAN BE CHANGED: \b os_mutex_cb is implementation specific in every CMSIS-RTOS. +typedef struct os_mutex_cb *osMutexId; + +/// Semaphore ID identifies the semaphore (pointer to a semaphore control block). +/// \note CAN BE CHANGED: \b os_semaphore_cb is implementation specific in every CMSIS-RTOS. +typedef struct os_semaphore_cb *osSemaphoreId; + +/// Pool ID identifies the memory pool (pointer to a memory pool control block). +/// \note CAN BE CHANGED: \b os_pool_cb is implementation specific in every CMSIS-RTOS. +typedef struct os_pool_cb *osPoolId; + +/// Message ID identifies the message queue (pointer to a message queue control block). +/// \note CAN BE CHANGED: \b os_messageQ_cb is implementation specific in every CMSIS-RTOS. +typedef struct os_messageQ_cb *osMessageQId; + +/// Mail ID identifies the mail queue (pointer to a mail queue control block). +/// \note CAN BE CHANGED: \b os_mailQ_cb is implementation specific in every CMSIS-RTOS. +typedef struct os_mailQ_cb *osMailQId; + + +/// Thread Definition structure contains startup information of a thread. +/// \note CAN BE CHANGED: \b os_thread_def is implementation specific in every CMSIS-RTOS. +typedef struct os_thread_def { + os_pthread pthread; ///< start address of thread function + osPriority tpriority; ///< initial thread priority + uint32_t stacksize; ///< stack size requirements in bytes + unsigned char *stack_pointer; ///< pointer to the stack memory block + struct OS_TCB tcb; +} osThreadDef_t; + +/// Timer Definition structure contains timer parameters. +/// \note CAN BE CHANGED: \b os_timer_def is implementation specific in every CMSIS-RTOS. +typedef struct os_timer_def { + os_ptimer ptimer; ///< start address of a timer function + void *timer; ///< pointer to internal data +} osTimerDef_t; + +/// Mutex Definition structure contains setup information for a mutex. +/// \note CAN BE CHANGED: \b os_mutex_def is implementation specific in every CMSIS-RTOS. +typedef struct os_mutex_def { + void *mutex; ///< pointer to internal data +} osMutexDef_t; + +/// Semaphore Definition structure contains setup information for a semaphore. +/// \note CAN BE CHANGED: \b os_semaphore_def is implementation specific in every CMSIS-RTOS. +typedef struct os_semaphore_def { + void *semaphore; ///< pointer to internal data +} osSemaphoreDef_t; + +/// Definition structure for memory block allocation. +/// \note CAN BE CHANGED: \b os_pool_def is implementation specific in every CMSIS-RTOS. +typedef struct os_pool_def { + uint32_t pool_sz; ///< number of items (elements) in the pool + uint32_t item_sz; ///< size of an item + void *pool; ///< pointer to memory for pool +} osPoolDef_t; + +/// Definition structure for message queue. +/// \note CAN BE CHANGED: \b os_messageQ_def is implementation specific in every CMSIS-RTOS. +typedef struct os_messageQ_def { + uint32_t queue_sz; ///< number of elements in the queue + void *pool; ///< memory array for messages +} osMessageQDef_t; + +/// Definition structure for mail queue. +/// \note CAN BE CHANGED: \b os_mailQ_def is implementation specific in every CMSIS-RTOS. +typedef struct os_mailQ_def { + uint32_t queue_sz; ///< number of elements in the queue + uint32_t item_sz; ///< size of an item + void *pool; ///< memory array for mail +} osMailQDef_t; + +/// Event structure contains detailed information about an event. +/// \note MUST REMAIN UNCHANGED: \b os_event shall be consistent in every CMSIS-RTOS. +/// However the struct may be extended at the end. +typedef struct { + osStatus status; ///< status code: event or error information + union { + uint32_t v; ///< message as 32-bit value + void *p; ///< message or mail as void pointer + int32_t signals; ///< signal flags + } value; ///< event value + union { + osMailQId mail_id; ///< mail id obtained by \ref osMailCreate + osMessageQId message_id; ///< message id obtained by \ref osMessageCreate + } def; ///< event definition +} osEvent; + + +// ==== Kernel Control Functions ==== + +/// Initialize the RTOS Kernel for creating objects. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osKernelInitialize shall be consistent in every CMSIS-RTOS. +osStatus osKernelInitialize (void); + +/// Start the RTOS Kernel. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osKernelStart shall be consistent in every CMSIS-RTOS. +osStatus osKernelStart (void); + +/// Check if the RTOS kernel is already started. +/// \note MUST REMAIN UNCHANGED: \b osKernelRunning shall be consistent in every CMSIS-RTOS. +/// \return 0 RTOS is not started, 1 RTOS is started. +int32_t osKernelRunning(void); + + +// ==== Thread Management ==== + +/// Create a Thread Definition with function, priority, and stack requirements. +/// \param name name of the thread function. +/// \param priority initial priority of the thread function. +/// \param stacksz stack size (in bytes) requirements for the thread function. +/// \note CAN BE CHANGED: The parameters to \b osThreadDef shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#if defined (osObjectsExternal) // object is external +#define osThreadDef(name, priority, stacksz) \ +extern osThreadDef_t os_thread_def_##name +#else // define the object +#define osThreadDef(name, priority, stacksz) \ +unsigned char os_thread_def_stack_##name [stacksz]; \ +osThreadDef_t os_thread_def_##name = \ +{ (name), (priority), (stacksz), (os_thread_def_stack_##name)} +#endif + +/// Access a Thread definition. +/// \param name name of the thread definition object. +/// \note CAN BE CHANGED: The parameter to \b osThread shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#define osThread(name) \ +&os_thread_def_##name + +/// Create a thread and add it to Active Threads and set it to state READY. +/// \param[in] thread_def thread definition referenced with \ref osThread. +/// \param[in] argument pointer that is passed to the thread function as start argument. +/// \return thread ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osThreadCreate shall be consistent in every CMSIS-RTOS. +osThreadId osThreadCreate (osThreadDef_t *thread_def, void *argument); + +/// Return the thread ID of the current running thread. +/// \return thread ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osThreadGetId shall be consistent in every CMSIS-RTOS. +osThreadId osThreadGetId (void); + +/// Terminate execution of a thread and remove it from Active Threads. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osThreadTerminate shall be consistent in every CMSIS-RTOS. +osStatus osThreadTerminate (osThreadId thread_id); + +/// Pass control to next thread that is in state \b READY. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osThreadYield shall be consistent in every CMSIS-RTOS. +osStatus osThreadYield (void); + +/// Change priority of an active thread. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \param[in] priority new priority value for the thread function. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osThreadSetPriority shall be consistent in every CMSIS-RTOS. +osStatus osThreadSetPriority (osThreadId thread_id, osPriority priority); + +/// Get current priority of an active thread. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \return current priority value of the thread function. +/// \note MUST REMAIN UNCHANGED: \b osThreadGetPriority shall be consistent in every CMSIS-RTOS. +osPriority osThreadGetPriority (osThreadId thread_id); + + +// ==== Generic Wait Functions ==== + +/// Wait for Timeout (Time Delay). +/// \param[in] millisec time delay value +/// \return status code that indicates the execution status of the function. +osStatus osDelay (uint32_t millisec); + +#if (defined (osFeature_Wait) && (osFeature_Wait != 0)) // Generic Wait available + +/// Wait for Signal, Message, Mail, or Timeout. +/// \param[in] millisec timeout value or 0 in case of no time-out +/// \return event that contains signal, message, or mail information or error code. +/// \note MUST REMAIN UNCHANGED: \b osWait shall be consistent in every CMSIS-RTOS. +os_InRegs osEvent osWait (uint32_t millisec); + +#endif // Generic Wait available + + +// ==== Timer Management Functions ==== +/// Define a Timer object. +/// \param name name of the timer object. +/// \param function name of the timer call back function. +/// \note CAN BE CHANGED: The parameter to \b osTimerDef shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#if defined (osObjectsExternal) // object is external +#define osTimerDef(name, function) \ +extern osTimerDef_t os_timer_def_##name +#else // define the object +#define osTimerDef(name, function) \ +uint32_t os_timer_cb_##name[5]; \ +osTimerDef_t os_timer_def_##name = \ +{ (function), (os_timer_cb_##name) } +#endif + +/// Access a Timer definition. +/// \param name name of the timer object. +/// \note CAN BE CHANGED: The parameter to \b osTimer shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#define osTimer(name) \ +&os_timer_def_##name + +/// Create a timer. +/// \param[in] timer_def timer object referenced with \ref osTimer. +/// \param[in] type osTimerOnce for one-shot or osTimerPeriodic for periodic behavior. +/// \param[in] argument argument to the timer call back function. +/// \return timer ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osTimerCreate shall be consistent in every CMSIS-RTOS. +osTimerId osTimerCreate (osTimerDef_t *timer_def, os_timer_type type, void *argument); + +/// Start or restart a timer. +/// \param[in] timer_id timer ID obtained by \ref osTimerCreate. +/// \param[in] millisec time delay value of the timer. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osTimerStart shall be consistent in every CMSIS-RTOS. +osStatus osTimerStart (osTimerId timer_id, uint32_t millisec); + +/// Stop the timer. +/// \param[in] timer_id timer ID obtained by \ref osTimerCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osTimerStop shall be consistent in every CMSIS-RTOS. +osStatus osTimerStop (osTimerId timer_id); + +/// Delete a timer that was created by \ref osTimerCreate. +/// \param[in] timer_id timer ID obtained by \ref osTimerCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osTimerDelete shall be consistent in every CMSIS-RTOS. +osStatus osTimerDelete (osTimerId timer_id); + + +// ==== Signal Management ==== + +/// Set the specified Signal Flags of an active thread. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \param[in] signals specifies the signal flags of the thread that should be set. +/// \return previous signal flags of the specified thread or 0x80000000 in case of incorrect parameters. +/// \note MUST REMAIN UNCHANGED: \b osSignalSet shall be consistent in every CMSIS-RTOS. +int32_t osSignalSet (osThreadId thread_id, int32_t signals); + +/// Clear the specified Signal Flags of an active thread. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \param[in] signals specifies the signal flags of the thread that shall be cleared. +/// \return previous signal flags of the specified thread or 0x80000000 in case of incorrect parameters. +/// \note MUST REMAIN UNCHANGED: \b osSignalClear shall be consistent in every CMSIS-RTOS. +int32_t osSignalClear (osThreadId thread_id, int32_t signals); + +/// Get Signal Flags status of an active thread. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \return previous signal flags of the specified thread or 0x80000000 in case of incorrect parameters. +/// \note MUST REMAIN UNCHANGED: \b osSignalGet shall be consistent in every CMSIS-RTOS. +int32_t osSignalGet (osThreadId thread_id); + +/// Wait for one or more Signal Flags to become signaled for the current \b RUNNING thread. +/// \param[in] signals wait until all specified signal flags set or 0 for any single signal flag. +/// \param[in] millisec timeout value or 0 in case of no time-out. +/// \return event flag information or error code. +/// \note MUST REMAIN UNCHANGED: \b osSignalWait shall be consistent in every CMSIS-RTOS. +os_InRegs osEvent osSignalWait (int32_t signals, uint32_t millisec); + + +// ==== Mutex Management ==== + +/// Define a Mutex. +/// \param name name of the mutex object. +/// \note CAN BE CHANGED: The parameter to \b osMutexDef shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#if defined (osObjectsExternal) // object is external +#define osMutexDef(name) \ +extern osMutexDef_t os_mutex_def_##name +#else // define the object +#define osMutexDef(name) \ +uint32_t os_mutex_cb_##name[3]; \ +osMutexDef_t os_mutex_def_##name = { (os_mutex_cb_##name) } +#endif + +/// Access a Mutex definition. +/// \param name name of the mutex object. +/// \note CAN BE CHANGED: The parameter to \b osMutex shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#define osMutex(name) \ +&os_mutex_def_##name + +/// Create and Initialize a Mutex object. +/// \param[in] mutex_def mutex definition referenced with \ref osMutex. +/// \return mutex ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osMutexCreate shall be consistent in every CMSIS-RTOS. +osMutexId osMutexCreate (osMutexDef_t *mutex_def); + +/// Wait until a Mutex becomes available. +/// \param[in] mutex_id mutex ID obtained by \ref osMutexCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMutexWait shall be consistent in every CMSIS-RTOS. +osStatus osMutexWait (osMutexId mutex_id, uint32_t millisec); + +/// Release a Mutex that was obtained by \ref osMutexWait. +/// \param[in] mutex_id mutex ID obtained by \ref osMutexCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMutexRelease shall be consistent in every CMSIS-RTOS. +osStatus osMutexRelease (osMutexId mutex_id); + +/// Delete a Mutex that was created by \ref osMutexCreate. +/// \param[in] mutex_id mutex ID obtained by \ref osMutexCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMutexDelete shall be consistent in every CMSIS-RTOS. +osStatus osMutexDelete (osMutexId mutex_id); + + +// ==== Semaphore Management Functions ==== + +#if (defined (osFeature_Semaphore) && (osFeature_Semaphore != 0)) // Semaphore available + +/// Define a Semaphore object. +/// \param name name of the semaphore object. +/// \note CAN BE CHANGED: The parameter to \b osSemaphoreDef shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#if defined (osObjectsExternal) // object is external +#define osSemaphoreDef(name) \ +extern osSemaphoreDef_t os_semaphore_def_##name +#else // define the object +#define osSemaphoreDef(name) \ +uint32_t os_semaphore_cb_##name[2]; \ +osSemaphoreDef_t os_semaphore_def_##name = { (os_semaphore_cb_##name) } +#endif + +/// Access a Semaphore definition. +/// \param name name of the semaphore object. +/// \note CAN BE CHANGED: The parameter to \b osSemaphore shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#define osSemaphore(name) \ +&os_semaphore_def_##name + +/// Create and Initialize a Semaphore object used for managing resources. +/// \param[in] semaphore_def semaphore definition referenced with \ref osSemaphore. +/// \param[in] count number of available resources. +/// \return semaphore ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osSemaphoreCreate shall be consistent in every CMSIS-RTOS. +osSemaphoreId osSemaphoreCreate (osSemaphoreDef_t *semaphore_def, int32_t count); + +/// Wait until a Semaphore token becomes available. +/// \param[in] semaphore_id semaphore object referenced with \ref osSemaphoreCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out. +/// \return number of available tokens, or -1 in case of incorrect parameters. +/// \note MUST REMAIN UNCHANGED: \b osSemaphoreWait shall be consistent in every CMSIS-RTOS. +int32_t osSemaphoreWait (osSemaphoreId semaphore_id, uint32_t millisec); + +/// Release a Semaphore token. +/// \param[in] semaphore_id semaphore object referenced with \ref osSemaphoreCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osSemaphoreRelease shall be consistent in every CMSIS-RTOS. +osStatus osSemaphoreRelease (osSemaphoreId semaphore_id); + +/// Delete a Semaphore that was created by \ref osSemaphoreCreate. +/// \param[in] semaphore_id semaphore object referenced with \ref osSemaphoreCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osSemaphoreDelete shall be consistent in every CMSIS-RTOS. +osStatus osSemaphoreDelete (osSemaphoreId semaphore_id); + +#endif // Semaphore available + + +// ==== Memory Pool Management Functions ==== + +#if (defined (osFeature_Pool) && (osFeature_Pool != 0)) // Memory Pool Management available + +/// \brief Define a Memory Pool. +/// \param name name of the memory pool. +/// \param no maximum number of blocks (objects) in the memory pool. +/// \param type data type of a single block (object). +/// \note CAN BE CHANGED: The parameter to \b osPoolDef shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#if defined (osObjectsExternal) // object is external +#define osPoolDef(name, no, type) \ +extern osPoolDef_t os_pool_def_##name +#else // define the object +#define osPoolDef(name, no, type) \ +uint32_t os_pool_m_##name[3+((sizeof(type)+3)/4)*(no)]; \ +osPoolDef_t os_pool_def_##name = \ +{ (no), sizeof(type), (os_pool_m_##name) } +#endif + +/// \brief Access a Memory Pool definition. +/// \param name name of the memory pool +/// \note CAN BE CHANGED: The parameter to \b osPool shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#define osPool(name) \ +&os_pool_def_##name + +/// Create and Initialize a memory pool. +/// \param[in] pool_def memory pool definition referenced with \ref osPool. +/// \return memory pool ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osPoolCreate shall be consistent in every CMSIS-RTOS. +osPoolId osPoolCreate (osPoolDef_t *pool_def); + +/// Allocate a memory block from a memory pool. +/// \param[in] pool_id memory pool ID obtain referenced with \ref osPoolCreate. +/// \return address of the allocated memory block or NULL in case of no memory available. +/// \note MUST REMAIN UNCHANGED: \b osPoolAlloc shall be consistent in every CMSIS-RTOS. +void *osPoolAlloc (osPoolId pool_id); + +/// Allocate a memory block from a memory pool and set memory block to zero. +/// \param[in] pool_id memory pool ID obtain referenced with \ref osPoolCreate. +/// \return address of the allocated memory block or NULL in case of no memory available. +/// \note MUST REMAIN UNCHANGED: \b osPoolCAlloc shall be consistent in every CMSIS-RTOS. +void *osPoolCAlloc (osPoolId pool_id); + +/// Return an allocated memory block back to a specific memory pool. +/// \param[in] pool_id memory pool ID obtain referenced with \ref osPoolCreate. +/// \param[in] block address of the allocated memory block that is returned to the memory pool. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osPoolFree shall be consistent in every CMSIS-RTOS. +osStatus osPoolFree (osPoolId pool_id, void *block); + +#endif // Memory Pool Management available + + +// ==== Message Queue Management Functions ==== + +#if (defined (osFeature_MessageQ) && (osFeature_MessageQ != 0)) // Message Queues available + +/// \brief Create a Message Queue Definition. +/// \param name name of the queue. +/// \param queue_sz maximum number of messages in the queue. +/// \param type data type of a single message element (for debugger). +/// \note CAN BE CHANGED: The parameter to \b osMessageQDef shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#if defined (osObjectsExternal) // object is external +#define osMessageQDef(name, queue_sz, type) \ +extern osMessageQDef_t os_messageQ_def_##name +#else // define the object +#define osMessageQDef(name, queue_sz, type) \ +uint32_t os_messageQ_q_##name[4+(queue_sz)]; \ +osMessageQDef_t os_messageQ_def_##name = \ +{ (queue_sz), (os_messageQ_q_##name) } +#endif + +/// \brief Access a Message Queue Definition. +/// \param name name of the queue +/// \note CAN BE CHANGED: The parameter to \b osMessageQ shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#define osMessageQ(name) \ +&os_messageQ_def_##name + +/// Create and Initialize a Message Queue. +/// \param[in] queue_def queue definition referenced with \ref osMessageQ. +/// \param[in] thread_id thread ID (obtained by \ref osThreadCreate or \ref osThreadGetId) or NULL. +/// \return message queue ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osMessageCreate shall be consistent in every CMSIS-RTOS. +osMessageQId osMessageCreate (osMessageQDef_t *queue_def, osThreadId thread_id); + +/// Put a Message to a Queue. +/// \param[in] queue_id message queue ID obtained with \ref osMessageCreate. +/// \param[in] info message information. +/// \param[in] millisec timeout value or 0 in case of no time-out. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMessagePut shall be consistent in every CMSIS-RTOS. +osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec); + +/// Get a Message or Wait for a Message from a Queue. +/// \param[in] queue_id message queue ID obtained with \ref osMessageCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out. +/// \return event information that includes status code. +/// \note MUST REMAIN UNCHANGED: \b osMessageGet shall be consistent in every CMSIS-RTOS. +os_InRegs osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec); + +#endif // Message Queues available + + +// ==== Mail Queue Management Functions ==== + +#if (defined (osFeature_MailQ) && (osFeature_MailQ != 0)) // Mail Queues available + +/// \brief Create a Mail Queue Definition. +/// \param name name of the queue +/// \param queue_sz maximum number of messages in queue +/// \param type data type of a single message element +/// \note CAN BE CHANGED: The parameter to \b osMailQDef shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#if defined (osObjectsExternal) // object is external +#define osMailQDef(name, queue_sz, type) \ +extern osMailQDef_t os_mailQ_def_##name +#else // define the object +#define osMailQDef(name, queue_sz, type) \ +uint32_t os_mailQ_q_##name[4+(queue_sz)]; \ +uint32_t os_mailQ_m_##name[3+((sizeof(type)+3)/4)*(queue_sz)]; \ +void * os_mailQ_p_##name[2] = { (os_mailQ_q_##name), os_mailQ_m_##name }; \ +osMailQDef_t os_mailQ_def_##name = \ +{ (queue_sz), sizeof(type), (os_mailQ_p_##name) } +#endif + +/// \brief Access a Mail Queue Definition. +/// \param name name of the queue +/// \note CAN BE CHANGED: The parameter to \b osMailQ shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#define osMailQ(name) \ +&os_mailQ_def_##name + +/// Create and Initialize mail queue. +/// \param[in] queue_def reference to the mail queue definition obtain with \ref osMailQ +/// \param[in] thread_id thread ID (obtained by \ref osThreadCreate or \ref osThreadGetId) or NULL. +/// \return mail queue ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osMailCreate shall be consistent in every CMSIS-RTOS. +osMailQId osMailCreate (osMailQDef_t *queue_def, osThreadId thread_id); + +/// Allocate a memory block from a mail. +/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out +/// \return pointer to memory block that can be filled with mail or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osMailAlloc shall be consistent in every CMSIS-RTOS. +void *osMailAlloc (osMailQId queue_id, uint32_t millisec); + +/// Allocate a memory block from a mail and set memory block to zero. +/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out +/// \return pointer to memory block that can be filled with mail or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osMailCAlloc shall be consistent in every CMSIS-RTOS. +void *osMailCAlloc (osMailQId queue_id, uint32_t millisec); + +/// Put a mail to a queue. +/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate. +/// \param[in] mail memory block previously allocated with \ref osMailAlloc or \ref osMailCAlloc. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMailPut shall be consistent in every CMSIS-RTOS. +osStatus osMailPut (osMailQId queue_id, void *mail); + +/// Get a mail from a queue. +/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out +/// \return event that contains mail information or error code. +/// \note MUST REMAIN UNCHANGED: \b osMailGet shall be consistent in every CMSIS-RTOS. +os_InRegs osEvent osMailGet (osMailQId queue_id, uint32_t millisec); + +/// Free a memory block from a mail. +/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate. +/// \param[in] mail pointer to the memory block that was obtained with \ref osMailGet. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMailFree shall be consistent in every CMSIS-RTOS. +osStatus osMailFree (osMailQId queue_id, void *mail); + +#endif // Mail Queues available + + +#ifdef __cplusplus +} +#endif + +#endif // _CMSIS_OS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/os_tcb.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,50 @@ +#ifndef OS_TCB_H +#define OS_TCB_H + +/* Types */ +typedef char S8; +typedef unsigned char U8; +typedef short S16; +typedef unsigned short U16; +typedef int S32; +typedef unsigned int U32; +typedef long long S64; +typedef unsigned long long U64; +typedef unsigned char BIT; +typedef unsigned int BOOL; +typedef void (*FUNCP)(void); + +typedef struct OS_TCB { + /* General part: identical for all implementations. */ + U8 cb_type; /* Control Block Type */ + U8 state; /* Task state */ + U8 prio; /* Execution priority */ + U8 task_id; /* Task ID value for optimized TCB access */ + struct OS_TCB *p_lnk; /* Link pointer for ready/sem. wait list */ + struct OS_TCB *p_rlnk; /* Link pointer for sem./mbx lst backwards */ + struct OS_TCB *p_dlnk; /* Link pointer for delay list */ + struct OS_TCB *p_blnk; /* Link pointer for delay list backwards */ + U16 delta_time; /* Time until time out */ + U16 interval_time; /* Time interval for periodic waits */ + U16 events; /* Event flags */ + U16 waits; /* Wait flags */ + void **msg; /* Direct message passing when task waits */ + + /* Hardware dependant part: specific for CM processor */ + U8 stack_frame; /* Stack frame: 0=Basic, 1=Extended */ + U8 reserved; + U16 priv_stack; /* Private stack size in bytes */ + U32 tsk_stack; /* Current task Stack pointer (R13) */ + U32 *stack; /* Pointer to Task Stack memory block */ + + /* Library dependant part */ +#if defined (__CC_ARM) && !defined (__MICROLIB) + /* A memory space for arm standard library. */ + U32 std_libspace[96/4]; +#endif + + /* Task entry point used for uVision debugger */ + FUNCP ptask; /* Task entry address */ +} *P_TCB; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_CMSIS.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,1918 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: rt_CMSIS.c + * Purpose: CMSIS RTOS API + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +#define __CMSIS_GENERIC + +#if defined (__CORTEX_M4) || defined (__CORTEX_M4F) + #include "core_cm4.h" +#elif defined (__CORTEX_M3) + #include "core_cm3.h" +#elif defined (__CORTEX_M0) + #include "core_cm0.h" +#else + #error "Missing __CORTEX_Mx definition" +#endif + +#include "rt_TypeDef.h" +#include "RTX_Config.h" +#include "rt_System.h" +#include "rt_Task.h" +#include "rt_Event.h" +#include "rt_List.h" +#include "rt_Time.h" +#include "rt_Mutex.h" +#include "rt_Semaphore.h" +#include "rt_Mailbox.h" +#include "rt_MemBox.h" +#include "rt_HAL_CM.h" + +#define os_thread_cb OS_TCB + +#include "cmsis_os.h" + +#if (osFeature_Signals != 16) +#error Invalid "osFeature_Signals" value! +#endif +#if (osFeature_Semaphore > 65535) +#error Invalid "osFeature_Semaphore" value! +#endif +#if (osFeature_Wait != 0) +#error osWait not supported! +#endif + + +// ==== Enumeration, structures, defines ==== + +// Service Calls defines + +#if defined (__CC_ARM) /* ARM Compiler */ + +#define __NO_RETURN __declspec(noreturn) + +#define osEvent_type osEvent +#define osEvent_ret_status ret +#define osEvent_ret_value ret +#define osEvent_ret_msg ret +#define osEvent_ret_mail ret + +#define osCallback_type osCallback +#define osCallback_ret ret + +#define SVC_0_1(f,t,...) \ +__svc_indirect(0) t _##f (t(*)()); \ + t f (void); \ +__attribute__((always_inline)) \ +static __inline t __##f (void) { \ + return _##f(f); \ +} + +#define SVC_1_1(f,t,t1,...) \ +__svc_indirect(0) t _##f (t(*)(t1),t1); \ + t f (t1 a1); \ +__attribute__((always_inline)) \ +static __inline t __##f (t1 a1) { \ + return _##f(f,a1); \ +} + +#define SVC_2_1(f,t,t1,t2,...) \ +__svc_indirect(0) t _##f (t(*)(t1,t2),t1,t2); \ + t f (t1 a1, t2 a2); \ +__attribute__((always_inline)) \ +static __inline t __##f (t1 a1, t2 a2) { \ + return _##f(f,a1,a2); \ +} + +#define SVC_3_1(f,t,t1,t2,t3,...) \ +__svc_indirect(0) t _##f (t(*)(t1,t2,t3),t1,t2,t3); \ + t f (t1 a1, t2 a2, t3 a3); \ +__attribute__((always_inline)) \ +static __inline t __##f (t1 a1, t2 a2, t3 a3) { \ + return _##f(f,a1,a2,a3); \ +} + +#define SVC_4_1(f,t,t1,t2,t3,t4,...) \ +__svc_indirect(0) t _##f (t(*)(t1,t2,t3,t4),t1,t2,t3,t4); \ + t f (t1 a1, t2 a2, t3 a3, t4 a4); \ +__attribute__((always_inline)) \ +static __inline t __##f (t1 a1, t2 a2, t3 a3, t4 a4) { \ + return _##f(f,a1,a2,a3,a4); \ +} + +#define SVC_1_2 SVC_1_1 +#define SVC_1_3 SVC_1_1 +#define SVC_2_3 SVC_2_1 + +#elif defined (__GNUC__) /* GNU Compiler */ + +#define __NO_RETURN __attribute__((noreturn)) + +typedef uint32_t __attribute__((vector_size(8))) ret64; +typedef uint32_t __attribute__((vector_size(16))) ret128; + +#define RET_pointer __r0 +#define RET_int32_t __r0 +#define RET_osStatus __r0 +#define RET_osPriority __r0 +#define RET_osEvent {(osStatus)__r0, {(uint32_t)__r1}, {(void *)__r2}} +#define RET_osCallback {(void *)__r0, (void *)__r1} + +#define osEvent_type ret128 +#define osEvent_ret_status (ret128){ret.status} +#define osEvent_ret_value (ret128){ret.status, ret.value.v} +#define osEvent_ret_msg (ret128){ret.status, ret.value.v, (uint32_t)ret.def.message_id} +#define osEvent_ret_mail (ret128){ret.status, ret.value.v, (uint32_t)ret.def.mail_id} + +#define osCallback_type ret64 +#define osCallback_ret (ret64) {(uint32_t)ret.fp, (uint32_t)ret.arg} + +#define SVC_ArgN(n) \ + register int __r##n __asm("r"#n); + +#define SVC_ArgR(n,t,a) \ + register t __r##n __asm("r"#n) = a; + +#define SVC_Arg0() \ + SVC_ArgN(0) \ + SVC_ArgN(1) \ + SVC_ArgN(2) \ + SVC_ArgN(3) + +#define SVC_Arg1(t1) \ + SVC_ArgR(0,t1,a1) \ + SVC_ArgN(1) \ + SVC_ArgN(2) \ + SVC_ArgN(3) + +#define SVC_Arg2(t1,t2) \ + SVC_ArgR(0,t1,a1) \ + SVC_ArgR(1,t2,a2) \ + SVC_ArgN(2) \ + SVC_ArgN(3) + +#define SVC_Arg3(t1,t2,t3) \ + SVC_ArgR(0,t1,a1) \ + SVC_ArgR(1,t2,a2) \ + SVC_ArgR(2,t3,a3) \ + SVC_ArgN(3) + +#define SVC_Arg4(t1,t2,t3,t4) \ + SVC_ArgR(0,t1,a1) \ + SVC_ArgR(1,t2,a2) \ + SVC_ArgR(2,t3,a3) \ + SVC_ArgR(3,t4,a4) + +#if (defined (__CORTEX_M0)) +#define SVC_Call(f) \ + __asm volatile \ + ( \ + "ldr r7,="#f"\n\t" \ + "mov r12,r7\n\t" \ + "svc 0" \ + : "=r" (__r0), "=r" (__r1), "=r" (__r2), "=r" (__r3) \ + : "r" (__r0), "r" (__r1), "r" (__r2), "r" (__r3) \ + : "r7", "r12", "lr", "cc" \ + ); +#else +#define SVC_Call(f) \ + __asm volatile \ + ( \ + "ldr r12,="#f"\n\t" \ + "svc 0" \ + : "=r" (__r0), "=r" (__r1), "=r" (__r2), "=r" (__r3) \ + : "r" (__r0), "r" (__r1), "r" (__r2), "r" (__r3) \ + : "r12", "lr", "cc" \ + ); +#endif + +#define SVC_0_1(f,t,rv) \ +__attribute__((always_inline)) \ +static inline t __##f (void) { \ + SVC_Arg0(); \ + SVC_Call(f); \ + return (t) rv; \ +} + +#define SVC_1_1(f,t,t1,rv) \ +__attribute__((always_inline)) \ +static inline t __##f (t1 a1) { \ + SVC_Arg1(t1); \ + SVC_Call(f); \ + return (t) rv; \ +} + +#define SVC_2_1(f,t,t1,t2,rv) \ +__attribute__((always_inline)) \ +static inline t __##f (t1 a1, t2 a2) { \ + SVC_Arg2(t1,t2); \ + SVC_Call(f); \ + return (t) rv; \ +} + +#define SVC_3_1(f,t,t1,t2,t3,rv) \ +__attribute__((always_inline)) \ +static inline t __##f (t1 a1, t2 a2, t3 a3) { \ + SVC_Arg3(t1,t2,t3); \ + SVC_Call(f); \ + return (t) rv; \ +} + +#define SVC_4_1(f,t,t1,t2,t3,t4,rv) \ +__attribute__((always_inline)) \ +static inline t __##f (t1 a1, t2 a2, t3 a3, t4 a4) { \ + SVC_Arg4(t1,t2,t3,t4); \ + SVC_Call(f); \ + return (t) rv; \ +} + +#define SVC_1_2 SVC_1_1 +#define SVC_1_3 SVC_1_1 +#define SVC_2_3 SVC_2_1 + +#elif defined (__ICCARM__) /* IAR Compiler */ + +#define __NO_RETURN __noreturn + +#define RET_osEvent "=r"(ret.status), "=r"(ret.value), "=r"(ret.def) +#define RET_osCallback "=r"(ret.fp), "=r"(ret.arg) + +#define osEvent_type osEvent +#define osEvent_ret_status ret +#define osEvent_ret_value ret +#define osEvent_ret_msg ret +#define osEvent_ret_mail ret + +#define osCallback_type uint64_t +#define osCallback_ret ((uint64_t)ret.fp | ((uint64_t)ret.arg)<<32) + +#define SVC_Setup(f) \ + __asm( \ + "mov r12,%0\n" \ + :: "r"(&f): "r12" \ + ); + +#define SVC_Ret3() \ + __asm( \ + "ldr r0,[sp,#0]\n" \ + "ldr r1,[sp,#4]\n" \ + "ldr r2,[sp,#8]\n" \ + ); + +#define SVC_0_1(f,t,...) \ +t f (void); \ +_Pragma("swi_number=0") __swi t _##f (void); \ +static inline t __##f (void) { \ + SVC_Setup(f); \ + return _##f(); \ +} + +#define SVC_1_1(f,t,t1,...) \ +t f (t1 a1); \ +_Pragma("swi_number=0") __swi t _##f (t1 a1); \ +static inline t __##f (t1 a1) { \ + SVC_Setup(f); \ + return _##f(a1); \ +} + +#define SVC_2_1(f,t,t1,t2,...) \ +t f (t1 a1, t2 a2); \ +_Pragma("swi_number=0") __swi t _##f (t1 a1, t2 a2); \ +static inline t __##f (t1 a1, t2 a2) { \ + SVC_Setup(f); \ + return _##f(a1,a2); \ +} + +#define SVC_3_1(f,t,t1,t2,t3,...) \ +t f (t1 a1, t2 a2, t3 a3); \ +_Pragma("swi_number=0") __swi t _##f (t1 a1, t2 a2, t3 a3); \ +static inline t __##f (t1 a1, t2 a2, t3 a3) { \ + SVC_Setup(f); \ + return _##f(a1,a2,a3); \ +} + +#define SVC_4_1(f,t,t1,t2,t3,t4,...) \ +t f (t1 a1, t2 a2, t3 a3, t4 a4); \ +_Pragma("swi_number=0") __swi t _##f (t1 a1, t2 a2, t3 a3, t4 a4); \ +static inline t __##f (t1 a1, t2 a2, t3 a3, t4 a4) { \ + SVC_Setup(f); \ + return _##f(a1,a2,a3,a4); \ +} + +#define SVC_1_2(f,t,t1,rr) \ +uint64_t f (t1 a1); \ +_Pragma("swi_number=0") __swi uint64_t _##f (t1 a1); \ +static inline t __##f (t1 a1) { \ + t ret; \ + SVC_Setup(f); \ + _##f(a1); \ + __asm("" : rr : :); \ + return ret; \ +} + +#define SVC_1_3(f,t,t1,rr) \ +t f (t1 a1); \ +void f##_ (t1 a1) { \ + f(a1); \ + SVC_Ret3(); \ +} \ +_Pragma("swi_number=0") __swi void _##f (t1 a1); \ +static inline t __##f (t1 a1) { \ + t ret; \ + SVC_Setup(f##_); \ + _##f(a1); \ + __asm("" : rr : :); \ + return ret; \ +} + +#define SVC_2_3(f,t,t1,t2,rr) \ +t f (t1 a1, t2 a2); \ +void f##_ (t1 a1, t2 a2) { \ + f(a1,a2); \ + SVC_Ret3(); \ +} \ +_Pragma("swi_number=0") __swi void _##f (t1 a1, t2 a2); \ +static inline t __##f (t1 a1, t2 a2) { \ + t ret; \ + SVC_Setup(f##_); \ + _##f(a1,a2); \ + __asm("" : rr : :); \ + return ret; \ +} + +#endif + + +// Callback structure +typedef struct { + void *fp; // Function pointer + void *arg; // Function argument +} osCallback; + + +// OS Section definitions +#ifdef OS_SECTIONS_LINK_INFO +extern const uint32_t os_section_id$$Base; +extern const uint32_t os_section_id$$Limit; +#endif + +// OS Timers external resources +extern osThreadDef_t os_thread_def_osTimerThread; +extern osThreadId osThreadId_osTimerThread; +extern osMessageQDef_t os_messageQ_def_osTimerMessageQ; +extern osMessageQId osMessageQId_osTimerMessageQ; + + +// ==== Helper Functions ==== + +/// Convert timeout in millisec to system ticks +static uint32_t rt_ms2tick (uint32_t millisec) { + uint32_t tick; + + if (millisec == osWaitForever) return 0xFFFF; // Indefinite timeout + if (millisec > 4000000) return 0xFFFE; // Max ticks supported + + tick = ((1000 * millisec) + os_clockrate - 1) / os_clockrate; + if (tick > 0xFFFE) return 0xFFFE; + + return tick; +} + +/// Convert Thread ID to TCB pointer +static P_TCB rt_tid2ptcb (osThreadId thread_id) { + P_TCB ptcb; + + if (thread_id == NULL) return NULL; + + if ((uint32_t)thread_id & 3) return NULL; + +#ifdef OS_SECTIONS_LINK_INFO + if ((os_section_id$$Base != 0) && (os_section_id$$Limit != 0)) { + if (thread_id < (osThreadId)os_section_id$$Base) return NULL; + if (thread_id >= (osThreadId)os_section_id$$Limit) return NULL; + } +#endif + + ptcb = thread_id; + + if (ptcb->cb_type != TCB) return NULL; + + return ptcb; +} + +/// Convert ID pointer to Object pointer +static void *rt_id2obj (void *id) { + + if ((uint32_t)id & 3) return NULL; + +#ifdef OS_SECTIONS_LINK_INFO + if ((os_section_id$$Base != 0) && (os_section_id$$Limit != 0)) { + if (id < (void *)os_section_id$$Base) return NULL; + if (id >= (void *)os_section_id$$Limit) return NULL; + } +#endif + + return id; +} + + +// ==== Kernel Control ==== + +uint8_t os_initialized; // Kernel Initialized flag +uint8_t os_running; // Kernel Running flag + +// Kernel Control Service Calls declarations +SVC_0_1(svcKernelInitialize, osStatus, RET_osStatus) +SVC_0_1(svcKernelStart, osStatus, RET_osStatus) +SVC_0_1(svcKernelRunning, int32_t, RET_int32_t) + +extern void sysThreadError (osStatus status); +osThreadId svcThreadCreate (osThreadDef_t *thread_def, void *argument); +osMessageQId svcMessageCreate (osMessageQDef_t *queue_def, osThreadId thread_id); + +// Kernel Control Service Calls + +/// Initialize the RTOS Kernel for creating objects +osStatus svcKernelInitialize (void) { + if (os_initialized) return osOK; + + rt_sys_init(); // RTX System Initialization + os_tsk.run->prio = 255; // Highest priority + + sysThreadError(osOK); + + os_initialized = 1; + + return osOK; +} + +/// Start the RTOS Kernel +osStatus svcKernelStart (void) { + + if (os_running) return osOK; + + // Create OS Timers resources (Message Queue & Thread) + osMessageQId_osTimerMessageQ = svcMessageCreate (&os_messageQ_def_osTimerMessageQ, NULL); + osThreadId_osTimerThread = svcThreadCreate(&os_thread_def_osTimerThread, NULL); + + rt_tsk_prio(0, 0); // Lowest priority + __set_PSP(os_tsk.run->tsk_stack + 8*4); // New context + os_tsk.run = NULL; // Force context switch + + rt_sys_start(); + + os_running = 1; + + return osOK; +} + +/// Check if the RTOS kernel is already started +int32_t svcKernelRunning(void) { + return os_running; +} + +// Kernel Control Public API + +/// Initialize the RTOS Kernel for creating objects +osStatus osKernelInitialize (void) { + if (__get_IPSR() != 0) return osErrorISR; // Not allowed in ISR + if ((__get_CONTROL() & 1) == 0) { // Privileged mode + return svcKernelInitialize(); + } else { + return __svcKernelInitialize(); + } +} + +/// Start the RTOS Kernel +osStatus osKernelStart (void) { + uint32_t stack[8]; + + if (__get_IPSR() != 0) return osErrorISR; // Not allowed in ISR + switch (__get_CONTROL() & 0x03) { + case 0x00: // Privileged Thread mode & MSP + __set_PSP((uint32_t)(stack + 8)); // Initial PSP + if (os_flags & 1) { + __set_CONTROL(0x02); // Set Privileged Thread mode & PSP + } else { + __set_CONTROL(0x03); // Set Unprivileged Thread mode & PSP + } + __DSB(); + __ISB(); + break; + case 0x01: // Unprivileged Thread mode & MSP + return osErrorOS; + case 0x02: // Privileged Thread mode & PSP + if ((os_flags & 1) == 0) { // Unprivileged Thread mode requested + __set_CONTROL(0x03); // Set Unprivileged Thread mode & PSP + __DSB(); + __ISB(); + } + break; + case 0x03: // Unprivileged Thread mode & PSP + if (os_flags & 1) return osErrorOS; // Privileged Thread mode requested + break; + } + return __svcKernelStart(); +} + +/// Check if the RTOS kernel is already started +int32_t osKernelRunning(void) { + if ((__get_IPSR() != 0) || ((__get_CONTROL() & 1) == 0)) { + // in ISR or Privileged + return os_running; + } else { + return __svcKernelRunning(); + } +} + + +// ==== Thread Management ==== + +__NO_RETURN void osThreadExit (void); + +// Thread Service Calls declarations +SVC_2_1(svcThreadCreate, osThreadId, osThreadDef_t *, void *, RET_pointer) +SVC_0_1(svcThreadGetId, osThreadId, RET_pointer) +SVC_1_1(svcThreadTerminate, osStatus, osThreadId, RET_osStatus) +SVC_0_1(svcThreadYield, osStatus, RET_osStatus) +SVC_2_1(svcThreadSetPriority, osStatus, osThreadId, osPriority, RET_osStatus) +SVC_1_1(svcThreadGetPriority, osPriority, osThreadId, RET_osPriority) + +// Thread Service Calls +extern OS_TID rt_get_TID (void); +extern void rt_init_context (P_TCB p_TCB, U8 priority, FUNCP task_body); + +/// Create a thread and add it to Active Threads and set it to state READY +osThreadId svcThreadCreate (osThreadDef_t *thread_def, void *argument) { + P_TCB ptcb; + + if ((thread_def == NULL) || + (thread_def->pthread == NULL) || + (thread_def->tpriority < osPriorityIdle) || + (thread_def->tpriority > osPriorityRealtime) || + (thread_def->stacksize == 0) || + (thread_def->stack_pointer == NULL) ) { + sysThreadError(osErrorParameter); + return NULL; + } + + U8 priority = thread_def->tpriority - osPriorityIdle + 1; + P_TCB task_context = &thread_def->tcb; + + /* If "size != 0" use a private user provided stack. */ + task_context->stack = (U32*)thread_def->stack_pointer; + task_context->priv_stack = thread_def->stacksize; + /* Pass parameter 'argv' to 'rt_init_context' */ + task_context->msg = argument; + /* For 'size == 0' system allocates the user stack from the memory pool. */ + rt_init_context (task_context, priority, (FUNCP)thread_def->pthread); + + /* Find a free entry in 'os_active_TCB' table. */ + OS_TID tsk = rt_get_TID (); + os_active_TCB[tsk-1] = task_context; + task_context->task_id = tsk; + DBG_TASK_NOTIFY(task_context, __TRUE); + rt_dispatch (task_context); + + ptcb = (P_TCB)os_active_TCB[tsk - 1]; // TCB pointer + + *((uint32_t *)ptcb->tsk_stack + 13) = (uint32_t)osThreadExit; + + return ptcb; +} + +/// Return the thread ID of the current running thread +osThreadId svcThreadGetId (void) { + OS_TID tsk; + + tsk = rt_tsk_self(); + if (tsk == 0) return NULL; + return (P_TCB)os_active_TCB[tsk - 1]; +} + +/// Terminate execution of a thread and remove it from ActiveThreads +osStatus svcThreadTerminate (osThreadId thread_id) { + OS_RESULT res; + P_TCB ptcb; + + ptcb = rt_tid2ptcb(thread_id); // Get TCB pointer + if (ptcb == NULL) return osErrorParameter; + + res = rt_tsk_delete(ptcb->task_id); // Delete task + + if (res == OS_R_NOK) return osErrorResource; // Delete task failed + + return osOK; +} + +/// Pass control to next thread that is in state READY +osStatus svcThreadYield (void) { + rt_tsk_pass(); // Pass control to next task + return osOK; +} + +/// Change priority of an active thread +osStatus svcThreadSetPriority (osThreadId thread_id, osPriority priority) { + OS_RESULT res; + P_TCB ptcb; + + ptcb = rt_tid2ptcb(thread_id); // Get TCB pointer + if (ptcb == NULL) return osErrorParameter; + + if ((priority < osPriorityIdle) || (priority > osPriorityRealtime)) { + return osErrorValue; + } + + res = rt_tsk_prio( // Change task priority + ptcb->task_id, // Task ID + priority - osPriorityIdle + 1 // New task priority + ); + + if (res == OS_R_NOK) return osErrorResource; // Change task priority failed + + return osOK; +} + +/// Get current priority of an active thread +osPriority svcThreadGetPriority (osThreadId thread_id) { + P_TCB ptcb; + + ptcb = rt_tid2ptcb(thread_id); // Get TCB pointer + if (ptcb == NULL) return osPriorityError; + + return (osPriority)(ptcb->prio - 1 + osPriorityIdle); +} + + +// Thread Public API + +/// Create a thread and add it to Active Threads and set it to state READY +osThreadId osThreadCreate (osThreadDef_t *thread_def, void *argument) { + if (__get_IPSR() != 0) return NULL; // Not allowed in ISR + if (((__get_CONTROL() & 1) == 0) && (os_running == 0)) { + // Privileged and not running + return svcThreadCreate(thread_def, argument); + } else { + return __svcThreadCreate(thread_def, argument); + } +} + +/// Return the thread ID of the current running thread +osThreadId osThreadGetId (void) { + if (__get_IPSR() != 0) return NULL; // Not allowed in ISR + return __svcThreadGetId(); +} + +/// Terminate execution of a thread and remove it from ActiveThreads +osStatus osThreadTerminate (osThreadId thread_id) { + if (__get_IPSR() != 0) return osErrorISR; // Not allowed in ISR + return __svcThreadTerminate(thread_id); +} + +/// Pass control to next thread that is in state READY +osStatus osThreadYield (void) { + if (__get_IPSR() != 0) return osErrorISR; // Not allowed in ISR + return __svcThreadYield(); +} + +/// Change priority of an active thread +osStatus osThreadSetPriority (osThreadId thread_id, osPriority priority) { + if (__get_IPSR() != 0) return osErrorISR; // Not allowed in ISR + return __svcThreadSetPriority(thread_id, priority); +} + +/// Get current priority of an active thread +osPriority osThreadGetPriority (osThreadId thread_id) { + if (__get_IPSR() != 0) return osPriorityError;// Not allowed in ISR + return __svcThreadGetPriority(thread_id); +} + +/// INTERNAL - Not Public +/// Auto Terminate Thread on exit (used implicitly when thread exists) +__NO_RETURN void osThreadExit (void) { + __svcThreadTerminate(__svcThreadGetId()); + for (;;); // Should never come here +} + + +// ==== Generic Wait Functions ==== + +// Generic Wait Service Calls declarations +SVC_1_1(svcDelay, osStatus, uint32_t, RET_osStatus) +#if osFeature_Wait != 0 +SVC_1_3(svcWait, os_InRegs osEvent, uint32_t, RET_osEvent) +#endif + +// Generic Wait Service Calls + +/// Wait for Timeout (Time Delay) +osStatus svcDelay (uint32_t millisec) { + if (millisec == 0) return osOK; + rt_dly_wait(rt_ms2tick(millisec)); + return osEventTimeout; +} + +/// Wait for Signal, Message, Mail, or Timeout +#if osFeature_Wait != 0 +os_InRegs osEvent_type svcWait (uint32_t millisec) { + osEvent ret; + + if (millisec == 0) { + ret.status = osOK; + return osEvent_ret_status; + } + + /* To Do: osEventSignal, osEventMessage, osEventMail */ + rt_dly_wait(rt_ms2tick(millisec)); + ret.status = osEventTimeout; + + return osEvent_ret_status; +} +#endif + + +// Generic Wait API + +/// Wait for Timeout (Time Delay) +osStatus osDelay (uint32_t millisec) { + if (__get_IPSR() != 0) return osErrorISR; // Not allowed in ISR + return __svcDelay(millisec); +} + +/// Wait for Signal, Message, Mail, or Timeout +os_InRegs osEvent osWait (uint32_t millisec) { + osEvent ret; + +#if osFeature_Wait == 0 + ret.status = osErrorOS; + return ret; +#else + if (__get_IPSR() != 0) { // Not allowed in ISR + ret.status = osErrorISR; + return ret; + } + return __svcWait(millisec); +#endif +} + + +// ==== Timer Management ==== + +// Timer definitions +#define osTimerInvalid 0 +#define osTimerStopped 1 +#define osTimerRunning 2 + +// Timer structures + +typedef struct os_timer_cb_ { // Timer Control Block + struct os_timer_cb_ *next; // Pointer to next active Timer + uint8_t state; // Timer State + uint8_t type; // Timer Type (Periodic/One-shot) + uint16_t reserved; // Reserved + uint16_t tcnt; // Timer Delay Count + uint16_t icnt; // Timer Initial Count + void *arg; // Timer Function Argument + osTimerDef_t *timer; // Pointer to Timer definition +} os_timer_cb; + +// Timer variables +os_timer_cb *os_timer_head; // Pointer to first active Timer + + +// Timer Helper Functions + +// Insert Timer into the list sorted by time +static void rt_timer_insert (os_timer_cb *pt, uint32_t tcnt) { + os_timer_cb *p, *prev; + + prev = NULL; + p = os_timer_head; + while (p != NULL) { + if (tcnt < p->tcnt) break; + tcnt -= p->tcnt; + prev = p; + p = p->next; + } + pt->next = p; + pt->tcnt = (uint16_t)tcnt; + if (p != NULL) { + p->tcnt -= pt->tcnt; + } + if (prev != NULL) { + prev->next = pt; + } else { + os_timer_head = pt; + } +} + +// Remove Timer from the list +static int rt_timer_remove (os_timer_cb *pt) { + os_timer_cb *p, *prev; + + prev = NULL; + p = os_timer_head; + while (p != NULL) { + if (p == pt) break; + prev = p; + p = p->next; + } + if (p == NULL) return -1; + if (prev != NULL) { + prev->next = pt->next; + } else { + os_timer_head = pt->next; + } + if (pt->next != NULL) { + pt->next->tcnt += pt->tcnt; + } + + return 0; +} + + +// Timer Service Calls declarations +SVC_3_1(svcTimerCreate, osTimerId, osTimerDef_t *, os_timer_type, void *, RET_pointer) +SVC_2_1(svcTimerStart, osStatus, osTimerId, uint32_t, RET_osStatus) +SVC_1_1(svcTimerStop, osStatus, osTimerId, RET_osStatus) +SVC_1_1(svcTimerDelete, osStatus, osTimerId, RET_osStatus) +SVC_1_2(svcTimerCall, os_InRegs osCallback, osTimerId, RET_osCallback) + +// Timer Management Service Calls + +/// Create timer +osTimerId svcTimerCreate (osTimerDef_t *timer_def, os_timer_type type, void *argument) { + os_timer_cb *pt; + + if ((timer_def == NULL) || (timer_def->ptimer == NULL)) { + sysThreadError(osErrorParameter); + return NULL; + } + + pt = timer_def->timer; + if (pt == NULL) { + sysThreadError(osErrorParameter); + return NULL; + } + + if ((type != osTimerOnce) && (type != osTimerPeriodic)) { + sysThreadError(osErrorValue); + return NULL; + } + + if (osThreadId_osTimerThread == NULL) { + sysThreadError(osErrorResource); + return NULL; + } + + if (pt->state != osTimerInvalid){ + sysThreadError(osErrorResource); + return NULL; + } + + pt->state = osTimerStopped; + pt->type = (uint8_t)type; + pt->arg = argument; + pt->timer = timer_def; + + return (osTimerId)pt; +} + +/// Start or restart timer +osStatus svcTimerStart (osTimerId timer_id, uint32_t millisec) { + os_timer_cb *pt; + uint32_t tcnt; + + pt = rt_id2obj(timer_id); + if (pt == NULL) return osErrorParameter; + + tcnt = rt_ms2tick(millisec); + if (tcnt == 0) return osErrorValue; + + switch (pt->state) { + case osTimerRunning: + if (rt_timer_remove(pt) != 0) { + return osErrorResource; + } + break; + case osTimerStopped: + pt->state = osTimerRunning; + pt->icnt = (uint16_t)tcnt; + break; + default: + return osErrorResource; + } + + rt_timer_insert(pt, tcnt); + + return osOK; +} + +/// Stop timer +osStatus svcTimerStop (osTimerId timer_id) { + os_timer_cb *pt; + + pt = rt_id2obj(timer_id); + if (pt == NULL) return osErrorParameter; + + if (pt->state != osTimerRunning) return osErrorResource; + + pt->state = osTimerStopped; + + if (rt_timer_remove(pt) != 0) { + return osErrorResource; + } + + return osOK; +} + +/// Delete timer +osStatus svcTimerDelete (osTimerId timer_id) { + os_timer_cb *pt; + + pt = rt_id2obj(timer_id); + if (pt == NULL) return osErrorParameter; + + switch (pt->state) { + case osTimerRunning: + rt_timer_remove(pt); + break; + case osTimerStopped: + break; + default: + return osErrorResource; + } + + pt->state = osTimerInvalid; + + return osOK; +} + +/// Get timer callback parameters +os_InRegs osCallback_type svcTimerCall (osTimerId timer_id) { + os_timer_cb *pt; + osCallback ret; + + pt = rt_id2obj(timer_id); + if (pt == NULL) { + ret.fp = NULL; + ret.arg = NULL; + return osCallback_ret; + } + + ret.fp = (void *)pt->timer->ptimer; + ret.arg = pt->arg; + + return osCallback_ret; +} + +static __INLINE osStatus isrMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec); + +/// Timer Tick (called each SysTick) +void sysTimerTick (void) { + os_timer_cb *pt, *p; + + p = os_timer_head; + if (p == NULL) return; + + p->tcnt--; + while ((p != NULL) && (p->tcnt == 0)) { + pt = p; + p = p->next; + os_timer_head = p; + isrMessagePut(osMessageQId_osTimerMessageQ, (uint32_t)pt, 0); + if (pt->type == osTimerPeriodic) { + rt_timer_insert(pt, pt->icnt); + } else { + pt->state = osTimerStopped; + } + } +} + + +// Timer Management Public API + +/// Create timer +osTimerId osTimerCreate (osTimerDef_t *timer_def, os_timer_type type, void *argument) { + if (__get_IPSR() != 0) return NULL; // Not allowed in ISR + if (((__get_CONTROL() & 1) == 0) && (os_running == 0)) { + // Privileged and not running + return svcTimerCreate(timer_def, type, argument); + } else { + return __svcTimerCreate(timer_def, type, argument); + } +} + +/// Start or restart timer +osStatus osTimerStart (osTimerId timer_id, uint32_t millisec) { + if (__get_IPSR() != 0) return osErrorISR; // Not allowed in ISR + return __svcTimerStart(timer_id, millisec); +} + +/// Stop timer +osStatus osTimerStop (osTimerId timer_id) { + if (__get_IPSR() != 0) return osErrorISR; // Not allowed in ISR + return __svcTimerStop(timer_id); +} + +/// Delete timer +osStatus osTimerDelete (osTimerId timer_id) { + if (__get_IPSR() != 0) return osErrorISR; // Not allowed in ISR + return __svcTimerDelete(timer_id); +} + +/// INTERNAL - Not Public +/// Get timer callback parameters (used by OS Timer Thread) +os_InRegs osCallback osTimerCall (osTimerId timer_id) { + return __svcTimerCall(timer_id); +} + + +// Timer Thread +__NO_RETURN void osTimerThread (void const *argument) { + osCallback cb; + osEvent evt; + + for (;;) { + evt = osMessageGet(osMessageQId_osTimerMessageQ, osWaitForever); + if (evt.status == osEventMessage) { + cb = osTimerCall(evt.value.p); + if (cb.fp != NULL) { + (*(os_ptimer)cb.fp)(cb.arg); + } + } + } +} + + +// ==== Signal Management ==== + +// Signal Service Calls declarations +SVC_2_1(svcSignalSet, int32_t, osThreadId, int32_t, RET_int32_t) +SVC_2_1(svcSignalClear, int32_t, osThreadId, int32_t, RET_int32_t) +SVC_1_1(svcSignalGet, int32_t, osThreadId, RET_int32_t) +SVC_2_3(svcSignalWait, os_InRegs osEvent, int32_t, uint32_t, RET_osEvent) + +// Signal Service Calls + +/// Set the specified Signal Flags of an active thread +int32_t svcSignalSet (osThreadId thread_id, int32_t signals) { + P_TCB ptcb; + int32_t sig; + + ptcb = rt_tid2ptcb(thread_id); // Get TCB pointer + if (ptcb == NULL) return 0x80000000; + + if (signals & (0xFFFFFFFF << osFeature_Signals)) return 0x80000000; + + sig = ptcb->events; // Previous signal flags + + rt_evt_set(signals, ptcb->task_id); // Set event flags + + return sig; +} + +/// Clear the specified Signal Flags of an active thread +int32_t svcSignalClear (osThreadId thread_id, int32_t signals) { + P_TCB ptcb; + int32_t sig; + + ptcb = rt_tid2ptcb(thread_id); // Get TCB pointer + if (ptcb == NULL) return 0x80000000; + + if (signals & (0xFFFFFFFF << osFeature_Signals)) return 0x80000000; + + sig = ptcb->events; // Previous signal flags + + rt_evt_clr(signals, ptcb->task_id); // Clear event flags + + return sig; +} + +/// Get Signal Flags status of an active thread +int32_t svcSignalGet (osThreadId thread_id) { + P_TCB ptcb; + + ptcb = rt_tid2ptcb(thread_id); // Get TCB pointer + if (ptcb == NULL) return 0x80000000; + + return ptcb->events; // Return event flags +} + +/// Wait for one or more Signal Flags to become signaled for the current RUNNING thread +os_InRegs osEvent_type svcSignalWait (int32_t signals, uint32_t millisec) { + OS_RESULT res; + osEvent ret; + + if (signals & (0xFFFFFFFF << osFeature_Signals)) { + ret.status = osErrorValue; + return osEvent_ret_status; + } + + if (signals != 0) { // Wait for all specified signals + res = rt_evt_wait(signals, rt_ms2tick(millisec), __TRUE); + } else { // Wait for any signal + res = rt_evt_wait(0xFFFF, rt_ms2tick(millisec), __FALSE); + } + + if (res == OS_R_EVT) { + ret.status = osEventSignal; + ret.value.signals = signals ? signals : os_tsk.run->waits; + } else { + ret.status = millisec ? osEventTimeout : osOK; + ret.value.signals = 0; + } + + return osEvent_ret_value; +} + + +// Signal ISR Calls + +/// Set the specified Signal Flags of an active thread +static __INLINE int32_t isrSignalSet (osThreadId thread_id, int32_t signals) { + P_TCB ptcb; + int32_t sig; + + ptcb = rt_tid2ptcb(thread_id); // Get TCB pointer + if (ptcb == NULL) return 0x80000000; + + if (signals & (0xFFFFFFFF << osFeature_Signals)) return 0x80000000; + + sig = ptcb->events; // Previous signal flags + + isr_evt_set(signals, ptcb->task_id); // Set event flags + + return sig; +} + + +// Signal Public API + +/// Set the specified Signal Flags of an active thread +int32_t osSignalSet (osThreadId thread_id, int32_t signals) { + if (__get_IPSR() != 0) { // in ISR + return isrSignalSet(thread_id, signals); + } else { // in Thread + return __svcSignalSet(thread_id, signals); + } +} + +/// Clear the specified Signal Flags of an active thread +int32_t osSignalClear (osThreadId thread_id, int32_t signals) { + if (__get_IPSR() != 0) return osErrorISR; // Not allowed in ISR + return __svcSignalClear(thread_id, signals); +} + +/// Get Signal Flags status of an active thread +int32_t osSignalGet (osThreadId thread_id) { + if (__get_IPSR() != 0) return osErrorISR; // Not allowed in ISR + return __svcSignalGet(thread_id); +} + +/// Wait for one or more Signal Flags to become signaled for the current RUNNING thread +os_InRegs osEvent osSignalWait (int32_t signals, uint32_t millisec) { + osEvent ret; + + if (__get_IPSR() != 0) { // Not allowed in ISR + ret.status = osErrorISR; + return ret; + } + return __svcSignalWait(signals, millisec); +} + + +// ==== Mutex Management ==== + +// Mutex Service Calls declarations +SVC_1_1(svcMutexCreate, osMutexId, osMutexDef_t *, RET_pointer) +SVC_2_1(svcMutexWait, osStatus, osMutexId, uint32_t, RET_osStatus) +SVC_1_1(svcMutexRelease, osStatus, osMutexId, RET_osStatus) +SVC_1_1(svcMutexDelete, osStatus, osMutexId, RET_osStatus) + +// Mutex Service Calls + +/// Create and Initialize a Mutex object +osMutexId svcMutexCreate (osMutexDef_t *mutex_def) { + OS_ID mut; + + if (mutex_def == NULL) { + sysThreadError(osErrorParameter); + return NULL; + } + + mut = mutex_def->mutex; + if (mut == NULL) { + sysThreadError(osErrorParameter); + return NULL; + } + + if (((P_MUCB)mut)->cb_type != 0) { + sysThreadError(osErrorParameter); + return NULL; + } + + rt_mut_init(mut); // Initialize Mutex + + return mut; +} + +/// Wait until a Mutex becomes available +osStatus svcMutexWait (osMutexId mutex_id, uint32_t millisec) { + OS_ID mut; + OS_RESULT res; + + mut = rt_id2obj(mutex_id); + if (mut == NULL) return osErrorParameter; + + if (((P_MUCB)mut)->cb_type != MUCB) return osErrorParameter; + + res = rt_mut_wait(mut, rt_ms2tick(millisec)); // Wait for Mutex + + if (res == OS_R_TMO) { + return (millisec ? osErrorTimeoutResource : osErrorResource); + } + + return osOK; +} + +/// Release a Mutex that was obtained with osMutexWait +osStatus svcMutexRelease (osMutexId mutex_id) { + OS_ID mut; + OS_RESULT res; + + mut = rt_id2obj(mutex_id); + if (mut == NULL) return osErrorParameter; + + if (((P_MUCB)mut)->cb_type != MUCB) return osErrorParameter; + + res = rt_mut_release(mut); // Release Mutex + + if (res == OS_R_NOK) return osErrorResource; // Thread not owner or Zero Counter + + return osOK; +} + +/// Delete a Mutex that was created by osMutexCreate +osStatus svcMutexDelete (osMutexId mutex_id) { + OS_ID mut; + + mut = rt_id2obj(mutex_id); + if (mut == NULL) return osErrorParameter; + + if (((P_MUCB)mut)->cb_type != MUCB) return osErrorParameter; + + rt_mut_delete(mut); // Release Mutex + + return osOK; +} + + +// Mutex Public API + +/// Create and Initialize a Mutex object +osMutexId osMutexCreate (osMutexDef_t *mutex_def) { + if (__get_IPSR() != 0) return NULL; // Not allowed in ISR + if (((__get_CONTROL() & 1) == 0) && (os_running == 0)) { + // Privileged and not running + return svcMutexCreate(mutex_def); + } else { + return __svcMutexCreate(mutex_def); + } +} + +/// Wait until a Mutex becomes available +osStatus osMutexWait (osMutexId mutex_id, uint32_t millisec) { + if (__get_IPSR() != 0) return osErrorISR; // Not allowed in ISR + return __svcMutexWait(mutex_id, millisec); +} + +/// Release a Mutex that was obtained with osMutexWait +osStatus osMutexRelease (osMutexId mutex_id) { + if (__get_IPSR() != 0) return osErrorISR; // Not allowed in ISR + return __svcMutexRelease(mutex_id); +} + +/// Delete a Mutex that was created by osMutexCreate +osStatus osMutexDelete (osMutexId mutex_id) { + if (__get_IPSR() != 0) return osErrorISR; // Not allowed in ISR + return __svcMutexDelete(mutex_id); +} + + +// ==== Semaphore Management ==== + +// Semaphore Service Calls declarations +SVC_2_1(svcSemaphoreCreate, osSemaphoreId, const osSemaphoreDef_t *, int32_t, RET_pointer) +SVC_2_1(svcSemaphoreWait, int32_t, osSemaphoreId, uint32_t, RET_int32_t) +SVC_1_1(svcSemaphoreRelease, osStatus, osSemaphoreId, RET_osStatus) +SVC_1_1(svcSemaphoreDelete, osStatus, osSemaphoreId, RET_osStatus) + +// Semaphore Service Calls + +/// Create and Initialize a Semaphore object +osSemaphoreId svcSemaphoreCreate (const osSemaphoreDef_t *semaphore_def, int32_t count) { + OS_ID sem; + + if (semaphore_def == NULL) { + sysThreadError(osErrorParameter); + return NULL; + } + + sem = semaphore_def->semaphore; + if (sem == NULL) { + sysThreadError(osErrorParameter); + return NULL; + } + + if (((P_SCB)sem)->cb_type != 0) { + sysThreadError(osErrorParameter); + return NULL; + } + + if (count > osFeature_Semaphore) { + sysThreadError(osErrorValue); + return NULL; + } + + rt_sem_init(sem, count); // Initialize Semaphore + + return sem; +} + +/// Wait until a Semaphore becomes available +int32_t svcSemaphoreWait (osSemaphoreId semaphore_id, uint32_t millisec) { + OS_ID sem; + OS_RESULT res; + + sem = rt_id2obj(semaphore_id); + if (sem == NULL) return -1; + + if (((P_SCB)sem)->cb_type != SCB) return -1; + + res = rt_sem_wait(sem, rt_ms2tick(millisec)); // Wait for Semaphore + + if (res == OS_R_TMO) return 0; // Timeout + + return (((P_SCB)sem)->tokens + 1); +} + +/// Release a Semaphore +osStatus svcSemaphoreRelease (osSemaphoreId semaphore_id) { + OS_ID sem; + + sem = rt_id2obj(semaphore_id); + if (sem == NULL) return osErrorParameter; + + if (((P_SCB)sem)->cb_type != SCB) return osErrorParameter; + + if (((P_SCB)sem)->tokens == osFeature_Semaphore) return osErrorResource; + + rt_sem_send(sem); // Release Semaphore + + return osOK; +} + +/// Delete a Semaphore that was created by osSemaphoreCreate +osStatus svcSemaphoreDelete (osSemaphoreId semaphore_id) { + OS_ID sem; + + sem = rt_id2obj(semaphore_id); + if (sem == NULL) return osErrorParameter; + + if (((P_SCB)sem)->cb_type != SCB) return osErrorParameter; + + rt_sem_delete(sem); // Delete Semaphore + + return osOK; +} + + +// Semaphore ISR Calls + +/// Release a Semaphore +static __INLINE osStatus isrSemaphoreRelease (osSemaphoreId semaphore_id) { + OS_ID sem; + + sem = rt_id2obj(semaphore_id); + if (sem == NULL) return osErrorParameter; + + if (((P_SCB)sem)->cb_type != SCB) return osErrorParameter; + + if (((P_SCB)sem)->tokens == osFeature_Semaphore) return osErrorResource; + + isr_sem_send(sem); // Release Semaphore + + return osOK; +} + + +// Semaphore Public API + +/// Create and Initialize a Semaphore object +osSemaphoreId osSemaphoreCreate (osSemaphoreDef_t *semaphore_def, int32_t count) { + if (__get_IPSR() != 0) return NULL; // Not allowed in ISR + if (((__get_CONTROL() & 1) == 0) && (os_running == 0)) { + // Privileged and not running + return svcSemaphoreCreate(semaphore_def, count); + } else { + return __svcSemaphoreCreate(semaphore_def, count); + } +} + +/// Wait until a Semaphore becomes available +int32_t osSemaphoreWait (osSemaphoreId semaphore_id, uint32_t millisec) { + if (__get_IPSR() != 0) return -1; // Not allowed in ISR + return __svcSemaphoreWait(semaphore_id, millisec); +} + +/// Release a Semaphore +osStatus osSemaphoreRelease (osSemaphoreId semaphore_id) { + if (__get_IPSR() != 0) { // in ISR + return isrSemaphoreRelease(semaphore_id); + } else { // in Thread + return __svcSemaphoreRelease(semaphore_id); + } +} + +/// Delete a Semaphore that was created by osSemaphoreCreate +osStatus osSemaphoreDelete (osSemaphoreId semaphore_id) { + if (__get_IPSR() != 0) return osErrorISR; // Not allowed in ISR + return __svcSemaphoreDelete(semaphore_id); +} + + +// ==== Memory Management Functions ==== + +// Memory Management Helper Functions + +// Clear Memory Box (Zero init) +static void rt_clr_box (void *box_mem, void *box) { + uint32_t *p, n; + + if (box) { + p = box; + for (n = ((P_BM)box_mem)->blk_size; n; n -= 4) { + *p++ = 0; + } + } +} + +// Memory Management Service Calls declarations +SVC_1_1(svcPoolCreate, osPoolId, const osPoolDef_t *, RET_pointer) +SVC_2_1(sysPoolAlloc, void *, osPoolId, uint32_t, RET_pointer) +SVC_2_1(sysPoolFree, osStatus, osPoolId, void *, RET_osStatus) + +// Memory Management Service & ISR Calls + +/// Create and Initialize memory pool +osPoolId svcPoolCreate (const osPoolDef_t *pool_def) { + uint32_t blk_sz; + + if ((pool_def == NULL) || + (pool_def->pool_sz == 0) || + (pool_def->item_sz == 0) || + (pool_def->pool == NULL)) { + sysThreadError(osErrorParameter); + return NULL; + } + + blk_sz = (pool_def->item_sz + 3) & ~3; + + _init_box(pool_def->pool, sizeof(struct OS_BM) + pool_def->pool_sz * blk_sz, blk_sz); + + return pool_def->pool; +} + +/// Allocate a memory block from a memory pool +void *sysPoolAlloc (osPoolId pool_id, uint32_t clr) { + void *ptr; + + if (pool_id == NULL) return NULL; + + ptr = rt_alloc_box(pool_id); + if (clr) { + rt_clr_box(pool_id, ptr); + } + + return ptr; +} + +/// Return an allocated memory block back to a specific memory pool +osStatus sysPoolFree (osPoolId pool_id, void *block) { + int32_t res; + + if (pool_id == NULL) return osErrorParameter; + + res = rt_free_box(pool_id, block); + if (res != 0) return osErrorValue; + + return osOK; +} + + +// Memory Management Public API + +/// Create and Initialize memory pool +osPoolId osPoolCreate (osPoolDef_t *pool_def) { + if (__get_IPSR() != 0) return NULL; // Not allowed in ISR + if (((__get_CONTROL() & 1) == 0) && (os_running == 0)) { + // Privileged and not running + return svcPoolCreate(pool_def); + } else { + return __svcPoolCreate(pool_def); + } +} + +/// Allocate a memory block from a memory pool +void *osPoolAlloc (osPoolId pool_id) { + if ((__get_IPSR() != 0) || ((__get_CONTROL() & 1) == 0)) { // in ISR or Privileged + return sysPoolAlloc(pool_id, 0); + } else { // in Thread + return __sysPoolAlloc(pool_id, 0); + } +} + +/// Allocate a memory block from a memory pool and set memory block to zero +void *osPoolCAlloc (osPoolId pool_id) { + if ((__get_IPSR() != 0) || ((__get_CONTROL() & 1) == 0)) { // in ISR or Privileged + return sysPoolAlloc(pool_id, 1); + } else { // in Thread + return __sysPoolAlloc(pool_id, 1); + } +} + +/// Return an allocated memory block back to a specific memory pool +osStatus osPoolFree (osPoolId pool_id, void *block) { + if ((__get_IPSR() != 0) || ((__get_CONTROL() & 1) == 0)) { // in ISR or Privileged + return sysPoolFree(pool_id, block); + } else { // in Thread + return __sysPoolFree(pool_id, block); + } +} + + +// ==== Message Queue Management Functions ==== + +// Message Queue Management Service Calls declarations +SVC_2_1(svcMessageCreate, osMessageQId, osMessageQDef_t *, osThreadId, RET_pointer) +SVC_3_1(svcMessagePut, osStatus, osMessageQId, uint32_t, uint32_t, RET_osStatus) +SVC_2_3(svcMessageGet, os_InRegs osEvent, osMessageQId, uint32_t, RET_osEvent) + +// Message Queue Service Calls + +/// Create and Initialize Message Queue +osMessageQId svcMessageCreate (osMessageQDef_t *queue_def, osThreadId thread_id) { + + if ((queue_def == NULL) || + (queue_def->queue_sz == 0) || + (queue_def->pool == NULL)) { + sysThreadError(osErrorParameter); + return NULL; + } + + if (((P_MCB)queue_def->pool)->cb_type != 0) { + sysThreadError(osErrorParameter); + return NULL; + } + + rt_mbx_init(queue_def->pool, 4*(queue_def->queue_sz + 4)); + + return queue_def->pool; +} + +/// Put a Message to a Queue +osStatus svcMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec) { + OS_RESULT res; + + if (queue_id == NULL) return osErrorParameter; + + if (((P_MCB)queue_id)->cb_type != MCB) return osErrorParameter; + + res = rt_mbx_send(queue_id, (void *)info, rt_ms2tick(millisec)); + + if (res == OS_R_TMO) { + return (millisec ? osErrorTimeoutResource : osErrorResource); + } + + return osOK; +} + +/// Get a Message or Wait for a Message from a Queue +os_InRegs osEvent_type svcMessageGet (osMessageQId queue_id, uint32_t millisec) { + OS_RESULT res; + osEvent ret; + + if (queue_id == NULL) { + ret.status = osErrorParameter; + return osEvent_ret_status; + } + + if (((P_MCB)queue_id)->cb_type != MCB) { + ret.status = osErrorParameter; + return osEvent_ret_status; + } + + res = rt_mbx_wait(queue_id, &ret.value.p, rt_ms2tick(millisec)); + + if (res == OS_R_TMO) { + ret.status = millisec ? osEventTimeout : osOK; + return osEvent_ret_value; + } + + ret.status = osEventMessage; + + return osEvent_ret_value; +} + + +// Message Queue ISR Calls + +/// Put a Message to a Queue +static __INLINE osStatus isrMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec) { + + if ((queue_id == NULL) || (millisec != 0)) { + return osErrorParameter; + } + + if (((P_MCB)queue_id)->cb_type != MCB) return osErrorParameter; + + if (rt_mbx_check(queue_id) == 0) { // Check if Queue is full + return osErrorResource; + } + + isr_mbx_send(queue_id, (void *)info); + + return osOK; +} + +/// Get a Message or Wait for a Message from a Queue +static __INLINE os_InRegs osEvent isrMessageGet (osMessageQId queue_id, uint32_t millisec) { + OS_RESULT res; + osEvent ret; + + if ((queue_id == NULL) || (millisec != 0)) { + ret.status = osErrorParameter; + return ret; + } + + if (((P_MCB)queue_id)->cb_type != MCB) { + ret.status = osErrorParameter; + return ret; + } + + res = isr_mbx_receive(queue_id, &ret.value.p); + + if (res != OS_R_MBX) { + ret.status = osOK; + return ret; + } + + ret.status = osEventMessage; + + return ret; +} + + +// Message Queue Management Public API + +/// Create and Initialize Message Queue +osMessageQId osMessageCreate (osMessageQDef_t *queue_def, osThreadId thread_id) { + if (__get_IPSR() != 0) return NULL; // Not allowed in ISR + if (((__get_CONTROL() & 1) == 0) && (os_running == 0)) { + // Privileged and not running + return svcMessageCreate(queue_def, thread_id); + } else { + return __svcMessageCreate(queue_def, thread_id); + } +} + +/// Put a Message to a Queue +osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec) { + if (__get_IPSR() != 0) { // in ISR + return isrMessagePut(queue_id, info, millisec); + } else { // in Thread + return __svcMessagePut(queue_id, info, millisec); + } +} + +/// Get a Message or Wait for a Message from a Queue +os_InRegs osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec) { + if (__get_IPSR() != 0) { // in ISR + return isrMessageGet(queue_id, millisec); + } else { // in Thread + return __svcMessageGet(queue_id, millisec); + } +} + + +// ==== Mail Queue Management Functions ==== + +// Mail Queue Management Service Calls declarations +SVC_2_1(svcMailCreate, osMailQId, osMailQDef_t *, osThreadId, RET_pointer) +SVC_4_1(sysMailAlloc, void *, osMailQId, uint32_t, uint32_t, uint32_t, RET_pointer) +SVC_3_1(sysMailFree, osStatus, osMailQId, void *, uint32_t, RET_osStatus) + +// Mail Queue Management Service & ISR Calls + +/// Create and Initialize mail queue +osMailQId svcMailCreate (osMailQDef_t *queue_def, osThreadId thread_id) { + uint32_t blk_sz; + P_MCB pmcb; + void *pool; + + if ((queue_def == NULL) || + (queue_def->queue_sz == 0) || + (queue_def->item_sz == 0) || + (queue_def->pool == NULL)) { + sysThreadError(osErrorParameter); + return NULL; + } + + pmcb = *(((void **)queue_def->pool) + 0); + pool = *(((void **)queue_def->pool) + 1); + + if ((pool == NULL) || (pmcb == NULL) || (pmcb->cb_type != 0)) { + sysThreadError(osErrorParameter); + return NULL; + } + + blk_sz = (queue_def->item_sz + 3) & ~3; + + _init_box(pool, sizeof(struct OS_BM) + queue_def->queue_sz * blk_sz, blk_sz); + + rt_mbx_init(pmcb, 4*(queue_def->queue_sz + 4)); + + + return queue_def->pool; +} + +/// Allocate a memory block from a mail +void *sysMailAlloc (osMailQId queue_id, uint32_t millisec, uint32_t isr, uint32_t clr) { + P_MCB pmcb; + void *pool; + void *mem; + + if (queue_id == NULL) return NULL; + + pmcb = *(((void **)queue_id) + 0); + pool = *(((void **)queue_id) + 1); + + if ((pool == NULL) || (pmcb == NULL)) return NULL; + + if (isr && (millisec != 0)) return NULL; + + mem = rt_alloc_box(pool); + if (clr) { + rt_clr_box(pool, mem); + } + + if ((mem == NULL) && (millisec != 0)) { + // Put Task to sleep when Memory not available + if (pmcb->p_lnk != NULL) { + rt_put_prio((P_XCB)pmcb, os_tsk.run); + } else { + pmcb->p_lnk = os_tsk.run; + os_tsk.run->p_lnk = NULL; + os_tsk.run->p_rlnk = (P_TCB)pmcb; + // Task is waiting to allocate a message + pmcb->state = 3; + } + rt_block(rt_ms2tick(millisec), WAIT_MBX); + } + + return mem; +} + +/// Free a memory block from a mail +osStatus sysMailFree (osMailQId queue_id, void *mail, uint32_t isr) { + P_MCB pmcb; + P_TCB ptcb; + void *pool; + void *mem; + int32_t res; + + if (queue_id == NULL) return osErrorParameter; + + pmcb = *(((void **)queue_id) + 0); + pool = *(((void **)queue_id) + 1); + + if ((pmcb == NULL) || (pool == NULL)) return osErrorParameter; + + res = rt_free_box(pool, mail); + + if (res != 0) return osErrorValue; + + if (pmcb->state == 3) { + // Task is waiting to allocate a message + if (isr) { + rt_psq_enq (pmcb, (U32)pool); + rt_psh_req (); + } else { + mem = rt_alloc_box(pool); + if (mem != NULL) { + ptcb = rt_get_first((P_XCB)pmcb); + if (pmcb->p_lnk == NULL) { + pmcb->state = 0; + } + rt_ret_val(ptcb, (U32)mem); + rt_rmv_dly(ptcb); + rt_dispatch(ptcb); + } + } + } + + return osOK; +} + + +// Mail Queue Management Public API + +/// Create and Initialize mail queue +osMailQId osMailCreate (osMailQDef_t *queue_def, osThreadId thread_id) { + if (__get_IPSR() != 0) return NULL; // Not allowed in ISR + if (((__get_CONTROL() & 1) == 0) && (os_running == 0)) { + // Privileged and not running + return svcMailCreate(queue_def, thread_id); + } else { + return __svcMailCreate(queue_def, thread_id); + } +} + +/// Allocate a memory block from a mail +void *osMailAlloc (osMailQId queue_id, uint32_t millisec) { + if (__get_IPSR() != 0) { // in ISR + return sysMailAlloc(queue_id, millisec, 1, 0); + } else { // in Thread + return __sysMailAlloc(queue_id, millisec, 0, 0); + } +} + +/// Allocate a memory block from a mail and set memory block to zero +void *osMailCAlloc (osMailQId queue_id, uint32_t millisec) { + if (__get_IPSR() != 0) { // in ISR + return sysMailAlloc(queue_id, millisec, 1, 1); + } else { // in Thread + return __sysMailAlloc(queue_id, millisec, 0, 1); + } +} + +/// Free a memory block from a mail +osStatus osMailFree (osMailQId queue_id, void *mail) { + if (__get_IPSR() != 0) { // in ISR + return sysMailFree(queue_id, mail, 1); + } else { // in Thread + return __sysMailFree(queue_id, mail, 0); + } +} + +/// Put a mail to a queue +osStatus osMailPut (osMailQId queue_id, void *mail) { + if (queue_id == NULL) return osErrorParameter; + if (mail == NULL) return osErrorValue; + return osMessagePut(*((void **)queue_id), (uint32_t)mail, 0); +} + +/// Get a mail from a queue +os_InRegs osEvent osMailGet (osMailQId queue_id, uint32_t millisec) { + osEvent ret; + + if (queue_id == NULL) { + ret.status = osErrorParameter; + return ret; + } + + ret = osMessageGet(*((void **)queue_id), millisec); + if (ret.status == osEventMessage) ret.status = osEventMail; + + return ret; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_Event.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,190 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_EVENT.C + * Purpose: Implements waits and wake-ups for event flags + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +#include "rt_TypeDef.h" +#include "RTX_Config.h" +#include "rt_System.h" +#include "rt_Event.h" +#include "rt_List.h" +#include "rt_Task.h" +#include "rt_HAL_CM.h" + + +/*---------------------------------------------------------------------------- + * Functions + *---------------------------------------------------------------------------*/ + + +/*--------------------------- rt_evt_wait -----------------------------------*/ + +OS_RESULT rt_evt_wait (U16 wait_flags, U16 timeout, BOOL and_wait) { + /* Wait for one or more event flags with optional time-out. */ + /* "wait_flags" identifies the flags to wait for. */ + /* "timeout" is the time-out limit in system ticks (0xffff if no time-out) */ + /* "and_wait" specifies the AND-ing of "wait_flags" as condition to be met */ + /* to complete the wait. (OR-ing if set to 0). */ + U32 block_state; + + if (and_wait) { + /* Check for AND-connected events */ + if ((os_tsk.run->events & wait_flags) == wait_flags) { + os_tsk.run->events &= ~wait_flags; + return (OS_R_EVT); + } + block_state = WAIT_AND; + } + else { + /* Check for OR-connected events */ + if (os_tsk.run->events & wait_flags) { + os_tsk.run->waits = os_tsk.run->events & wait_flags; + os_tsk.run->events &= ~wait_flags; + return (OS_R_EVT); + } + block_state = WAIT_OR; + } + /* Task has to wait */ + os_tsk.run->waits = wait_flags; + rt_block (timeout, (U8)block_state); + return (OS_R_TMO); +} + + +/*--------------------------- rt_evt_set ------------------------------------*/ + +void rt_evt_set (U16 event_flags, OS_TID task_id) { + /* Set one or more event flags of a selectable task. */ + P_TCB p_tcb; + + p_tcb = os_active_TCB[task_id-1]; + if (p_tcb == NULL) { + return; + } + p_tcb->events |= event_flags; + event_flags = p_tcb->waits; + /* If the task is not waiting for an event, it should not be put */ + /* to ready state. */ + if (p_tcb->state == WAIT_AND) { + /* Check for AND-connected events */ + if ((p_tcb->events & event_flags) == event_flags) { + goto wkup; + } + } + if (p_tcb->state == WAIT_OR) { + /* Check for OR-connected events */ + if (p_tcb->events & event_flags) { + p_tcb->waits &= p_tcb->events; +wkup: p_tcb->events &= ~event_flags; + rt_rmv_dly (p_tcb); + p_tcb->state = READY; +#ifdef __CMSIS_RTOS + rt_ret_val2(p_tcb, 0x08/*osEventSignal*/, p_tcb->waits); +#else + rt_ret_val (p_tcb, OS_R_EVT); +#endif + rt_dispatch (p_tcb); + } + } +} + + +/*--------------------------- rt_evt_clr ------------------------------------*/ + +void rt_evt_clr (U16 clear_flags, OS_TID task_id) { + /* Clear one or more event flags (identified by "clear_flags") of a */ + /* selectable task (identified by "task"). */ + P_TCB task = os_active_TCB[task_id-1]; + + if (task == NULL) { + return; + } + task->events &= ~clear_flags; +} + + +/*--------------------------- isr_evt_set -----------------------------------*/ + +void isr_evt_set (U16 event_flags, OS_TID task_id) { + /* Same function as "os_evt_set", but to be called by ISRs. */ + P_TCB p_tcb = os_active_TCB[task_id-1]; + + if (p_tcb == NULL) { + return; + } + rt_psq_enq (p_tcb, event_flags); + rt_psh_req (); +} + + +/*--------------------------- rt_evt_get ------------------------------------*/ + +U16 rt_evt_get (void) { + /* Get events of a running task after waiting for OR connected events. */ + return (os_tsk.run->waits); +} + + +/*--------------------------- rt_evt_psh ------------------------------------*/ + +void rt_evt_psh (P_TCB p_CB, U16 set_flags) { + /* Check if task has to be waken up */ + U16 event_flags; + + p_CB->events |= set_flags; + event_flags = p_CB->waits; + if (p_CB->state == WAIT_AND) { + /* Check for AND-connected events */ + if ((p_CB->events & event_flags) == event_flags) { + goto rdy; + } + } + if (p_CB->state == WAIT_OR) { + /* Check for OR-connected events */ + if (p_CB->events & event_flags) { + p_CB->waits &= p_CB->events; +rdy: p_CB->events &= ~event_flags; + rt_rmv_dly (p_CB); + p_CB->state = READY; +#ifdef __CMSIS_RTOS + rt_ret_val2(p_CB, 0x08/*osEventSignal*/, p_CB->waits); +#else + rt_ret_val (p_CB, OS_R_EVT); +#endif + rt_put_prio (&os_rdy, p_CB); + } + } +} + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_Event.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,46 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_EVENT.H + * Purpose: Implements waits and wake-ups for event flags + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +/* Functions */ +extern OS_RESULT rt_evt_wait (U16 wait_flags, U16 timeout, BOOL and_wait); +extern void rt_evt_set (U16 event_flags, OS_TID task_id); +extern void rt_evt_clr (U16 clear_flags, OS_TID task_id); +extern void isr_evt_set (U16 event_flags, OS_TID task_id); +extern U16 rt_evt_get (void); +extern void rt_evt_psh (P_TCB p_CB, U16 set_flags); + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_HAL_CM.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,276 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_HAL_CM.H + * Purpose: Hardware Abstraction Layer for Cortex-M definitions + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +/* Definitions */ +#define INITIAL_xPSR 0x01000000 +#define DEMCR_TRCENA 0x01000000 +#define ITM_ITMENA 0x00000001 +#define MAGIC_WORD 0xE25A2EA5 + +#if defined (__CC_ARM) /* ARM Compiler */ + +#if ((__TARGET_ARCH_7_M || __TARGET_ARCH_7E_M) && !NO_EXCLUSIVE_ACCESS) + #define __USE_EXCLUSIVE_ACCESS +#else + #undef __USE_EXCLUSIVE_ACCESS +#endif + +#elif defined (__GNUC__) /* GNU Compiler */ + +#undef __USE_EXCLUSIVE_ACCESS + +#if defined (__CORTEX_M0) +#define __TARGET_ARCH_6S_M 1 +#else +#define __TARGET_ARCH_6S_M 0 +#endif + +#if defined (__VFP_FP__) && !defined(__SOFTFP__) +#define __TARGET_FPU_VFP 1 +#else +#define __TARGET_FPU_VFP 0 +#endif + +#define __inline inline +#define __weak __attribute__((weak)) + +#ifndef __CMSIS_GENERIC + +__attribute__((always_inline)) static inline void __enable_irq(void) +{ + __asm volatile ("cpsie i"); +} + +__attribute__((always_inline)) static inline U32 __disable_irq(void) +{ + U32 result; + + __asm volatile ("mrs %0, primask" : "=r" (result)); + __asm volatile ("cpsid i"); + return(result & 1); +} + +#endif + +__attribute__(( always_inline)) static inline U8 __clz(U32 value) +{ + U8 result; + + __asm volatile ("clz %0, %1" : "=r" (result) : "r" (value)); + return(result); +} + +#elif defined (__ICCARM__) /* IAR Compiler */ + +#undef __USE_EXCLUSIVE_ACCESS + +#if (__CORE__ == __ARM6M__) +#define __TARGET_ARCH_6S_M 1 +#else +#define __TARGET_ARCH_6S_M 0 +#endif + +#if defined __ARMVFP__ +#define __TARGET_FPU_VFP 1 +#else +#define __TARGET_FPU_VFP 0 +#endif + +#define __inline inline + +#ifndef __CMSIS_GENERIC + +static inline void __enable_irq(void) +{ + __asm volatile ("cpsie i"); +} + +static inline U32 __disable_irq(void) +{ + U32 result; + + __asm volatile ("mrs %0, primask" : "=r" (result)); + __asm volatile ("cpsid i"); + return(result & 1); +} + +#endif + +static inline U8 __clz(U32 value) +{ + U8 result; + + __asm volatile ("clz %0, %1" : "=r" (result) : "r" (value)); + return(result); +} + +#endif + +/* NVIC registers */ +#define NVIC_ST_CTRL (*((volatile U32 *)0xE000E010)) +#define NVIC_ST_RELOAD (*((volatile U32 *)0xE000E014)) +#define NVIC_ST_CURRENT (*((volatile U32 *)0xE000E018)) +#define NVIC_ISER ((volatile U32 *)0xE000E100) +#define NVIC_ICER ((volatile U32 *)0xE000E180) +#if (__TARGET_ARCH_6S_M) +#define NVIC_IP ((volatile U32 *)0xE000E400) +#else +#define NVIC_IP ((volatile U8 *)0xE000E400) +#endif +#define NVIC_INT_CTRL (*((volatile U32 *)0xE000ED04)) +#define NVIC_AIR_CTRL (*((volatile U32 *)0xE000ED0C)) +#define NVIC_SYS_PRI2 (*((volatile U32 *)0xE000ED1C)) +#define NVIC_SYS_PRI3 (*((volatile U32 *)0xE000ED20)) + +#define OS_PEND_IRQ() NVIC_INT_CTRL = (1<<28) +#define OS_PENDING ((NVIC_INT_CTRL >> 26) & (1<<2 | 1)) +#define OS_UNPEND(fl) NVIC_INT_CTRL = (*fl = OS_PENDING) << 25 +#define OS_PEND(fl,p) NVIC_INT_CTRL = (fl | p<<2) << 26 +#define OS_LOCK() NVIC_ST_CTRL = 0x0005 +#define OS_UNLOCK() NVIC_ST_CTRL = 0x0007 + +#define OS_X_PENDING ((NVIC_INT_CTRL >> 28) & 1) +#define OS_X_UNPEND(fl) NVIC_INT_CTRL = (*fl = OS_X_PENDING) << 27 +#define OS_X_PEND(fl,p) NVIC_INT_CTRL = (fl | p) << 28 +#if (__TARGET_ARCH_6S_M) +#define OS_X_INIT(n) NVIC_IP[n>>2] |= 0xFF << (8*(n & 0x03)); \ + NVIC_ISER[n>>5] = 1 << (n & 0x1F) +#else +#define OS_X_INIT(n) NVIC_IP[n] = 0xFF; \ + NVIC_ISER[n>>5] = 1 << (n & 0x1F) +#endif +#define OS_X_LOCK(n) NVIC_ICER[n>>5] = 1 << (n & 0x1F) +#define OS_X_UNLOCK(n) NVIC_ISER[n>>5] = 1 << (n & 0x1F) + +/* Core Debug registers */ +#define DEMCR (*((volatile U32 *)0xE000EDFC)) + +/* ITM registers */ +#define ITM_CONTROL (*((volatile U32 *)0xE0000E80)) +#define ITM_ENABLE (*((volatile U32 *)0xE0000E00)) +#define ITM_PORT30_U32 (*((volatile U32 *)0xE0000078)) +#define ITM_PORT31_U32 (*((volatile U32 *)0xE000007C)) +#define ITM_PORT31_U16 (*((volatile U16 *)0xE000007C)) +#define ITM_PORT31_U8 (*((volatile U8 *)0xE000007C)) + +/* Variables */ +extern BIT dbg_msg; + +/* Functions */ +#ifdef __USE_EXCLUSIVE_ACCESS + #define rt_inc(p) while(__strex((__ldrex(p)+1),p)) + #define rt_dec(p) while(__strex((__ldrex(p)-1),p)) +#else + #define rt_inc(p) __disable_irq();(*p)++;__enable_irq(); + #define rt_dec(p) __disable_irq();(*p)--;__enable_irq(); +#endif + +__inline static U32 rt_inc_qi (U32 size, U8 *count, U8 *first) { + U32 cnt,c2; +#ifdef __USE_EXCLUSIVE_ACCESS + do { + if ((cnt = __ldrex(count)) == size) { + __clrex(); + return (cnt); } + } while (__strex(cnt+1, count)); + do { + c2 = (cnt = __ldrex(first)) + 1; + if (c2 == size) c2 = 0; + } while (__strex(c2, first)); +#else + __disable_irq(); + if ((cnt = *count) < size) { + *count = cnt+1; + c2 = (cnt = *first) + 1; + if (c2 == size) c2 = 0; + *first = c2; + } + __enable_irq (); +#endif + return (cnt); +} + +__inline static void rt_systick_init (void) { + NVIC_ST_RELOAD = os_trv; + NVIC_ST_CURRENT = 0; + NVIC_ST_CTRL = 0x0007; + NVIC_SYS_PRI3 |= 0xFF000000; +} + +__inline static void rt_svc_init (void) { +#if !(__TARGET_ARCH_6S_M) + int sh,prigroup; +#endif + NVIC_SYS_PRI3 |= 0x00FF0000; +#if (__TARGET_ARCH_6S_M) + NVIC_SYS_PRI2 |= (NVIC_SYS_PRI3<<(8+1)) & 0xFC000000; +#else + sh = 8 - __clz (~((NVIC_SYS_PRI3 << 8) & 0xFF000000)); + prigroup = ((NVIC_AIR_CTRL >> 8) & 0x07); + if (prigroup >= sh) { + sh = prigroup + 1; + } + NVIC_SYS_PRI2 = ((0xFEFFFFFF << sh) & 0xFF000000) | (NVIC_SYS_PRI2 & 0x00FFFFFF); +#endif +} + +extern void rt_set_PSP (U32 stack); +extern U32 rt_get_PSP (void); +extern void os_set_env (void); +extern void *_alloc_box (void *box_mem); +extern int _free_box (void *box_mem, void *box); + +extern void rt_init_stack (P_TCB p_TCB, FUNCP task_body); +extern void rt_ret_val (P_TCB p_TCB, U32 v0); +extern void rt_ret_val2 (P_TCB p_TCB, U32 v0, U32 v1); + +extern void dbg_init (void); +extern void dbg_task_notify (P_TCB p_tcb, BOOL create); +extern void dbg_task_switch (U32 task_id); + +#ifdef DBG_MSG +#define DBG_INIT() dbg_init() +#define DBG_TASK_NOTIFY(p_tcb,create) if (dbg_msg) dbg_task_notify(p_tcb,create) +#define DBG_TASK_SWITCH(task_id) if (dbg_msg && (os_tsk.new_tsk != os_tsk.run)) \ + dbg_task_switch(task_id) +#else +#define DBG_INIT() +#define DBG_TASK_NOTIFY(p_tcb,create) +#define DBG_TASK_SWITCH(task_id) +#endif + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_List.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,320 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_LIST.C + * Purpose: Functions for the management of different lists + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +#include "rt_TypeDef.h" +#include "RTX_Config.h" +#include "rt_System.h" +#include "rt_List.h" +#include "rt_Task.h" +#include "rt_Time.h" +#include "rt_HAL_CM.h" + +/*---------------------------------------------------------------------------- + * Global Variables + *---------------------------------------------------------------------------*/ + +/* List head of chained ready tasks */ +struct OS_XCB os_rdy; +/* List head of chained delay tasks */ +struct OS_XCB os_dly; + + +/*---------------------------------------------------------------------------- + * Functions + *---------------------------------------------------------------------------*/ + + +/*--------------------------- rt_put_prio -----------------------------------*/ + +void rt_put_prio (P_XCB p_CB, P_TCB p_task) { + /* Put task identified with "p_task" into list ordered by priority. */ + /* "p_CB" points to head of list; list has always an element at end with */ + /* a priority less than "p_task->prio". */ + P_TCB p_CB2; + U32 prio; + BOOL sem_mbx = __FALSE; + + if (p_CB->cb_type == SCB || p_CB->cb_type == MCB || p_CB->cb_type == MUCB) { + sem_mbx = __TRUE; + } + prio = p_task->prio; + p_CB2 = p_CB->p_lnk; + /* Search for an entry in the list */ + while (p_CB2 != NULL && prio <= p_CB2->prio) { + p_CB = (P_XCB)p_CB2; + p_CB2 = p_CB2->p_lnk; + } + /* Entry found, insert the task into the list */ + p_task->p_lnk = p_CB2; + p_CB->p_lnk = p_task; + if (sem_mbx) { + if (p_CB2 != NULL) { + p_CB2->p_rlnk = p_task; + } + p_task->p_rlnk = (P_TCB)p_CB; + } + else { + p_task->p_rlnk = NULL; + } +} + + +/*--------------------------- rt_get_first ----------------------------------*/ + +P_TCB rt_get_first (P_XCB p_CB) { + /* Get task at head of list: it is the task with highest priority. */ + /* "p_CB" points to head of list. */ + P_TCB p_first; + + p_first = p_CB->p_lnk; + p_CB->p_lnk = p_first->p_lnk; + if (p_CB->cb_type == SCB || p_CB->cb_type == MCB || p_CB->cb_type == MUCB) { + if (p_first->p_lnk != NULL) { + p_first->p_lnk->p_rlnk = (P_TCB)p_CB; + p_first->p_lnk = NULL; + } + p_first->p_rlnk = NULL; + } + else { + p_first->p_lnk = NULL; + } + return (p_first); +} + + +/*--------------------------- rt_put_rdy_first ------------------------------*/ + +void rt_put_rdy_first (P_TCB p_task) { + /* Put task identified with "p_task" at the head of the ready list. The */ + /* task must have at least a priority equal to highest priority in list. */ + p_task->p_lnk = os_rdy.p_lnk; + p_task->p_rlnk = NULL; + os_rdy.p_lnk = p_task; +} + + +/*--------------------------- rt_get_same_rdy_prio --------------------------*/ + +P_TCB rt_get_same_rdy_prio (void) { + /* Remove a task of same priority from ready list if any exists. Other- */ + /* wise return NULL. */ + P_TCB p_first; + + p_first = os_rdy.p_lnk; + if (p_first->prio == os_tsk.run->prio) { + os_rdy.p_lnk = os_rdy.p_lnk->p_lnk; + return (p_first); + } + return (NULL); +} + + +/*--------------------------- rt_resort_prio --------------------------------*/ + +void rt_resort_prio (P_TCB p_task) { + /* Re-sort ordered lists after the priority of 'p_task' has changed. */ + P_TCB p_CB; + + if (p_task->p_rlnk == NULL) { + if (p_task->state == READY) { + /* Task is chained into READY list. */ + p_CB = (P_TCB)&os_rdy; + goto res; + } + } + else { + p_CB = p_task->p_rlnk; + while (p_CB->cb_type == TCB) { + /* Find a header of this task chain list. */ + p_CB = p_CB->p_rlnk; + } +res:rt_rmv_list (p_task); + rt_put_prio ((P_XCB)p_CB, p_task); + } +} + + +/*--------------------------- rt_put_dly ------------------------------------*/ + +void rt_put_dly (P_TCB p_task, U16 delay) { + /* Put a task identified with "p_task" into chained delay wait list using */ + /* a delay value of "delay". */ + P_TCB p; + U32 delta,idelay = delay; + + p = (P_TCB)&os_dly; + if (p->p_dlnk == NULL) { + /* Delay list empty */ + delta = 0; + goto last; + } + delta = os_dly.delta_time; + while (delta < idelay) { + if (p->p_dlnk == NULL) { + /* End of list found */ +last: p_task->p_dlnk = NULL; + p->p_dlnk = p_task; + p_task->p_blnk = p; + p->delta_time = (U16)(idelay - delta); + p_task->delta_time = 0; + return; + } + p = p->p_dlnk; + delta += p->delta_time; + } + /* Right place found */ + p_task->p_dlnk = p->p_dlnk; + p->p_dlnk = p_task; + p_task->p_blnk = p; + if (p_task->p_dlnk != NULL) { + p_task->p_dlnk->p_blnk = p_task; + } + p_task->delta_time = (U16)(delta - idelay); + p->delta_time -= p_task->delta_time; +} + + +/*--------------------------- rt_dec_dly ------------------------------------*/ + +void rt_dec_dly (void) { + /* Decrement delta time of list head: remove tasks having a value of zero.*/ + P_TCB p_rdy; + + if (os_dly.p_dlnk == NULL) { + return; + } + os_dly.delta_time--; + while ((os_dly.delta_time == 0) && (os_dly.p_dlnk != NULL)) { + p_rdy = os_dly.p_dlnk; + if (p_rdy->p_rlnk != NULL) { + /* Task is really enqueued, remove task from semaphore/mailbox */ + /* timeout waiting list. */ + p_rdy->p_rlnk->p_lnk = p_rdy->p_lnk; + if (p_rdy->p_lnk != NULL) { + p_rdy->p_lnk->p_rlnk = p_rdy->p_rlnk; + p_rdy->p_lnk = NULL; + } + p_rdy->p_rlnk = NULL; + } + rt_put_prio (&os_rdy, p_rdy); + os_dly.delta_time = p_rdy->delta_time; + if (p_rdy->state == WAIT_ITV) { + /* Calculate the next time for interval wait. */ + p_rdy->delta_time = p_rdy->interval_time + (U16)os_time; + } + p_rdy->state = READY; + os_dly.p_dlnk = p_rdy->p_dlnk; + if (p_rdy->p_dlnk != NULL) { + p_rdy->p_dlnk->p_blnk = (P_TCB)&os_dly; + p_rdy->p_dlnk = NULL; + } + p_rdy->p_blnk = NULL; + } +} + + +/*--------------------------- rt_rmv_list -----------------------------------*/ + +void rt_rmv_list (P_TCB p_task) { + /* Remove task identified with "p_task" from ready, semaphore or mailbox */ + /* waiting list if enqueued. */ + P_TCB p_b; + + if (p_task->p_rlnk != NULL) { + /* A task is enqueued in semaphore / mailbox waiting list. */ + p_task->p_rlnk->p_lnk = p_task->p_lnk; + if (p_task->p_lnk != NULL) { + p_task->p_lnk->p_rlnk = p_task->p_rlnk; + } + return; + } + + p_b = (P_TCB)&os_rdy; + while (p_b != NULL) { + /* Search the ready list for task "p_task" */ + if (p_b->p_lnk == p_task) { + p_b->p_lnk = p_task->p_lnk; + return; + } + p_b = p_b->p_lnk; + } +} + + +/*--------------------------- rt_rmv_dly ------------------------------------*/ + +void rt_rmv_dly (P_TCB p_task) { + /* Remove task identified with "p_task" from delay list if enqueued. */ + P_TCB p_b; + + p_b = p_task->p_blnk; + if (p_b != NULL) { + /* Task is really enqueued */ + p_b->p_dlnk = p_task->p_dlnk; + if (p_task->p_dlnk != NULL) { + /* 'p_task' is in the middle of list */ + p_b->delta_time += p_task->delta_time; + p_task->p_dlnk->p_blnk = p_b; + p_task->p_dlnk = NULL; + } + else { + /* 'p_task' is at the end of list */ + p_b->delta_time = 0; + } + p_task->p_blnk = NULL; + } +} + + +/*--------------------------- rt_psq_enq ------------------------------------*/ + +void rt_psq_enq (OS_ID entry, U32 arg) { + /* Insert post service request "entry" into ps-queue. */ + U32 idx; + + idx = rt_inc_qi (os_psq->size, &os_psq->count, &os_psq->first); + if (idx < os_psq->size) { + os_psq->q[idx].id = entry; + os_psq->q[idx].arg = arg; + } + else { + os_error (OS_ERR_FIFO_OVF); + } +} + + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_List.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,67 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_LIST.H + * Purpose: Functions for the management of different lists + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +/* Definitions */ + +/* Values for 'cb_type' */ +#define TCB 0 +#define MCB 1 +#define SCB 2 +#define MUCB 3 +#define HCB 4 + +/* Variables */ +extern struct OS_XCB os_rdy; +extern struct OS_XCB os_dly; + +/* Functions */ +extern void rt_put_prio (P_XCB p_CB, P_TCB p_task); +extern P_TCB rt_get_first (P_XCB p_CB); +extern void rt_put_rdy_first (P_TCB p_task); +extern P_TCB rt_get_same_rdy_prio (void); +extern void rt_resort_prio (P_TCB p_task); +extern void rt_put_dly (P_TCB p_task, U16 delay); +extern void rt_dec_dly (void); +extern void rt_rmv_list (P_TCB p_task); +extern void rt_rmv_dly (P_TCB p_task); +extern void rt_psq_enq (OS_ID entry, U32 arg); + +/* This is a fast macro generating in-line code */ +#define rt_rdy_prio(void) (os_rdy.p_lnk->prio) + + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_Mailbox.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,292 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_MAILBOX.C + * Purpose: Implements waits and wake-ups for mailbox messages + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +#include "rt_TypeDef.h" +#include "RTX_Config.h" +#include "rt_System.h" +#include "rt_List.h" +#include "rt_Mailbox.h" +#include "rt_MemBox.h" +#include "rt_Task.h" +#include "rt_HAL_CM.h" + + +/*---------------------------------------------------------------------------- + * Functions + *---------------------------------------------------------------------------*/ + + +/*--------------------------- rt_mbx_init -----------------------------------*/ + +void rt_mbx_init (OS_ID mailbox, U16 mbx_size) { + /* Initialize a mailbox */ + P_MCB p_MCB = mailbox; + + p_MCB->cb_type = MCB; + p_MCB->state = 0; + p_MCB->isr_st = 0; + p_MCB->p_lnk = NULL; + p_MCB->first = 0; + p_MCB->last = 0; + p_MCB->count = 0; + p_MCB->size = (mbx_size + sizeof(void *) - sizeof(struct OS_MCB)) / + (U32)sizeof (void *); +} + + +/*--------------------------- rt_mbx_send -----------------------------------*/ + +OS_RESULT rt_mbx_send (OS_ID mailbox, void *p_msg, U16 timeout) { + /* Send message to a mailbox */ + P_MCB p_MCB = mailbox; + P_TCB p_TCB; + + if ((p_MCB->p_lnk != NULL) && (p_MCB->state == 1)) { + /* A task is waiting for message */ + p_TCB = rt_get_first ((P_XCB)p_MCB); +#ifdef __CMSIS_RTOS + rt_ret_val2(p_TCB, 0x10/*osEventMessage*/, (U32)p_msg); +#else + *p_TCB->msg = p_msg; + rt_ret_val (p_TCB, OS_R_MBX); +#endif + rt_rmv_dly (p_TCB); + rt_dispatch (p_TCB); + } + else { + /* Store message in mailbox queue */ + if (p_MCB->count == p_MCB->size) { + /* No free message entry, wait for one. If message queue is full, */ + /* then no task is waiting for message. The 'p_MCB->p_lnk' list */ + /* pointer can now be reused for send message waits task list. */ + if (timeout == 0) { + return (OS_R_TMO); + } + if (p_MCB->p_lnk != NULL) { + rt_put_prio ((P_XCB)p_MCB, os_tsk.run); + } + else { + p_MCB->p_lnk = os_tsk.run; + os_tsk.run->p_lnk = NULL; + os_tsk.run->p_rlnk = (P_TCB)p_MCB; + /* Task is waiting to send a message */ + p_MCB->state = 2; + } + os_tsk.run->msg = p_msg; + rt_block (timeout, WAIT_MBX); + return (OS_R_TMO); + } + /* Yes, there is a free entry in a mailbox. */ + p_MCB->msg[p_MCB->first] = p_msg; + rt_inc (&p_MCB->count); + if (++p_MCB->first == p_MCB->size) { + p_MCB->first = 0; + } + } + return (OS_R_OK); +} + + +/*--------------------------- rt_mbx_wait -----------------------------------*/ + +OS_RESULT rt_mbx_wait (OS_ID mailbox, void **message, U16 timeout) { + /* Receive a message; possibly wait for it */ + P_MCB p_MCB = mailbox; + P_TCB p_TCB; + + /* If a message is available in the fifo buffer */ + /* remove it from the fifo buffer and return. */ + if (p_MCB->count) { + *message = p_MCB->msg[p_MCB->last]; + if (++p_MCB->last == p_MCB->size) { + p_MCB->last = 0; + } + if ((p_MCB->p_lnk != NULL) && (p_MCB->state == 2)) { + /* A task is waiting to send message */ + p_TCB = rt_get_first ((P_XCB)p_MCB); +#ifdef __CMSIS_RTOS + rt_ret_val(p_TCB, 0/*osOK*/); +#else + rt_ret_val(p_TCB, OS_R_OK); +#endif + p_MCB->msg[p_MCB->first] = p_TCB->msg; + if (++p_MCB->first == p_MCB->size) { + p_MCB->first = 0; + } + rt_rmv_dly (p_TCB); + rt_dispatch (p_TCB); + } + else { + rt_dec (&p_MCB->count); + } + return (OS_R_OK); + } + /* No message available: wait for one */ + if (timeout == 0) { + return (OS_R_TMO); + } + if (p_MCB->p_lnk != NULL) { + rt_put_prio ((P_XCB)p_MCB, os_tsk.run); + } + else { + p_MCB->p_lnk = os_tsk.run; + os_tsk.run->p_lnk = NULL; + os_tsk.run->p_rlnk = (P_TCB)p_MCB; + /* Task is waiting to receive a message */ + p_MCB->state = 1; + } + rt_block(timeout, WAIT_MBX); +#ifndef __CMSIS_RTOS + os_tsk.run->msg = message; +#endif + return (OS_R_TMO); +} + + +/*--------------------------- rt_mbx_check ----------------------------------*/ + +OS_RESULT rt_mbx_check (OS_ID mailbox) { + /* Check for free space in a mailbox. Returns the number of messages */ + /* that can be stored to a mailbox. It returns 0 when mailbox is full. */ + P_MCB p_MCB = mailbox; + + return (p_MCB->size - p_MCB->count); +} + + +/*--------------------------- isr_mbx_send ----------------------------------*/ + +void isr_mbx_send (OS_ID mailbox, void *p_msg) { + /* Same function as "os_mbx_send", but to be called by ISRs. */ + P_MCB p_MCB = mailbox; + + rt_psq_enq (p_MCB, (U32)p_msg); + rt_psh_req (); +} + + +/*--------------------------- isr_mbx_receive -------------------------------*/ + +OS_RESULT isr_mbx_receive (OS_ID mailbox, void **message) { + /* Receive a message in the interrupt function. The interrupt function */ + /* should not wait for a message since this would block the rtx os. */ + P_MCB p_MCB = mailbox; + + if (p_MCB->count) { + /* A message is available in the fifo buffer. */ + *message = p_MCB->msg[p_MCB->last]; + if (p_MCB->state == 2) { + /* A task is locked waiting to send message */ + rt_psq_enq (p_MCB, 0); + rt_psh_req (); + } + rt_dec (&p_MCB->count); + if (++p_MCB->last == p_MCB->size) { + p_MCB->last = 0; + } + return (OS_R_MBX); + } + return (OS_R_OK); +} + + +/*--------------------------- rt_mbx_psh ------------------------------------*/ + +void rt_mbx_psh (P_MCB p_CB, void *p_msg) { + /* Store the message to the mailbox queue or pass it to task directly. */ + P_TCB p_TCB; + void *mem; + + if (p_CB->p_lnk != NULL) switch (p_CB->state) { +#ifdef __CMSIS_RTOS + case 3: + /* Task is waiting to allocate memory, remove it from the waiting list */ + mem = rt_alloc_box(p_msg); + if (mem == NULL) break; + p_TCB = rt_get_first ((P_XCB)p_CB); + rt_ret_val(p_TCB, (U32)mem); + p_TCB->state = READY; + rt_rmv_dly (p_TCB); + rt_put_prio (&os_rdy, p_TCB); + break; +#endif + case 2: + /* Task is waiting to send a message, remove it from the waiting list */ + p_TCB = rt_get_first ((P_XCB)p_CB); +#ifdef __CMSIS_RTOS + rt_ret_val(p_TCB, 0/*osOK*/); +#else + rt_ret_val(p_TCB, OS_R_OK); +#endif + p_CB->msg[p_CB->first] = p_TCB->msg; + rt_inc (&p_CB->count); + if (++p_CB->first == p_CB->size) { + p_CB->first = 0; + } + p_TCB->state = READY; + rt_rmv_dly (p_TCB); + rt_put_prio (&os_rdy, p_TCB); + break; + case 1: + /* Task is waiting for a message, pass the message to the task directly */ + p_TCB = rt_get_first ((P_XCB)p_CB); +#ifdef __CMSIS_RTOS + rt_ret_val2(p_TCB, 0x10/*osEventMessage*/, (U32)p_msg); +#else + *p_TCB->msg = p_msg; + rt_ret_val (p_TCB, OS_R_MBX); +#endif + p_TCB->state = READY; + rt_rmv_dly (p_TCB); + rt_put_prio (&os_rdy, p_TCB); + break; + } else { + /* No task is waiting for a message, store it to the mailbox queue */ + if (p_CB->count < p_CB->size) { + p_CB->msg[p_CB->first] = p_msg; + rt_inc (&p_CB->count); + if (++p_CB->first == p_CB->size) { + p_CB->first = 0; + } + } + else { + os_error (OS_ERR_MBX_OVF); + } + } +} + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_Mailbox.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,48 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_MAILBOX.H + * Purpose: Implements waits and wake-ups for mailbox messages + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +/* Functions */ +extern void rt_mbx_init (OS_ID mailbox, U16 mbx_size); +extern OS_RESULT rt_mbx_send (OS_ID mailbox, void *p_msg, U16 timeout); +extern OS_RESULT rt_mbx_wait (OS_ID mailbox, void **message, U16 timeout); +extern OS_RESULT rt_mbx_check (OS_ID mailbox); +extern void isr_mbx_send (OS_ID mailbox, void *p_msg); +extern OS_RESULT isr_mbx_receive (OS_ID mailbox, void **message); +extern void rt_mbx_psh (P_MCB p_CB, void *p_msg); + + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_MemBox.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,166 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_MEMBOX.C + * Purpose: Interface functions for fixed memory block management system + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +#include "rt_TypeDef.h" +#include "RTX_Config.h" +#include "rt_System.h" +#include "rt_MemBox.h" +#include "rt_HAL_CM.h" + +/*---------------------------------------------------------------------------- + * Global Functions + *---------------------------------------------------------------------------*/ + + +/*--------------------------- _init_box -------------------------------------*/ + +int _init_box (void *box_mem, U32 box_size, U32 blk_size) { + /* Initialize memory block system, returns 0 if OK, 1 if fails. */ + void *end; + void *blk; + void *next; + U32 sizeof_bm; + + /* Create memory structure. */ + if (blk_size & BOX_ALIGN_8) { + /* Memory blocks 8-byte aligned. */ + blk_size = ((blk_size & ~BOX_ALIGN_8) + 7) & ~7; + sizeof_bm = (sizeof (struct OS_BM) + 7) & ~7; + } + else { + /* Memory blocks 4-byte aligned. */ + blk_size = (blk_size + 3) & ~3; + sizeof_bm = sizeof (struct OS_BM); + } + if (blk_size == 0) { + return (1); + } + if ((blk_size + sizeof_bm) > box_size) { + return (1); + } + /* Create a Memory structure. */ + blk = ((U8 *) box_mem) + sizeof_bm; + ((P_BM) box_mem)->free = blk; + end = ((U8 *) box_mem) + box_size; + ((P_BM) box_mem)->end = end; + ((P_BM) box_mem)->blk_size = blk_size; + + /* Link all free blocks using offsets. */ + end = ((U8 *) end) - blk_size; + while (1) { + next = ((U8 *) blk) + blk_size; + if (next > end) break; + *((void **)blk) = next; + blk = next; + } + /* end marker */ + *((void **)blk) = 0; + return (0); +} + +/*--------------------------- rt_alloc_box ----------------------------------*/ + +void *rt_alloc_box (void *box_mem) { + /* Allocate a memory block and return start address. */ + void **free; +#ifndef __USE_EXCLUSIVE_ACCESS + int irq_dis; + + irq_dis = __disable_irq (); + free = ((P_BM) box_mem)->free; + if (free) { + ((P_BM) box_mem)->free = *free; + } + if (!irq_dis) __enable_irq (); +#else + do { + if ((free = (void **)__ldrex(&((P_BM) box_mem)->free)) == 0) { + __clrex(); + break; + } + } while (__strex((U32)*free, &((P_BM) box_mem)->free)); +#endif + return (free); +} + + +/*--------------------------- _calloc_box -----------------------------------*/ + +void *_calloc_box (void *box_mem) { + /* Allocate a 0-initialized memory block and return start address. */ + void *free; + U32 *p; + U32 i; + + free = _alloc_box (box_mem); + if (free) { + p = free; + for (i = ((P_BM) box_mem)->blk_size; i; i -= 4) { + *p = 0; + p++; + } + } + return (free); +} + + +/*--------------------------- rt_free_box -----------------------------------*/ + +int rt_free_box (void *box_mem, void *box) { + /* Free a memory block, returns 0 if OK, 1 if box does not belong to box_mem */ +#ifndef __USE_EXCLUSIVE_ACCESS + int irq_dis; +#endif + + if (box < box_mem || box >= ((P_BM) box_mem)->end) { + return (1); + } + +#ifndef __USE_EXCLUSIVE_ACCESS + irq_dis = __disable_irq (); + *((void **)box) = ((P_BM) box_mem)->free; + ((P_BM) box_mem)->free = box; + if (!irq_dis) __enable_irq (); +#else + do { + *((void **)box) = (void *)__ldrex(&((P_BM) box_mem)->free); + } while (__strex ((U32)box, &((P_BM) box_mem)->free)); +#endif + return (0); +} + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_MemBox.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,46 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_MEMBOX.H + * Purpose: Interface functions for fixed memory block management system + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +/* Functions */ +#define rt_init_box _init_box +#define rt_calloc_box _calloc_box +extern int _init_box (void *box_mem, U32 box_size, U32 blk_size); +extern void *rt_alloc_box (void *box_mem); +extern void * _calloc_box (void *box_mem); +extern int rt_free_box (void *box_mem, void *box); + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_Mutex.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,197 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_MUTEX.C + * Purpose: Implements mutex synchronization objects + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +#include "rt_TypeDef.h" +#include "RTX_Config.h" +#include "rt_List.h" +#include "rt_Task.h" +#include "rt_Mutex.h" +#include "rt_HAL_CM.h" + + +/*---------------------------------------------------------------------------- + * Functions + *---------------------------------------------------------------------------*/ + + +/*--------------------------- rt_mut_init -----------------------------------*/ + +void rt_mut_init (OS_ID mutex) { + /* Initialize a mutex object */ + P_MUCB p_MCB = mutex; + + p_MCB->cb_type = MUCB; + p_MCB->prio = 0; + p_MCB->level = 0; + p_MCB->p_lnk = NULL; + p_MCB->owner = NULL; +} + + +/*--------------------------- rt_mut_delete ---------------------------------*/ + +#ifdef __CMSIS_RTOS +OS_RESULT rt_mut_delete (OS_ID mutex) { + /* Delete a mutex object */ + P_MUCB p_MCB = mutex; + P_TCB p_TCB; + + /* Restore owner task's priority. */ + if (p_MCB->level != 0) { + p_MCB->owner->prio = p_MCB->prio; + if (p_MCB->owner != os_tsk.run) { + rt_resort_prio (p_MCB->owner); + } + } + + while (p_MCB->p_lnk != NULL) { + /* A task is waiting for mutex. */ + p_TCB = rt_get_first ((P_XCB)p_MCB); + rt_ret_val(p_TCB, 0/*osOK*/); + rt_rmv_dly(p_TCB); + p_TCB->state = READY; + rt_put_prio (&os_rdy, p_TCB); + } + + if (os_rdy.p_lnk && (os_rdy.p_lnk->prio > os_tsk.run->prio)) { + /* preempt running task */ + rt_put_prio (&os_rdy, os_tsk.run); + os_tsk.run->state = READY; + rt_dispatch (NULL); + } + + p_MCB->cb_type = 0; + + return (OS_R_OK); +} +#endif + + +/*--------------------------- rt_mut_release --------------------------------*/ + +OS_RESULT rt_mut_release (OS_ID mutex) { + /* Release a mutex object */ + P_MUCB p_MCB = mutex; + P_TCB p_TCB; + + if (p_MCB->level == 0 || p_MCB->owner != os_tsk.run) { + /* Unbalanced mutex release or task is not the owner */ + return (OS_R_NOK); + } + if (--p_MCB->level != 0) { + return (OS_R_OK); + } + /* Restore owner task's priority. */ + os_tsk.run->prio = p_MCB->prio; + if (p_MCB->p_lnk != NULL) { + /* A task is waiting for mutex. */ + p_TCB = rt_get_first ((P_XCB)p_MCB); +#ifdef __CMSIS_RTOS + rt_ret_val(p_TCB, 0/*osOK*/); +#else + rt_ret_val(p_TCB, OS_R_MUT); +#endif + rt_rmv_dly (p_TCB); + /* A waiting task becomes the owner of this mutex. */ + p_MCB->level = 1; + p_MCB->owner = p_TCB; + p_MCB->prio = p_TCB->prio; + /* Priority inversion, check which task continues. */ + if (os_tsk.run->prio >= rt_rdy_prio()) { + rt_dispatch (p_TCB); + } + else { + /* Ready task has higher priority than running task. */ + rt_put_prio (&os_rdy, os_tsk.run); + rt_put_prio (&os_rdy, p_TCB); + os_tsk.run->state = READY; + p_TCB->state = READY; + rt_dispatch (NULL); + } + } + else { + /* Check if own priority raised by priority inversion. */ + if (rt_rdy_prio() > os_tsk.run->prio) { + rt_put_prio (&os_rdy, os_tsk.run); + os_tsk.run->state = READY; + rt_dispatch (NULL); + } + } + return (OS_R_OK); +} + + +/*--------------------------- rt_mut_wait -----------------------------------*/ + +OS_RESULT rt_mut_wait (OS_ID mutex, U16 timeout) { + /* Wait for a mutex, continue when mutex is free. */ + P_MUCB p_MCB = mutex; + + if (p_MCB->level == 0) { + p_MCB->owner = os_tsk.run; + p_MCB->prio = os_tsk.run->prio; + goto inc; + } + if (p_MCB->owner == os_tsk.run) { + /* OK, running task is the owner of this mutex. */ +inc:p_MCB->level++; + return (OS_R_OK); + } + /* Mutex owned by another task, wait until released. */ + if (timeout == 0) { + return (OS_R_TMO); + } + /* Raise the owner task priority if lower than current priority. */ + /* This priority inversion is called priority inheritance. */ + if (p_MCB->prio < os_tsk.run->prio) { + p_MCB->owner->prio = os_tsk.run->prio; + rt_resort_prio (p_MCB->owner); + } + if (p_MCB->p_lnk != NULL) { + rt_put_prio ((P_XCB)p_MCB, os_tsk.run); + } + else { + p_MCB->p_lnk = os_tsk.run; + os_tsk.run->p_lnk = NULL; + os_tsk.run->p_rlnk = (P_TCB)p_MCB; + } + rt_block(timeout, WAIT_MUT); + return (OS_R_TMO); +} + + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_Mutex.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,44 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_MUTEX.H + * Purpose: Implements mutex synchronization objects + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +/* Functions */ +extern void rt_mut_init (OS_ID mutex); +extern OS_RESULT rt_mut_delete (OS_ID mutex); +extern OS_RESULT rt_mut_release (OS_ID mutex); +extern OS_RESULT rt_mut_wait (OS_ID mutex, U16 timeout); + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_Robin.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,84 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_ROBIN.C + * Purpose: Round Robin Task switching + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +#include "rt_TypeDef.h" +#include "RTX_Config.h" +#include "rt_List.h" +#include "rt_Task.h" +#include "rt_Time.h" +#include "rt_Robin.h" +#include "rt_HAL_CM.h" + +/*---------------------------------------------------------------------------- + * Global Variables + *---------------------------------------------------------------------------*/ + +struct OS_ROBIN os_robin; + + +/*---------------------------------------------------------------------------- + * Global Functions + *---------------------------------------------------------------------------*/ + +/*--------------------------- rt_init_robin ---------------------------------*/ + +__weak void rt_init_robin (void) { + /* Initialize Round Robin variables. */ + os_robin.task = NULL; + os_robin.tout = (U16)os_rrobin; +} + +/*--------------------------- rt_chk_robin ----------------------------------*/ + +__weak void rt_chk_robin (void) { + /* Check if Round Robin timeout expired and switch to the next ready task.*/ + P_TCB p_new; + + if (os_robin.task != os_rdy.p_lnk) { + /* New task was suspended, reset Round Robin timeout. */ + os_robin.task = os_rdy.p_lnk; + os_robin.time = (U16)os_time + os_robin.tout - 1; + } + if (os_robin.time == (U16)os_time) { + /* Round Robin timeout has expired, swap Robin tasks. */ + os_robin.task = NULL; + p_new = rt_get_first (&os_rdy); + rt_put_prio ((P_XCB)&os_rdy, p_new); + } +} + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_Robin.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,45 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_ROBIN.H + * Purpose: Round Robin Task switching definitions + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +/* Variables */ +extern struct OS_ROBIN os_robin; + +/* Functions */ +extern void rt_init_robin (void); +extern void rt_chk_robin (void); + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_Semaphore.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,183 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_SEMAPHORE.C + * Purpose: Implements binary and counting semaphores + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +#include "rt_TypeDef.h" +#include "RTX_Config.h" +#include "rt_System.h" +#include "rt_List.h" +#include "rt_Task.h" +#include "rt_Semaphore.h" +#include "rt_HAL_CM.h" + + +/*---------------------------------------------------------------------------- + * Functions + *---------------------------------------------------------------------------*/ + + +/*--------------------------- rt_sem_init -----------------------------------*/ + +void rt_sem_init (OS_ID semaphore, U16 token_count) { + /* Initialize a semaphore */ + P_SCB p_SCB = semaphore; + + p_SCB->cb_type = SCB; + p_SCB->p_lnk = NULL; + p_SCB->tokens = token_count; +} + + +/*--------------------------- rt_sem_delete ---------------------------------*/ + +#ifdef __CMSIS_RTOS +OS_RESULT rt_sem_delete (OS_ID semaphore) { + /* Delete semaphore */ + P_SCB p_SCB = semaphore; + P_TCB p_TCB; + + while (p_SCB->p_lnk != NULL) { + /* A task is waiting for token */ + p_TCB = rt_get_first ((P_XCB)p_SCB); + rt_ret_val(p_TCB, 0); + rt_rmv_dly(p_TCB); + p_TCB->state = READY; + rt_put_prio (&os_rdy, p_TCB); + } + + if (os_rdy.p_lnk && (os_rdy.p_lnk->prio > os_tsk.run->prio)) { + /* preempt running task */ + rt_put_prio (&os_rdy, os_tsk.run); + os_tsk.run->state = READY; + rt_dispatch (NULL); + } + + p_SCB->cb_type = 0; + + return (OS_R_OK); +} +#endif + + +/*--------------------------- rt_sem_send -----------------------------------*/ + +OS_RESULT rt_sem_send (OS_ID semaphore) { + /* Return a token to semaphore */ + P_SCB p_SCB = semaphore; + P_TCB p_TCB; + + if (p_SCB->p_lnk != NULL) { + /* A task is waiting for token */ + p_TCB = rt_get_first ((P_XCB)p_SCB); +#ifdef __CMSIS_RTOS + rt_ret_val(p_TCB, 1); +#else + rt_ret_val(p_TCB, OS_R_SEM); +#endif + rt_rmv_dly (p_TCB); + rt_dispatch (p_TCB); + } + else { + /* Store token. */ + p_SCB->tokens++; + } + return (OS_R_OK); +} + + +/*--------------------------- rt_sem_wait -----------------------------------*/ + +OS_RESULT rt_sem_wait (OS_ID semaphore, U16 timeout) { + /* Obtain a token; possibly wait for it */ + P_SCB p_SCB = semaphore; + + if (p_SCB->tokens) { + p_SCB->tokens--; + return (OS_R_OK); + } + /* No token available: wait for one */ + if (timeout == 0) { + return (OS_R_TMO); + } + if (p_SCB->p_lnk != NULL) { + rt_put_prio ((P_XCB)p_SCB, os_tsk.run); + } + else { + p_SCB->p_lnk = os_tsk.run; + os_tsk.run->p_lnk = NULL; + os_tsk.run->p_rlnk = (P_TCB)p_SCB; + } + rt_block(timeout, WAIT_SEM); + return (OS_R_TMO); +} + + +/*--------------------------- isr_sem_send ----------------------------------*/ + +void isr_sem_send (OS_ID semaphore) { + /* Same function as "os_sem"send", but to be called by ISRs */ + P_SCB p_SCB = semaphore; + + rt_psq_enq (p_SCB, 0); + rt_psh_req (); +} + + +/*--------------------------- rt_sem_psh ------------------------------------*/ + +void rt_sem_psh (P_SCB p_CB) { + /* Check if task has to be waken up */ + P_TCB p_TCB; + + if (p_CB->p_lnk != NULL) { + /* A task is waiting for token */ + p_TCB = rt_get_first ((P_XCB)p_CB); + rt_rmv_dly (p_TCB); + p_TCB->state = READY; +#ifdef __CMSIS_RTOS + rt_ret_val(p_TCB, 1); +#else + rt_ret_val(p_TCB, OS_R_SEM); +#endif + rt_put_prio (&os_rdy, p_TCB); + } + else { + /* Store token */ + p_CB->tokens++; + } +} + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_Semaphore.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,46 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_SEMAPHORE.H + * Purpose: Implements binary and counting semaphores + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +/* Functions */ +extern void rt_sem_init (OS_ID semaphore, U16 token_count); +extern OS_RESULT rt_sem_delete(OS_ID semaphore); +extern OS_RESULT rt_sem_send (OS_ID semaphore); +extern OS_RESULT rt_sem_wait (OS_ID semaphore, U16 timeout); +extern void isr_sem_send (OS_ID semaphore); +extern void rt_sem_psh (P_SCB p_CB); + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_System.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,299 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_SYSTEM.C + * Purpose: System Task Manager + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +#include "rt_TypeDef.h" +#include "RTX_Config.h" +#include "rt_Task.h" +#include "rt_System.h" +#include "rt_Event.h" +#include "rt_List.h" +#include "rt_Mailbox.h" +#include "rt_Semaphore.h" +#include "rt_Time.h" +#include "rt_Robin.h" +#include "rt_HAL_CM.h" + +/*---------------------------------------------------------------------------- + * Global Variables + *---------------------------------------------------------------------------*/ + +int os_tick_irqn; + +/*---------------------------------------------------------------------------- + * Local Variables + *---------------------------------------------------------------------------*/ + +static volatile BIT os_lock; +static volatile BIT os_psh_flag; +static U8 pend_flags; + +/*---------------------------------------------------------------------------- + * Global Functions + *---------------------------------------------------------------------------*/ + +#if defined (__CC_ARM) +__asm void $$RTX$$version (void) { + /* Export a version number symbol for a version control. */ + + EXPORT __RL_RTX_VER + +__RL_RTX_VER EQU 0x450 +} +#endif + + +/*--------------------------- rt_suspend ------------------------------------*/ +U32 rt_suspend (void) { + /* Suspend OS scheduler */ + U32 delta = 0xFFFF; + + rt_tsk_lock(); + + if (os_dly.p_dlnk) { + delta = os_dly.delta_time; + } +#ifndef __CMSIS_RTOS + if (os_tmr.next) { + if (os_tmr.tcnt < delta) delta = os_tmr.tcnt; + } +#endif + + return (delta); +} + + +/*--------------------------- rt_resume -------------------------------------*/ +void rt_resume (U32 sleep_time) { + /* Resume OS scheduler after suspend */ + P_TCB next; + U32 delta; + + os_tsk.run->state = READY; + rt_put_rdy_first (os_tsk.run); + + os_robin.task = NULL; + + /* Update delays. */ + if (os_dly.p_dlnk) { + delta = sleep_time; + if (delta >= os_dly.delta_time) { + delta -= os_dly.delta_time; + os_time += os_dly.delta_time; + os_dly.delta_time = 1; + while (os_dly.p_dlnk) { + rt_dec_dly(); + if (delta == 0) break; + delta--; + os_time++; + } + } else { + os_time += delta; + os_dly.delta_time -= delta; + } + } else { + os_time += sleep_time; + } + +#ifndef __CMSIS_RTOS + /* Check the user timers. */ + if (os_tmr.next) { + delta = sleep_time; + if (delta >= os_tmr.tcnt) { + delta -= os_tmr.tcnt; + os_tmr.tcnt = 1; + while (os_tmr.next) { + rt_tmr_tick(); + if (delta == 0) break; + delta--; + } + } else { + os_tmr.tcnt -= delta; + } + } +#endif + + /* Switch back to highest ready task */ + next = rt_get_first (&os_rdy); + rt_switch_req (next); + + rt_tsk_unlock(); +} + + +/*--------------------------- rt_tsk_lock -----------------------------------*/ + +void rt_tsk_lock (void) { + /* Prevent task switching by locking out scheduler */ + if (os_tick_irqn < 0) { + OS_LOCK(); + os_lock = __TRUE; + OS_UNPEND (&pend_flags); + } else { + OS_X_LOCK(os_tick_irqn); + os_lock = __TRUE; + OS_X_UNPEND (&pend_flags); + } +} + + +/*--------------------------- rt_tsk_unlock ---------------------------------*/ + +void rt_tsk_unlock (void) { + /* Unlock scheduler and re-enable task switching */ + if (os_tick_irqn < 0) { + OS_UNLOCK(); + os_lock = __FALSE; + OS_PEND (pend_flags, os_psh_flag); + os_psh_flag = __FALSE; + } else { + OS_X_UNLOCK(os_tick_irqn); + os_lock = __FALSE; + OS_X_PEND (pend_flags, os_psh_flag); + os_psh_flag = __FALSE; + } +} + + +/*--------------------------- rt_psh_req ------------------------------------*/ + +void rt_psh_req (void) { + /* Initiate a post service handling request if required. */ + if (os_lock == __FALSE) { + OS_PEND_IRQ (); + } + else { + os_psh_flag = __TRUE; + } +} + + +/*--------------------------- rt_pop_req ------------------------------------*/ + +void rt_pop_req (void) { + /* Process an ISR post service requests. */ + struct OS_XCB *p_CB; + P_TCB next; + U32 idx; + + os_tsk.run->state = READY; + rt_put_rdy_first (os_tsk.run); + + idx = os_psq->last; + while (os_psq->count) { + p_CB = os_psq->q[idx].id; + if (p_CB->cb_type == TCB) { + /* Is of TCB type */ + rt_evt_psh ((P_TCB)p_CB, (U16)os_psq->q[idx].arg); + } + else if (p_CB->cb_type == MCB) { + /* Is of MCB type */ + rt_mbx_psh ((P_MCB)p_CB, (void *)os_psq->q[idx].arg); + } + else { + /* Must be of SCB type */ + rt_sem_psh ((P_SCB)p_CB); + } + if (++idx == os_psq->size) idx = 0; + rt_dec (&os_psq->count); + } + os_psq->last = idx; + + next = rt_get_first (&os_rdy); + rt_switch_req (next); +} + + +/*--------------------------- os_tick_init ----------------------------------*/ + +__weak int os_tick_init (void) { + /* Initialize SysTick timer as system tick timer. */ + rt_systick_init (); + return (-1); /* Return IRQ number of SysTick timer */ +} + + +/*--------------------------- os_tick_irqack --------------------------------*/ + +__weak void os_tick_irqack (void) { + /* Acknowledge timer interrupt. */ +} + + +/*--------------------------- rt_systick ------------------------------------*/ + +extern void sysTimerTick(void); + +void rt_systick (void) { + /* Check for system clock update, suspend running task. */ + P_TCB next; + + os_tsk.run->state = READY; + rt_put_rdy_first (os_tsk.run); + + /* Check Round Robin timeout. */ + rt_chk_robin (); + + /* Update delays. */ + os_time++; + rt_dec_dly (); + + /* Check the user timers. */ +#ifdef __CMSIS_RTOS + sysTimerTick(); +#else + rt_tmr_tick (); +#endif + + /* Switch back to highest ready task */ + next = rt_get_first (&os_rdy); + rt_switch_req (next); +} + +/*--------------------------- rt_stk_check ----------------------------------*/ +__weak void rt_stk_check (void) { + /* Check for stack overflow. */ + if (os_tsk.run->task_id == 0x01) { + // TODO: For the main thread the check should be done against the main heap pointer + } else { + if ((os_tsk.run->tsk_stack < (U32)os_tsk.run->stack) || + (os_tsk.run->stack[0] != MAGIC_WORD)) { + os_error (OS_ERR_STK_OVF); + } + } +} + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_System.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,52 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_SYSTEM.H + * Purpose: System Task Manager definitions + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +/* Variables */ +#define os_psq ((P_PSQ)&os_fifo) +extern int os_tick_irqn; + +/* Functions */ +extern U32 rt_suspend (void); +extern void rt_resume (U32 sleep_time); +extern void rt_tsk_lock (void); +extern void rt_tsk_unlock (void); +extern void rt_psh_req (void); +extern void rt_pop_req (void); +extern void rt_systick (void); +extern void rt_stk_check (void); + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_Task.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,339 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_TASK.C + * Purpose: Task functions and system start up. + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +#include "rt_TypeDef.h" +#include "RTX_Config.h" +#include "rt_System.h" +#include "rt_Task.h" +#include "rt_List.h" +#include "rt_MemBox.h" +#include "rt_Robin.h" +#include "rt_HAL_CM.h" + +/*---------------------------------------------------------------------------- + * Global Variables + *---------------------------------------------------------------------------*/ + +/* Running and next task info. */ +struct OS_TSK os_tsk; + +/* Task Control Blocks of idle demon */ +struct OS_TCB os_idle_TCB; + + +/*---------------------------------------------------------------------------- + * Local Functions + *---------------------------------------------------------------------------*/ + +OS_TID rt_get_TID (void) { + U32 tid; + + for (tid = 1; tid <= os_maxtaskrun; tid++) { + if (os_active_TCB[tid-1] == NULL) { + return ((OS_TID)tid); + } + } + return (0); +} + +#if defined (__CC_ARM) && !defined (__MICROLIB) +/*--------------------------- __user_perthread_libspace ---------------------*/ +extern void *__libspace_start; + +void *__user_perthread_libspace (void) { + /* Provide a separate libspace for each task. */ + if (os_tsk.run == NULL) { + /* RTX not running yet. */ + return (&__libspace_start); + } + return (void *)(os_tsk.run->std_libspace); +} +#endif + +/*--------------------------- rt_init_context -------------------------------*/ + +void rt_init_context (P_TCB p_TCB, U8 priority, FUNCP task_body) { + /* Initialize general part of the Task Control Block. */ + p_TCB->cb_type = TCB; + p_TCB->state = READY; + p_TCB->prio = priority; + p_TCB->p_lnk = NULL; + p_TCB->p_rlnk = NULL; + p_TCB->p_dlnk = NULL; + p_TCB->p_blnk = NULL; + p_TCB->delta_time = 0; + p_TCB->interval_time = 0; + p_TCB->events = 0; + p_TCB->waits = 0; + p_TCB->stack_frame = 0; + + rt_init_stack (p_TCB, task_body); +} + + +/*--------------------------- rt_switch_req ---------------------------------*/ + +void rt_switch_req (P_TCB p_new) { + /* Switch to next task (identified by "p_new"). */ + os_tsk.new_tsk = p_new; + p_new->state = RUNNING; + DBG_TASK_SWITCH(p_new->task_id); +} + + +/*--------------------------- rt_dispatch -----------------------------------*/ + +void rt_dispatch (P_TCB next_TCB) { + /* Dispatch next task if any identified or dispatch highest ready task */ + /* "next_TCB" identifies a task to run or has value NULL (=no next task) */ + if (next_TCB == NULL) { + /* Running task was blocked: continue with highest ready task */ + next_TCB = rt_get_first (&os_rdy); + rt_switch_req (next_TCB); + } + else { + /* Check which task continues */ + if (next_TCB->prio > os_tsk.run->prio) { + /* preempt running task */ + rt_put_rdy_first (os_tsk.run); + os_tsk.run->state = READY; + rt_switch_req (next_TCB); + } + else { + /* put next task into ready list, no task switch takes place */ + next_TCB->state = READY; + rt_put_prio (&os_rdy, next_TCB); + } + } +} + + +/*--------------------------- rt_block --------------------------------------*/ + +void rt_block (U16 timeout, U8 block_state) { + /* Block running task and choose next ready task. */ + /* "timeout" sets a time-out value or is 0xffff (=no time-out). */ + /* "block_state" defines the appropriate task state */ + P_TCB next_TCB; + + if (timeout) { + if (timeout < 0xffff) { + rt_put_dly (os_tsk.run, timeout); + } + os_tsk.run->state = block_state; + next_TCB = rt_get_first (&os_rdy); + rt_switch_req (next_TCB); + } +} + + +/*--------------------------- rt_tsk_pass -----------------------------------*/ + +void rt_tsk_pass (void) { + /* Allow tasks of same priority level to run cooperatively.*/ + P_TCB p_new; + + p_new = rt_get_same_rdy_prio(); + if (p_new != NULL) { + rt_put_prio ((P_XCB)&os_rdy, os_tsk.run); + os_tsk.run->state = READY; + rt_switch_req (p_new); + } +} + + +/*--------------------------- rt_tsk_self -----------------------------------*/ + +OS_TID rt_tsk_self (void) { + /* Return own task identifier value. */ + if (os_tsk.run == NULL) { + return (0); + } + return (os_tsk.run->task_id); +} + + +/*--------------------------- rt_tsk_prio -----------------------------------*/ + +OS_RESULT rt_tsk_prio (OS_TID task_id, U8 new_prio) { + /* Change execution priority of a task to "new_prio". */ + P_TCB p_task; + + if (task_id == 0) { + /* Change execution priority of calling task. */ + os_tsk.run->prio = new_prio; +run:if (rt_rdy_prio() > new_prio) { + rt_put_prio (&os_rdy, os_tsk.run); + os_tsk.run->state = READY; + rt_dispatch (NULL); + } + return (OS_R_OK); + } + + /* Find the task in the "os_active_TCB" array. */ + if (task_id > os_maxtaskrun || os_active_TCB[task_id-1] == NULL) { + /* Task with "task_id" not found or not started. */ + return (OS_R_NOK); + } + p_task = os_active_TCB[task_id-1]; + p_task->prio = new_prio; + if (p_task == os_tsk.run) { + goto run; + } + rt_resort_prio (p_task); + if (p_task->state == READY) { + /* Task enqueued in a ready list. */ + p_task = rt_get_first (&os_rdy); + rt_dispatch (p_task); + } + return (OS_R_OK); +} + +/*--------------------------- rt_tsk_delete ---------------------------------*/ + +OS_RESULT rt_tsk_delete (OS_TID task_id) { + /* Terminate the task identified with "task_id". */ + P_TCB task_context; + + if (task_id == 0 || task_id == os_tsk.run->task_id) { + /* Terminate itself. */ + os_tsk.run->state = INACTIVE; + os_tsk.run->tsk_stack = rt_get_PSP (); + rt_stk_check (); + os_active_TCB[os_tsk.run->task_id-1] = NULL; + + os_tsk.run->stack = NULL; + DBG_TASK_NOTIFY(os_tsk.run, __FALSE); + os_tsk.run = NULL; + rt_dispatch (NULL); + /* The program should never come to this point. */ + } + else { + /* Find the task in the "os_active_TCB" array. */ + if (task_id > os_maxtaskrun || os_active_TCB[task_id-1] == NULL) { + /* Task with "task_id" not found or not started. */ + return (OS_R_NOK); + } + task_context = os_active_TCB[task_id-1]; + rt_rmv_list (task_context); + rt_rmv_dly (task_context); + os_active_TCB[task_id-1] = NULL; + + task_context->stack = NULL; + DBG_TASK_NOTIFY(task_context, __FALSE); + } + return (OS_R_OK); +} + + +/*--------------------------- rt_sys_init -----------------------------------*/ + +#ifdef __CMSIS_RTOS +void rt_sys_init (void) { +#else +void rt_sys_init (FUNCP first_task, U32 prio_stksz, void *stk) { +#endif + /* Initialize system and start up task declared with "first_task". */ + U32 i; + + DBG_INIT(); + + /* Initialize dynamic memory and task TCB pointers to NULL. */ + for (i = 0; i < os_maxtaskrun; i++) { + os_active_TCB[i] = NULL; + } + + /* Set up TCB of idle demon */ + os_idle_TCB.task_id = 255; + os_idle_TCB.priv_stack = idle_task_stack_size; + os_idle_TCB.stack = idle_task_stack; + rt_init_context (&os_idle_TCB, 0, os_idle_demon); + + /* Set up ready list: initially empty */ + os_rdy.cb_type = HCB; + os_rdy.p_lnk = NULL; + /* Set up delay list: initially empty */ + os_dly.cb_type = HCB; + os_dly.p_dlnk = NULL; + os_dly.p_blnk = NULL; + os_dly.delta_time = 0; + + /* Fix SP and systemvariables to assume idle task is running */ + /* Transform main program into idle task by assuming idle TCB */ +#ifndef __CMSIS_RTOS + rt_set_PSP (os_idle_TCB.tsk_stack+32); +#endif + os_tsk.run = &os_idle_TCB; + os_tsk.run->state = RUNNING; + + /* Initialize ps queue */ + os_psq->first = 0; + os_psq->last = 0; + os_psq->size = os_fifo_size; + + rt_init_robin (); + + /* Intitialize SVC and PendSV */ + rt_svc_init (); + +#ifndef __CMSIS_RTOS + /* Intitialize and start system clock timer */ + os_tick_irqn = os_tick_init (); + if (os_tick_irqn >= 0) { + OS_X_INIT(os_tick_irqn); + } + + /* Start up first user task before entering the endless loop */ + rt_tsk_create (first_task, prio_stksz, stk, NULL); +#endif +} + + +/*--------------------------- rt_sys_start ----------------------------------*/ + +#ifdef __CMSIS_RTOS +void rt_sys_start (void) { + /* Start system */ + + /* Intitialize and start system clock timer */ + os_tick_irqn = os_tick_init (); + if (os_tick_irqn >= 0) { + OS_X_INIT(os_tick_irqn); + } +} +#endif + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_Task.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,73 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_TASK.H + * Purpose: Task functions and system start up. + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +/* Definitions */ +#define __CMSIS_RTOS 1 + +/* Values for 'state' */ +#define INACTIVE 0 +#define READY 1 +#define RUNNING 2 +#define WAIT_DLY 3 +#define WAIT_ITV 4 +#define WAIT_OR 5 +#define WAIT_AND 6 +#define WAIT_SEM 7 +#define WAIT_MBX 8 +#define WAIT_MUT 9 + +/* Return codes */ +#define OS_R_TMO 0x01 +#define OS_R_EVT 0x02 +#define OS_R_SEM 0x03 +#define OS_R_MBX 0x04 +#define OS_R_MUT 0x05 + +#define OS_R_OK 0x00 +#define OS_R_NOK 0xff + +/* Variables */ +extern struct OS_TSK os_tsk; +extern struct OS_TCB os_idle_TCB; + +/* Functions */ +extern void rt_switch_req (P_TCB p_new); +extern void rt_dispatch (P_TCB next_TCB); +extern void rt_block (U16 timeout, U8 block_state); +extern void rt_tsk_pass (void); +extern OS_TID rt_tsk_self (void); +extern OS_RESULT rt_tsk_prio (OS_TID task_id, U8 new_prio); +extern OS_RESULT rt_tsk_delete (OS_TID task_id); +extern void rt_sys_init (void); +extern void rt_sys_start (void);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_Time.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,94 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_TIME.C + * Purpose: Delay and interval wait functions + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +#include "rt_TypeDef.h" +#include "RTX_Config.h" +#include "rt_Task.h" +#include "rt_Time.h" + +/*---------------------------------------------------------------------------- + * Global Variables + *---------------------------------------------------------------------------*/ + +/* Free running system tick counter */ +U32 os_time; + + +/*---------------------------------------------------------------------------- + * Functions + *---------------------------------------------------------------------------*/ + + +/*--------------------------- rt_time_get -----------------------------------*/ + +U32 rt_time_get (void) { + /* Get system time tick */ + return (os_time); +} + + +/*--------------------------- rt_dly_wait -----------------------------------*/ + +void rt_dly_wait (U16 delay_time) { + /* Delay task by "delay_time" */ + rt_block (delay_time, WAIT_DLY); +} + + +/*--------------------------- rt_itv_set ------------------------------------*/ + +void rt_itv_set (U16 interval_time) { + /* Set interval length and define start of first interval */ + os_tsk.run->interval_time = interval_time; + os_tsk.run->delta_time = interval_time + (U16)os_time; +} + + +/*--------------------------- rt_itv_wait -----------------------------------*/ + +void rt_itv_wait (void) { + /* Wait for interval end and define start of next one */ + U16 delta; + + delta = os_tsk.run->delta_time - (U16)os_time; + os_tsk.run->delta_time += os_tsk.run->interval_time; + if ((delta & 0x8000) == 0) { + rt_block (delta, WAIT_ITV); + } +} + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_Time.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,47 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_TIME.H + * Purpose: Delay and interval wait functions definitions + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ + +/* Variables */ +extern U32 os_time; + +/* Functions */ +extern U32 rt_time_get (void); +extern void rt_dly_wait (U16 delay_time); +extern void rt_itv_set (U16 interval_time); +extern void rt_itv_wait (void); + +/*---------------------------------------------------------------------------- + * end of file + *---------------------------------------------------------------------------*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/mbed-rtos/rtx/rt_TypeDef.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,128 @@ +/*---------------------------------------------------------------------------- + * RL-ARM - RTX + *---------------------------------------------------------------------------- + * Name: RT_TYPEDEF.H + * Purpose: Type Definitions + * Rev.: V4.60 + *---------------------------------------------------------------------------- + * + * Copyright (c) 1999-2009 KEIL, 2009-2012 ARM Germany GmbH + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------*/ +#ifndef RT_TYPE_DEF_H +#define RT_TYPE_DEF_H + +#include "os_tcb.h" + +typedef U32 OS_TID; +typedef void *OS_ID; +typedef U32 OS_RESULT; + +#define TCB_STACKF 32 /* 'stack_frame' offset */ +#define TCB_TSTACK 36 /* 'tsk_stack' offset */ + +typedef struct OS_PSFE { /* Post Service Fifo Entry */ + void *id; /* Object Identification */ + U32 arg; /* Object Argument */ +} *P_PSFE; + +typedef struct OS_PSQ { /* Post Service Queue */ + U8 first; /* FIFO Head Index */ + U8 last; /* FIFO Tail Index */ + U8 count; /* Number of stored items in FIFO */ + U8 size; /* FIFO Size */ + struct OS_PSFE q[1]; /* FIFO Content */ +} *P_PSQ; + +typedef struct OS_TSK { + P_TCB run; /* Current running task */ + P_TCB new_tsk; /* Scheduled task to run */ +} *P_TSK; + +typedef struct OS_ROBIN { /* Round Robin Control */ + P_TCB task; /* Round Robin task */ + U16 time; /* Round Robin switch time */ + U16 tout; /* Round Robin timeout */ +} *P_ROBIN; + +typedef struct OS_XCB { + U8 cb_type; /* Control Block Type */ + struct OS_TCB *p_lnk; /* Link pointer for ready/sem. wait list */ + struct OS_TCB *p_rlnk; /* Link pointer for sem./mbx lst backwards */ + struct OS_TCB *p_dlnk; /* Link pointer for delay list */ + struct OS_TCB *p_blnk; /* Link pointer for delay list backwards */ + U16 delta_time; /* Time until time out */ +} *P_XCB; + +typedef struct OS_MCB { + U8 cb_type; /* Control Block Type */ + U8 state; /* State flag variable */ + U8 isr_st; /* State flag variable for isr functions */ + struct OS_TCB *p_lnk; /* Chain of tasks waiting for message */ + U16 first; /* Index of the message list begin */ + U16 last; /* Index of the message list end */ + U16 count; /* Actual number of stored messages */ + U16 size; /* Maximum number of stored messages */ + void *msg[1]; /* FIFO for Message pointers 1st element */ +} *P_MCB; + +typedef struct OS_SCB { + U8 cb_type; /* Control Block Type */ + U8 mask; /* Semaphore token mask */ + U16 tokens; /* Semaphore tokens */ + struct OS_TCB *p_lnk; /* Chain of tasks waiting for tokens */ +} *P_SCB; + +typedef struct OS_MUCB { + U8 cb_type; /* Control Block Type */ + U8 prio; /* Owner task default priority */ + U16 level; /* Call nesting level */ + struct OS_TCB *p_lnk; /* Chain of tasks waiting for mutex */ + struct OS_TCB *owner; /* Mutex owner task */ +} *P_MUCB; + +typedef struct OS_XTMR { + struct OS_TMR *next; + U16 tcnt; +} *P_XTMR; + +typedef struct OS_TMR { + struct OS_TMR *next; /* Link pointer to Next timer */ + U16 tcnt; /* Timer delay count */ + U16 info; /* User defined call info */ +} *P_TMR; + +typedef struct OS_BM { + void *free; /* Pointer to first free memory block */ + void *end; /* Pointer to memory block end */ + U32 blk_size; /* Memory block size */ +} *P_BM; + +/* Definitions */ +#define __TRUE 1 +#define __FALSE 0 +#define NULL ((void *) 0) + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHostBTstack/USBHostBTstack.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,215 @@ +// USBHostBTstack.cpp +#include "USBHostBTstack.h" +#include "dbg.h" + +//#define BTSTACK_DEBUG + +#ifdef BTSTACK_DEBUG +#define BT_DBG(x, ...) std::printf("[%s:%d]"x"\r\n", __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); +#define BT_DBG_BYTES(STR,BUF,LEN) _debug_bytes(__PRETTY_FUNCTION__,__LINE__,STR,BUF,LEN) +#else +#define BT_DBG(...) while(0); +#define BT_DBG_BYTES(S,BUF,LEN) while(0); +#endif + +#define HCI_COMMAND_DATA_PACKET 0x01 +#define HCI_ACL_DATA_PACKET 0x02 +#define HCI_SCO_DATA_PACKET 0x03 +#define HCI_EVENT_PACKET 0x04 + +USBHostBTstack::USBHostBTstack() +{ + host = USBHost::getHostInst(); + init(); + m_pCb = NULL; +} + +void USBHostBTstack::init() +{ + BT_DBG(""); + dev = NULL; + int_in = NULL; + bulk_in = NULL; + bulk_out = NULL; + btstack_intf = -1; + btstack_device_found = false; + dev_connected = false; + ep_int_in = false; + ep_bulk_in = false; + ep_bulk_out = false; +} + +bool USBHostBTstack::connected() +{ + return dev_connected; +} + +bool USBHostBTstack::connect() +{ + if (dev_connected) { + return true; + } + + for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; i++) { + if ((dev = host->getDevice(i)) != NULL) { + + BT_DBG("Trying to connect BTstack device\r\n"); + + if(host->enumerate(dev, this)) { + break; + } + if (btstack_device_found) { + int_in = dev->getEndpoint(btstack_intf, INTERRUPT_ENDPOINT, IN); + bulk_in = dev->getEndpoint(btstack_intf, BULK_ENDPOINT, IN); + bulk_out = dev->getEndpoint(btstack_intf, BULK_ENDPOINT, OUT); + if (!int_in || !bulk_in || !bulk_out) { + continue; + } + USB_INFO("New BTstack device: VID:%04x PID:%04x [dev: %p - intf: %d]", dev->getVid(), dev->getPid(), dev, btstack_intf); + dev->setName("BTstack", btstack_intf); + host->registerDriver(dev, btstack_intf, this, &USBHostBTstack::init); + + int_in->attach(this, &USBHostBTstack::int_rxHandler); + host->interruptRead(dev, int_in, int_report, int_in->getSize(), false); + + bulk_in->attach(this, &USBHostBTstack::bulk_rxHandler); + host->bulkRead(dev, bulk_in, bulk_report, bulk_in->getSize(), false); + + dev_connected = true; + return true; + } + } + } + init(); + return false; +} + +void USBHostBTstack::int_rxHandler() +{ + int len = int_in->getLengthTransferred(); + BT_DBG_BYTES("HCI_EVT:", int_report, len); + Packet* pkt = mail_box.alloc(); + TEST_ASSERT(pkt); + pkt->type = HCI_EVENT_PACKET; + pkt->buf = int_report; + pkt->len = len; + mail_box.put(pkt); +} + +void USBHostBTstack::bulk_rxHandler() +{ + int len = bulk_in->getLengthTransferred(); + BT_DBG_BYTES("ACL_RCV:", bulk_report, len); + Packet* pkt = mail_box.alloc(); + TEST_ASSERT(pkt); + pkt->type = HCI_ACL_DATA_PACKET; + pkt->buf = bulk_report; + pkt->len = len; + mail_box.put(pkt); +} + +/*virtual*/ void USBHostBTstack::setVidPid(uint16_t vid, uint16_t pid) +{ + BT_DBG("vid:%04x,pid:%04x", vid, pid); +} + +/*virtual*/ bool USBHostBTstack::parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) //Must return true if the interface should be parsed +{ + BT_DBG("intf_nb=%d,intf_class=%02X,intf_subclass=%d,intf_protocol=%d", intf_nb, intf_class, intf_subclass, intf_protocol); + if ((btstack_intf == -1) && intf_class == 0xe0) { + btstack_intf = intf_nb; + return true; + } + return false; +} + +/*virtual*/ bool USBHostBTstack::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) //Must return true if the endpoint will be used +{ + BT_DBG("intf_nb:%d,type:%d,dir:%d",intf_nb, type, dir); + if (intf_nb == btstack_intf) { + if (ep_int_in == false && type == INTERRUPT_ENDPOINT && dir == IN) { + ep_int_in = true; + } else if (ep_bulk_in == false && type == BULK_ENDPOINT && dir == IN) { + ep_bulk_in = true; + } else if (ep_bulk_out == false && type == BULK_ENDPOINT && dir == OUT) { + ep_bulk_out = true; + } else { + return false; + } + if (ep_int_in && ep_bulk_in && ep_bulk_out) { + btstack_device_found = true; + } + return true; + } + return false; +} + +int USBHostBTstack::open() +{ + BT_DBG("%p", this); + while(!connected()) { + if (connect()) { + break; + } + Thread::wait(500); + } + return 0; +} + +int USBHostBTstack::send_packet(uint8_t packet_type, uint8_t* packet, int size) +{ + USB_TYPE res; + switch(packet_type){ + case HCI_COMMAND_DATA_PACKET: + BT_DBG_BYTES("HCI_CMD:", packet, size); + res = host->controlWrite(dev, + USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_DEVICE, + 0, 0, 0, packet, size); + TEST_ASSERT(res == USB_TYPE_OK); + break; + case HCI_ACL_DATA_PACKET: + BT_DBG_BYTES("ACL_SND:", packet, size); + res = host->bulkWrite(dev, bulk_out, packet, size); + TEST_ASSERT(res == USB_TYPE_OK); + break; + default: + TEST_ASSERT(0); + } + return 0; +} + +void USBHostBTstack::register_packet_handler(void (*pMethod)(uint8_t, uint8_t*, uint16_t) ) +{ + BT_DBG("pMethod: %p", pMethod); + m_pCb = pMethod; +} + +void USBHostBTstack::poll() +{ + osEvent evt = mail_box.get(0); // non-blocking + if (evt.status == osEventMail) { + Packet* pkt = reinterpret_cast<Packet*>(evt.value.p); + TEST_ASSERT(pkt); + //BT_DBG("type: %d, buf: %p, len: %d", pkt->type, pkt->buf, pkt->len); + if (m_pCb) { + m_pCb(pkt->type, pkt->buf, pkt->len); + } + if (dev) { + if (pkt->type == HCI_EVENT_PACKET) { + host->interruptRead(dev, int_in, int_report, int_in->getSize(), false); + } else { // HCI_ACL_DATA_PACKET + host->bulkRead(dev, bulk_in, bulk_report, bulk_in->getSize(), false); + } + } + mail_box.free(pkt); + } +} + +void _debug_bytes(const char* pretty, int line, const char* s, uint8_t* buf, int len) +{ + printf("[%s:%d]\n%s", pretty, line, s); + for(int i = 0; i < len; i++) { + printf(" %02x", buf[i]); + } + printf("\n"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHostBTstack/USBHostBTstack.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,71 @@ +#include "USBHostConf.h" +#include "USBHost.h" +#pragma once + +#define TEST_ASSERT(A) while(!(A)){fprintf(stderr,"\n\n%s@%d %s ASSERT!\n\n",__PRETTY_FUNCTION__,__LINE__,#A);exit(1);}; + +struct Packet { + uint8_t type; + uint8_t* buf; + uint16_t len; +}; + +/** + * A class to communicate a BTstack + */ +class USBHostBTstack : public IUSBEnumerator { +public: + /** + * Constructor + * + */ + USBHostBTstack(); + + /** + * Check if a BTstack device is connected + * + * @return true if a BTstack device is connected + */ + bool connected(); + + /** + * Try to connect to a BTstack device + * + * @return true if connection was successful + */ + bool connect(); + + int open(); + int send_packet(uint8_t packet_type, uint8_t* packet, int size); + void register_packet_handler( void (*pMethod)(uint8_t, uint8_t*, uint16_t)); + void poll(); + +protected: + //From IUSBEnumerator + virtual void setVidPid(uint16_t vid, uint16_t pid); + virtual bool parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol); //Must return true if the interface should be parsed + virtual bool useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir); //Must return true if the endpoint will be used + +private: + USBHost * host; + USBDeviceConnected * dev; + bool dev_connected; + uint8_t int_report[64]; + uint8_t bulk_report[64]; + USBEndpoint * int_in; + USBEndpoint * bulk_in; + USBEndpoint * bulk_out; + bool ep_int_in; + bool ep_bulk_in; + bool ep_bulk_out; + + bool btstack_device_found; + int btstack_intf; + void (*m_pCb)(uint8_t, uint8_t*, uint16_t); + Mail<Packet, 2> mail_box; + void int_rxHandler(); + void bulk_rxHandler(); + void init(); +}; + +void _debug_bytes(const char* pretty, int line, const char* s, uint8_t* buf, int len);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Wiimote/Wiimote.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,101 @@ +/* Copyright (c) 2011, mbed + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Simple decode class for Wii Remote data + +#include "Wiimote.h" + +// Wii Report Formats: +// * http://wiibrew.org/wiki/Wiimote#0x37:_Core_Buttons_and_Accelerometer_with_10_IR_bytes_and_6_Extension_Bytes +// * http://wiki.wiimoteproject.com/Reports#0x37_BTN_.2B_XLR_.2B_IR_.2B_6_EXT + +// Input Report 0x37: +// +// 0 [ ? | X(1:0) | PLUS | UP | DOWN | RIGHT | LEFT ] +// 1 [ HOME | Z(1) | Y(1) | MINUS | A | B | ONE | TWO ] +// 2 [ X(9:2) ] +// 3 [ Y(9:2) ] +// 4 [ Z(9:2) ] +// + +#include <stdio.h> +#include <math.h> + +#define WII_ZERO 0x206 +#define WII_1G 0x6A + +void Wiimote::decode(char* data) { + + // read buttons + left = (data[0] >> 0) & 0x1; + right = (data[0] >> 1) & 0x1; + down = (data[0] >> 2) & 0x1; + up = (data[0] >> 3) & 0x1; + plus = (data[0] >> 4) & 0x1; + two = (data[1] >> 0) & 0x1; + one = (data[1] >> 1) & 0x1; + b = (data[1] >> 2) & 0x1; + a = (data[1] >> 3) & 0x1; + minus = (data[1] >> 4) & 0x1; + home = (data[1] >> 7) & 0x1; + + // read accelerometers + rawx = (data[2] << 2) | (data[0] & 0x60) >> 5; + rawy = (data[3] << 2) | (data[1] & 0x20) >> 4; + rawz = (data[4] << 2) | (data[1] & 0x40) >> 5; + + // calculate accelerometer gravity + x = (float)((int)rawx - WII_ZERO) / (float)WII_1G; + y = (float)((int)rawy - WII_ZERO) / (float)WII_1G; + z = (float)((int)rawz - WII_ZERO) / (float)WII_1G; + + // calculate wheel angle + wheel = atan2(-y, -x) * (180.0 / 3.141592); +} + +void Wiimote::dump() { + printf("%d%d%d%d%d%d%d%d%d%d%d %.3f %.3f %.3f %.2f\n", left, right, down, up, plus, two, one, b, a, minus, home, x, y, z, wheel); +} + + +// Accelerometer data +// y- +// +---+ +--+ +// | + | + B +// | A | A | +// | | | | +// x+ | | x- z+ | | z- +// | 1 | 1 | +// | 2 | 2 | +// +---+ +--+ +// y+ +// +// x+ 0x19B +// x0 0x205 +// x- 0x26E +// +// y+ 0x19C 0x6A +// y0 0x206 +// y- 0x26E 0x68 +// +// z+ 0x19C +// z0 0x208 +// z- 0x26E
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Wiimote/Wiimote.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,61 @@ +/* Copyright (c) 2011, mbed + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Simple decoder class for Wii Remote data + +#ifndef WIIMOTE_H +#define WIIMOTE_H + +#include <stdint.h> + +class Wiimote { + +public: + + void decode(char *data); + void dump(); + + // buttons + uint32_t left:1; + uint32_t right:1; + uint32_t down:1; + uint32_t up:1; + uint32_t plus:1; + uint32_t two:1; + uint32_t one:1; + uint32_t a:1; + uint32_t b:1; + uint32_t minus:1; + uint32_t home:1; + + // accelerometers + uint16_t rawx; + uint16_t rawy; + uint16_t rawz; + + float x; + float y; + float z; + float wheel; +}; + +#endif + \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/bt_control.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * bt_control.h + * + * BT Control API -- allows BT Daemon to initialize and control differnt hardware + * + * Created by Matthias Ringwald on 5/19/09. + * + */ + +#pragma once + +#include <stdint.h> + +typedef enum { + POWER_WILL_SLEEP = 1, + POWER_WILL_WAKE_UP +} POWER_NOTIFICATION_t; + +typedef struct { + int (*on) (void *config); // <-- turn BT module on and configure + int (*off) (void *config); // <-- turn BT module off + int (*sleep)(void *config); // <-- put BT module to sleep - only to be called after ON + int (*wake) (void *config); // <-- wake BT module from sleep - only to be called after SLEEP + int (*valid)(void *config); // <-- test if hardware can be supported + const char * (*name) (void *config); // <-- return hardware name + + /** support for UART baud rate changes - cmd has to be stored in hci_cmd_buffer + * @return have command + */ + int (*baudrate_cmd)(void * config, uint32_t baudrate, uint8_t *hci_cmd_buffer); + + /** support custom init sequences after RESET command - cmd has to be stored in hci_cmd_buffer + * @return have command + */ + int (*next_cmd)(void *config, uint8_t * hci_cmd_buffer); + + void (*register_for_power_notifications)(void (*cb)(POWER_NOTIFICATION_t event)); + + void (*hw_error)(void); +} bt_control_t;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/btstack.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2009 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * btstack.h + * + * Created by Matthias Ringwald on 7/1/09. + * + * BTstack client API + * + */ + +#pragma once + +#include <btstack/hci_cmds.h> +#include <btstack/run_loop.h> +#include <btstack/utils.h> + +#include <stdint.h> + +#if defined __cplusplus +extern "C" { +#endif + +// Default TCP port for BTstack daemon +#define BTSTACK_PORT 13333 + +// UNIX domain socket for BTstack */ +#define BTSTACK_UNIX "/tmp/BTstack" + +// packet handler +typedef void (*btstack_packet_handler_t) (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); + +// optional: if called before bt_open, TCP socket is used instead of local unix socket +// note: address is not copied and must be valid during bt_open +void bt_use_tcp(const char * address, uint16_t port); + +// init BTstack library +int bt_open(void); + +// stop using BTstack library +int bt_close(void); + +// send hci cmd packet +int bt_send_cmd(const hci_cmd_t *cmd, ...); + +// register packet handler -- channel only valid for l2cap and rfcomm packets +// @returns old packet handler +btstack_packet_handler_t bt_register_packet_handler(btstack_packet_handler_t handler); + +void bt_send_acl(uint8_t * data, uint16_t len); + +void bt_send_l2cap(uint16_t local_cid, uint8_t *data, uint16_t len); +void bt_send_rfcomm(uint16_t rfcom_cid, uint8_t *data, uint16_t len); + +#if defined __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/btstack_memory.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * btstsack_memory.h + * + * @brief BTstack memory management via configurable memory pools + * + * @note code semi-atuomatically generated by btstack_memory_generator.py + * + */ + +#include "btstack_memory.h" +#include <btstack/memory_pool.h> + +#include <stdlib.h> + +#include "config.h" +#include "hci.h" +#include "l2cap.h" +#include "rfcomm.h" + +// MARK: hci_connection_t +#ifdef MAX_NO_HCI_CONNECTIONS +#if MAX_NO_HCI_CONNECTIONS > 0 +static hci_connection_t hci_connection_storage[MAX_NO_HCI_CONNECTIONS]; +static memory_pool_t hci_connection_pool; +void * btstack_memory_hci_connection_get(void){ + return memory_pool_get(&hci_connection_pool); +} +void btstack_memory_hci_connection_free(void *hci_connection){ + memory_pool_free(&hci_connection_pool, hci_connection); +} +#else +void * btstack_memory_hci_connection_get(void){ + return NULL; +} +void btstack_memory_hci_connection_free(void *hci_connection){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_hci_connection_get(void){ + return malloc(sizeof(hci_connection_t)); +} +void btstack_memory_hci_connection_free(void *hci_connection){ + free(hci_connection); +} +#endif + + +// MARK: l2cap_service_t +#ifdef MAX_NO_L2CAP_SERVICES +#if MAX_NO_L2CAP_SERVICES > 0 +static l2cap_service_t l2cap_service_storage[MAX_NO_L2CAP_SERVICES]; +static memory_pool_t l2cap_service_pool; +void * btstack_memory_l2cap_service_get(void){ + return memory_pool_get(&l2cap_service_pool); +} +void btstack_memory_l2cap_service_free(void *l2cap_service){ + memory_pool_free(&l2cap_service_pool, l2cap_service); +} +#else +void * btstack_memory_l2cap_service_get(void){ + return NULL; +} +void btstack_memory_l2cap_service_free(void *l2cap_service){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_l2cap_service_get(void){ + return malloc(sizeof(l2cap_service_t)); +} +void btstack_memory_l2cap_service_free(void *l2cap_service){ + free(l2cap_service); +} +#endif + + +// MARK: l2cap_channel_t +#ifdef MAX_NO_L2CAP_CHANNELS +#if MAX_NO_L2CAP_CHANNELS > 0 +static l2cap_channel_t l2cap_channel_storage[MAX_NO_L2CAP_CHANNELS]; +static memory_pool_t l2cap_channel_pool; +void * btstack_memory_l2cap_channel_get(void){ + return memory_pool_get(&l2cap_channel_pool); +} +void btstack_memory_l2cap_channel_free(void *l2cap_channel){ + memory_pool_free(&l2cap_channel_pool, l2cap_channel); +} +#else +void * btstack_memory_l2cap_channel_get(void){ + return NULL; +} +void btstack_memory_l2cap_channel_free(void *l2cap_channel){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_l2cap_channel_get(void){ + return malloc(sizeof(l2cap_channel_t)); +} +void btstack_memory_l2cap_channel_free(void *l2cap_channel){ + free(l2cap_channel); +} +#endif + + +// MARK: rfcomm_multiplexer_t +#ifdef MAX_NO_RFCOMM_MULTIPLEXERS +#if MAX_NO_RFCOMM_MULTIPLEXERS > 0 +static rfcomm_multiplexer_t rfcomm_multiplexer_storage[MAX_NO_RFCOMM_MULTIPLEXERS]; +static memory_pool_t rfcomm_multiplexer_pool; +void * btstack_memory_rfcomm_multiplexer_get(void){ + return memory_pool_get(&rfcomm_multiplexer_pool); +} +void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){ + memory_pool_free(&rfcomm_multiplexer_pool, rfcomm_multiplexer); +} +#else +void * btstack_memory_rfcomm_multiplexer_get(void){ + return NULL; +} +void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_rfcomm_multiplexer_get(void){ + return malloc(sizeof(rfcomm_multiplexer_t)); +} +void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){ + free(rfcomm_multiplexer); +} +#endif + + +// MARK: rfcomm_service_t +#ifdef MAX_NO_RFCOMM_SERVICES +#if MAX_NO_RFCOMM_SERVICES > 0 +static rfcomm_service_t rfcomm_service_storage[MAX_NO_RFCOMM_SERVICES]; +static memory_pool_t rfcomm_service_pool; +void * btstack_memory_rfcomm_service_get(void){ + return memory_pool_get(&rfcomm_service_pool); +} +void btstack_memory_rfcomm_service_free(void *rfcomm_service){ + memory_pool_free(&rfcomm_service_pool, rfcomm_service); +} +#else +void * btstack_memory_rfcomm_service_get(void){ + return NULL; +} +void btstack_memory_rfcomm_service_free(void *rfcomm_service){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_rfcomm_service_get(void){ + return malloc(sizeof(rfcomm_service_t)); +} +void btstack_memory_rfcomm_service_free(void *rfcomm_service){ + free(rfcomm_service); +} +#endif + + +// MARK: rfcomm_channel_t +#ifdef MAX_NO_RFCOMM_CHANNELS +#if MAX_NO_RFCOMM_CHANNELS > 0 +static rfcomm_channel_t rfcomm_channel_storage[MAX_NO_RFCOMM_CHANNELS]; +static memory_pool_t rfcomm_channel_pool; +void * btstack_memory_rfcomm_channel_get(void){ + return memory_pool_get(&rfcomm_channel_pool); +} +void btstack_memory_rfcomm_channel_free(void *rfcomm_channel){ + memory_pool_free(&rfcomm_channel_pool, rfcomm_channel); +} +#else +void * btstack_memory_rfcomm_channel_get(void){ + return NULL; +} +void btstack_memory_rfcomm_channel_free(void *rfcomm_channel){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_rfcomm_channel_get(void){ + return malloc(sizeof(rfcomm_channel_t)); +} +void btstack_memory_rfcomm_channel_free(void *rfcomm_channel){ + free(rfcomm_channel); +} +#endif + + +// MARK: db_mem_device_name_t +#ifdef MAX_NO_DB_MEM_DEVICE_NAMES +#if MAX_NO_DB_MEM_DEVICE_NAMES > 0 +static db_mem_device_name_t db_mem_device_name_storage[MAX_NO_DB_MEM_DEVICE_NAMES]; +static memory_pool_t db_mem_device_name_pool; +void * btstack_memory_db_mem_device_name_get(void){ + return memory_pool_get(&db_mem_device_name_pool); +} +void btstack_memory_db_mem_device_name_free(void *db_mem_device_name){ + memory_pool_free(&db_mem_device_name_pool, db_mem_device_name); +} +#else +void * btstack_memory_db_mem_device_name_get(void){ + return NULL; +} +void btstack_memory_db_mem_device_name_free(void *db_mem_device_name){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_db_mem_device_name_get(void){ + return malloc(sizeof(db_mem_device_name_t)); +} +void btstack_memory_db_mem_device_name_free(void *db_mem_device_name){ + free(db_mem_device_name); +} +#endif + + +// MARK: db_mem_device_link_key_t +#ifdef MAX_NO_DB_MEM_DEVICE_LINK_KEYS +#if MAX_NO_DB_MEM_DEVICE_LINK_KEYS > 0 +static db_mem_device_link_key_t db_mem_device_link_key_storage[MAX_NO_DB_MEM_DEVICE_LINK_KEYS]; +static memory_pool_t db_mem_device_link_key_pool; +void * btstack_memory_db_mem_device_link_key_get(void){ + return memory_pool_get(&db_mem_device_link_key_pool); +} +void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){ + memory_pool_free(&db_mem_device_link_key_pool, db_mem_device_link_key); +} +#else +void * btstack_memory_db_mem_device_link_key_get(void){ + return NULL; +} +void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_db_mem_device_link_key_get(void){ + return malloc(sizeof(db_mem_device_link_key_t)); +} +void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){ + free(db_mem_device_link_key); +} +#endif + + +// MARK: db_mem_service_t +#ifdef MAX_NO_DB_MEM_SERVICES +#if MAX_NO_DB_MEM_SERVICES > 0 +static db_mem_service_t db_mem_service_storage[MAX_NO_DB_MEM_SERVICES]; +static memory_pool_t db_mem_service_pool; +void * btstack_memory_db_mem_service_get(void){ + return memory_pool_get(&db_mem_service_pool); +} +void btstack_memory_db_mem_service_free(void *db_mem_service){ + memory_pool_free(&db_mem_service_pool, db_mem_service); +} +#else +void * btstack_memory_db_mem_service_get(void){ + return NULL; +} +void btstack_memory_db_mem_service_free(void *db_mem_service){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_db_mem_service_get(void){ + return malloc(sizeof(db_mem_service_t)); +} +void btstack_memory_db_mem_service_free(void *db_mem_service){ + free(db_mem_service); +} +#endif + +// init +void btstack_memory_init(void){ +#if MAX_NO_HCI_CONNECTIONS > 0 + memory_pool_create(&hci_connection_pool, hci_connection_storage, MAX_NO_HCI_CONNECTIONS, sizeof(hci_connection_t)); +#endif +#if MAX_NO_L2CAP_SERVICES > 0 + memory_pool_create(&l2cap_service_pool, l2cap_service_storage, MAX_NO_L2CAP_SERVICES, sizeof(l2cap_service_t)); +#endif +#if MAX_NO_L2CAP_CHANNELS > 0 + memory_pool_create(&l2cap_channel_pool, l2cap_channel_storage, MAX_NO_L2CAP_CHANNELS, sizeof(l2cap_channel_t)); +#endif +#if MAX_NO_RFCOMM_MULTIPLEXERS > 0 + memory_pool_create(&rfcomm_multiplexer_pool, rfcomm_multiplexer_storage, MAX_NO_RFCOMM_MULTIPLEXERS, sizeof(rfcomm_multiplexer_t)); +#endif +#if MAX_NO_RFCOMM_SERVICES > 0 + memory_pool_create(&rfcomm_service_pool, rfcomm_service_storage, MAX_NO_RFCOMM_SERVICES, sizeof(rfcomm_service_t)); +#endif +#if MAX_NO_RFCOMM_CHANNELS > 0 + memory_pool_create(&rfcomm_channel_pool, rfcomm_channel_storage, MAX_NO_RFCOMM_CHANNELS, sizeof(rfcomm_channel_t)); +#endif +#if MAX_NO_DB_MEM_DEVICE_NAMES > 0 + memory_pool_create(&db_mem_device_name_pool, db_mem_device_name_storage, MAX_NO_DB_MEM_DEVICE_NAMES, sizeof(db_mem_device_name_t)); +#endif +#if MAX_NO_DB_MEM_DEVICE_LINK_KEYS > 0 + memory_pool_create(&db_mem_device_link_key_pool, db_mem_device_link_key_storage, MAX_NO_DB_MEM_DEVICE_LINK_KEYS, sizeof(db_mem_device_link_key_t)); +#endif +#if MAX_NO_DB_MEM_SERVICES > 0 + memory_pool_create(&db_mem_service_pool, db_mem_service_storage, MAX_NO_DB_MEM_SERVICES, sizeof(db_mem_service_t)); +#endif +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/btstack_memory.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * btstsack_memory.h + * + * @brief BTstack memory management via configurable memory pools + * + */ + +#pragma once + +#if defined __cplusplus +extern "C" { +#endif + +void btstack_memory_init(void); + +void * btstack_memory_hci_connection_get(void); +void btstack_memory_hci_connection_free(void *hci_connection); +void * btstack_memory_l2cap_service_get(void); +void btstack_memory_l2cap_service_free(void *l2cap_service); +void * btstack_memory_l2cap_channel_get(void); +void btstack_memory_l2cap_channel_free(void *l2cap_channel); +void * btstack_memory_rfcomm_multiplexer_get(void); +void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer); +void * btstack_memory_rfcomm_service_get(void); +void btstack_memory_rfcomm_service_free(void *rfcomm_service); +void * btstack_memory_rfcomm_channel_get(void); +void btstack_memory_rfcomm_channel_free(void *rfcomm_channel); +void * btstack_memory_db_mem_device_name_get(void); +void btstack_memory_db_mem_device_name_free(void *db_mem_device_name); +void * btstack_memory_db_mem_device_link_key_get(void); +void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key); +void * btstack_memory_db_mem_service_get(void); +void btstack_memory_db_mem_service_free(void *db_mem_service); + +#if defined __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/config.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,24 @@ +#define EMBEDDED +#define HAVE_TICK + +//#define HAVE_INIT_SCRIPT +//#define HAVE_BZERO +//#define HAVE_EHCILL + +#define ENABLE_LOG_INFO +#define ENABLE_LOG_ERROR + +#define HCI_ACL_PAYLOAD_SIZE 52 + +#define HAVE_MALLOC + +//#define MAX_SPP_CONNECTIONS 1 +//#define MAX_NO_HCI_CONNECTIONS MAX_SPP_CONNECTIONS +//#define MAX_NO_L2CAP_SERVICES 2 +//#define MAX_NO_L2CAP_CHANNELS (1+MAX_SPP_CONNECTIONS) +//#define MAX_NO_RFCOMM_MULTIPLEXERS MAX_SPP_CONNECTIONS +//#define MAX_NO_RFCOMM_SERVICES 1 +//#define MAX_NO_RFCOMM_CHANNELS MAX_SPP_CONNECTIONS +//#define MAX_NO_DB_MEM_DEVICE_LINK_KEYS 2 +//#define MAX_NO_DB_MEM_DEVICE_NAMES 0 +//#define MAX_NO_DB_MEM_SERVICES 1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/debug.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * debug.h + * + * allow to funnel debug & error messages + */ + +#include "config.h" +#include "hci_dump.h" + +#include <stdio.h> + +#ifdef ENABLE_LOG_DEBUG +#ifdef HAVE_HCI_DUMP +#define log_debug(format, ...) hci_dump_log(format, ## __VA_ARGS__) +#else +#define log_debug(format, ...) printf(format, ## __VA_ARGS__) +#endif +#else +#define log_debug(...) +#endif + +#ifdef ENABLE_LOG_INFO +#ifdef HAVE_HCI_DUMP +#define log_info(format, ...) hci_dump_log(format, ## __VA_ARGS__) +#else +#define log_info(format, ...) printf(format, ## __VA_ARGS__) +#endif +#else +#define log_info(...) +#endif + +#ifdef ENABLE_LOG_ERROR +#ifdef HAVE_HCI_DUMP +#define log_error(format, ...) hci_dump_log(format, ## __VA_ARGS__) +#else +#define log_error(format, ...) printf(format, ## __VA_ARGS__) +#endif +#else +#define log_error(...) +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/hal_cpu.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2011 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * hal_cpu.h + * + * Low power mode for MCU requires that IRQs can be first blocked + * and then unblocked while entering low power mode atomically + */ +#if defined __cplusplus +extern "C" { +#endif + +void hal_cpu_disable_irqs(void); +void hal_cpu_enable_irqs(void); +void hal_cpu_enable_irqs_and_sleep(void); + +#if defined __cplusplus +} +#endif + \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/hal_mbed/hal_cpu.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2011 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * hal_cpu.c + * + * Implementation for mbed + * + */ + +#include <btstack/hal_cpu.h> + +void hal_cpu_disable_irqs(){ + +} + +void hal_cpu_enable_irqs(){ + +} + +void hal_cpu_enable_irqs_and_sleep(){ + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/hal_mbed/hal_tick.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2011 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * hal_tick.c + * + * Implementation for mbed + * + */ + +#include <btstack/hal_tick.h> +#include "mbed.h" +static Ticker tick; + +static void dummy_handler(void){}; + +static void (*tick_handler)(void) = &dummy_handler; + +void hal_tick_init(void){ + +} + +void hal_tick_set_handler(void (*handler)(void)){ + if (handler == NULL){ + tick_handler = &dummy_handler; + return; + } + tick_handler = handler; + tick.attach(tick_handler, 0.001); +} + +int hal_tick_get_tick_period_in_ms(void){ + return 1; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/hal_tick.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2011 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * hal_tick.h + * + * Hardware abstraction layer for periodic ticks + * + */ + +#pragma once + +#include <stdint.h> + +#if defined __cplusplus +extern "C" { +#endif + +void hal_tick_init(void); +void hal_tick_set_handler(void (*tick_handler)(void)); +int hal_tick_get_tick_period_in_ms(void); + +#if defined __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/hci.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,1415 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * hci.c + * + * Created by Matthias Ringwald on 4/29/09. + * + */ + +#include "config.h" + +#include "hci.h" + +#include <stdarg.h> +#include <string.h> +#include <stdio.h> + +#ifndef EMBEDDED +#include <unistd.h> // gethostbyname +#include <btstack/version.h> +#endif + +#include "btstack_memory.h" +#include "debug.h" +#include "hci_dump.h" + +#include <btstack/hci_cmds.h> + +#define HCI_CONNECTION_TIMEOUT_MS 10000 + +#ifdef USE_BLUETOOL +#include "bt_control_iphone.h" +#endif + +static void hci_update_scan_enable(void); + +// the STACK is here +static hci_stack_t hci_stack; + +/** + * get connection for a given handle + * + * @return connection OR NULL, if not found + */ +hci_connection_t * connection_for_handle(hci_con_handle_t con_handle){ + linked_item_t *it; + for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next){ + if ( ((hci_connection_t *) it)->con_handle == con_handle){ + return (hci_connection_t *) it; + } + } + return NULL; +} + +static void hci_connection_timeout_handler(timer_source_t *timer){ + hci_connection_t * connection = (hci_connection_t *) linked_item_get_user(&timer->item); +#ifdef HAVE_TIME + struct timeval tv; + gettimeofday(&tv, NULL); + if (tv.tv_sec >= connection->timestamp.tv_sec + HCI_CONNECTION_TIMEOUT_MS/1000) { + // connections might be timed out + hci_emit_l2cap_check_timeout(connection); + } +#endif +#ifdef HAVE_TICK + if (embedded_get_ticks() > connection->timestamp + embedded_ticks_for_ms(HCI_CONNECTION_TIMEOUT_MS)){ + // connections might be timed out + hci_emit_l2cap_check_timeout(connection); + } +#endif + run_loop_set_timer(timer, HCI_CONNECTION_TIMEOUT_MS); + run_loop_add_timer(timer); +} + +static void hci_connection_timestamp(hci_connection_t *connection){ +#ifdef HAVE_TIME + gettimeofday(&connection->timestamp, NULL); +#endif +#ifdef HAVE_TICK + connection->timestamp = embedded_get_ticks(); +#endif +} + +/** + * create connection for given address + * + * @return connection OR NULL, if no memory left + */ +static hci_connection_t * create_connection_for_addr(bd_addr_t addr){ + hci_connection_t * conn = (hci_connection_t *) btstack_memory_hci_connection_get(); + if (!conn) return NULL; + BD_ADDR_COPY(conn->address, addr); + conn->con_handle = 0xffff; + conn->authentication_flags = AUTH_FLAGS_NONE; + linked_item_set_user(&conn->timeout.item, conn); + conn->timeout.process = hci_connection_timeout_handler; + hci_connection_timestamp(conn); + conn->acl_recombination_length = 0; + conn->acl_recombination_pos = 0; + conn->num_acl_packets_sent = 0; + linked_list_add(&hci_stack.connections, (linked_item_t *) conn); + return conn; +} + +/** + * get connection for given address + * + * @return connection OR NULL, if not found + */ +static hci_connection_t * connection_for_address(bd_addr_t address){ + linked_item_t *it; + for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next){ + if ( ! BD_ADDR_CMP( ((hci_connection_t *) it)->address, address) ){ + return (hci_connection_t *) it; + } + } + return NULL; +} + +inline static void connectionSetAuthenticationFlags(hci_connection_t * conn, hci_authentication_flags_t flags){ + conn->authentication_flags = (hci_authentication_flags_t)(conn->authentication_flags | flags); +} + +inline static void connectionClearAuthenticationFlags(hci_connection_t * conn, hci_authentication_flags_t flags){ + conn->authentication_flags = (hci_authentication_flags_t)(conn->authentication_flags & ~flags); +} + + +/** + * add authentication flags and reset timer + */ +static void hci_add_connection_flags_for_flipped_bd_addr(uint8_t *bd_addr, hci_authentication_flags_t flags){ + bd_addr_t addr; + bt_flip_addr(addr, *(bd_addr_t *) bd_addr); + hci_connection_t * conn = connection_for_address(addr); + if (conn) { + connectionSetAuthenticationFlags(conn, flags); + hci_connection_timestamp(conn); + } +} + +int hci_authentication_active_for_handle(hci_con_handle_t handle){ + hci_connection_t * conn = connection_for_handle(handle); + if (!conn) return 0; + if (!conn->authentication_flags) return 0; + if (conn->authentication_flags & SENT_LINK_KEY_REPLY) return 0; + if (conn->authentication_flags & RECV_LINK_KEY_NOTIFICATION) return 0; + return 1; +} + +void hci_drop_link_key_for_bd_addr(bd_addr_t *addr){ + if (hci_stack.remote_device_db) { + hci_stack.remote_device_db->delete_link_key(addr); + } +} + + +/** + * count connections + */ +static int nr_hci_connections(void){ + int count = 0; + linked_item_t *it; + for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next, count++); + return count; +} + +/** + * Dummy handler called by HCI + */ +static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){ +} + +uint8_t hci_number_outgoing_packets(hci_con_handle_t handle){ + hci_connection_t * connection = connection_for_handle(handle); + if (!connection) { + log_error("hci_number_outgoing_packets connectino for handle %u does not exist!\n", handle); + return 0; + } + return connection->num_acl_packets_sent; +} + +uint8_t hci_number_free_acl_slots(){ + uint8_t free_slots = hci_stack.total_num_acl_packets; + linked_item_t *it; + for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next){ + hci_connection_t * connection = (hci_connection_t *) it; + if (free_slots < connection->num_acl_packets_sent) { + log_error("hci_number_free_acl_slots: sum of outgoing packets > total acl packets!\n"); + return 0; + } + free_slots -= connection->num_acl_packets_sent; + } + return free_slots; +} + +int hci_can_send_packet_now(uint8_t packet_type){ + + // check for async hci transport implementations + if (hci_stack.hci_transport->can_send_packet_now){ + if (!hci_stack.hci_transport->can_send_packet_now(packet_type)){ + return 0; + } + } + + // check regular Bluetooth flow control + switch (packet_type) { + case HCI_ACL_DATA_PACKET: + return hci_number_free_acl_slots(); + case HCI_COMMAND_DATA_PACKET: + return hci_stack.num_cmd_packets; + default: + return 0; + } +} + +int hci_send_acl_packet(uint8_t *packet, int size){ + + // check for free places on BT module + if (!hci_number_free_acl_slots()) return BTSTACK_ACL_BUFFERS_FULL; + + hci_con_handle_t con_handle = READ_ACL_CONNECTION_HANDLE(packet); + hci_connection_t *connection = connection_for_handle( con_handle); + if (!connection) return 0; + hci_connection_timestamp(connection); + + // count packet + connection->num_acl_packets_sent++; + // log_info("hci_send_acl_packet - handle %u, sent %u\n", connection->con_handle, connection->num_acl_packets_sent); + + // send packet + int err = hci_stack.hci_transport->send_packet(HCI_ACL_DATA_PACKET, packet, size); + + return err; +} + +static void acl_handler(uint8_t *packet, int size){ + + // get info + hci_con_handle_t con_handle = READ_ACL_CONNECTION_HANDLE(packet); + hci_connection_t *conn = connection_for_handle(con_handle); + uint8_t acl_flags = READ_ACL_FLAGS(packet); + uint16_t acl_length = READ_ACL_LENGTH(packet); + + // ignore non-registered handle + if (!conn){ + log_error( "hci.c: acl_handler called with non-registered handle %u!\n" , con_handle); + return; + } + + // update idle timestamp + hci_connection_timestamp(conn); + + // handle different packet types + switch (acl_flags & 0x03) { + + case 0x01: // continuation fragment + + // sanity check + if (conn->acl_recombination_pos == 0) { + log_error( "ACL Cont Fragment but no first fragment for handle 0x%02x\n", con_handle); + return; + } + + // append fragment payload (header already stored) + memcpy(&conn->acl_recombination_buffer[conn->acl_recombination_pos], &packet[4], acl_length ); + conn->acl_recombination_pos += acl_length; + + // log_error( "ACL Cont Fragment: acl_len %u, combined_len %u, l2cap_len %u\n", acl_length, + // conn->acl_recombination_pos, conn->acl_recombination_length); + + // forward complete L2CAP packet if complete. + if (conn->acl_recombination_pos >= conn->acl_recombination_length + 4 + 4){ // pos already incl. ACL header + + hci_stack.packet_handler(HCI_ACL_DATA_PACKET, conn->acl_recombination_buffer, conn->acl_recombination_pos); + // reset recombination buffer + conn->acl_recombination_length = 0; + conn->acl_recombination_pos = 0; + } + break; + + case 0x02: { // first fragment + + // sanity check + if (conn->acl_recombination_pos) { + log_error( "ACL First Fragment but data in buffer for handle 0x%02x\n", con_handle); + return; + } + + // peek into L2CAP packet! + uint16_t l2cap_length = READ_L2CAP_LENGTH( packet ); + + // log_error( "ACL First Fragment: acl_len %u, l2cap_len %u\n", acl_length, l2cap_length); + + // compare fragment size to L2CAP packet size + if (acl_length >= l2cap_length + 4){ + + // forward fragment as L2CAP packet + hci_stack.packet_handler(HCI_ACL_DATA_PACKET, packet, acl_length + 4); + + } else { + // store first fragment and tweak acl length for complete package + memcpy(conn->acl_recombination_buffer, packet, acl_length + 4); + conn->acl_recombination_pos = acl_length + 4; + conn->acl_recombination_length = l2cap_length; + bt_store_16(conn->acl_recombination_buffer, 2, l2cap_length +4); + } + break; + + } + default: + log_error( "hci.c: acl_handler called with invalid packet boundary flags %u\n", acl_flags & 0x03); + return; + } + + // execute main loop + hci_run(); +} + +static void hci_shutdown_connection(hci_connection_t *conn){ + log_info("Connection closed: handle %u, %s\n", conn->con_handle, bd_addr_to_str(conn->address)); + + // cancel all l2cap connections + hci_emit_disconnection_complete(conn->con_handle, 0x16); // terminated by local host + + run_loop_remove_timer(&conn->timeout); + + linked_list_remove(&hci_stack.connections, (linked_item_t *) conn); + btstack_memory_hci_connection_free( conn ); + + // now it's gone + hci_emit_nr_connections_changed(); +} + +static const uint16_t packet_type_sizes[] = { + 0, HCI_ACL_2DH1_SIZE, HCI_ACL_3DH1_SIZE, HCI_ACL_DM1_SIZE, + HCI_ACL_DH1_SIZE, 0, 0, 0, + HCI_ACL_2DH3_SIZE, HCI_ACL_3DH3_SIZE, HCI_ACL_DM3_SIZE, HCI_ACL_DH3_SIZE, + HCI_ACL_2DH5_SIZE, HCI_ACL_3DH5_SIZE, HCI_ACL_DM5_SIZE, HCI_ACL_DH5_SIZE +}; + +static uint16_t hci_acl_packet_types_for_buffer_size(uint16_t buffer_size){ + uint16_t packet_types = 0; + int i; + for (i=0;i<16;i++){ + if (packet_type_sizes[i] == 0) continue; + if (packet_type_sizes[i] <= buffer_size){ + packet_types |= 1 << i; + } + } + // flip bits for "may not be used" + packet_types ^= 0x3306; + return packet_types; +} + +uint16_t hci_usable_acl_packet_types(void){ + return hci_stack.packet_types; +} + +uint8_t* hci_get_outgoing_acl_packet_buffer(void){ + // hci packet buffer is >= acl data packet length + return hci_stack.hci_packet_buffer; +} + +uint16_t hci_max_acl_data_packet_length(){ + return hci_stack.acl_data_packet_length; +} + +// avoid huge local variables +#ifndef EMBEDDED +static device_name_t device_name; +#endif +static void event_handler(uint8_t *packet, int size){ + bd_addr_t addr; + uint8_t link_type; + hci_con_handle_t handle; + hci_connection_t * conn; + int i; + + // printf("HCI:EVENT:%02x\n", packet[0]); + + switch (packet[0]) { + + case HCI_EVENT_COMMAND_COMPLETE: + // get num cmd packets + // log_info("HCI_EVENT_COMMAND_COMPLETE cmds old %u - new %u\n", hci_stack.num_cmd_packets, packet[2]); + hci_stack.num_cmd_packets = packet[2]; + + if (COMMAND_COMPLETE_EVENT(packet, hci_read_buffer_size)){ + // from offset 5 + // status + // "The HC_ACL_Data_Packet_Length return parameter will be used to determine the size of the L2CAP segments contained in ACL Data Packets" + hci_stack.acl_data_packet_length = READ_BT_16(packet, 6); + // ignore: SCO data packet len (8) + hci_stack.total_num_acl_packets = packet[9]; + // ignore: total num SCO packets + if (hci_stack.state == HCI_STATE_INITIALIZING){ + // determine usable ACL payload size + if (HCI_ACL_PAYLOAD_SIZE < hci_stack.acl_data_packet_length){ + hci_stack.acl_data_packet_length = HCI_ACL_PAYLOAD_SIZE; + } + // determine usable ACL packet types + hci_stack.packet_types = hci_acl_packet_types_for_buffer_size(hci_stack.acl_data_packet_length); + + log_error("hci_read_buffer_size: used size %u, count %u, packet types %04x\n", + hci_stack.acl_data_packet_length, hci_stack.total_num_acl_packets, hci_stack.packet_types); + } + } + // Dump local address + if (COMMAND_COMPLETE_EVENT(packet, hci_read_bd_addr)) { + bd_addr_t addr; + bt_flip_addr(addr, &packet[OFFSET_OF_DATA_IN_COMMAND_COMPLETE + 1]); + log_info("Local Address, Status: 0x%02x: Addr: %s\n", + packet[OFFSET_OF_DATA_IN_COMMAND_COMPLETE], bd_addr_to_str(addr)); + } + if (COMMAND_COMPLETE_EVENT(packet, hci_write_scan_enable)){ + hci_emit_discoverable_enabled(hci_stack.discoverable); + } + break; + + case HCI_EVENT_COMMAND_STATUS: + // get num cmd packets + // log_info("HCI_EVENT_COMMAND_STATUS cmds - old %u - new %u\n", hci_stack.num_cmd_packets, packet[3]); + hci_stack.num_cmd_packets = packet[3]; + break; + + case HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS: + for (i=0; i<packet[2];i++){ +#if 0 + handle = READ_BT_16(packet, 3 + 2*i); + uint16_t num_packets = READ_BT_16(packet, 3 + packet[2]*2 + 2*i); +#else + //sibu + handle = READ_BT_16(packet, 3 + 4*i); + uint16_t num_packets = READ_BT_16(packet, 3 + 2 + 4*i); +#endif + conn = connection_for_handle(handle); + if (!conn){ + log_error("hci_number_completed_packet lists unused con handle %u\n", handle); + continue; + } + conn->num_acl_packets_sent -= num_packets; + // log_info("hci_number_completed_packet %u processed for handle %u, outstanding %u\n", num_packets, handle, conn->num_acl_packets_sent); + } + break; + + case HCI_EVENT_CONNECTION_REQUEST: + bt_flip_addr(addr, &packet[2]); + // TODO: eval COD 8-10 + link_type = packet[11]; + log_info("Connection_incoming: %s, type %u\n", bd_addr_to_str(addr), link_type); + if (link_type == 1) { // ACL + conn = connection_for_address(addr); + if (!conn) { + conn = create_connection_for_addr(addr); + } + if (!conn) { + // CONNECTION REJECTED DUE TO LIMITED RESOURCES (0X0D) + hci_stack.decline_reason = 0x0d; + BD_ADDR_COPY(hci_stack.decline_addr, addr); + break; + } + conn->state = RECEIVED_CONNECTION_REQUEST; + hci_run(); + } else { + // SYNCHRONOUS CONNECTION LIMIT TO A DEVICE EXCEEDED (0X0A) + hci_stack.decline_reason = 0x0a; + BD_ADDR_COPY(hci_stack.decline_addr, addr); + } + break; + + case HCI_EVENT_CONNECTION_COMPLETE: + // Connection management + bt_flip_addr(addr, &packet[5]); + log_info("Connection_complete (status=%u) %s\n", packet[2], bd_addr_to_str(addr)); + conn = connection_for_address(addr); + if (conn) { + if (!packet[2]){ + conn->state = OPEN; + conn->con_handle = READ_BT_16(packet, 3); + + // restart timer + run_loop_set_timer(&conn->timeout, HCI_CONNECTION_TIMEOUT_MS); + run_loop_add_timer(&conn->timeout); + + log_info("New connection: handle %u, %s\n", conn->con_handle, bd_addr_to_str(conn->address)); + + hci_emit_nr_connections_changed(); + } else { + // connection failed, remove entry + linked_list_remove(&hci_stack.connections, (linked_item_t *) conn); + btstack_memory_hci_connection_free( conn ); + + // if authentication error, also delete link key + if (packet[2] == 0x05) { + hci_drop_link_key_for_bd_addr(&addr); + } + } + } + break; + + case HCI_EVENT_LINK_KEY_REQUEST: + log_info("HCI_EVENT_LINK_KEY_REQUEST\n"); + hci_add_connection_flags_for_flipped_bd_addr(&packet[2], RECV_LINK_KEY_REQUEST); + if (!hci_stack.remote_device_db) break; + hci_add_connection_flags_for_flipped_bd_addr(&packet[2], HANDLE_LINK_KEY_REQUEST); + hci_run(); + // request handled by hci_run() as HANDLE_LINK_KEY_REQUEST gets set + return; + + case HCI_EVENT_LINK_KEY_NOTIFICATION: + hci_add_connection_flags_for_flipped_bd_addr(&packet[2], RECV_LINK_KEY_NOTIFICATION); + if (!hci_stack.remote_device_db) break; + bt_flip_addr(addr, &packet[2]); + hci_stack.remote_device_db->put_link_key(&addr, (link_key_t *) &packet[8]); + // still forward event to allow dismiss of pairing dialog + break; + + case HCI_EVENT_PIN_CODE_REQUEST: + hci_add_connection_flags_for_flipped_bd_addr(&packet[2], RECV_PIN_CODE_REQUEST); + // PIN CODE REQUEST means the link key request didn't succee -> delete stored link key + if (!hci_stack.remote_device_db) break; + bt_flip_addr(addr, &packet[2]); + hci_stack.remote_device_db->delete_link_key(&addr); + break; + +#ifndef EMBEDDED + case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE: + if (!hci_stack.remote_device_db) break; + if (packet[2]) break; // status not ok + bt_flip_addr(addr, &packet[3]); + // fix for invalid remote names - terminate on 0xff + for (i=0; i<248;i++){ + if (packet[9+i] == 0xff){ + packet[9+i] = 0; + break; + } + } + memset(&device_name, 0, sizeof(device_name_t)); + strncpy((char*) device_name, (char*) &packet[9], 248); + hci_stack.remote_device_db->put_name(&addr, &device_name); + break; + + case HCI_EVENT_INQUIRY_RESULT: + case HCI_EVENT_INQUIRY_RESULT_WITH_RSSI: + if (!hci_stack.remote_device_db) break; + // first send inq result packet + hci_stack.packet_handler(HCI_EVENT_PACKET, packet, size); + // then send cached remote names + for (i=0; i<packet[2];i++){ + bt_flip_addr(addr, &packet[3+i*6]); + if (hci_stack.remote_device_db->get_name(&addr, &device_name)){ + hci_emit_remote_name_cached(&addr, &device_name); + } + } + return; +#endif + + case HCI_EVENT_DISCONNECTION_COMPLETE: + if (!packet[2]){ + handle = READ_BT_16(packet, 3); + hci_connection_t * conn = connection_for_handle(handle); + if (conn) { + hci_shutdown_connection(conn); + } + } + break; + + case HCI_EVENT_HARDWARE_ERROR: + if(hci_stack.control->hw_error){ + (*hci_stack.control->hw_error)(); + } + break; + +#ifdef HAVE_BLE + case HCI_EVENT_LE_META: + switch (packet[2]) { + case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: + // Connection management + bt_flip_addr(addr, &packet[8]); + log_info("LE Connection_complete (status=%u) %s\n", packet[3], bd_addr_to_str(addr)); + // LE connections are auto-accepted, so just create a connection if there isn't one already + conn = connection_for_address(addr); + if (packet[3]){ + if (conn){ + // outgoing connection failed, remove entry + linked_list_remove(&hci_stack.connections, (linked_item_t *) conn); + btstack_memory_hci_connection_free( conn ); + + } + // if authentication error, also delete link key + if (packet[3] == 0x05) { + hci_drop_link_key_for_bd_addr(&addr); + } + break; + } + if (!conn){ + conn = create_connection_for_addr(addr); + } + if (!conn){ + // no memory + break; + } + + conn->state = OPEN; + conn->con_handle = READ_BT_16(packet, 4); + + // TODO: store - role, peer address type, conn_interval, conn_latency, supervision timeout, master clock + + // restart timer + // run_loop_set_timer(&conn->timeout, HCI_CONNECTION_TIMEOUT_MS); + // run_loop_add_timer(&conn->timeout); + + log_info("New connection: handle %u, %s\n", conn->con_handle, bd_addr_to_str(conn->address)); + + hci_emit_nr_connections_changed(); + break; + + default: + break; + } + break; +#endif + + default: + break; + } + + // handle BT initialization + if (hci_stack.state == HCI_STATE_INITIALIZING){ + // handle H4 synchronization loss on restart + // if (hci_stack.substate == 1 && packet[0] == HCI_EVENT_HARDWARE_ERROR){ + // hci_stack.substate = 0; + // } + // handle normal init sequence + if (hci_stack.substate % 2){ + // odd: waiting for event + if (packet[0] == HCI_EVENT_COMMAND_COMPLETE){ + hci_stack.substate++; + } + } + } + + // help with BT sleep + if (hci_stack.state == HCI_STATE_FALLING_ASLEEP + && hci_stack.substate == 1 + && COMMAND_COMPLETE_EVENT(packet, hci_write_scan_enable)){ + hci_stack.substate++; + } + + hci_stack.packet_handler(HCI_EVENT_PACKET, packet, size); + + // execute main loop + hci_run(); +} + +void packet_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){ + switch (packet_type) { + case HCI_EVENT_PACKET: + event_handler(packet, size); + break; + case HCI_ACL_DATA_PACKET: + acl_handler(packet, size); + break; + default: + break; + } +} + +/** Register HCI packet handlers */ +void hci_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){ + hci_stack.packet_handler = handler; +} + +void hci_init(hci_transport_t *transport, void *config, bt_control_t *control, remote_device_db_t const* remote_device_db){ + + // reference to use transport layer implementation + hci_stack.hci_transport = transport; + + // references to used control implementation + hci_stack.control = control; + + // reference to used config + hci_stack.config = config; + + // no connections yet + hci_stack.connections = NULL; + hci_stack.discoverable = 0; + hci_stack.connectable = 0; + + // no pending cmds + hci_stack.decline_reason = 0; + hci_stack.new_scan_enable_value = 0xff; + + // higher level handler + hci_stack.packet_handler = dummy_handler; + + // store and open remote device db + hci_stack.remote_device_db = remote_device_db; + if (hci_stack.remote_device_db) { + hci_stack.remote_device_db->open(); + } + + // max acl payload size defined in config.h + hci_stack.acl_data_packet_length = HCI_ACL_PAYLOAD_SIZE; + + // register packet handlers with transport + transport->register_packet_handler(&packet_handler); + + hci_stack.state = HCI_STATE_OFF; +} + +void hci_close(){ + // close remote device db + if (hci_stack.remote_device_db) { + hci_stack.remote_device_db->close(); + } + while (hci_stack.connections) { + hci_shutdown_connection((hci_connection_t *) hci_stack.connections); +} + hci_power_control(HCI_POWER_OFF); +} + +// State-Module-Driver overview +// state module low-level +// HCI_STATE_OFF off close +// HCI_STATE_INITIALIZING, on open +// HCI_STATE_WORKING, on open +// HCI_STATE_HALTING, on open +// HCI_STATE_SLEEPING, off/sleep close +// HCI_STATE_FALLING_ASLEEP on open + +static int hci_power_control_on(void){ + + // power on + int err = 0; + if (hci_stack.control && hci_stack.control->on){ + err = (*hci_stack.control->on)(hci_stack.config); + } + if (err){ + log_error( "POWER_ON failed\n"); + hci_emit_hci_open_failed(); + return err; + } + + // open low-level device + err = hci_stack.hci_transport->open(hci_stack.config); + if (err){ + log_error( "HCI_INIT failed, turning Bluetooth off again\n"); + if (hci_stack.control && hci_stack.control->off){ + (*hci_stack.control->off)(hci_stack.config); + } + hci_emit_hci_open_failed(); + return err; + } + return 0; +} + +static void hci_power_control_off(void){ + + log_info("hci_power_control_off\n"); + + // close low-level device + hci_stack.hci_transport->close(hci_stack.config); + + log_info("hci_power_control_off - hci_transport closed\n"); + + // power off + if (hci_stack.control && hci_stack.control->off){ + (*hci_stack.control->off)(hci_stack.config); + } + + log_info("hci_power_control_off - control closed\n"); + + hci_stack.state = HCI_STATE_OFF; +} + +static void hci_power_control_sleep(void){ + + log_info("hci_power_control_sleep\n"); + +#if 0 + // don't close serial port during sleep + + // close low-level device + hci_stack.hci_transport->close(hci_stack.config); +#endif + + // sleep mode + if (hci_stack.control && hci_stack.control->sleep){ + (*hci_stack.control->sleep)(hci_stack.config); + } + + hci_stack.state = HCI_STATE_SLEEPING; +} + +static int hci_power_control_wake(void){ + + log_info("hci_power_control_wake\n"); + + // wake on + if (hci_stack.control && hci_stack.control->wake){ + (*hci_stack.control->wake)(hci_stack.config); + } + +#if 0 + // open low-level device + int err = hci_stack.hci_transport->open(hci_stack.config); + if (err){ + log_error( "HCI_INIT failed, turning Bluetooth off again\n"); + if (hci_stack.control && hci_stack.control->off){ + (*hci_stack.control->off)(hci_stack.config); + } + hci_emit_hci_open_failed(); + return err; + } +#endif + + return 0; +} + + +int hci_power_control(HCI_POWER_MODE power_mode){ + + log_info("hci_power_control: %u, current mode %u\n", power_mode, hci_stack.state); + + int err = 0; + switch (hci_stack.state){ + + case HCI_STATE_OFF: + switch (power_mode){ + case HCI_POWER_ON: + err = hci_power_control_on(); + if (err) return err; + // set up state machine + hci_stack.num_cmd_packets = 1; // assume that one cmd can be sent + hci_stack.state = HCI_STATE_INITIALIZING; + hci_stack.substate = 0; + break; + case HCI_POWER_OFF: + // do nothing + break; + case HCI_POWER_SLEEP: + // do nothing (with SLEEP == OFF) + break; + } + break; + + case HCI_STATE_INITIALIZING: + switch (power_mode){ + case HCI_POWER_ON: + // do nothing + break; + case HCI_POWER_OFF: + // no connections yet, just turn it off + hci_power_control_off(); + break; + case HCI_POWER_SLEEP: + // no connections yet, just turn it off + hci_power_control_sleep(); + break; + } + break; + + case HCI_STATE_WORKING: + switch (power_mode){ + case HCI_POWER_ON: + // do nothing + break; + case HCI_POWER_OFF: + // see hci_run + hci_stack.state = HCI_STATE_HALTING; + break; + case HCI_POWER_SLEEP: + // see hci_run + hci_stack.state = HCI_STATE_FALLING_ASLEEP; + hci_stack.substate = 0; + break; + } + break; + + case HCI_STATE_HALTING: + switch (power_mode){ + case HCI_POWER_ON: + // set up state machine + hci_stack.state = HCI_STATE_INITIALIZING; + hci_stack.substate = 0; + break; + case HCI_POWER_OFF: + // do nothing + break; + case HCI_POWER_SLEEP: + // see hci_run + hci_stack.state = HCI_STATE_FALLING_ASLEEP; + hci_stack.substate = 0; + break; + } + break; + + case HCI_STATE_FALLING_ASLEEP: + switch (power_mode){ + case HCI_POWER_ON: + +#if defined(USE_POWERMANAGEMENT) && defined(USE_BLUETOOL) + // nothing to do, if H4 supports power management + if (bt_control_iphone_power_management_enabled()){ + hci_stack.state = HCI_STATE_INITIALIZING; + hci_stack.substate = 6; + break; + } +#endif + // set up state machine + hci_stack.num_cmd_packets = 1; // assume that one cmd can be sent + hci_stack.state = HCI_STATE_INITIALIZING; + hci_stack.substate = 0; + break; + case HCI_POWER_OFF: + // see hci_run + hci_stack.state = HCI_STATE_HALTING; + break; + case HCI_POWER_SLEEP: + // do nothing + break; + } + break; + + case HCI_STATE_SLEEPING: + switch (power_mode){ + case HCI_POWER_ON: + +#if defined(USE_POWERMANAGEMENT) && defined(USE_BLUETOOL) + // nothing to do, if H4 supports power management + if (bt_control_iphone_power_management_enabled()){ + hci_stack.state = HCI_STATE_INITIALIZING; + hci_stack.substate = 6; + hci_update_scan_enable(); + break; + } +#endif + err = hci_power_control_wake(); + if (err) return err; + // set up state machine + hci_stack.num_cmd_packets = 1; // assume that one cmd can be sent + hci_stack.state = HCI_STATE_INITIALIZING; + hci_stack.substate = 0; + break; + case HCI_POWER_OFF: + hci_stack.state = HCI_STATE_HALTING; + break; + case HCI_POWER_SLEEP: + // do nothing + break; + } + break; + } + + // create internal event + hci_emit_state(); + + // trigger next/first action + hci_run(); + + return 0; +} + +static void hci_update_scan_enable(void){ + // 2 = page scan, 1 = inq scan + hci_stack.new_scan_enable_value = hci_stack.connectable << 1 | hci_stack.discoverable; + hci_run(); +} + +void hci_discoverable_control(uint8_t enable){ + if (enable) enable = 1; // normalize argument + + if (hci_stack.discoverable == enable){ + hci_emit_discoverable_enabled(hci_stack.discoverable); + return; + } + + hci_stack.discoverable = enable; + hci_update_scan_enable(); +} + +void hci_connectable_control(uint8_t enable){ + if (enable) enable = 1; // normalize argument + + // don't emit event + if (hci_stack.connectable == enable) return; + + hci_stack.connectable = enable; + hci_update_scan_enable(); +} + +void hci_run(){ + + hci_connection_t * connection; + linked_item_t * it; + + if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return; + + // global/non-connection oriented commands + + // decline incoming connections + if (hci_stack.decline_reason){ + uint8_t reason = hci_stack.decline_reason; + hci_stack.decline_reason = 0; + hci_send_cmd(&hci_reject_connection_request, hci_stack.decline_addr, reason); + } + + if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return; + + // send scan enable + if (hci_stack.new_scan_enable_value != 0xff){ + hci_send_cmd(&hci_write_scan_enable, hci_stack.new_scan_enable_value); + hci_stack.new_scan_enable_value = 0xff; + } + + // send pending HCI commands + for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next){ + + if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return; + + connection = (hci_connection_t *) it; + + if (connection->state == RECEIVED_CONNECTION_REQUEST){ + log_info("sending hci_accept_connection_request\n"); + hci_send_cmd(&hci_accept_connection_request, connection->address, 1); + connection->state = ACCEPTED_CONNECTION_REQUEST; + } + + if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return; + + if (connection->authentication_flags & HANDLE_LINK_KEY_REQUEST){ + link_key_t link_key; + log_info("responding to link key request\n"); + if (hci_stack.remote_device_db->get_link_key( &connection->address, &link_key)){ + hci_send_cmd(&hci_link_key_request_reply, connection->address, &link_key); + } else { + hci_send_cmd(&hci_link_key_request_negative_reply, connection->address); + } + connectionClearAuthenticationFlags(connection, HANDLE_LINK_KEY_REQUEST); + } + } + + if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return; + + switch (hci_stack.state){ + case HCI_STATE_INITIALIZING: + // log_info("hci_init: substate %u\n", hci_stack.substate); + if (hci_stack.substate % 2) { + // odd: waiting for command completion + return; + } + switch (hci_stack.substate >> 1){ + case 0: // RESET + hci_send_cmd(&hci_reset); + if (hci_stack.config == 0 || ((hci_uart_config_t *)hci_stack.config)->baudrate_main == 0){ + // skip baud change + hci_stack.substate = 4; // >> 1 = 2 + } + break; + case 1: // SEND BAUD CHANGE + hci_stack.control->baudrate_cmd(hci_stack.config, ((hci_uart_config_t *)hci_stack.config)->baudrate_main, hci_stack.hci_packet_buffer); + hci_send_cmd_packet(hci_stack.hci_packet_buffer, 3 + hci_stack.hci_packet_buffer[2]); + break; + case 2: // LOCAL BAUD CHANGE + hci_stack.hci_transport->set_baudrate(((hci_uart_config_t *)hci_stack.config)->baudrate_main); + hci_stack.substate += 2; + // break missing here for fall through + + case 3: + // custom initialization + if (hci_stack.control && hci_stack.control->next_cmd){ + int valid_cmd = (*hci_stack.control->next_cmd)(hci_stack.config, hci_stack.hci_packet_buffer); + if (valid_cmd){ + int size = 3 + hci_stack.hci_packet_buffer[2]; + hci_stack.hci_transport->send_packet(HCI_COMMAND_DATA_PACKET, hci_stack.hci_packet_buffer, size); + hci_stack.substate = 4; // more init commands + break; + } + log_info("hci_run: init script done\n\r"); + } + // otherwise continue + hci_send_cmd(&hci_read_bd_addr); + break; + case 4: + hci_send_cmd(&hci_read_buffer_size); + break; + case 5: + // ca. 15 sec + hci_send_cmd(&hci_write_page_timeout, 0x6000); + break; + case 6: + hci_send_cmd(&hci_write_scan_enable, (hci_stack.connectable << 1) | hci_stack.discoverable); // page scan + break; + case 7: +#ifndef EMBEDDED + { + char hostname[30]; + gethostname(hostname, 30); + hostname[29] = '\0'; + hci_send_cmd(&hci_write_local_name, hostname); + break; + } + case 8: +#ifdef USE_BLUETOOL + hci_send_cmd(&hci_write_class_of_device, 0x007a020c); // Smartphone + break; + + case 9: +#endif +#endif + // done. + hci_stack.state = HCI_STATE_WORKING; + hci_emit_state(); + break; + default: + break; + } + hci_stack.substate++; + break; + + case HCI_STATE_HALTING: + + log_info("HCI_STATE_HALTING\n"); + // close all open connections + connection = (hci_connection_t *) hci_stack.connections; + if (connection){ + + // send disconnect + if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return; + + log_info("HCI_STATE_HALTING, connection %p, handle %u\n", connection, (uint16_t)connection->con_handle); + hci_send_cmd(&hci_disconnect, connection->con_handle, 0x13); // remote closed connection + + // send disconnected event right away - causes higher layer connections to get closed, too. + hci_shutdown_connection(connection); + return; + } + log_info("HCI_STATE_HALTING, calling off\n"); + + // switch mode + hci_power_control_off(); + + log_info("HCI_STATE_HALTING, emitting state\n"); + hci_emit_state(); + log_info("HCI_STATE_HALTING, done\n"); + break; + + case HCI_STATE_FALLING_ASLEEP: + switch(hci_stack.substate) { + case 0: + log_info("HCI_STATE_FALLING_ASLEEP\n"); + // close all open connections + connection = (hci_connection_t *) hci_stack.connections; + +#if defined(USE_POWERMANAGEMENT) && defined(USE_BLUETOOL) + // don't close connections, if H4 supports power management + if (bt_control_iphone_power_management_enabled()){ + connection = NULL; + } +#endif + if (connection){ + + // send disconnect + if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return; + + log_info("HCI_STATE_FALLING_ASLEEP, connection %p, handle %u\n", connection, (uint16_t)connection->con_handle); + hci_send_cmd(&hci_disconnect, connection->con_handle, 0x13); // remote closed connection + + // send disconnected event right away - causes higher layer connections to get closed, too. + hci_shutdown_connection(connection); + return; + } + + // disable page and inquiry scan + if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return; + + log_info("HCI_STATE_HALTING, disabling inq cans\n"); + hci_send_cmd(&hci_write_scan_enable, hci_stack.connectable << 1); // drop inquiry scan but keep page scan + + // continue in next sub state + hci_stack.substate++; + break; + case 1: + // wait for command complete "hci_write_scan_enable" in event_handler(); + break; + case 2: + log_info("HCI_STATE_HALTING, calling sleep\n"); +#if defined(USE_POWERMANAGEMENT) && defined(USE_BLUETOOL) + // don't actually go to sleep, if H4 supports power management + if (bt_control_iphone_power_management_enabled()){ + // SLEEP MODE reached + hci_stack.state = HCI_STATE_SLEEPING; + hci_emit_state(); + break; + } +#endif + // switch mode + hci_power_control_sleep(); // changes hci_stack.state to SLEEP + hci_emit_state(); + break; + + default: + break; + } + break; + + default: + break; + } +} + +int hci_send_cmd_packet(uint8_t *packet, int size){ + bd_addr_t addr; + hci_connection_t * conn; + // house-keeping + + // create_connection? + if (IS_COMMAND(packet, hci_create_connection)){ + bt_flip_addr(addr, &packet[3]); + log_info("Create_connection to %s\n", bd_addr_to_str(addr)); + conn = connection_for_address(addr); + if (conn) { + // if connection exists + if (conn->state == OPEN) { + // and OPEN, emit connection complete command + hci_emit_connection_complete(conn, 0); + } + // otherwise, just ignore as it is already in the open process + return 0; // don't sent packet to controller + + } + // create connection struct and register, state = SENT_CREATE_CONNECTION + conn = create_connection_for_addr(addr); + if (!conn){ + // notify client that alloc failed + hci_emit_connection_complete(conn, BTSTACK_MEMORY_ALLOC_FAILED); + return 0; // don't sent packet to controller + } + conn->state = SENT_CREATE_CONNECTION; + } + + if (IS_COMMAND(packet, hci_link_key_request_reply)){ + hci_add_connection_flags_for_flipped_bd_addr(&packet[3], SENT_LINK_KEY_REPLY); + } + if (IS_COMMAND(packet, hci_link_key_request_negative_reply)){ + hci_add_connection_flags_for_flipped_bd_addr(&packet[3], SENT_LINK_KEY_NEGATIVE_REQUEST); + } + if (IS_COMMAND(packet, hci_pin_code_request_reply)){ + hci_add_connection_flags_for_flipped_bd_addr(&packet[3], SENT_PIN_CODE_REPLY); + } + if (IS_COMMAND(packet, hci_pin_code_request_negative_reply)){ + hci_add_connection_flags_for_flipped_bd_addr(&packet[3], SENT_PIN_CODE_NEGATIVE_REPLY); + } + + if (IS_COMMAND(packet, hci_delete_stored_link_key)){ + if (hci_stack.remote_device_db){ + bt_flip_addr(addr, &packet[3]); + hci_stack.remote_device_db->delete_link_key(&addr); + } + } + + hci_stack.num_cmd_packets--; + return hci_stack.hci_transport->send_packet(HCI_COMMAND_DATA_PACKET, packet, size); +} + +/** + * pre: numcmds >= 0 - it's allowed to send a command to the controller + */ +int hci_send_cmd(const hci_cmd_t *cmd, ...){ + va_list argptr; + va_start(argptr, cmd); + uint16_t size = hci_create_cmd_internal(hci_stack.hci_packet_buffer, cmd, argptr); + va_end(argptr); + return hci_send_cmd_packet(hci_stack.hci_packet_buffer, size); +} + +// Create various non-HCI events. +// TODO: generalize, use table similar to hci_create_command + +void hci_emit_state(){ + uint8_t event[3]; + event[0] = BTSTACK_EVENT_STATE; + event[1] = sizeof(event) - 2; + event[2] = hci_stack.state; + hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); + hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event)); +} + +void hci_emit_connection_complete(hci_connection_t *conn, uint8_t status){ + uint8_t event[13]; + event[0] = HCI_EVENT_CONNECTION_COMPLETE; + event[1] = sizeof(event) - 2; + event[2] = status; + bt_store_16(event, 3, conn->con_handle); + bt_flip_addr(&event[5], conn->address); + event[11] = 1; // ACL connection + event[12] = 0; // encryption disabled + hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); + hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event)); +} + +void hci_emit_disconnection_complete(uint16_t handle, uint8_t reason){ + uint8_t event[6]; + event[0] = HCI_EVENT_DISCONNECTION_COMPLETE; + event[1] = sizeof(event) - 2; + event[2] = 0; // status = OK + bt_store_16(event, 3, handle); + event[5] = reason; + hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); + hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event)); +} + +void hci_emit_l2cap_check_timeout(hci_connection_t *conn){ + uint8_t event[4]; + event[0] = L2CAP_EVENT_TIMEOUT_CHECK; + event[1] = sizeof(event) - 2; + bt_store_16(event, 2, conn->con_handle); + hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); + hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event)); +} + +void hci_emit_nr_connections_changed(){ + uint8_t event[3]; + event[0] = BTSTACK_EVENT_NR_CONNECTIONS_CHANGED; + event[1] = sizeof(event) - 2; + event[2] = nr_hci_connections(); + hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); + hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event)); +} + +void hci_emit_hci_open_failed(){ + uint8_t event[2]; + event[0] = BTSTACK_EVENT_POWERON_FAILED; + event[1] = sizeof(event) - 2; + hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); + hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event)); +} + +#ifndef EMBEDDED +void hci_emit_btstack_version() { + uint8_t event[6]; + event[0] = BTSTACK_EVENT_VERSION; + event[1] = sizeof(event) - 2; + event[2] = BTSTACK_MAJOR; + event[3] = BTSTACK_MINOR; + bt_store_16(event, 4, BTSTACK_REVISION); + hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); + hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event)); +} +#endif + +void hci_emit_system_bluetooth_enabled(uint8_t enabled){ + uint8_t event[3]; + event[0] = BTSTACK_EVENT_SYSTEM_BLUETOOTH_ENABLED; + event[1] = sizeof(event) - 2; + event[2] = enabled; + hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); + hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event)); +} + +void hci_emit_remote_name_cached(bd_addr_t *addr, device_name_t *name){ + uint8_t event[2+1+6+248]; + event[0] = BTSTACK_EVENT_REMOTE_NAME_CACHED; + event[1] = sizeof(event) - 2; + event[2] = 0; // just to be compatible with HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE + bt_flip_addr(&event[3], *addr); + memcpy(&event[9], name, 248); + hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); + hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event)); +} + +void hci_emit_discoverable_enabled(uint8_t enabled){ + uint8_t event[3]; + event[0] = BTSTACK_EVENT_DISCOVERABLE_ENABLED; + event[1] = sizeof(event) - 2; + event[2] = enabled; + hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); + hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/hci.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * hci.h + * + * Created by Matthias Ringwald on 4/29/09. + * + */ + +#pragma once + +#include "config.h" + +#include <btstack/hci_cmds.h> +#include <btstack/utils.h> +#include "hci_transport.h" +#include "bt_control.h" +#include "remote_device_db.h" + +#include <stdint.h> +#include <stdlib.h> +#include <stdarg.h> + +#if defined __cplusplus +extern "C" { +#endif + +// packet header sizes +#define HCI_CMD_HEADER_SIZE 3 +#define HCI_ACL_HEADER_SIZE 4 +#define HCI_SCO_HEADER_SIZE 3 +#define HCI_EVENT_HEADER_SIZE 2 + +// packet sizes (max payload) +#define HCI_ACL_DM1_SIZE 17 +#define HCI_ACL_DH1_SIZE 27 +#define HCI_ACL_2DH1_SIZE 54 +#define HCI_ACL_3DH1_SIZE 83 +#define HCI_ACL_DM3_SIZE 121 +#define HCI_ACL_DH3_SIZE 183 +#define HCI_ACL_DM5_SIZE 224 +#define HCI_ACL_DH5_SIZE 339 +#define HCI_ACL_2DH3_SIZE 367 +#define HCI_ACL_3DH3_SIZE 552 +#define HCI_ACL_2DH5_SIZE 679 +#define HCI_ACL_3DH5_SIZE 1021 + +#define HCI_EVENT_PAYLOAD_SIZE 255 +#define HCI_CMD_PAYLOAD_SIZE 255 + +// packet buffer sizes +// HCI_ACL_PAYLOAD_SIZE is configurable and defined in config.h +#define HCI_EVENT_BUFFER_SIZE (HCI_EVENT_HEADER_SIZE + HCI_EVENT_PAYLOAD_SIZE) +#define HCI_CMD_BUFFER_SIZE (HCI_CMD_HEADER_SIZE + HCI_CMD_PAYLOAD_SIZE) +#define HCI_ACL_BUFFER_SIZE (HCI_ACL_HEADER_SIZE + HCI_ACL_PAYLOAD_SIZE) + +// size of hci buffers, big enough for command, event, or acl packet without H4 packet type +// @note cmd buffer is bigger than event buffer +#if HCI_ACL_BUFFER_SIZE > HCI_CMD_BUFFER_SIZE +#define HCI_PACKET_BUFFER_SIZE HCI_ACL_BUFFER_SIZE +#else +#define HCI_PACKET_BUFFER_SIZE HCI_CMD_BUFFER_SIZE +#endif + +// OGFs +#define OGF_STATUS_PARAMETERS 0x05 //sibu +#define OGF_LINK_CONTROL 0x01 +#define OGF_LINK_POLICY 0x02 +#define OGF_CONTROLLER_BASEBAND 0x03 +#define OGF_INFORMATIONAL_PARAMETERS 0x04 +#define OGF_LE_CONTROLLER 0x08 +#define OGF_BTSTACK 0x3d +#define OGF_VENDOR 0x3f + +// cmds for BTstack +// get state: @returns HCI_STATE +#define BTSTACK_GET_STATE 0x01 + +// set power mode: @param HCI_POWER_MODE +#define BTSTACK_SET_POWER_MODE 0x02 + +// set capture mode: @param on +#define BTSTACK_SET_ACL_CAPTURE_MODE 0x03 + +// get BTstack version +#define BTSTACK_GET_VERSION 0x04 + +// get system Bluetooth state +#define BTSTACK_GET_SYSTEM_BLUETOOTH_ENABLED 0x05 + +// set system Bluetooth state +#define BTSTACK_SET_SYSTEM_BLUETOOTH_ENABLED 0x06 + +// enable inquiry scan for this client +#define BTSTACK_SET_DISCOVERABLE 0x07 + +// set global Bluetooth state +#define BTSTACK_SET_BLUETOOTH_ENABLED 0x08 + +// create l2cap channel: @param bd_addr(48), psm (16) +#define L2CAP_CREATE_CHANNEL 0x20 + +// disconnect l2cap disconnect, @param channel(16), reason(8) +#define L2CAP_DISCONNECT 0x21 + +// register l2cap service: @param psm(16), mtu (16) +#define L2CAP_REGISTER_SERVICE 0x22 + +// unregister l2cap disconnect, @param psm(16) +#define L2CAP_UNREGISTER_SERVICE 0x23 + +// accept connection @param bd_addr(48), dest cid (16) +#define L2CAP_ACCEPT_CONNECTION 0x24 + +// decline l2cap disconnect,@param bd_addr(48), dest cid (16), reason(8) +#define L2CAP_DECLINE_CONNECTION 0x25 + +// create l2cap channel: @param bd_addr(48), psm (16), mtu (16) +#define L2CAP_CREATE_CHANNEL_MTU 0x26 + +// register SDP Service Record: service record (size) +#define SDP_REGISTER_SERVICE_RECORD 0x30 + +// unregister SDP Service Record +#define SDP_UNREGISTER_SERVICE_RECORD 0x31 + +// RFCOMM "HCI" Commands +#define RFCOMM_CREATE_CHANNEL 0x40 +#define RFCOMM_DISCONNECT 0x41 +#define RFCOMM_REGISTER_SERVICE 0x42 +#define RFCOMM_UNREGISTER_SERVICE 0x43 +#define RFCOMM_ACCEPT_CONNECTION 0x44 +#define RFCOMM_DECLINE_CONNECTION 0x45 +#define RFCOMM_PERSISTENT_CHANNEL 0x46 +#define RFCOMM_CREATE_CHANNEL_WITH_CREDITS 0x47 +#define RFCOMM_REGISTER_SERVICE_WITH_CREDITS 0x48 +#define RFCOMM_GRANT_CREDITS 0x49 + +// +#define IS_COMMAND(packet, command) (READ_BT_16(packet,0) == command.opcode) + +// data: event(8) +#define DAEMON_EVENT_CONNECTION_OPENED 0x50 + +// data: event(8) +#define DAEMON_EVENT_CONNECTION_CLOSED 0x51 + +// data: event(8), nr_connections(8) +#define DAEMON_NR_CONNECTIONS_CHANGED 0x52 + +// data: event(8) +#define DAEMON_EVENT_NEW_RFCOMM_CREDITS 0x53 + +// data: event() +#define DAEMON_EVENT_HCI_PACKET_SENT 0x54 + +/** + * Connection State + */ +typedef enum { + AUTH_FLAGS_NONE = 0x00, + RECV_LINK_KEY_REQUEST = 0x01, + HANDLE_LINK_KEY_REQUEST = 0x02, + SENT_LINK_KEY_REPLY = 0x04, + SENT_LINK_KEY_NEGATIVE_REQUEST = 0x08, + RECV_LINK_KEY_NOTIFICATION = 0x10, + RECV_PIN_CODE_REQUEST = 0x20, + SENT_PIN_CODE_REPLY = 0x40, + SENT_PIN_CODE_NEGATIVE_REPLY = 0x80 +} hci_authentication_flags_t; + +typedef enum { + SENT_CREATE_CONNECTION = 1, + RECEIVED_CONNECTION_REQUEST, + ACCEPTED_CONNECTION_REQUEST, + REJECTED_CONNECTION_REQUEST, + OPEN, + SENT_DISCONNECT +} CONNECTION_STATE; + +typedef enum { + BLUETOOTH_OFF = 1, + BLUETOOTH_ON, + BLUETOOTH_ACTIVE +} BLUETOOTH_STATE; + +typedef struct { + // linked list - assert: first field + linked_item_t item; + + // remote side + bd_addr_t address; + + // module handle + hci_con_handle_t con_handle; + + // state + CONNECTION_STATE state; + + // errands + hci_authentication_flags_t authentication_flags; + + timer_source_t timeout; + +#ifdef HAVE_TIME + // timer + struct timeval timestamp; +#endif +#ifdef HAVE_TICK + uint32_t timestamp; // timeout in system ticks +#endif + + // ACL packet recombination - ACL Header + ACL payload + uint8_t acl_recombination_buffer[4 + HCI_ACL_BUFFER_SIZE]; + uint16_t acl_recombination_pos; + uint16_t acl_recombination_length; + + // number ACL packets sent to controller + uint8_t num_acl_packets_sent; + +} hci_connection_t; + +/** + * main data structure + */ +typedef struct { + // transport component with configuration + hci_transport_t * hci_transport; + void * config; + + // hardware power controller + bt_control_t * control; + + // list of existing baseband connections + linked_list_t connections; + + // single buffer for HCI Command assembly + uint8_t hci_packet_buffer[HCI_PACKET_BUFFER_SIZE]; // opcode (16), len(8) + + /* host to controller flow control */ + uint8_t num_cmd_packets; + // uint8_t total_num_cmd_packets; + uint8_t total_num_acl_packets; + uint16_t acl_data_packet_length; + + // usable packet types given acl_data_packet_length and HCI_ACL_BUFFER_SIZE + uint16_t packet_types; + + /* callback to L2CAP layer */ + void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size); + + /* remote device db */ + remote_device_db_t const*remote_device_db; + + /* hci state machine */ + HCI_STATE state; + uint8_t substate; + uint8_t cmds_ready; + + uint8_t discoverable; + uint8_t connectable; + + /* buffer for scan enable cmd - 0xff no change */ + uint8_t new_scan_enable_value; + + // buffer for single connection decline + uint8_t decline_reason; + bd_addr_t decline_addr; + +} hci_stack_t; + +// create and send hci command packets based on a template and a list of parameters +uint16_t hci_create_cmd(uint8_t *hci_cmd_buffer, hci_cmd_t *cmd, ...); +uint16_t hci_create_cmd_internal(uint8_t *hci_cmd_buffer, const hci_cmd_t *cmd, va_list argptr); + +// set up HCI +void hci_init(hci_transport_t *transport, void *config, bt_control_t *control, remote_device_db_t const* remote_device_db); +void hci_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)); +void hci_close(void); + +// power and inquriy scan control +int hci_power_control(HCI_POWER_MODE mode); +void hci_discoverable_control(uint8_t enable); +void hci_connectable_control(uint8_t enable); + +/** + * run the hci control loop once + */ +void hci_run(void); + +// create and send hci command packets based on a template and a list of parameters +int hci_send_cmd(const hci_cmd_t *cmd, ...); + +// send complete CMD packet +int hci_send_cmd_packet(uint8_t *packet, int size); + +// send ACL packet +int hci_send_acl_packet(uint8_t *packet, int size); + +// non-blocking UART driver needs +int hci_can_send_packet_now(uint8_t packet_type); + +hci_connection_t * connection_for_handle(hci_con_handle_t con_handle); +uint8_t hci_number_outgoing_packets(hci_con_handle_t handle); +uint8_t hci_number_free_acl_slots(void); +int hci_authentication_active_for_handle(hci_con_handle_t handle); +void hci_drop_link_key_for_bd_addr(bd_addr_t *addr); +uint16_t hci_max_acl_data_packet_length(void); +uint16_t hci_usable_acl_packet_types(void); +uint8_t* hci_get_outgoing_acl_packet_buffer(void); + +// +void hci_emit_state(void); +void hci_emit_connection_complete(hci_connection_t *conn, uint8_t status); +void hci_emit_l2cap_check_timeout(hci_connection_t *conn); +void hci_emit_disconnection_complete(uint16_t handle, uint8_t reason); +void hci_emit_nr_connections_changed(void); +void hci_emit_hci_open_failed(void); +void hci_emit_btstack_version(void); +void hci_emit_system_bluetooth_enabled(uint8_t enabled); +void hci_emit_remote_name_cached(bd_addr_t *addr, device_name_t *name); +void hci_emit_discoverable_enabled(uint8_t enabled); + +#if defined __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/hci_cmds.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,693 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * hci_cmds.c + * + * Created by Matthias Ringwald on 7/23/09. + */ + +#include <btstack/hci_cmds.h> + +#include <string.h> + +#include <btstack/sdp_util.h> +#include "config.h" +#include "hci.h" + +// calculate combined ogf/ocf value +#define OPCODE(ogf, ocf) (ocf | ogf << 10) + +/** + * construct HCI Command based on template + * + * Format: + * 1,2,3,4: one to four byte value + * H: HCI connection handle + * B: Bluetooth Baseband Address (BD_ADDR) + * E: Extended Inquiry Result + * N: Name up to 248 chars, \0 terminated + * P: 16 byte Pairing code + * S: Service Record (Data Element Sequence) + */ +uint16_t hci_create_cmd_internal(uint8_t *hci_cmd_buffer, const hci_cmd_t *cmd, va_list argptr){ + + hci_cmd_buffer[0] = cmd->opcode & 0xff; + hci_cmd_buffer[1] = cmd->opcode >> 8; + int pos = 3; + + const char *format = cmd->format; + uint16_t word; + uint32_t longword; + uint8_t * ptr; + while (*format) { + switch(*format) { + case '1': // 8 bit value + case '2': // 16 bit value + case 'H': // hci_handle + word = va_arg(argptr, int); // minimal va_arg is int: 2 bytes on 8+16 bit CPUs + hci_cmd_buffer[pos++] = word & 0xff; + if (*format == '2') { + hci_cmd_buffer[pos++] = word >> 8; + } else if (*format == 'H') { + // TODO implement opaque client connection handles + // pass module handle for now + hci_cmd_buffer[pos++] = word >> 8; + } + break; + case '3': + case '4': + longword = va_arg(argptr, uint32_t); + // longword = va_arg(argptr, int); + hci_cmd_buffer[pos++] = longword; + hci_cmd_buffer[pos++] = longword >> 8; + hci_cmd_buffer[pos++] = longword >> 16; + if (*format == '4'){ + hci_cmd_buffer[pos++] = longword >> 24; + } + break; + case 'B': // bt-addr + ptr = va_arg(argptr, uint8_t *); + hci_cmd_buffer[pos++] = ptr[5]; + hci_cmd_buffer[pos++] = ptr[4]; + hci_cmd_buffer[pos++] = ptr[3]; + hci_cmd_buffer[pos++] = ptr[2]; + hci_cmd_buffer[pos++] = ptr[1]; + hci_cmd_buffer[pos++] = ptr[0]; + break; + case 'E': // Extended Inquiry Information 240 octets + ptr = va_arg(argptr, uint8_t *); + memcpy(&hci_cmd_buffer[pos], ptr, 240); + pos += 240; + break; + case 'N': { // UTF-8 string, null terminated + ptr = va_arg(argptr, uint8_t *); + uint16_t len = strlen((const char*) ptr); + if (len > 248) { + len = 248; + } + memcpy(&hci_cmd_buffer[pos], ptr, len); + if (len < 248) { + // fill remaining space with zeroes + memset(&hci_cmd_buffer[pos+len], 0, 248-len); + } + pos += 248; + break; + } + case 'P': // 16 byte PIN code or link key + ptr = va_arg(argptr, uint8_t *); + memcpy(&hci_cmd_buffer[pos], ptr, 16); + pos += 16; + break; +#ifdef HAVE_BLE + case 'A': // 31 bytes advertising data + ptr = va_arg(argptr, uint8_t *); + memcpy(&hci_cmd_buffer[pos], ptr, 31); + pos += 31; + break; +#endif +#ifdef HAVE_SDP + case 'S': { // Service Record (Data Element Sequence) + ptr = va_arg(argptr, uint8_t *); + uint16_t len = de_get_len(ptr); + memcpy(&hci_cmd_buffer[pos], ptr, len); + pos += len; + break; + } +#endif + default: + break; + } + format++; + }; + hci_cmd_buffer[2] = pos - 3; + return pos; +} + +/** + * construct HCI Command based on template + * + * mainly calls hci_create_cmd_internal + */ +uint16_t hci_create_cmd(uint8_t *hci_cmd_buffer, hci_cmd_t *cmd, ...){ + va_list argptr; + va_start(argptr, cmd); + uint16_t len = hci_create_cmd_internal(hci_cmd_buffer, cmd, argptr); + va_end(argptr); + return len; +} + + +/** + * Link Control Commands + */ +const hci_cmd_t hci_inquiry = { +OPCODE(OGF_LINK_CONTROL, 0x01), "311" +// LAP, Inquiry length, Num_responses +}; +const hci_cmd_t hci_inquiry_cancel = { +OPCODE(OGF_LINK_CONTROL, 0x02), "" +// no params +}; +const hci_cmd_t hci_create_connection = { +OPCODE(OGF_LINK_CONTROL, 0x05), "B21121" +// BD_ADDR, Packet_Type, Page_Scan_Repetition_Mode, Reserved, Clock_Offset, Allow_Role_Switch +}; +const hci_cmd_t hci_disconnect = { +OPCODE(OGF_LINK_CONTROL, 0x06), "H1" +// Handle, Reason: 0x05, 0x13-0x15, 0x1a, 0x29 +// see Errors Codes in BT Spec Part D +}; +const hci_cmd_t hci_create_connection_cancel = { +OPCODE(OGF_LINK_CONTROL, 0x08), "B" +// BD_ADDR +}; +const hci_cmd_t hci_accept_connection_request = { +OPCODE(OGF_LINK_CONTROL, 0x09), "B1" +// BD_ADDR, Role: become master, stay slave +}; +const hci_cmd_t hci_reject_connection_request = { +OPCODE(OGF_LINK_CONTROL, 0x0a), "B1" +// BD_ADDR, reason e.g. CONNECTION REJECTED DUE TO LIMITED RESOURCES (0x0d) +}; +const hci_cmd_t hci_link_key_request_reply = { +OPCODE(OGF_LINK_CONTROL, 0x0b), "BP" +// BD_ADDR, LINK_KEY +}; +const hci_cmd_t hci_link_key_request_negative_reply = { +OPCODE(OGF_LINK_CONTROL, 0x0c), "B" +// BD_ADDR +}; +const hci_cmd_t hci_pin_code_request_reply = { +OPCODE(OGF_LINK_CONTROL, 0x0d), "B1P" +// BD_ADDR, pin length, PIN: c-string +}; +const hci_cmd_t hci_pin_code_request_negative_reply = { +OPCODE(OGF_LINK_CONTROL, 0x0e), "B" +// BD_ADDR +}; +const hci_cmd_t hci_authentication_requested = { +OPCODE(OGF_LINK_CONTROL, 0x11), "H" +// Handle +}; +const hci_cmd_t hci_set_connection_encryption = { +OPCODE(OGF_LINK_CONTROL, 0x13), "H1" +// Handle, Encryption_Enable +}; +const hci_cmd_t hci_change_connection_link_key = { +OPCODE(OGF_LINK_CONTROL, 0x15), "H" +// Handle +}; +const hci_cmd_t hci_remote_name_request = { +OPCODE(OGF_LINK_CONTROL, 0x19), "B112" +// BD_ADDR, Page_Scan_Repetition_Mode, Reserved, Clock_Offset +}; +const hci_cmd_t hci_remote_name_request_cancel = { +OPCODE(OGF_LINK_CONTROL, 0x1A), "B" +// BD_ADDR +}; + +/** + * Link Policy Commands + */ +const hci_cmd_t hci_sniff_mode = { +OPCODE(OGF_LINK_POLICY, 0x03), "H2222" +// handle, Sniff_Max_Interval, Sniff_Min_Interval, Sniff_Attempt, Sniff_Timeout: +}; +const hci_cmd_t hci_qos_setup = { +OPCODE(OGF_LINK_POLICY, 0x07), "H114444" +// handle, flags, service_type, token rate (bytes/s), peak bandwith (bytes/s), +// latency (us), delay_variation (us) +}; +const hci_cmd_t hci_role_discovery = { +OPCODE(OGF_LINK_POLICY, 0x09), "H" +// handle +}; +const hci_cmd_t hci_switch_role_command= { +OPCODE(OGF_LINK_POLICY, 0x0b), "B1" +// BD_ADDR, role: {0=master,1=slave} +}; +const hci_cmd_t hci_read_link_policy_settings = { +OPCODE(OGF_LINK_POLICY, 0x0c), "H" +// handle +}; +const hci_cmd_t hci_write_link_policy_settings = { +OPCODE(OGF_LINK_POLICY, 0x0d), "H2" +// handle, settings +}; + +/** + * Controller & Baseband Commands + */ +const hci_cmd_t hci_set_event_mask = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x01), "44" +// event_mask lower 4 octets, higher 4 bytes +}; +const hci_cmd_t hci_reset = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x03), "" +// no params +}; +const hci_cmd_t hci_delete_stored_link_key = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x12), "B1" +// BD_ADDR, Delete_All_Flag +}; +const hci_cmd_t hci_write_local_name = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x13), "N" +// Local name (UTF-8, Null Terminated, max 248 octets) +}; +const hci_cmd_t hci_write_page_timeout = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x18), "2" +// Page_Timeout * 0.625 ms +}; +const hci_cmd_t hci_write_scan_enable = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x1A), "1" +// Scan_enable: no, inq, page, inq+page +}; +const hci_cmd_t hci_write_authentication_enable = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x20), "1" +// Authentication_Enable +}; +const hci_cmd_t hci_write_class_of_device = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x24), "3" +// Class of Device +}; +const hci_cmd_t hci_read_num_broadcast_retransmissions = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x29), "" +}; +const hci_cmd_t hci_write_num_broadcast_retransmissions = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x2a), "1" +// Num broadcast retransmissions (e.g. 0 for a single broadcast) +}; +const hci_cmd_t hci_host_buffer_size = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x33), "2122" +// Host_ACL_Data_Packet_Length:, Host_Synchronous_Data_Packet_Length:, Host_Total_Num_ACL_Data_Packets:, Host_Total_Num_Synchronous_Data_Packets: +}; +const hci_cmd_t hci_read_link_supervision_timeout = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x36), "H" +// handle +}; +const hci_cmd_t hci_write_link_supervision_timeout = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x37), "H2" +// handle, Range for N: 0x0001 Ð 0xFFFF Time (Range: 0.625ms Ð 40.9 sec) +}; +const hci_cmd_t hci_write_inquiry_mode = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x45), "1" +// Inquiry mode: 0x00 = standard, 0x01 = with RSSI, 0x02 = extended +}; +const hci_cmd_t hci_write_extended_inquiry_response = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x52), "1E" +// FEC_Required, Exstended Inquiry Response +}; +const hci_cmd_t hci_write_simple_pairing_mode = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x56), "1" +// mode: 0 = off, 1 = on +}; +const hci_cmd_t hci_read_le_host_supported = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x6c), "" +// params: none +// return: status, le supported host, simultaneous le host +}; +const hci_cmd_t hci_write_le_host_supported = { +OPCODE(OGF_CONTROLLER_BASEBAND, 0x6d), "11" +// param: le supported host, simultaneous le host +// return: status +}; + +/** + * Informational Parameters + */ +const hci_cmd_t hci_read_local_supported_features = { +OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x03), "" +// no params +}; +const hci_cmd_t hci_read_buffer_size = { +OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x05), "" +// no params +}; +const hci_cmd_t hci_read_bd_addr = { +OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x09), "" +// no params +}; + +//sibu +const hci_cmd_t hci_read_local_supprted_commands = { +OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x02), "" +// no params +}; +/** + * Status Parameters + */ +const hci_cmd_t hci_read_rssi = { +OPCODE(OGF_STATUS_PARAMETERS, 0x05), "H" +// handle +// return: handle,RSSI +}; + +#ifdef HAVE_BLE +/** + * Low Energy Commands + */ +const hci_cmd_t hci_le_set_event_mask = { +OPCODE(OGF_LE_CONTROLLER, 0x01), "44" +// params: event_mask lower 4 octets, higher 4 bytes +// return: status +}; +const hci_cmd_t hci_le_read_buffer_size = { +OPCODE(OGF_LE_CONTROLLER, 0x02), "" +// params: none +// return: status, le acl data packet len (16), total num le acl data packets(8) +}; +const hci_cmd_t hci_le_read_supported_features = { +OPCODE(OGF_LE_CONTROLLER, 0x03), "" +// params: none +// return: LE_Features See [Vol 6] Part B, Section 4.6 +}; +const hci_cmd_t hci_le_set_random_address = { +OPCODE(OGF_LE_CONTROLLER, 0x05), "B" +// params: random device address +// return: status +}; +const hci_cmd_t hci_le_set_advertising_parameters = { +OPCODE(OGF_LE_CONTROLLER, 0x06), "22111B11" +// param: min advertising interval, [0x0020,0x4000], default: 0x0800, unit: 0.625 msec +// param: max advertising interval, [0x0020,0x4000], default: 0x0800, unit: 0.625 msec +// param: advertising type (enum from 0): ADV_IND, ADC_DIRECT_IND, ADV_SCAN_IND, ADV_NONCONN_IND +// param: own address type (enum from 0): public device address, random device address +// param: direct address type (enum from 0): public device address, random device address +// param: direct address - public or random address of device to be connecteed +// param: advertising channel map (flags): chan_37(1), chan_38(2), chan_39(4) +// param: advertising filter policy (enum from 0): scan any conn any, scan whitelist, con any, scan any conn whitelist, scan whitelist, con whitelist +// return: status +}; +const hci_cmd_t hci_le_read_advertising_channel_tx_power = { +OPCODE(OGF_LE_CONTROLLER, 0x07), "" +// params: none +// return: status, level [-20,10] signed int (8), units dBm +}; +const hci_cmd_t hci_le_set_advertising_data= { +OPCODE(OGF_LE_CONTROLLER, 0x08), "1A" +// param: advertising data len +// param: advertising data (31 bytes) +// return: status +}; +const hci_cmd_t hci_le_set_scan_response_data= { +OPCODE(OGF_LE_CONTROLLER, 0x09), "1A" +// param: scan response data len +// param: scan response data (31 bytes) +// return: status +}; +const hci_cmd_t hci_le_set_advertise_enable = { +OPCODE(OGF_LE_CONTROLLER, 0x0a), "1" +// params: avertise enable: off (0), on (1) +// return: status +}; +const hci_cmd_t hci_le_set_scan_parameters = { +OPCODE(OGF_LE_CONTROLLER, 0x0b), "12211" +// param: le scan type: passive (0), active (1) +// param: le scan interval [0x0004,0x4000], unit: 0.625 msec +// param: le scan window [0x0004,0x4000], unit: 0.625 msec +// param: own address type: public (0), random (1) +// param: scanning filter policy: any (0), only whitelist (1) +// return: status +}; +const hci_cmd_t hci_le_set_scan_enable = { +OPCODE(OGF_LE_CONTROLLER, 0x0c), "11" +// param: le scan enable: disabled (0), enabled (1) +// param: filter duplices: disabled (0), enabled (1) +// return: status +}; +const hci_cmd_t hci_le_create_connection= { +OPCODE(OGF_LE_CONTROLLER, 0x0d), "2211B1222222" +// param: le scan interval, [0x0004, 0x4000], unit: 0.625 msec +// param: le scan window, [0x0004, 0x4000], unit: 0.625 msec +// param: initiator filter policy: peer address type + peer address (0), whitelist (1) +// param: peer address type: public (0), random (1) +// param: peer address +// param: own address type: public (0), random (1) +// param: conn interval min, [0x0006, 0x0c80], unit: 1.25 msec +// param: conn interval max, [0x0006, 0x0c80], unit: 1.25 msec +// param: conn latency, number of connection events [0x0000, 0x01f4] +// param: supervision timeout, [0x000a, 0x0c80], unit: 10 msec +// param: minimum CE length, [0x0000, 0xffff], unit: 0.625 msec +// return: none -> le create connection complete event +}; +const hci_cmd_t hci_le_create_connection_cancel = { +OPCODE(OGF_LE_CONTROLLER, 0x0e), "" +// params: none +// return: status +}; +const hci_cmd_t hci_le_read_white_list_size = { +OPCODE(OGF_LE_CONTROLLER, 0x0f), "" +// params: none +// return: status, number of entries in controller whitelist +}; +const hci_cmd_t hci_le_clear_white_list = { +OPCODE(OGF_LE_CONTROLLER, 0x10), "" +// params: none +// return: status +}; +const hci_cmd_t hci_le_add_device_to_whitelist = { +OPCODE(OGF_LE_CONTROLLER, 0x11), "1B" +// param: address type: public (0), random (1) +// param: address +// return: status +}; +const hci_cmd_t hci_le_remove_device_from_whitelist = { +OPCODE(OGF_LE_CONTROLLER, 0x12), "1B" +// param: address type: public (0), random (1) +// param: address +// return: status +}; +const hci_cmd_t hci_le_connection_update = { +OPCODE(OGF_LE_CONTROLLER, 0x13), "H222222" +// param: conn handle +// param: conn interval min, [0x0006,0x0c80], unit: 1.25 msec +// param: conn interval max, [0x0006,0x0c80], unit: 1.25 msec +// param: conn latency, [0x0000,0x03e8], number of connection events +// param: supervision timeout, [0x000a,0x0c80], unit: 10 msec +// param: minimum CE length, [0x0000,0xffff], unit: 0.625 msec +// param: maximum CE length, [0x0000,0xffff], unit: 0.625 msec +// return: none -> le connection update complete event +}; +const hci_cmd_t hci_le_set_host_channel_classification = { +OPCODE(OGF_LE_CONTROLLER, 0x14), "41" +// param: channel map 37 bit, split into first 32 and higher 5 bits +// return: status +}; +const hci_cmd_t hci_le_read_channel_map = { +OPCODE(OGF_LE_CONTROLLER, 0x15), "H" +// params: connection handle +// return: status, connection handle, channel map (5 bytes, 37 used) +}; +const hci_cmd_t hci_le_read_remote_used_features = { +OPCODE(OGF_LE_CONTROLLER, 0x16), "H" +// params: connection handle +// return: none -> le read remote used features complete event +}; +const hci_cmd_t hci_le_encrypt = { +OPCODE(OGF_LE_CONTROLLER, 0x17), "PP" +// param: key (128) for AES-128 +// param: plain text (128) +// return: status, encrypted data (128) +}; +const hci_cmd_t hci_le_rand = { +OPCODE(OGF_LE_CONTROLLER, 0x18), "" +// params: none +// return: status, random number (64) +}; +const hci_cmd_t hci_le_start_encryption = { +OPCODE(OGF_LE_CONTROLLER, 0x19), "H442P" +// param: connection handle +// param: 64 bit random number lower 32 bit +// param: 64 bit random number higher 32 bit +// param: encryption diversifier (16) +// param: long term key (128) +// return: none -> encryption changed or encryption key refresh complete event +}; +const hci_cmd_t hci_le_long_term_key_request_reply = { +OPCODE(OGF_LE_CONTROLLER, 0x1a), "HP" +// param: connection handle +// param: long term key (128) +// return: status, connection handle +}; +const hci_cmd_t hci_le_long_term_key_negative_reply = { +OPCODE(OGF_LE_CONTROLLER, 0x1b), "H" +// param: connection handle +// return: status, connection handle +}; +const hci_cmd_t hci_le_read_supported_states = { +OPCODE(OGF_LE_CONTROLLER, 0x1c), "H" +// param: none +// return: status, LE states (64) +}; +const hci_cmd_t hci_le_receiver_test = { +OPCODE(OGF_LE_CONTROLLER, 0x1d), "1" +// param: rx frequency, [0x00 0x27], frequency (MHz): 2420 + N*2 +// return: status +}; +const hci_cmd_t hci_le_transmitter_test = { + OPCODE(OGF_LE_CONTROLLER, 0x1e), "111" + // param: tx frequency, [0x00 0x27], frequency (MHz): 2420 + N*2 + // param: lengh of test payload [0x00,0x25] + // param: packet payload [0,7] different patterns + // return: status +}; +const hci_cmd_t hci_le_test_end = { + OPCODE(OGF_LE_CONTROLLER, 0x1f), "1" + // params: none + // return: status, number of packets (8) +}; +#endif + +// BTstack commands +const hci_cmd_t btstack_get_state = { +OPCODE(OGF_BTSTACK, BTSTACK_GET_STATE), "" +// no params -> +}; + +const hci_cmd_t btstack_set_power_mode = { +OPCODE(OGF_BTSTACK, BTSTACK_SET_POWER_MODE), "1" +// mode: 0 = off, 1 = on +}; + +const hci_cmd_t btstack_set_acl_capture_mode = { +OPCODE(OGF_BTSTACK, BTSTACK_SET_ACL_CAPTURE_MODE), "1" +// mode: 0 = off, 1 = on +}; + +const hci_cmd_t btstack_get_version = { +OPCODE(OGF_BTSTACK, BTSTACK_GET_VERSION), "" +}; + +const hci_cmd_t btstack_get_system_bluetooth_enabled = { +OPCODE(OGF_BTSTACK, BTSTACK_GET_SYSTEM_BLUETOOTH_ENABLED), "" +}; + +const hci_cmd_t btstack_set_system_bluetooth_enabled = { +OPCODE(OGF_BTSTACK, BTSTACK_SET_SYSTEM_BLUETOOTH_ENABLED), "1" +}; + +const hci_cmd_t btstack_set_discoverable = { +OPCODE(OGF_BTSTACK, BTSTACK_SET_DISCOVERABLE), "1" +}; + +const hci_cmd_t btstack_set_bluetooth_enabled = { +// only used by btstack config +OPCODE(OGF_BTSTACK, BTSTACK_SET_BLUETOOTH_ENABLED), "1" +}; + +const hci_cmd_t l2cap_create_channel = { +OPCODE(OGF_BTSTACK, L2CAP_CREATE_CHANNEL), "B2" +// @param bd_addr(48), psm (16) +}; +const hci_cmd_t l2cap_create_channel_mtu = { +OPCODE(OGF_BTSTACK, L2CAP_CREATE_CHANNEL_MTU), "B22" +// @param bd_addr(48), psm (16), mtu (16) +}; +const hci_cmd_t l2cap_disconnect = { +OPCODE(OGF_BTSTACK, L2CAP_DISCONNECT), "21" +// @param channel(16), reason(8) +}; +const hci_cmd_t l2cap_register_service = { +OPCODE(OGF_BTSTACK, L2CAP_REGISTER_SERVICE), "22" +// @param psm (16), mtu (16) +}; +const hci_cmd_t l2cap_unregister_service = { +OPCODE(OGF_BTSTACK, L2CAP_UNREGISTER_SERVICE), "2" +// @param psm (16) +}; +const hci_cmd_t l2cap_accept_connection = { +OPCODE(OGF_BTSTACK, L2CAP_ACCEPT_CONNECTION), "2" +// @param source cid (16) +}; +const hci_cmd_t l2cap_decline_connection = { +OPCODE(OGF_BTSTACK, L2CAP_DECLINE_CONNECTION), "21" +// @param source cid (16), reason(8) +}; +const hci_cmd_t sdp_register_service_record = { +OPCODE(OGF_BTSTACK, SDP_REGISTER_SERVICE_RECORD), "S" +// @param service record handle (DES) +}; +const hci_cmd_t sdp_unregister_service_record = { +OPCODE(OGF_BTSTACK, SDP_UNREGISTER_SERVICE_RECORD), "4" +// @param service record handle (32) +}; + +// create rfcomm channel: @param bd_addr(48), channel (8) +const hci_cmd_t rfcomm_create_channel = { + OPCODE(OGF_BTSTACK, RFCOMM_CREATE_CHANNEL), "B1" +}; +// create rfcomm channel: @param bd_addr(48), channel (8), mtu (16), credits (8) +const hci_cmd_t rfcomm_create_channel_with_initial_credits = { + OPCODE(OGF_BTSTACK, RFCOMM_CREATE_CHANNEL_WITH_CREDITS), "B121" +}; +// grant credits: @param rfcomm_cid(16), credits (8) +const hci_cmd_t rfcomm_grants_credits= { + OPCODE(OGF_BTSTACK, RFCOMM_GRANT_CREDITS), "21" +}; +// disconnect rfcomm disconnect, @param rfcomm_cid(16), reason(8) +const hci_cmd_t rfcomm_disconnect = { + OPCODE(OGF_BTSTACK, RFCOMM_DISCONNECT), "21" +}; + +// register rfcomm service: @param channel(8), mtu (16) +const hci_cmd_t rfcomm_register_service = { + OPCODE(OGF_BTSTACK, RFCOMM_REGISTER_SERVICE), "12" +}; +// register rfcomm service: @param channel(8), mtu (16), initial credits (8) +const hci_cmd_t rfcomm_register_service_with_initial_credits = { + OPCODE(OGF_BTSTACK, RFCOMM_REGISTER_SERVICE_WITH_CREDITS), "121" +}; + +// unregister rfcomm service, @param service_channel(16) +const hci_cmd_t rfcomm_unregister_service = { + OPCODE(OGF_BTSTACK, RFCOMM_UNREGISTER_SERVICE), "2" +}; +// accept connection @param source cid (16) +const hci_cmd_t rfcomm_accept_connection = { + OPCODE(OGF_BTSTACK, RFCOMM_ACCEPT_CONNECTION), "2" +}; +// decline connection @param source cid (16) +const hci_cmd_t rfcomm_decline_connection = { + OPCODE(OGF_BTSTACK, RFCOMM_DECLINE_CONNECTION), "21" +}; +// request persisten rfcomm channel number for named service +const hci_cmd_t rfcomm_persistent_channel_for_service = { + OPCODE(OGF_BTSTACK, RFCOMM_PERSISTENT_CHANNEL), "N" +}; + +// register rfcomm service: @param channel(8), mtu (16), initial credits (8) +extern const hci_cmd_t rfcomm_register_service_with_initial_credits;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/hci_cmds.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2009 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * hci_cmds.h + * + * Created by Matthias Ringwald on 7/23/09. + */ + +#pragma once + +#include <stdint.h> + +#if defined __cplusplus +extern "C" { +#endif + +/** + * packet types - used in BTstack and over the H4 UART interface + */ +#define HCI_COMMAND_DATA_PACKET 0x01 +#define HCI_ACL_DATA_PACKET 0x02 +#define HCI_SCO_DATA_PACKET 0x03 +#define HCI_EVENT_PACKET 0x04 + +// extension for client/server communication +#define DAEMON_EVENT_PACKET 0x05 + +// L2CAP data +#define L2CAP_DATA_PACKET 0x06 + +// RFCOMM data +#define RFCOMM_DATA_PACKET 0x07 + +// Attribute protocol data +#define ATT_DATA_PACKET 0x08 + +// Security Manager protocol data +#define SM_DATA_PACKET 0x09 + +// debug log messages +#define LOG_MESSAGE_PACKET 0xfc + + +// Fixed PSM numbers +#define PSM_SDP 0x01 +#define PSM_RFCOMM 0x03 +#define PSM_HID_CONTROL 0x11 +#define PSM_HID_INTERRUPT 0x13 + +// Events from host controller to host +#define HCI_EVENT_INQUIRY_COMPLETE 0x01 +#define HCI_EVENT_INQUIRY_RESULT 0x02 +#define HCI_EVENT_CONNECTION_COMPLETE 0x03 +#define HCI_EVENT_CONNECTION_REQUEST 0x04 +#define HCI_EVENT_DISCONNECTION_COMPLETE 0x05 +#define HCI_EVENT_AUTHENTICATION_COMPLETE_EVENT 0x06 +#define HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE 0x07 +#define HCI_EVENT_ENCRYPTION_CHANGE 0x08 +#define HCI_EVENT_CHANGE_CONNECTION_LINK_KEY_COMPLETE 0x09 +#define HCI_EVENT_MASTER_LINK_KEY_COMPLETE 0x0A +#define HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE 0x0B +#define HCI_EVENT_READ_REMOTE_VERSION_INFORMATION_COMPLETE 0x0C +#define HCI_EVENT_QOS_SETUP_COMPLETE 0x0D +#define HCI_EVENT_COMMAND_COMPLETE 0x0E +#define HCI_EVENT_COMMAND_STATUS 0x0F +#define HCI_EVENT_HARDWARE_ERROR 0x10 +#define HCI_EVENT_FLUSH_OCCURED 0x11 +#define HCI_EVENT_ROLE_CHANGE 0x12 +#define HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS 0x13 +#define HCI_EVENT_MODE_CHANGE_EVENT 0x14 +#define HCI_EVENT_RETURN_LINK_KEYS 0x15 +#define HCI_EVENT_PIN_CODE_REQUEST 0x16 +#define HCI_EVENT_LINK_KEY_REQUEST 0x17 +#define HCI_EVENT_LINK_KEY_NOTIFICATION 0x18 +#define HCI_EVENT_DATA_BUFFER_OVERFLOW 0x1A +#define HCI_EVENT_MAX_SLOTS_CHANGED 0x1B +#define HCI_EVENT_READ_CLOCK_OFFSET_COMPLETE 0x1C +#define HCI_EVENT_PACKET_TYPE_CHANGED 0x1D +#define HCI_EVENT_INQUIRY_RESULT_WITH_RSSI 0x22 +#define HCI_EVENT_EXTENDED_INQUIRY_RESPONSE 0x2F +#define HCI_EVENT_LE_META 0x3E +#define HCI_EVENT_VENDOR_SPECIFIC 0xFF + +#define HCI_SUBEVENT_LE_CONNECTION_COMPLETE 0x01 +#define HCI_SUBEVENT_LE_ADVERTISING_REPORT 0x02 +#define HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE 0x03 +#define HCI_SUBEVENT_LE_READ_REMOTE_USED_FEATURES_COMPLETE 0x04 +#define HCI_SUBEVENT_LE_LONG_TERM_KEY_REQUEST 0x05 + +// last used HCI_EVENT in 2.1 is 0x3d + +// events 0x50-0x5f are used internally + +// BTSTACK DAEMON EVENTS + +// events from BTstack for application/client lib +#define BTSTACK_EVENT_STATE 0x60 + +// data: event(8), len(8), nr hci connections +#define BTSTACK_EVENT_NR_CONNECTIONS_CHANGED 0x61 + +// data: none +#define BTSTACK_EVENT_POWERON_FAILED 0x62 + +// data: majot (8), minor (8), revision(16) +#define BTSTACK_EVENT_VERSION 0x63 + +// data: system bluetooth on/off (bool) +#define BTSTACK_EVENT_SYSTEM_BLUETOOTH_ENABLED 0x64 + +// data: event (8), len(8), status (8) == 0, address (48), name (1984 bits = 248 bytes) +#define BTSTACK_EVENT_REMOTE_NAME_CACHED 0x65 + +// data: discoverable enabled (bool) +#define BTSTACK_EVENT_DISCOVERABLE_ENABLED 0x66 + +// L2CAP EVENTS + +// data: event (8), len(8), status (8), address(48), handle (16), psm (16), local_cid(16), remote_cid (16), local_mtu(16), remote_mtu(16) +#define L2CAP_EVENT_CHANNEL_OPENED 0x70 + +// data: event (8), len(8), channel (16) +#define L2CAP_EVENT_CHANNEL_CLOSED 0x71 + +// data: event (8), len(8), address(48), handle (16), psm (16), local_cid(16), remote_cid (16) +#define L2CAP_EVENT_INCOMING_CONNECTION 0x72 + +// data: event(8), len(8), handle(16) +#define L2CAP_EVENT_TIMEOUT_CHECK 0x73 + +// data: event(8), len(8), local_cid(16), credits(8) +#define L2CAP_EVENT_CREDITS 0x74 + +// data: event(8), len(8), status (8), psm (16) +#define L2CAP_EVENT_SERVICE_REGISTERED 0x75 + + +// RFCOMM EVENTS + +// data: event(8), len(8), status (8), address (48), handle (16), server channel(8), rfcomm_cid(16), max frame size(16) +#define RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE 0x80 + +// data: event(8), len(8), rfcomm_cid(16) +#define RFCOMM_EVENT_CHANNEL_CLOSED 0x81 + +// data: event (8), len(8), address(48), channel (8), rfcomm_cid (16) +#define RFCOMM_EVENT_INCOMING_CONNECTION 0x82 + +// data: event (8), len(8), rfcommid (16), ... +#define RFCOMM_EVENT_REMOTE_LINE_STATUS 0x83 + +// data: event(8), len(8), rfcomm_cid(16), credits(8) +#define RFCOMM_EVENT_CREDITS 0x84 + +// data: event(8), len(8), status (8), rfcomm server channel id (8) +#define RFCOMM_EVENT_SERVICE_REGISTERED 0x85 + +// data: event(8), len(8), status (8), rfcomm server channel id (8) +#define RFCOMM_EVENT_PERSISTENT_CHANNEL 0x86 + + +// data: event(8), len(8), status(8), service_record_handle(32) +#define SDP_SERVICE_REGISTERED 0x90 + + +// last error code in 2.1 is 0x38 - we start with 0x50 for BTstack errors + +#define BTSTACK_CONNECTION_TO_BTDAEMON_FAILED 0x50 +#define BTSTACK_ACTIVATION_FAILED_SYSTEM_BLUETOOTH 0x51 +#define BTSTACK_ACTIVATION_POWERON_FAILED 0x52 +#define BTSTACK_ACTIVATION_FAILED_UNKNOWN 0x53 +#define BTSTACK_NOT_ACTIVATED 0x54 +#define BTSTACK_BUSY 0x55 +#define BTSTACK_MEMORY_ALLOC_FAILED 0x56 +#define BTSTACK_ACL_BUFFERS_FULL 0x57 + +// l2cap errors - enumeration by the command that created them +#define L2CAP_COMMAND_REJECT_REASON_COMMAND_NOT_UNDERSTOOD 0x60 +#define L2CAP_COMMAND_REJECT_REASON_SIGNALING_MTU_EXCEEDED 0x61 +#define L2CAP_COMMAND_REJECT_REASON_INVALID_CID_IN_REQUEST 0x62 + +#define L2CAP_CONNECTION_RESPONSE_RESULT_SUCCESSFUL 0x63 +#define L2CAP_CONNECTION_RESPONSE_RESULT_PENDING 0x64 +#define L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_PSM 0x65 +#define L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_SECURITY 0x66 +#define L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_RESOURCES 0x65 + +#define L2CAP_CONFIG_RESPONSE_RESULT_SUCCESSFUL 0x66 +#define L2CAP_CONFIG_RESPONSE_RESULT_UNACCEPTABLE_PARAMS 0x67 +#define L2CAP_CONFIG_RESPONSE_RESULT_REJECTED 0x68 +#define L2CAP_CONFIG_RESPONSE_RESULT_UNKNOWN_OPTIONS 0x69 +#define L2CAP_SERVICE_ALREADY_REGISTERED 0x6a + +#define RFCOMM_MULTIPLEXER_STOPPED 0x70 +#define RFCOMM_CHANNEL_ALREADY_REGISTERED 0x71 +#define RFCOMM_NO_OUTGOING_CREDITS 0x72 + +#define SDP_HANDLE_ALREADY_REGISTERED 0x80 + +/** + * Default INQ Mode + */ +#define HCI_INQUIRY_LAP 0x9E8B33L // 0x9E8B33: General/Unlimited Inquiry Access Code (GIAC) +/** + * Hardware state of Bluetooth controller + */ +typedef enum { + HCI_POWER_OFF = 0, + HCI_POWER_ON, + HCI_POWER_SLEEP +} HCI_POWER_MODE; + +/** + * State of BTstack + */ +typedef enum { + HCI_STATE_OFF = 0, + HCI_STATE_INITIALIZING, + HCI_STATE_WORKING, + HCI_STATE_HALTING, + HCI_STATE_SLEEPING, + HCI_STATE_FALLING_ASLEEP +} HCI_STATE; + +/** + * compact HCI Command packet description + */ + typedef struct { + uint16_t opcode; + const char *format; +} hci_cmd_t; + + +// HCI Commands - see hci_cmds.c for info on parameters +extern const hci_cmd_t btstack_get_state; +extern const hci_cmd_t btstack_set_power_mode; +extern const hci_cmd_t btstack_set_acl_capture_mode; +extern const hci_cmd_t btstack_get_version; +extern const hci_cmd_t btstack_get_system_bluetooth_enabled; +extern const hci_cmd_t btstack_set_system_bluetooth_enabled; +extern const hci_cmd_t btstack_set_discoverable; +extern const hci_cmd_t btstack_set_bluetooth_enabled; // only used by btstack config + +extern const hci_cmd_t hci_accept_connection_request; +extern const hci_cmd_t hci_authentication_requested; +extern const hci_cmd_t hci_change_connection_link_key; +extern const hci_cmd_t hci_create_connection; +extern const hci_cmd_t hci_create_connection_cancel; +extern const hci_cmd_t hci_delete_stored_link_key; +extern const hci_cmd_t hci_disconnect; +extern const hci_cmd_t hci_host_buffer_size; +extern const hci_cmd_t hci_inquiry; +extern const hci_cmd_t hci_inquiry_cancel; +extern const hci_cmd_t hci_link_key_request_negative_reply; +extern const hci_cmd_t hci_link_key_request_reply; +extern const hci_cmd_t hci_pin_code_request_reply; +extern const hci_cmd_t hci_pin_code_request_negative_reply; +extern const hci_cmd_t hci_qos_setup; +extern const hci_cmd_t hci_read_bd_addr; +extern const hci_cmd_t hci_read_buffer_size; +extern const hci_cmd_t hci_read_le_host_supported; +extern const hci_cmd_t hci_read_link_policy_settings; +extern const hci_cmd_t hci_read_link_supervision_timeout; +extern const hci_cmd_t hci_read_local_supported_features; +extern const hci_cmd_t hci_read_num_broadcast_retransmissions; +extern const hci_cmd_t hci_reject_connection_request; +extern const hci_cmd_t hci_remote_name_request; +extern const hci_cmd_t hci_remote_name_request_cancel; +extern const hci_cmd_t hci_reset; +extern const hci_cmd_t hci_role_discovery; +extern const hci_cmd_t hci_set_event_mask; +extern const hci_cmd_t hci_set_connection_encryption; +extern const hci_cmd_t hci_sniff_mode; +extern const hci_cmd_t hci_switch_role_command; +extern const hci_cmd_t hci_write_authentication_enable; +extern const hci_cmd_t hci_write_class_of_device; +extern const hci_cmd_t hci_write_extended_inquiry_response; +extern const hci_cmd_t hci_write_inquiry_mode; +extern const hci_cmd_t hci_write_le_host_supported; +extern const hci_cmd_t hci_write_link_policy_settings; +extern const hci_cmd_t hci_write_link_supervision_timeout; +extern const hci_cmd_t hci_write_local_name; +extern const hci_cmd_t hci_write_num_broadcast_retransmissions; +extern const hci_cmd_t hci_write_page_timeout; +extern const hci_cmd_t hci_write_scan_enable; +extern const hci_cmd_t hci_write_simple_pairing_mode; + +extern const hci_cmd_t hci_le_add_device_to_whitelist; +extern const hci_cmd_t hci_le_clear_white_list; +extern const hci_cmd_t hci_le_connection_update; +extern const hci_cmd_t hci_le_create_connection; +extern const hci_cmd_t hci_le_create_connection_cancel; +extern const hci_cmd_t hci_le_encrypt; +extern const hci_cmd_t hci_le_long_term_key_negative_reply; +extern const hci_cmd_t hci_le_long_term_key_request_reply; +extern const hci_cmd_t hci_le_rand; +extern const hci_cmd_t hci_le_read_advertising_channel_tx_power; +extern const hci_cmd_t hci_le_read_buffer_size ; +extern const hci_cmd_t hci_le_read_channel_map; +extern const hci_cmd_t hci_le_read_remote_used_features; +extern const hci_cmd_t hci_le_read_supported_features; +extern const hci_cmd_t hci_le_read_supported_states; +extern const hci_cmd_t hci_le_read_white_list_size; +extern const hci_cmd_t hci_le_receiver_test; +extern const hci_cmd_t hci_le_remove_device_from_whitelist; +extern const hci_cmd_t hci_le_set_advertise_enable; +extern const hci_cmd_t hci_le_set_advertising_data; +extern const hci_cmd_t hci_le_set_advertising_parameters; +extern const hci_cmd_t hci_le_set_event_mask; +extern const hci_cmd_t hci_le_set_host_channel_classification; +extern const hci_cmd_t hci_le_set_random_address; +extern const hci_cmd_t hci_le_set_scan_enable; +extern const hci_cmd_t hci_le_set_scan_parameters; +extern const hci_cmd_t hci_le_set_scan_response_data; +extern const hci_cmd_t hci_le_start_encryption; +extern const hci_cmd_t hci_le_test_end; +extern const hci_cmd_t hci_le_transmitter_test; + +extern const hci_cmd_t l2cap_accept_connection; +extern const hci_cmd_t l2cap_create_channel; +extern const hci_cmd_t l2cap_create_channel_mtu; +extern const hci_cmd_t l2cap_decline_connection; +extern const hci_cmd_t l2cap_disconnect; +extern const hci_cmd_t l2cap_register_service; +extern const hci_cmd_t l2cap_unregister_service; + +extern const hci_cmd_t sdp_register_service_record; +extern const hci_cmd_t sdp_unregister_service_record; + +// accept connection @param bd_addr(48), rfcomm_cid (16) +extern const hci_cmd_t rfcomm_accept_connection; +// create rfcomm channel: @param bd_addr(48), channel (8) +extern const hci_cmd_t rfcomm_create_channel; +// create rfcomm channel: @param bd_addr(48), channel (8), mtu (16), credits (8) +extern const hci_cmd_t rfcomm_create_channel_with_initial_credits; +// decline rfcomm disconnect,@param bd_addr(48), rfcomm cid (16), reason(8) +extern const hci_cmd_t rfcomm_decline_connection; +// disconnect rfcomm disconnect, @param rfcomm_cid(8), reason(8) +extern const hci_cmd_t rfcomm_disconnect; +// register rfcomm service: @param channel(8), mtu (16) +extern const hci_cmd_t rfcomm_register_service; +// register rfcomm service: @param channel(8), mtu (16), initial credits (8) +extern const hci_cmd_t rfcomm_register_service_with_initial_credits; +// unregister rfcomm service, @param service_channel(16) +extern const hci_cmd_t rfcomm_unregister_service; +// request persisten rfcomm channel for service name: serive name (char*) +extern const hci_cmd_t rfcomm_persistent_channel_for_service; + +//sibu +extern const hci_cmd_t hci_read_local_supprted_commands; +extern const hci_cmd_t hci_read_rssi; + +#if defined __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/hci_dump.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * hci_dump.c + * + * Dump HCI trace in various formats: + * + * - BlueZ's hcidump format + * - Apple's PacketLogger + * - stdout hexdump + * + * Created by Matthias Ringwald on 5/26/09. + */ + +#include "config.h" + +#include "hci_dump.h" +#include "hci.h" +#include "hci_transport.h" +#include <btstack/hci_cmds.h> + +#ifndef EMBEDDED +#include <fcntl.h> // open +#include <arpa/inet.h> // hton.. +#include <unistd.h> // write +#include <stdio.h> +#include <time.h> +#include <sys/time.h> // for timestamps +#include <sys/stat.h> // for mode flags +#include <stdarg.h> // for va_list +#endif + +// BLUEZ hcidump +typedef struct { + uint16_t len; + uint8_t in; + uint8_t pad; + uint32_t ts_sec; + uint32_t ts_usec; + uint8_t packet_type; +} +#ifdef __GNUC__ +__attribute__ ((packed)) +#endif +hcidump_hdr; + +// APPLE PacketLogger +typedef struct { + uint32_t len; + uint32_t ts_sec; + uint32_t ts_usec; + uint8_t type; // 0xfc for note +} +#ifdef __GNUC__ +__attribute__ ((packed)) +#endif +pktlog_hdr; + +#ifndef EMBEDDED +static int dump_file = -1; +static int dump_format; +static hcidump_hdr header_bluez; +static pktlog_hdr header_packetlogger; +static char time_string[40]; +static int max_nr_packets = -1; +static int nr_packets = 0; +static char log_message_buffer[256]; +#endif + +void hci_dump_open(char *filename, hci_dump_format_t format){ +#ifndef EMBEDDED + dump_format = format; + if (dump_format == HCI_DUMP_STDOUT) { + dump_file = fileno(stdout); + } else { + dump_file = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + } +#endif +} + +#ifndef EMBEDDED +void hci_dump_set_max_packets(int packets){ + max_nr_packets = packets; +} +#endif + +void hci_dump_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len) { +#ifndef EMBEDDED + + if (dump_file < 0) return; // not activated yet + + // don't grow bigger than max_nr_packets + if (dump_format != HCI_DUMP_STDOUT && max_nr_packets > 0){ + if (nr_packets >= max_nr_packets){ + lseek(dump_file, 0, SEEK_SET); + ftruncate(dump_file, 0); + nr_packets = 0; + } + nr_packets++; + } + + // get time + struct timeval curr_time; + struct tm* ptm; + gettimeofday(&curr_time, NULL); + + switch (dump_format){ + case HCI_DUMP_STDOUT: { + /* Obtain the time of day, and convert it to a tm struct. */ + ptm = localtime (&curr_time.tv_sec); + /* Format the date and time, down to a single second. */ + strftime (time_string, sizeof (time_string), "[%Y-%m-%d %H:%M:%S", ptm); + /* Compute milliseconds from microseconds. */ + uint16_t milliseconds = curr_time.tv_usec / 1000; + /* Print the formatted time, in seconds, followed by a decimal point + and the milliseconds. */ + printf ("%s.%03u] ", time_string, milliseconds); + switch (packet_type){ + case HCI_COMMAND_DATA_PACKET: + printf("CMD => "); + break; + case HCI_EVENT_PACKET: + printf("EVT <= "); + break; + case HCI_ACL_DATA_PACKET: + if (in) { + printf("ACL <= "); + } else { + printf("ACL => "); + } + break; + case LOG_MESSAGE_PACKET: + // assume buffer is big enough + packet[len] = 0; + printf("LOG -- %s\n", (char*) packet); + return; + default: + return; + } + hexdump(packet, len); + break; + } + + case HCI_DUMP_BLUEZ: + bt_store_16( (uint8_t *) &header_bluez.len, 0, 1 + len); + header_bluez.in = in; + header_bluez.pad = 0; + bt_store_32( (uint8_t *) &header_bluez.ts_sec, 0, curr_time.tv_sec); + bt_store_32( (uint8_t *) &header_bluez.ts_usec, 0, curr_time.tv_usec); + header_bluez.packet_type = packet_type; + write (dump_file, &header_bluez, sizeof(hcidump_hdr) ); + write (dump_file, packet, len ); + break; + + case HCI_DUMP_PACKETLOGGER: + header_packetlogger.len = htonl( sizeof(pktlog_hdr) - 4 + len); + header_packetlogger.ts_sec = htonl(curr_time.tv_sec); + header_packetlogger.ts_usec = htonl(curr_time.tv_usec); + switch (packet_type){ + case HCI_COMMAND_DATA_PACKET: + header_packetlogger.type = 0x00; + break; + case HCI_ACL_DATA_PACKET: + if (in) { + header_packetlogger.type = 0x03; + } else { + header_packetlogger.type = 0x02; + } + break; + case HCI_EVENT_PACKET: + header_packetlogger.type = 0x01; + break; + case LOG_MESSAGE_PACKET: + header_packetlogger.type = 0xfc; + break; + default: + return; + } + write (dump_file, &header_packetlogger, sizeof(pktlog_hdr) ); + write (dump_file, packet, len ); + break; + + default: + break; + } +#endif +} + +void hci_dump_log(const char * format, ...){ +#ifndef EMBEDDED + va_list argptr; + va_start(argptr, format); + int len = vsnprintf(log_message_buffer, sizeof(log_message_buffer), format, argptr); + hci_dump_packet(LOG_MESSAGE_PACKET, 0, (uint8_t*) log_message_buffer, len); + va_end(argptr); +#endif +} + +void hci_dump_close(){ +#ifndef EMBEDDED + close(dump_file); + dump_file = -1; +#endif +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/hci_dump.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * hci_dump.h + * + * Dump HCI trace as BlueZ's hcidump format, Apple's PacketLogger, or stdout + * + * Created by Matthias Ringwald on 5/26/09. + */ + +#pragma once + +#include <stdint.h> + +typedef enum { + HCI_DUMP_BLUEZ = 0, + HCI_DUMP_PACKETLOGGER, + HCI_DUMP_STDOUT +} hci_dump_format_t; + +void hci_dump_open(char *filename, hci_dump_format_t format); +void hci_dump_set_max_packets(int packets); // -1 for unlimited +void hci_dump_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len); +void hci_dump_log(const char * format, ...); +void hci_dump_close(void);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/hci_transport.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * hci_transport.h + * + * HCI Transport API -- allows BT Daemon to use different transport protcols + * + * Created by Matthias Ringwald on 4/29/09. + * + */ +#pragma once + +#include <stdint.h> +#include <btstack/run_loop.h> + +#if defined __cplusplus +extern "C" { +#endif + +/* HCI packet types */ +typedef struct { + int (*open)(void *transport_config); + int (*close)(void *transport_config); + int (*send_packet)(uint8_t packet_type, uint8_t *packet, int size); + void (*register_packet_handler)(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)); + const char * (*get_transport_name)(void); + // custom extension for UART transport implementations + int (*set_baudrate)(uint32_t baudrate); + // support async transport layers, e.g. IRQ driven without buffers + int (*can_send_packet_now)(uint8_t packet_type); +} hci_transport_t; + +typedef struct { + const char *device_name; + uint32_t baudrate_init; // initial baud rate + uint32_t baudrate_main; // = 0: same as initial baudrate + int flowcontrol; // +} hci_uart_config_t; + + +// inline various hci_transport_X.h files +extern hci_transport_t * hci_transport_h4_instance(void); +extern hci_transport_t * hci_transport_h4_dma_instance(void); +extern hci_transport_t * hci_transport_h4_iphone_instance(void); +extern hci_transport_t * hci_transport_h5_instance(void); +extern hci_transport_t * hci_transport_usb_instance(void); + +// support for "enforece wake device" in h4 - used by iOS power management +extern void hci_transport_h4_iphone_set_enforce_wake_device(char *path); + +#if defined __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/hci_transport_usb.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * hci_transport_usb.cpp + * + * HCI Transport API implementation for USB + * + * Created by Matthias Ringwald on 7/5/09. + */ + +// delock bt class 2 - csr +// 0a12:0001 (bus 27, device 2) + +// Interface Number - Alternate Setting - suggested Endpoint Address - Endpoint Type - Suggested Max Packet Size +// HCI Commands 0 0 0x00 Control 8/16/32/64 +// HCI Events 0 0 0x81 Interrupt (IN) 16 +// ACL Data 0 0 0x82 Bulk (IN) 32/64 +// ACL Data 0 0 0x02 Bulk (OUT) 32/64 + +#include <stdio.h> +#include <string.h> +#include "config.h" +#include "debug.h" +#include "hci.h" +#include "hci_transport.h" +#include "hci_dump.h" +#include "USBHostBTstack.h" + +enum { + LIB_USB_CLOSED = 0, + LIB_USB_OPENED, + LIB_USB_DEVICE_OPENDED, + LIB_USB_KERNEL_DETACHED, + LIB_USB_INTERFACE_CLAIMED, + LIB_USB_TRANSFERS_ALLOCATED +} libusb_state = LIB_USB_CLOSED; + +// single instance +static hci_transport_t * hci_transport_usb = NULL; +static USBHostBTstack* bt = NULL; + +static int usb_process_ds(struct data_source *ds) { + if (bt) { + bt->poll(); + } + return 0; +} + +static int usb_open(void *transport_config){ + log_info("usb_open\n"); + data_source_t *ds = (data_source_t*)malloc(sizeof(data_source_t)); + ds->process = usb_process_ds; + run_loop_add_data_source(ds); + if (bt) { + return bt->open(); + } + return 0; +} +static int usb_close(void *transport_config){ + + return 0; +} + +static int usb_send_packet(uint8_t packet_type, uint8_t * packet, int size){ + //log_info("usb_send_packet\n"); + if (bt) { + bt->send_packet(packet_type, packet, size); + } + return 0; +} + +static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){ + log_info("registering packet handler\n"); + if (bt) { + bt->register_packet_handler(handler); + } +} + +static const char * usb_get_transport_name(void){ + return "USB"; +} + +// get usb singleton +hci_transport_t * hci_transport_usb_instance() { + if (!bt) { + bt = new USBHostBTstack; + } + if (!hci_transport_usb) { + hci_transport_usb = (hci_transport_t*)malloc( sizeof(hci_transport_t)); + hci_transport_usb->open = usb_open; + hci_transport_usb->close = usb_close; + hci_transport_usb->send_packet = usb_send_packet; + hci_transport_usb->register_packet_handler = usb_register_packet_handler; + hci_transport_usb->get_transport_name = usb_get_transport_name; + hci_transport_usb->set_baudrate = NULL; + hci_transport_usb->can_send_packet_now = NULL; + } + return hci_transport_usb; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/l2cap.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,1138 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * l2cap.c + * + * Logical Link Control and Adaption Protocl (L2CAP) + * + * Created by Matthias Ringwald on 5/16/09. + */ + +#include "l2cap.h" +#include "hci.h" +#include "hci_dump.h" +#include "debug.h" +#include "btstack_memory.h" + +#include <stdarg.h> +#include <string.h> + +#include <stdio.h> + +// nr of buffered acl packets in outgoing queue to get max performance +#define NR_BUFFERED_ACL_PACKETS 3 + +// used to cache l2cap rejects, echo, and informational requests +#define NR_PENDING_SIGNALING_RESPONSES 3 + +// offsets for L2CAP SIGNALING COMMANDS +#define L2CAP_SIGNALING_COMMAND_CODE_OFFSET 0 +#define L2CAP_SIGNALING_COMMAND_SIGID_OFFSET 1 +#define L2CAP_SIGNALING_COMMAND_LENGTH_OFFSET 2 +#define L2CAP_SIGNALING_COMMAND_DATA_OFFSET 4 + +static void null_packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); +static void l2cap_packet_handler(uint8_t packet_type, uint8_t *packet, uint16_t size); + +// used to cache l2cap rejects, echo, and informational requests +static l2cap_signaling_response_t signaling_responses[NR_PENDING_SIGNALING_RESPONSES]; +static int signaling_responses_pending; + +static linked_list_t l2cap_channels = NULL; +static linked_list_t l2cap_services = NULL; +static void (*packet_handler) (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) = null_packet_handler; +static int new_credits_blocked = 0; + +static btstack_packet_handler_t attribute_protocol_packet_handler = NULL; +static btstack_packet_handler_t security_protocol_packet_handler = NULL; + +// prototypes +static void l2cap_finialize_channel_close(l2cap_channel_t *channel); +static l2cap_service_t * l2cap_get_service(uint16_t psm); +static void l2cap_emit_channel_opened(l2cap_channel_t *channel, uint8_t status); +static void l2cap_emit_channel_closed(l2cap_channel_t *channel); +static void l2cap_emit_connection_request(l2cap_channel_t *channel); +static int l2cap_channel_ready_for_open(l2cap_channel_t *channel); + + +void l2cap_init(){ + new_credits_blocked = 0; + signaling_responses_pending = 0; + + l2cap_channels = NULL; + l2cap_services = NULL; + + packet_handler = null_packet_handler; + + // + // register callback with HCI + // + hci_register_packet_handler(&l2cap_packet_handler); + hci_connectable_control(0); // no services yet +} + + +/** Register L2CAP packet handlers */ +static void null_packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ +} +void l2cap_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size)){ + packet_handler = handler; +} + +// notify client/protocol handler +void l2cap_dispatch(l2cap_channel_t *channel, uint8_t type, uint8_t * data, uint16_t size){ + if (channel->packet_handler) { + (* (channel->packet_handler))(type, channel->local_cid, data, size); + } else { + (*packet_handler)(channel->connection, type, channel->local_cid, data, size); + } +} + +void l2cap_emit_channel_opened(l2cap_channel_t *channel, uint8_t status) { + uint8_t event[21]; + event[0] = L2CAP_EVENT_CHANNEL_OPENED; + event[1] = sizeof(event) - 2; + event[2] = status; + bt_flip_addr(&event[3], channel->address); + bt_store_16(event, 9, channel->handle); + bt_store_16(event, 11, channel->psm); + bt_store_16(event, 13, channel->local_cid); + bt_store_16(event, 15, channel->remote_cid); + bt_store_16(event, 17, channel->local_mtu); + bt_store_16(event, 19, channel->remote_mtu); + hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); + l2cap_dispatch(channel, HCI_EVENT_PACKET, event, sizeof(event)); +} + +void l2cap_emit_channel_closed(l2cap_channel_t *channel) { + uint8_t event[4]; + event[0] = L2CAP_EVENT_CHANNEL_CLOSED; + event[1] = sizeof(event) - 2; + bt_store_16(event, 2, channel->local_cid); + hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); + l2cap_dispatch(channel, HCI_EVENT_PACKET, event, sizeof(event)); +} + +void l2cap_emit_connection_request(l2cap_channel_t *channel) { + uint8_t event[16]; + event[0] = L2CAP_EVENT_INCOMING_CONNECTION; + event[1] = sizeof(event) - 2; + bt_flip_addr(&event[2], channel->address); + bt_store_16(event, 8, channel->handle); + bt_store_16(event, 10, channel->psm); + bt_store_16(event, 12, channel->local_cid); + bt_store_16(event, 14, channel->remote_cid); + hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); + l2cap_dispatch(channel, HCI_EVENT_PACKET, event, sizeof(event)); +} + +static void l2cap_emit_service_registered(void *connection, uint8_t status, uint16_t psm){ + uint8_t event[5]; + event[0] = L2CAP_EVENT_SERVICE_REGISTERED; + event[1] = sizeof(event) - 2; + event[2] = status; + bt_store_16(event, 3, psm); + hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); + (*packet_handler)(connection, HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + +void l2cap_emit_credits(l2cap_channel_t *channel, uint8_t credits) { + // track credits + channel->packets_granted += credits; + // log_info("l2cap_emit_credits for cid %u, credits given: %u (+%u)\n", channel->local_cid, channel->packets_granted, credits); + + uint8_t event[5]; + event[0] = L2CAP_EVENT_CREDITS; + event[1] = sizeof(event) - 2; + bt_store_16(event, 2, channel->local_cid); + event[4] = credits; + hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); + l2cap_dispatch(channel, HCI_EVENT_PACKET, event, sizeof(event)); +} + +void l2cap_block_new_credits(uint8_t blocked){ + new_credits_blocked = blocked; +} + +void l2cap_hand_out_credits(void){ + + if (new_credits_blocked) return; // we're told not to. used by daemon + + linked_item_t *it; + for (it = (linked_item_t *) l2cap_channels; it ; it = it->next){ + if (!hci_number_free_acl_slots()) return; + l2cap_channel_t * channel = (l2cap_channel_t *) it; + if (channel->state != L2CAP_STATE_OPEN) continue; + if (hci_number_outgoing_packets(channel->handle) < NR_BUFFERED_ACL_PACKETS && channel->packets_granted == 0) { + l2cap_emit_credits(channel, 1); + } + } +} + +l2cap_channel_t * l2cap_get_channel_for_local_cid(uint16_t local_cid){ + linked_item_t *it; + for (it = (linked_item_t *) l2cap_channels; it ; it = it->next){ + l2cap_channel_t * channel = (l2cap_channel_t *) it; + if ( channel->local_cid == local_cid) { + return channel; + } + } + return NULL; +} + +int l2cap_can_send_packet_now(uint16_t local_cid){ + l2cap_channel_t *channel = l2cap_get_channel_for_local_cid(local_cid); + if (!channel) return 0; + if (!channel->packets_granted) return 0; + return hci_can_send_packet_now(HCI_ACL_DATA_PACKET); +} + +uint16_t l2cap_get_remote_mtu_for_local_cid(uint16_t local_cid){ + l2cap_channel_t * channel = l2cap_get_channel_for_local_cid(local_cid); + if (channel) { + return channel->remote_mtu; + } + return 0; +} + +int l2cap_send_signaling_packet(hci_con_handle_t handle, L2CAP_SIGNALING_COMMANDS cmd, uint8_t identifier, ...){ + + if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)){ + log_info("l2cap_send_signaling_packet, cannot send\n"); + return BTSTACK_ACL_BUFFERS_FULL; + } + + // log_info("l2cap_send_signaling_packet type %u\n", cmd); + uint8_t *acl_buffer = hci_get_outgoing_acl_packet_buffer(); + va_list argptr; + va_start(argptr, identifier); + uint16_t len = l2cap_create_signaling_internal(acl_buffer, handle, cmd, identifier, argptr); + va_end(argptr); + // log_info("l2cap_send_signaling_packet con %u!\n", handle); + return hci_send_acl_packet(acl_buffer, len); +} + +uint8_t *l2cap_get_outgoing_buffer(void){ + return hci_get_outgoing_acl_packet_buffer() + COMPLETE_L2CAP_HEADER; // 8 bytes +} + +int l2cap_send_prepared(uint16_t local_cid, uint16_t len){ + + if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)){ + log_info("l2cap_send_internal cid %u, cannot send\n", local_cid); + return BTSTACK_ACL_BUFFERS_FULL; + } + + l2cap_channel_t * channel = l2cap_get_channel_for_local_cid(local_cid); + if (!channel) { + log_error("l2cap_send_internal no channel for cid %u\n", local_cid); + return -1; // TODO: define error + } + + if (channel->packets_granted == 0){ + log_error("l2cap_send_internal cid %u, no credits!\n", local_cid); + return -1; // TODO: define error + } + + --channel->packets_granted; + + log_debug("l2cap_send_internal cid %u, handle %u, 1 credit used, credits left %u;\n", + local_cid, channel->handle, channel->packets_granted); + + uint8_t *acl_buffer = hci_get_outgoing_acl_packet_buffer(); + + // 0 - Connection handle : PB=10 : BC=00 + bt_store_16(acl_buffer, 0, channel->handle | (2 << 12) | (0 << 14)); + // 2 - ACL length + bt_store_16(acl_buffer, 2, len + 4); + // 4 - L2CAP packet length + bt_store_16(acl_buffer, 4, len + 0); + // 6 - L2CAP channel DEST + bt_store_16(acl_buffer, 6, channel->remote_cid); + // send + int err = hci_send_acl_packet(acl_buffer, len+8); + + l2cap_hand_out_credits(); + + return err; +} + +int l2cap_send_prepared_connectionless(uint16_t handle, uint16_t cid, uint16_t len){ + + if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)){ + log_info("l2cap_send_prepared_to_handle cid %u, cannot send\n", cid); + return BTSTACK_ACL_BUFFERS_FULL; + } + + log_debug("l2cap_send_prepared_to_handle cid %u, handle %u\n", cid, handle); + + uint8_t *acl_buffer = hci_get_outgoing_acl_packet_buffer(); + + // 0 - Connection handle : PB=10 : BC=00 + bt_store_16(acl_buffer, 0, handle | (2 << 12) | (0 << 14)); + // 2 - ACL length + bt_store_16(acl_buffer, 2, len + 4); + // 4 - L2CAP packet length + bt_store_16(acl_buffer, 4, len + 0); + // 6 - L2CAP channel DEST + bt_store_16(acl_buffer, 6, cid); + // send + int err = hci_send_acl_packet(acl_buffer, len+8); + + l2cap_hand_out_credits(); + + return err; +} + +int l2cap_send_internal(uint16_t local_cid, uint8_t *data, uint16_t len){ + + if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)){ + log_info("l2cap_send_internal cid %u, cannot send\n", local_cid); + return BTSTACK_ACL_BUFFERS_FULL; + } + + uint8_t *acl_buffer = hci_get_outgoing_acl_packet_buffer(); + + memcpy(&acl_buffer[8], data, len); + + return l2cap_send_prepared(local_cid, len); +} + +int l2cap_send_connectionless(uint16_t handle, uint16_t cid, uint8_t *data, uint16_t len){ + + if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)){ + log_info("l2cap_send_internal cid %u, cannot send\n", cid); + return BTSTACK_ACL_BUFFERS_FULL; + } + + uint8_t *acl_buffer = hci_get_outgoing_acl_packet_buffer(); + + memcpy(&acl_buffer[8], data, len); + + return l2cap_send_prepared_connectionless(handle, cid, len); +} + +static inline void channelStateVarSetFlag(l2cap_channel_t *channel, L2CAP_CHANNEL_STATE_VAR flag){ + channel->state_var = (L2CAP_CHANNEL_STATE_VAR) (channel->state_var | flag); +} + +static inline void channelStateVarClearFlag(l2cap_channel_t *channel, L2CAP_CHANNEL_STATE_VAR flag){ + channel->state_var = (L2CAP_CHANNEL_STATE_VAR) (channel->state_var & ~flag); +} + + + +// MARK: L2CAP_RUN +// process outstanding signaling tasks +void l2cap_run(void){ + + // check pending signaling responses + while (signaling_responses_pending){ + + if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)) break; + + hci_con_handle_t handle = signaling_responses[0].handle; + uint8_t sig_id = signaling_responses[0].sig_id; + uint16_t infoType = signaling_responses[0].data; // INFORMATION_REQUEST + uint16_t result = signaling_responses[0].data; // CONNECTION_REQUEST + + switch (signaling_responses[0].code){ + case CONNECTION_REQUEST: + l2cap_send_signaling_packet(handle, CONNECTION_RESPONSE, sig_id, 0, 0, result, 0); + break; + case ECHO_REQUEST: + l2cap_send_signaling_packet(handle, ECHO_RESPONSE, sig_id, 0, NULL); + break; + case INFORMATION_REQUEST: + if (infoType == 2) { + uint32_t features = 0; + // extended features request supported, however no features present + l2cap_send_signaling_packet(handle, INFORMATION_RESPONSE, sig_id, infoType, 0, 4, &features); + } else { + // all other types are not supported + l2cap_send_signaling_packet(handle, INFORMATION_RESPONSE, sig_id, infoType, 1, 0, NULL); + } + break; + default: + // should not happen + break; + } + + // remove first item + signaling_responses_pending--; + int i; + for (i=0; i < signaling_responses_pending; i++){ + memcpy(&signaling_responses[i], &signaling_responses[i+1], sizeof(l2cap_signaling_response_t)); + } + } + + uint8_t config_options[4]; + linked_item_t *it; + linked_item_t *next; + for (it = (linked_item_t *) l2cap_channels; it ; it = next){ + next = it->next; // cache next item as current item might get freed + + if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) break; + if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)) break; + + l2cap_channel_t * channel = (l2cap_channel_t *) it; + + // log_info("l2cap_run: state %u, var 0x%02x\n", channel->state, channel->state_var); + + + switch (channel->state){ + + case L2CAP_STATE_WILL_SEND_CREATE_CONNECTION: + // send connection request - set state first + channel->state = L2CAP_STATE_WAIT_CONNECTION_COMPLETE; + // BD_ADDR, Packet_Type, Page_Scan_Repetition_Mode, Reserved, Clock_Offset, Allow_Role_Switch + hci_send_cmd(&hci_create_connection, channel->address, hci_usable_acl_packet_types(), 0, 0, 0, 1); + break; + + case L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_DECLINE: + l2cap_send_signaling_packet(channel->handle, CONNECTION_RESPONSE, channel->remote_sig_id, 0, 0, channel->reason, 0); + // discard channel - l2cap_finialize_channel_close without sending l2cap close event + linked_list_remove(&l2cap_channels, (linked_item_t *) channel); // -- remove from list + btstack_memory_l2cap_channel_free(channel); + break; + + case L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_ACCEPT: + channel->state = L2CAP_STATE_CONFIG; + channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ); + l2cap_send_signaling_packet(channel->handle, CONNECTION_RESPONSE, channel->remote_sig_id, channel->local_cid, channel->remote_cid, 0, 0); + break; + + case L2CAP_STATE_WILL_SEND_CONNECTION_REQUEST: + // success, start l2cap handshake + channel->local_sig_id = l2cap_next_sig_id(); + channel->state = L2CAP_STATE_WAIT_CONNECT_RSP; + l2cap_send_signaling_packet( channel->handle, CONNECTION_REQUEST, channel->local_sig_id, channel->psm, channel->local_cid); + break; + + case L2CAP_STATE_CONFIG: + if (channel->state_var & L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP){ + channelStateVarClearFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP); + channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SENT_CONF_RSP); + l2cap_send_signaling_packet(channel->handle, CONFIGURE_RESPONSE, channel->remote_sig_id, channel->remote_cid, 0, 0, 0, NULL); + } + else if (channel->state_var & L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ){ + channelStateVarClearFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ); + channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SENT_CONF_REQ); + channel->local_sig_id = l2cap_next_sig_id(); + config_options[0] = 1; // MTU + config_options[1] = 2; // len param + bt_store_16( (uint8_t*)&config_options, 2, channel->local_mtu); + l2cap_send_signaling_packet(channel->handle, CONFIGURE_REQUEST, channel->local_sig_id, channel->remote_cid, 0, 4, &config_options); + } + if (l2cap_channel_ready_for_open(channel)){ + channel->state = L2CAP_STATE_OPEN; + l2cap_emit_channel_opened(channel, 0); // success + l2cap_emit_credits(channel, 1); + } + break; + + case L2CAP_STATE_WILL_SEND_DISCONNECT_RESPONSE: + l2cap_send_signaling_packet( channel->handle, DISCONNECTION_RESPONSE, channel->remote_sig_id, channel->local_cid, channel->remote_cid); + l2cap_finialize_channel_close(channel); // -- remove from list + break; + + case L2CAP_STATE_WILL_SEND_DISCONNECT_REQUEST: + channel->local_sig_id = l2cap_next_sig_id(); + channel->state = L2CAP_STATE_WAIT_DISCONNECT; + l2cap_send_signaling_packet( channel->handle, DISCONNECTION_REQUEST, channel->local_sig_id, channel->remote_cid, channel->local_cid); + break; + default: + break; + } + } +} + +uint16_t l2cap_max_mtu(void){ + return hci_max_acl_data_packet_length() - L2CAP_HEADER_SIZE; +} + +// open outgoing L2CAP channel +void l2cap_create_channel_internal(void * connection, btstack_packet_handler_t packet_handler, + bd_addr_t address, uint16_t psm, uint16_t mtu){ + + // alloc structure + l2cap_channel_t * chan = (l2cap_channel_t*) btstack_memory_l2cap_channel_get(); + if (!chan) { + // emit error event + l2cap_channel_t dummy_channel; + BD_ADDR_COPY(dummy_channel.address, address); + dummy_channel.psm = psm; + l2cap_emit_channel_opened(&dummy_channel, BTSTACK_MEMORY_ALLOC_FAILED); + return; + } + // limit local mtu to max acl packet length + if (mtu > l2cap_max_mtu()) { + mtu = l2cap_max_mtu(); + } + + // fill in + BD_ADDR_COPY(chan->address, address); + chan->psm = psm; + chan->handle = 0; + chan->connection = connection; + chan->packet_handler = packet_handler; + chan->remote_mtu = L2CAP_MINIMAL_MTU; + chan->local_mtu = mtu; + chan->packets_granted = 0; + + // set initial state + chan->state = L2CAP_STATE_WILL_SEND_CREATE_CONNECTION; + chan->state_var = L2CAP_CHANNEL_STATE_VAR_NONE; + chan->remote_sig_id = L2CAP_SIG_ID_INVALID; + chan->local_sig_id = L2CAP_SIG_ID_INVALID; + + // add to connections list + linked_list_add(&l2cap_channels, (linked_item_t *) chan); + + l2cap_run(); +} + +void l2cap_disconnect_internal(uint16_t local_cid, uint8_t reason){ + // find channel for local_cid + l2cap_channel_t * channel = l2cap_get_channel_for_local_cid(local_cid); + if (channel) { + channel->state = L2CAP_STATE_WILL_SEND_DISCONNECT_REQUEST; + } + // process + l2cap_run(); +} + +static void l2cap_handle_connection_failed_for_addr(bd_addr_t address, uint8_t status){ + linked_item_t *it = (linked_item_t *) &l2cap_channels; + while (it->next){ + l2cap_channel_t * channel = (l2cap_channel_t *) it->next; + if ( ! BD_ADDR_CMP( channel->address, address) ){ + if (channel->state == L2CAP_STATE_WAIT_CONNECTION_COMPLETE || channel->state == L2CAP_STATE_WILL_SEND_CREATE_CONNECTION) { + // failure, forward error code + l2cap_emit_channel_opened(channel, status); + // discard channel + it->next = it->next->next; + btstack_memory_l2cap_channel_free(channel); + } + } else { + it = it->next; + } + } +} + +static void l2cap_handle_connection_success_for_addr(bd_addr_t address, hci_con_handle_t handle){ + linked_item_t *it; + for (it = (linked_item_t *) l2cap_channels; it ; it = it->next){ + l2cap_channel_t * channel = (l2cap_channel_t *) it; + if ( ! BD_ADDR_CMP( channel->address, address) ){ + if (channel->state == L2CAP_STATE_WAIT_CONNECTION_COMPLETE || channel->state == L2CAP_STATE_WILL_SEND_CREATE_CONNECTION) { + // success, start l2cap handshake + channel->state = L2CAP_STATE_WILL_SEND_CONNECTION_REQUEST; + channel->handle = handle; + channel->local_cid = l2cap_next_local_cid(); + } + } + } + // process + l2cap_run(); +} + +void l2cap_event_handler( uint8_t *packet, uint16_t size ){ + + bd_addr_t address; + hci_con_handle_t handle; + l2cap_channel_t * channel; + linked_item_t *it; + int hci_con_used; + + switch(packet[0]){ + + // handle connection complete events + case HCI_EVENT_CONNECTION_COMPLETE: + bt_flip_addr(address, &packet[5]); + if (packet[2] == 0){ + handle = READ_BT_16(packet, 3); + l2cap_handle_connection_success_for_addr(address, handle); + } else { + l2cap_handle_connection_failed_for_addr(address, packet[2]); + } + break; + + // handle successful create connection cancel command + case HCI_EVENT_COMMAND_COMPLETE: + if ( COMMAND_COMPLETE_EVENT(packet, hci_create_connection_cancel) ) { + if (packet[5] == 0){ + bt_flip_addr(address, &packet[6]); + // CONNECTION TERMINATED BY LOCAL HOST (0X16) + l2cap_handle_connection_failed_for_addr(address, 0x16); + } + } + l2cap_run(); // try sending signaling packets first + break; + + case HCI_EVENT_COMMAND_STATUS: + l2cap_run(); // try sending signaling packets first + break; + + // handle disconnection complete events + case HCI_EVENT_DISCONNECTION_COMPLETE: + // send l2cap disconnect events for all channels on this handle + handle = READ_BT_16(packet, 3); + it = (linked_item_t *) &l2cap_channels; + while (it->next){ + l2cap_channel_t * channel = (l2cap_channel_t *) it->next; + if ( channel->handle == handle ){ + // update prev item before free'ing next element - don't call l2cap_finalize_channel_close + it->next = it->next->next; + l2cap_emit_channel_closed(channel); + btstack_memory_l2cap_channel_free(channel); + } else { + it = it->next; + } + } + break; + + case HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS: + l2cap_run(); // try sending signaling packets first + l2cap_hand_out_credits(); + break; + + // HCI Connection Timeouts + case L2CAP_EVENT_TIMEOUT_CHECK: + handle = READ_BT_16(packet, 2); + if (hci_authentication_active_for_handle(handle)) break; + hci_con_used = 0; + for (it = (linked_item_t *) l2cap_channels; it ; it = it->next){ + channel = (l2cap_channel_t *) it; + if (channel->handle == handle) { + hci_con_used = 1; + } + } + if (hci_con_used) break; + if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) break; + hci_send_cmd(&hci_disconnect, handle, 0x13); // remote closed connection + break; + + case DAEMON_EVENT_HCI_PACKET_SENT: + for (it = (linked_item_t *) l2cap_channels; it ; it = it->next){ + channel = (l2cap_channel_t *) it; + if (channel->packet_handler) { + (* (channel->packet_handler))(HCI_EVENT_PACKET, channel->local_cid, packet, size); + } + } + if (attribute_protocol_packet_handler) { + (*attribute_protocol_packet_handler)(HCI_EVENT_PACKET, 0, packet, size); + } + if (security_protocol_packet_handler) { + (*security_protocol_packet_handler)(HCI_EVENT_PACKET, 0, packet, size); + } + break; + + default: + break; + } + + // pass on + (*packet_handler)(NULL, HCI_EVENT_PACKET, 0, packet, size); +} + +static void l2cap_handle_disconnect_request(l2cap_channel_t *channel, uint16_t identifier){ + channel->remote_sig_id = identifier; + channel->state = L2CAP_STATE_WILL_SEND_DISCONNECT_RESPONSE; + l2cap_run(); +} + +static void l2cap_register_signaling_response(hci_con_handle_t handle, uint8_t code, uint8_t sig_id, uint16_t data){ + // Vol 3, Part A, 4.3: "The DCID and SCID fields shall be ignored when the result field indi- cates the connection was refused." + if (signaling_responses_pending < NR_PENDING_SIGNALING_RESPONSES) { + signaling_responses[signaling_responses_pending].handle = handle; + signaling_responses[signaling_responses_pending].code = code; + signaling_responses[signaling_responses_pending].sig_id = sig_id; + signaling_responses[signaling_responses_pending].data = data; + signaling_responses_pending++; + l2cap_run(); + } +} + +static void l2cap_handle_connection_request(hci_con_handle_t handle, uint8_t sig_id, uint16_t psm, uint16_t source_cid){ + + // log_info("l2cap_handle_connection_request for handle %u, psm %u cid %u\n", handle, psm, source_cid); + l2cap_service_t *service = l2cap_get_service(psm); + if (!service) { + // 0x0002 PSM not supported + l2cap_register_signaling_response(handle, CONNECTION_REQUEST, sig_id, 0x0002); + return; + } + + hci_connection_t * hci_connection = connection_for_handle( handle ); + if (!hci_connection) { + // + log_error("no hci_connection for handle %u\n", handle); + return; + } + // alloc structure + // log_info("l2cap_handle_connection_request register channel\n"); + l2cap_channel_t * channel = (l2cap_channel_t*) btstack_memory_l2cap_channel_get(); + if (!channel){ + // 0x0004 No resources available + l2cap_register_signaling_response(handle, CONNECTION_REQUEST, sig_id, 0x0004); + return; + } + + // fill in + BD_ADDR_COPY(channel->address, hci_connection->address); + channel->psm = psm; + channel->handle = handle; + channel->connection = service->connection; + channel->packet_handler = service->packet_handler; + channel->local_cid = l2cap_next_local_cid(); + channel->remote_cid = source_cid; + channel->local_mtu = service->mtu; + channel->remote_mtu = L2CAP_DEFAULT_MTU; + channel->packets_granted = 0; + channel->remote_sig_id = sig_id; + + // limit local mtu to max acl packet length + if (channel->local_mtu > l2cap_max_mtu()) { + channel->local_mtu = l2cap_max_mtu(); + } + + // set initial state + channel->state = L2CAP_STATE_WAIT_CLIENT_ACCEPT_OR_REJECT; + channel->state_var = L2CAP_CHANNEL_STATE_VAR_NONE; + + // add to connections list + linked_list_add(&l2cap_channels, (linked_item_t *) channel); + + // emit incoming connection request + l2cap_emit_connection_request(channel); +} + +void l2cap_accept_connection_internal(uint16_t local_cid){ + l2cap_channel_t * channel = l2cap_get_channel_for_local_cid(local_cid); + if (!channel) { + log_error("l2cap_accept_connection_internal called but local_cid 0x%x not found", local_cid); + return; + } + + channel->state = L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_ACCEPT; + + // process + l2cap_run(); +} + +void l2cap_decline_connection_internal(uint16_t local_cid, uint8_t reason){ + l2cap_channel_t * channel = l2cap_get_channel_for_local_cid( local_cid); + if (!channel) { + log_error( "l2cap_decline_connection_internal called but local_cid 0x%x not found", local_cid); + return; + } + channel->state = L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_DECLINE; + channel->reason = reason; + l2cap_run(); +} + +void l2cap_signaling_handle_configure_request(l2cap_channel_t *channel, uint8_t *command){ + + channel->remote_sig_id = command[L2CAP_SIGNALING_COMMAND_SIGID_OFFSET]; + + // accept the other's configuration options + uint16_t end_pos = 4 + READ_BT_16(command, L2CAP_SIGNALING_COMMAND_LENGTH_OFFSET); + uint16_t pos = 8; + while (pos < end_pos){ + uint8_t type = command[pos++]; + uint8_t length = command[pos++]; + // MTU { type(8): 1, len(8):2, MTU(16) } + if ((type & 0x7f) == 1 && length == 2){ + channel->remote_mtu = READ_BT_16(command, pos); + // log_info("l2cap cid %u, remote mtu %u\n", channel->local_cid, channel->remote_mtu); + } + pos += length; + } +} + +static int l2cap_channel_ready_for_open(l2cap_channel_t *channel){ + // log_info("l2cap_channel_ready_for_open 0x%02x\n", channel->state_var); + if ((channel->state_var & L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_RSP) == 0) return 0; + if ((channel->state_var & L2CAP_CHANNEL_STATE_VAR_SENT_CONF_RSP) == 0) return 0; + return 1; +} + + +void l2cap_signaling_handler_channel(l2cap_channel_t *channel, uint8_t *command){ + + uint8_t code = command[L2CAP_SIGNALING_COMMAND_CODE_OFFSET]; + uint8_t identifier = command[L2CAP_SIGNALING_COMMAND_SIGID_OFFSET]; + uint16_t result = 0; + + log_info("L2CAP signaling handler code %u, state %u\n", code, channel->state); + + // handle DISCONNECT REQUESTS seperately + if (code == DISCONNECTION_REQUEST){ + switch (channel->state){ + case L2CAP_STATE_CONFIG: + case L2CAP_STATE_OPEN: + case L2CAP_STATE_WILL_SEND_DISCONNECT_REQUEST: + case L2CAP_STATE_WAIT_DISCONNECT: + l2cap_handle_disconnect_request(channel, identifier); + break; + + default: + // ignore in other states + break; + } + return; + } + + // @STATEMACHINE(l2cap) + switch (channel->state) { + + case L2CAP_STATE_WAIT_CONNECT_RSP: + switch (code){ + case CONNECTION_RESPONSE: + result = READ_BT_16 (command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET+4); + switch (result) { + case 0: + // successful connection + channel->remote_cid = READ_BT_16(command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET); + channel->state = L2CAP_STATE_CONFIG; + channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ); + break; + case 1: + // connection pending. get some coffee + break; + default: + // channel closed + channel->state = L2CAP_STATE_CLOSED; + + // map l2cap connection response result to BTstack status enumeration + l2cap_emit_channel_opened(channel, L2CAP_CONNECTION_RESPONSE_RESULT_SUCCESSFUL + result); + + // drop link key if security block + if (L2CAP_CONNECTION_RESPONSE_RESULT_SUCCESSFUL + result == L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_SECURITY){ + hci_drop_link_key_for_bd_addr(&channel->address); + } + + // discard channel + linked_list_remove(&l2cap_channels, (linked_item_t *) channel); + btstack_memory_l2cap_channel_free(channel); + break; + } + break; + + default: + //@TODO: implement other signaling packets + break; + } + break; + + case L2CAP_STATE_CONFIG: + switch (code) { + case CONFIGURE_REQUEST: + channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_REQ); + channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP); + l2cap_signaling_handle_configure_request(channel, command); + break; + case CONFIGURE_RESPONSE: + channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_RSP); + break; + default: + break; + } + if (l2cap_channel_ready_for_open(channel)){ + // for open: + channel->state = L2CAP_STATE_OPEN; + l2cap_emit_channel_opened(channel, 0); + l2cap_emit_credits(channel, 1); + } + break; + + case L2CAP_STATE_WAIT_DISCONNECT: + switch (code) { + case DISCONNECTION_RESPONSE: + l2cap_finialize_channel_close(channel); + break; + default: + //@TODO: implement other signaling packets + break; + } + break; + + case L2CAP_STATE_CLOSED: + // @TODO handle incoming requests + break; + + case L2CAP_STATE_OPEN: + //@TODO: implement other signaling packets, e.g. re-configure + break; + default: + break; + } + // log_info("new state %u\n", channel->state); +} + + +void l2cap_signaling_handler_dispatch( hci_con_handle_t handle, uint8_t * command){ + + // get code, signalind identifier and command len + uint8_t code = command[L2CAP_SIGNALING_COMMAND_CODE_OFFSET]; + uint8_t sig_id = command[L2CAP_SIGNALING_COMMAND_SIGID_OFFSET]; + + // not for a particular channel, and not CONNECTION_REQUEST, ECHO_[REQUEST|RESPONSE], INFORMATION_REQUEST + if (code < 1 || code == ECHO_RESPONSE || code > INFORMATION_REQUEST){ + return; + } + + // general commands without an assigned channel + switch(code) { + + case CONNECTION_REQUEST: { + uint16_t psm = READ_BT_16(command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET); + uint16_t source_cid = READ_BT_16(command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET+2); + l2cap_handle_connection_request(handle, sig_id, psm, source_cid); + return; + } + + case ECHO_REQUEST: + l2cap_register_signaling_response(handle, code, sig_id, 0); + return; + + case INFORMATION_REQUEST: { + uint16_t infoType = READ_BT_16(command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET); + l2cap_register_signaling_response(handle, code, sig_id, infoType); + return; + } + + default: + break; + } + + + // Get potential destination CID + uint16_t dest_cid = READ_BT_16(command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET); + + // Find channel for this sig_id and connection handle + linked_item_t *it; + for (it = (linked_item_t *) l2cap_channels; it ; it = it->next){ + l2cap_channel_t * channel = (l2cap_channel_t *) it; + if (channel->handle == handle) { + if (code & 1) { + // match odd commands (responses) by previous signaling identifier + if (channel->local_sig_id == sig_id) { + l2cap_signaling_handler_channel(channel, command); + break; + } + } else { + // match even commands (requests) by local channel id + if (channel->local_cid == dest_cid) { + l2cap_signaling_handler_channel(channel, command); + break; + } + } + } + } +} + +void l2cap_acl_handler( uint8_t *packet, uint16_t size ){ + + // Get Channel ID + uint16_t channel_id = READ_L2CAP_CHANNEL_ID(packet); + hci_con_handle_t handle = READ_ACL_CONNECTION_HANDLE(packet); + + switch (channel_id) { + + case L2CAP_CID_SIGNALING: { + + uint16_t command_offset = 8; + while (command_offset < size) { + + // handle signaling commands + l2cap_signaling_handler_dispatch(handle, &packet[command_offset]); + + // increment command_offset + command_offset += L2CAP_SIGNALING_COMMAND_DATA_OFFSET + READ_BT_16(packet, command_offset + L2CAP_SIGNALING_COMMAND_LENGTH_OFFSET); + } + break; + } + + case L2CAP_CID_ATTRIBUTE_PROTOCOL: + if (attribute_protocol_packet_handler) { + (*attribute_protocol_packet_handler)(ATT_DATA_PACKET, handle, &packet[COMPLETE_L2CAP_HEADER], size-COMPLETE_L2CAP_HEADER); + } + break; + + case L2CAP_CID_SECURITY_MANAGER_PROTOCOL: + if (security_protocol_packet_handler) { + (*security_protocol_packet_handler)(SM_DATA_PACKET, handle, &packet[COMPLETE_L2CAP_HEADER], size-COMPLETE_L2CAP_HEADER); + } + break; + + default: { + // Find channel for this channel_id and connection handle + l2cap_channel_t * channel = l2cap_get_channel_for_local_cid(channel_id); + if (channel) { + l2cap_dispatch(channel, L2CAP_DATA_PACKET, &packet[COMPLETE_L2CAP_HEADER], size-COMPLETE_L2CAP_HEADER); + } + break; + } + } + + l2cap_run(); +} + +static void l2cap_packet_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){ + switch (packet_type) { + case HCI_EVENT_PACKET: + l2cap_event_handler(packet, size); + break; + case HCI_ACL_DATA_PACKET: + l2cap_acl_handler(packet, size); + break; + default: + break; + } +} + +// finalize closed channel - l2cap_handle_disconnect_request & DISCONNECTION_RESPONSE +void l2cap_finialize_channel_close(l2cap_channel_t *channel){ + channel->state = L2CAP_STATE_CLOSED; + l2cap_emit_channel_closed(channel); + // discard channel + linked_list_remove(&l2cap_channels, (linked_item_t *) channel); + btstack_memory_l2cap_channel_free(channel); +} + +l2cap_service_t * l2cap_get_service(uint16_t psm){ + linked_item_t *it; + + // close open channels + for (it = (linked_item_t *) l2cap_services; it ; it = it->next){ + l2cap_service_t * service = ((l2cap_service_t *) it); + if ( service->psm == psm){ + return service; + }; + } + return NULL; +} + +void l2cap_register_service_internal(void *connection, btstack_packet_handler_t packet_handler, uint16_t psm, uint16_t mtu){ + // check for alread registered psm + // TODO: emit error event + l2cap_service_t *service = l2cap_get_service(psm); + if (service) { + log_error("l2cap_register_service_internal: PSM %u already registered\n", psm); + l2cap_emit_service_registered(connection, L2CAP_SERVICE_ALREADY_REGISTERED, psm); + return; + } + + // alloc structure + // TODO: emit error event + service = (l2cap_service_t *) btstack_memory_l2cap_service_get(); + if (!service) { + log_error("l2cap_register_service_internal: no memory for l2cap_service_t\n"); + l2cap_emit_service_registered(connection, BTSTACK_MEMORY_ALLOC_FAILED, psm); + return; + } + + // fill in + service->psm = psm; + service->mtu = mtu; + service->connection = connection; + service->packet_handler = packet_handler; + + // add to services list + linked_list_add(&l2cap_services, (linked_item_t *) service); + + // enable page scan + hci_connectable_control(1); + + // done + l2cap_emit_service_registered(connection, 0, psm); +} + +void l2cap_unregister_service_internal(void *connection, uint16_t psm){ + l2cap_service_t *service = l2cap_get_service(psm); + if (!service) return; + linked_list_remove(&l2cap_services, (linked_item_t *) service); + btstack_memory_l2cap_service_free(service); + + // disable page scan when no services registered + if (!linked_list_empty(&l2cap_services)) return; + hci_connectable_control(0); +} + +// +void l2cap_close_connection(void *connection){ + linked_item_t *it; + + // close open channels - note to myself: no channel is freed, so no new for fancy iterator tricks + l2cap_channel_t * channel; + for (it = (linked_item_t *) l2cap_channels; it ; it = it->next){ + channel = (l2cap_channel_t *) it; + if (channel->connection == connection) { + channel->state = L2CAP_STATE_WILL_SEND_DISCONNECT_REQUEST; + } + } + + // unregister services + it = (linked_item_t *) &l2cap_services; + while (it->next) { + l2cap_service_t * service = (l2cap_service_t *) it->next; + if (service->connection == connection){ + it->next = it->next->next; + btstack_memory_l2cap_service_free(service); + } else { + it = it->next; + } + } + + // process + l2cap_run(); +} + +// Bluetooth 4.0 - allows to register handler for Attribute Protocol and Security Manager Protocol +void l2cap_register_fixed_channel(btstack_packet_handler_t packet_handler, uint16_t channel_id) { + switch(channel_id){ + case L2CAP_CID_ATTRIBUTE_PROTOCOL: + attribute_protocol_packet_handler = packet_handler; + break; + case L2CAP_CID_SECURITY_MANAGER_PROTOCOL: + security_protocol_packet_handler = packet_handler; + break; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/l2cap.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * l2cap.h + * + * Logical Link Control and Adaption Protocl (L2CAP) + * + * Created by Matthias Ringwald on 5/16/09. + */ + +#pragma once + +#include "hci.h" +#include "l2cap_signaling.h" +#include <btstack/utils.h> +#include <btstack/btstack.h> + +#if defined __cplusplus +extern "C" { +#endif + +#define L2CAP_SIG_ID_INVALID 0 + +#define L2CAP_HEADER_SIZE 4 + +// size of HCI ACL + L2CAP Header for regular data packets (8) +#define COMPLETE_L2CAP_HEADER (HCI_ACL_HEADER_SIZE + L2CAP_HEADER_SIZE) + +// minimum signaling MTU +#define L2CAP_MINIMAL_MTU 48 +#define L2CAP_DEFAULT_MTU 672 + +// check L2CAP MTU +#if (L2CAP_MINIMAL_MTU + L2CAP_HEADER_SIZE) > HCI_ACL_PAYLOAD_SIZE +#error "HCI_ACL_PAYLOAD_SIZE too small for minimal L2CAP MTU of 48 bytes" +#endif + +// L2CAP Fixed Channel IDs +#define L2CAP_CID_SIGNALING 0x0001 +#define L2CAP_CID_CONNECTIONLESS_CHANNEL 0x0002 +#define L2CAP_CID_ATTRIBUTE_PROTOCOL 0x0004 +#define L2CAP_CID_SIGNALING_LE 0x0005 +#define L2CAP_CID_SECURITY_MANAGER_PROTOCOL 0x0006 + +void l2cap_init(void); +void l2cap_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size)); +void l2cap_create_channel_internal(void * connection, btstack_packet_handler_t packet_handler, bd_addr_t address, uint16_t psm, uint16_t mtu); +void l2cap_disconnect_internal(uint16_t local_cid, uint8_t reason); +uint16_t l2cap_get_remote_mtu_for_local_cid(uint16_t local_cid); +uint16_t l2cap_max_mtu(void); + +void l2cap_block_new_credits(uint8_t blocked); +int l2cap_can_send_packet_now(uint16_t local_cid); // non-blocking UART write + +// get outgoing buffer and prepare data +uint8_t *l2cap_get_outgoing_buffer(void); + +int l2cap_send_prepared(uint16_t local_cid, uint16_t len); +int l2cap_send_internal(uint16_t local_cid, uint8_t *data, uint16_t len); + +int l2cap_send_prepared_connectionless(uint16_t handle, uint16_t cid, uint16_t len); +int l2cap_send_connectionless(uint16_t handle, uint16_t cid, uint8_t *data, uint16_t len); + +void l2cap_close_connection(void *connection); + +void l2cap_register_service_internal(void *connection, btstack_packet_handler_t packet_handler, uint16_t psm, uint16_t mtu); +void l2cap_unregister_service_internal(void *connection, uint16_t psm); + +void l2cap_accept_connection_internal(uint16_t local_cid); +void l2cap_decline_connection_internal(uint16_t local_cid, uint8_t reason); + +// Bluetooth 4.0 - allows to register handler for Attribute Protocol and Security Manager Protocol +void l2cap_register_fixed_channel(btstack_packet_handler_t packet_handler, uint16_t channel_id); + + +// private structs +typedef enum { + L2CAP_STATE_CLOSED = 1, // no baseband + L2CAP_STATE_WILL_SEND_CREATE_CONNECTION, + L2CAP_STATE_WAIT_CONNECTION_COMPLETE, + L2CAP_STATE_WAIT_CLIENT_ACCEPT_OR_REJECT, + L2CAP_STATE_WAIT_CONNECT_RSP, // from peer + L2CAP_STATE_CONFIG, + L2CAP_STATE_OPEN, + L2CAP_STATE_WAIT_DISCONNECT, // from application + L2CAP_STATE_WILL_SEND_CONNECTION_REQUEST, + L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_DECLINE, + L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_ACCEPT, + L2CAP_STATE_WILL_SEND_DISCONNECT_REQUEST, + L2CAP_STATE_WILL_SEND_DISCONNECT_RESPONSE, +} L2CAP_STATE; + +typedef enum { + L2CAP_CHANNEL_STATE_VAR_NONE = 0, + L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_REQ = 1 << 0, + L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_RSP = 1 << 1, + L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ = 1 << 2, + L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP = 1 << 3, + L2CAP_CHANNEL_STATE_VAR_SENT_CONF_REQ = 1 << 4, + L2CAP_CHANNEL_STATE_VAR_SENT_CONF_RSP = 1 << 5, +} L2CAP_CHANNEL_STATE_VAR; + +// info regarding an actual coneection +typedef struct { + // linked list - assert: first field + linked_item_t item; + + L2CAP_STATE state; + L2CAP_CHANNEL_STATE_VAR state_var; + + bd_addr_t address; + hci_con_handle_t handle; + + uint8_t remote_sig_id; // used by other side, needed for delayed response + uint8_t local_sig_id; // own signaling identifier + + uint16_t local_cid; + uint16_t remote_cid; + + uint16_t local_mtu; + uint16_t remote_mtu; + + uint16_t psm; + + uint8_t packets_granted; // number of L2CAP/ACL packets client is allowed to send + + uint8_t reason; // used in decline internal + + // client connection + void * connection; + + // internal connection + btstack_packet_handler_t packet_handler; + +} l2cap_channel_t; + +// info regarding potential connections +typedef struct { + // linked list - assert: first field + linked_item_t item; + + // service id + uint16_t psm; + + // incoming MTU + uint16_t mtu; + + // client connection + void *connection; + + // internal connection + btstack_packet_handler_t packet_handler; + +} l2cap_service_t; + + +typedef struct l2cap_signaling_response { + hci_con_handle_t handle; + uint8_t sig_id; + uint8_t code; + uint16_t data; // infoType for INFORMATION REQUEST, result for CONNECTION request +} l2cap_signaling_response_t; + + +#if defined __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/l2cap_signaling.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * l2cap_signaling.h + * + * Created by Matthias Ringwald on 7/23/09. + */ + +#include "l2cap_signaling.h" + +#include <string.h> + +static char *l2cap_signaling_commands_format[] = { +"D", // 0x01 command reject: reason {cmd not understood (0), sig MTU exceeded (2:max sig MTU), invalid CID (4:req CID)}, data len, data +"22", // 0x02 connection request: PSM, Source CID +"2222", // 0x03 connection response: Dest CID, Source CID, Result, Status +"22D", // 0x04 config request: Dest CID, Flags, Configuration options +"222D", // 0x05 config response: Source CID, Flags, Result, Configuration options +"22", // 0x06 disconection request: Dest CID, Source CID +"22", // 0x07 disconection response: Dest CID, Source CID +"D", // 0x08 echo request: Data +"D", // 0x09 echo response: Data +"2", // 0x0a information request: InfoType {1=Connectionless MTU, 2=Extended features supported} +"22D", // 0x0b information response: InfoType, Result, Data +}; + +uint8_t sig_seq_nr = 0xff; +uint16_t source_cid = 0x40; + +uint8_t l2cap_next_sig_id(void){ + if (sig_seq_nr == 0xff) { + sig_seq_nr = 1; + } else { + sig_seq_nr++; + } + return sig_seq_nr; +} + +uint16_t l2cap_next_local_cid(void){ + return source_cid++; +} + +uint16_t l2cap_create_signaling_internal(uint8_t * acl_buffer, hci_con_handle_t handle, L2CAP_SIGNALING_COMMANDS cmd, uint8_t identifier, va_list argptr){ + + // 0 - Connection handle : PB=10 : BC=00 + bt_store_16(acl_buffer, 0, handle | (2 << 12) | (0 << 14)); + // 6 - L2CAP channel = 1 + bt_store_16(acl_buffer, 6, 1); + // 8 - Code + acl_buffer[8] = cmd; + // 9 - id (!= 0 sequentially) + acl_buffer[9] = identifier; + + // 12 - L2CAP signaling parameters + uint16_t pos = 12; + const char *format = l2cap_signaling_commands_format[cmd-1]; + uint16_t word; + uint8_t * ptr; + while (*format) { + switch(*format) { + case '1': // 8 bit value + case '2': // 16 bit value + word = va_arg(argptr, int); + // minimal va_arg is int: 2 bytes on 8+16 bit CPUs + acl_buffer[pos++] = word & 0xff; + if (*format == '2') { + acl_buffer[pos++] = word >> 8; + } + break; + case 'D': // variable data. passed: len, ptr + word = va_arg(argptr, int); + ptr = va_arg(argptr, uint8_t *); + memcpy(&acl_buffer[pos], ptr, word); + pos += word; + break; + default: + break; + } + format++; + }; + va_end(argptr); + + // Fill in various length fields: it's the number of bytes following for ACL lenght and l2cap parameter length + // - the l2cap payload length is counted after the following channel id (only payload) + + // 2 - ACL length + bt_store_16(acl_buffer, 2, pos - 4); + // 4 - L2CAP packet length + bt_store_16(acl_buffer, 4, pos - 6 - 2); + // 10 - L2CAP signaling parameter length + bt_store_16(acl_buffer, 10, pos - 12); + + return pos; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/l2cap_signaling.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * l2cap_signaling.h + * + * Created by Matthias Ringwald on 7/23/09. + */ + +#pragma once + +#include <stdint.h> +#include <stdarg.h> +#include <btstack/utils.h> +#include <btstack/hci_cmds.h> + +typedef enum { + COMMAND_REJECT = 1, + CONNECTION_REQUEST, + CONNECTION_RESPONSE, + CONFIGURE_REQUEST, + CONFIGURE_RESPONSE, + DISCONNECTION_REQUEST, + DISCONNECTION_RESPONSE, + ECHO_REQUEST, + ECHO_RESPONSE, + INFORMATION_REQUEST, + INFORMATION_RESPONSE +} L2CAP_SIGNALING_COMMANDS; + +uint16_t l2cap_create_signaling_internal(uint8_t * acl_buffer,hci_con_handle_t handle, L2CAP_SIGNALING_COMMANDS cmd, uint8_t identifier, va_list argptr); +uint8_t l2cap_next_sig_id(void); +uint16_t l2cap_next_local_cid(void); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/linked_list.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * linked_list.c + * + * Created by Matthias Ringwald on 7/13/09. + */ + +#include <btstack/linked_list.h> +#include <stdlib.h> +/** + * tests if list is empty + */ +int linked_list_empty(linked_list_t * list){ + return *list == (void *) 0; +} + +/** + * linked_list_get_last_item + */ +linked_item_t * linked_list_get_last_item(linked_list_t * list){ // <-- find the last item in the list + linked_item_t *lastItem = NULL; + linked_item_t *it; + for (it = *list; it ; it = it->next){ + if (it) { + lastItem = it; + } + } + return lastItem; +} + + +/** + * linked_list_add + */ +void linked_list_add(linked_list_t * list, linked_item_t *item){ // <-- add item to list + // check if already in list + linked_item_t *it; + for (it = *list; it ; it = it->next){ + if (it == item) { + return; + } + } + // add first + item->next = *list; + *list = item; +} + +void linked_list_add_tail(linked_list_t * list, linked_item_t *item){ // <-- add item to list as last element + // check if already in list + linked_item_t *it; + for (it = (linked_item_t *) list; it->next ; it = it->next){ + if (it->next == item) { + return; + } + } + item->next = (linked_item_t*) 0; + it->next = item; +} + +/** + * Remove data_source from run loop + * + * @note: assumes that data_source_t.next is first element in data_source + */ +int linked_list_remove(linked_list_t * list, linked_item_t *item){ // <-- remove item from list + linked_item_t *it; + for (it = (linked_item_t *) list; it ; it = it->next){ + if (it->next == item){ + it->next = item->next; + return 0; + } + } + return -1; +} + +void linked_item_set_user(linked_item_t *item, void *user_data){ + item->next = (linked_item_t *) 0; + item->user_data = user_data; +} + +void * linked_item_get_user(linked_item_t *item) { + return item->user_data; +} + +#if 0 +#include <stdio.h> +void test_linked_list(){ + linked_list_t testList = 0; + linked_item_t itemA; + linked_item_t itemB; + linked_item_t itemC; + linked_item_set_user(&itemA, (void *) 0); + linked_item_set_user(&itemB, (void *) 0); + linked_list_add(&testList, &itemA); + linked_list_add(&testList, &itemB); + linked_list_add_tail(&testList, &itemC); + // linked_list_remove(&testList, &itemB); + linked_item_t *it; + for (it = (linked_item_t *) &testList; it ; it = it->next){ + if (it->next == &itemA) printf("Item A\n"); + if (it->next == &itemB) printf("Item B\n"); + if (it->next == &itemC) printf("Item C\n"); + /* if (it->next == &itemB){ + it->next = it->next; + printf(" remove\n"); + } else { + printf(" keep\n"); + + */ + } +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/linked_list.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2009 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * linked_list.h + * + * Created by Matthias Ringwald on 7/13/09. + */ + +#pragma once + +#if defined __cplusplus +extern "C" { +#endif + +typedef struct linked_item { + struct linked_item *next; // <-- next element in list, or NULL + void *user_data; // <-- pointer to struct base +} linked_item_t; + +typedef linked_item_t * linked_list_t; + +void linked_item_set_user(linked_item_t *item, void *user_data); // <-- set user data +void * linked_item_get_user(linked_item_t *item); // <-- get user data +int linked_list_empty(linked_list_t * list); +void linked_list_add(linked_list_t * list, linked_item_t *item); // <-- add item to list as first element +void linked_list_add_tail(linked_list_t * list, linked_item_t *item); // <-- add item to list as last element +int linked_list_remove(linked_list_t * list, linked_item_t *item); // <-- remove item from list +linked_item_t * linked_list_get_last_item(linked_list_t * list); // <-- find the last item in the list + +void test_linked_list(void); + +#if defined __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/memory_pool.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2011 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * memory_pool.h + * + * @Brief Fixed-size block allocation + * + * @Assumption block_size >= sizeof(void *) + * @Assumption size of storage >= count * block_size + * + * @Note minimal implementation, no error checking/handling + */ + +#pragma once + +typedef void * memory_pool_t; + +// initialize memory pool with with given storage, block size and count +void memory_pool_create(memory_pool_t *pool, void * storage, int count, int block_size); + +// get free block from pool, @returns NULL or pointer to block +void * memory_pool_get(memory_pool_t *pool); + +// return previously reserved block to memory pool +void memory_pool_free(memory_pool_t *pool, void * block);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/remote_device_db.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/** + * interface to provide link key and remote name storage + */ + +#pragma once + +#include <btstack/utils.h> + +typedef struct { + + // management + void (*open)(void); + void (*close)(void); + + // link key + int (*get_link_key)(bd_addr_t *bd_addr, link_key_t *link_key); + void (*put_link_key)(bd_addr_t *bd_addr, link_key_t *key); + void (*delete_link_key)(bd_addr_t *bd_addr); + + // remote name + int (*get_name)(bd_addr_t *bd_addr, device_name_t *device_name); + void (*put_name)(bd_addr_t *bd_addr, device_name_t *device_name); + void (*delete_name)(bd_addr_t *bd_addr); + + // persistent rfcomm channel + uint8_t (*persistent_rfcomm_channel)(char *servicename); + +} remote_device_db_t; + +extern remote_device_db_t remote_device_db_iphone; +extern const remote_device_db_t remote_device_db_memory; + +// MARK: non-persisten implementation +#include <btstack/linked_list.h> +#define MAX_NAME_LEN 32 +typedef struct { + // linked list - assert: first field + linked_item_t item; + + bd_addr_t bd_addr; +} db_mem_device_t; + +typedef struct { + db_mem_device_t device; + link_key_t link_key; +} db_mem_device_link_key_t; + +typedef struct { + db_mem_device_t device; + char device_name[MAX_NAME_LEN]; +} db_mem_device_name_t; + +typedef struct { + // linked list - assert: first field + linked_item_t item; + + char service_name[MAX_NAME_LEN]; + uint8_t channel; +} db_mem_service_t;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/remote_device_db_memory.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +#include <string.h> +#include <stdlib.h> + +#include "remote_device_db.h" +#include "btstack_memory.h" +#include "debug.h" + +#include <btstack/utils.h> +#include <btstack/linked_list.h> + +// This lists should be only accessed by tests. +linked_list_t db_mem_link_keys = NULL; +linked_list_t db_mem_names = NULL; +static linked_list_t db_mem_services = NULL; + +// Device info +static void db_open(void){ +} + +static void db_close(void){ +} + +static db_mem_device_t * get_item(linked_list_t list, bd_addr_t *bd_addr) { + linked_item_t *it; + for (it = (linked_item_t *) list; it ; it = it->next){ + db_mem_device_t * item = (db_mem_device_t *) it; + if (BD_ADDR_CMP(item->bd_addr, *bd_addr) == 0) { + return item; + } + } + return NULL; +} + +static int get_name(bd_addr_t *bd_addr, device_name_t *device_name) { + db_mem_device_name_t * item = (db_mem_device_name_t *) get_item(db_mem_names, bd_addr); + + if (!item) return 0; + + strncpy((char*)device_name, item->device_name, MAX_NAME_LEN); + + linked_list_remove(&db_mem_names, (linked_item_t *) item); + linked_list_add(&db_mem_names, (linked_item_t *) item); + + return 1; +} + +static int get_link_key(bd_addr_t *bd_addr, link_key_t *link_key) { + db_mem_device_link_key_t * item = (db_mem_device_link_key_t *) get_item(db_mem_link_keys, bd_addr); + + if (!item) return 0; + + memcpy(link_key, item->link_key, LINK_KEY_LEN); + + linked_list_remove(&db_mem_link_keys, (linked_item_t *) item); + linked_list_add(&db_mem_link_keys, (linked_item_t *) item); + + return 1; +} + +static void delete_link_key(bd_addr_t *bd_addr){ + db_mem_device_t * item = get_item(db_mem_link_keys, bd_addr); + + if (!item) return; + + linked_list_remove(&db_mem_link_keys, (linked_item_t *) item); + btstack_memory_db_mem_device_link_key_free(item); +} + + +static void put_link_key(bd_addr_t *bd_addr, link_key_t *link_key){ + db_mem_device_link_key_t * existingRecord = (db_mem_device_link_key_t *) get_item(db_mem_link_keys, bd_addr); + + if (existingRecord){ + memcpy(existingRecord->link_key, link_key, LINK_KEY_LEN); + return; + } + + // Record not found, create new one for this device + db_mem_device_link_key_t * newItem = (db_mem_device_link_key_t*) btstack_memory_db_mem_device_link_key_get(); + if (!newItem){ + newItem = (db_mem_device_link_key_t*)linked_list_get_last_item(&db_mem_link_keys); + } + + if (!newItem) return; + + memcpy(newItem->device.bd_addr, bd_addr, sizeof(bd_addr_t)); + memcpy(newItem->link_key, link_key, LINK_KEY_LEN); + linked_list_add(&db_mem_link_keys, (linked_item_t *) newItem); +} + +static void delete_name(bd_addr_t *bd_addr){ + db_mem_device_t * item = get_item(db_mem_names, bd_addr); + + if (!item) return; + + linked_list_remove(&db_mem_names, (linked_item_t *) item); + btstack_memory_db_mem_device_name_free(item); +} + +static void put_name(bd_addr_t *bd_addr, device_name_t *device_name){ + db_mem_device_name_t * existingRecord = (db_mem_device_name_t *) get_item(db_mem_names, bd_addr); + + if (existingRecord){ + strncpy(existingRecord->device_name, (const char*) device_name, MAX_NAME_LEN); + return; + } + + // Record not found, create a new one for this device + db_mem_device_name_t * newItem = (db_mem_device_name_t *) btstack_memory_db_mem_device_name_get(); + if (!newItem) { + newItem = (db_mem_device_name_t*)linked_list_get_last_item(&db_mem_names); + }; + + if (!newItem) return; + + memcpy(newItem->device.bd_addr, bd_addr, sizeof(bd_addr_t)); + strncpy(newItem->device_name, (const char*) device_name, MAX_NAME_LEN); + linked_list_add(&db_mem_names, (linked_item_t *) newItem); +} + + +// MARK: PERSISTENT RFCOMM CHANNEL ALLOCATION + +static uint8_t persistent_rfcomm_channel(char *serviceName){ + linked_item_t *it; + db_mem_service_t * item; + uint8_t max_channel = 1; + + for (it = (linked_item_t *) db_mem_services; it ; it = it->next){ + item = (db_mem_service_t *) it; + if (strncmp(item->service_name, serviceName, MAX_NAME_LEN) == 0) { + // Match found + return item->channel; + } + + // TODO prevent overflow + if (item->channel >= max_channel) max_channel = item->channel + 1; + } + + // Allocate new persistant channel + db_mem_service_t * newItem = (db_mem_service_t *) btstack_memory_db_mem_service_get(); + + if (!newItem) return 0; + + strncpy(newItem->service_name, serviceName, MAX_NAME_LEN); + newItem->channel = max_channel; + linked_list_add(&db_mem_services, (linked_item_t *) newItem); + return max_channel; +} + + +const remote_device_db_t remote_device_db_memory = { + db_open, + db_close, + get_link_key, + put_link_key, + delete_link_key, + get_name, + put_name, + delete_name, + persistent_rfcomm_channel +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/rfcomm.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,1869 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * rfcomm.c + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> // memcpy +#include <stdint.h> + +#include <btstack/btstack.h> +#include <btstack/hci_cmds.h> +#include <btstack/utils.h> + +#include <btstack/utils.h> +#include "btstack_memory.h" +#include "hci.h" +#include "hci_dump.h" +#include "debug.h" +#include "rfcomm.h" + +// workaround for missing PRIxPTR on mspgcc (16/20-bit MCU) +#ifndef PRIxPTR +#if defined(__MSP430X__) && defined(__MSP430X_LARGE__) +#define PRIxPTR "lx" +#else +#define PRIxPTR "x" +#endif +#endif + + +// Control field values bit no. 1 2 3 4 PF 6 7 8 +#define BT_RFCOMM_SABM 0x3F // 1 1 1 1 1 1 0 0 +#define BT_RFCOMM_UA 0x73 // 1 1 0 0 1 1 1 0 +#define BT_RFCOMM_DM 0x0F // 1 1 1 1 0 0 0 0 +#define BT_RFCOMM_DM_PF 0x1F // 1 1 1 1 1 0 0 0 +#define BT_RFCOMM_DISC 0x53 // 1 1 0 0 1 0 1 0 +#define BT_RFCOMM_UIH 0xEF // 1 1 1 1 0 1 1 1 +#define BT_RFCOMM_UIH_PF 0xFF // 1 1 1 1 0 1 1 1 + +// Multiplexer message types +#define BT_RFCOMM_CLD_CMD 0xC3 +#define BT_RFCOMM_FCON_CMD 0xA3 +#define BT_RFCOMM_FCON_RSP 0xA1 +#define BT_RFCOMM_FCOFF_CMD 0x63 +#define BT_RFCOMM_FCOFF_RSP 0x61 +#define BT_RFCOMM_MSC_CMD 0xE3 +#define BT_RFCOMM_MSC_RSP 0xE1 +#define BT_RFCOMM_NSC_RSP 0x11 +#define BT_RFCOMM_PN_CMD 0x83 +#define BT_RFCOMM_PN_RSP 0x81 +#define BT_RFCOMM_RLS_CMD 0x53 +#define BT_RFCOMM_RLS_RSP 0x51 +#define BT_RFCOMM_RPN_CMD 0x93 +#define BT_RFCOMM_RPN_RSP 0x91 +#define BT_RFCOMM_TEST_CMD 0x23 +#define BT_RFCOMM_TEST_RSP 0x21 + +#define RFCOMM_MULIPLEXER_TIMEOUT_MS 60000 + +// FCS calc +#define BT_RFCOMM_CODE_WORD 0xE0 // pol = x8+x2+x1+1 +#define BT_RFCOMM_CRC_CHECK_LEN 3 +#define BT_RFCOMM_UIHCRC_CHECK_LEN 2 + +#include "l2cap.h" + +// used for debugging +// #define RFCOMM_LOG_CREDITS + +// global rfcomm data +static uint16_t rfcomm_client_cid_generator; // used for client channel IDs + +// linked lists for all +static linked_list_t rfcomm_multiplexers = NULL; +static linked_list_t rfcomm_channels = NULL; +static linked_list_t rfcomm_services = NULL; + +static void (*app_packet_handler)(void * connection, uint8_t packet_type, + uint16_t channel, uint8_t *packet, uint16_t size); + +static void rfcomm_run(void); +static void rfcomm_hand_out_credits(void); +static void rfcomm_channel_state_machine(rfcomm_channel_t *channel, rfcomm_channel_event_t *event); +static void rfcomm_channel_state_machine_2(rfcomm_multiplexer_t * multiplexer, uint8_t dlci, rfcomm_channel_event_t *event); +static int rfcomm_channel_ready_for_open(rfcomm_channel_t *channel); +static void rfcomm_multiplexer_state_machine(rfcomm_multiplexer_t * multiplexer, RFCOMM_MULTIPLEXER_EVENT event); + + +// MARK: RFCOMM CLIENT EVENTS + +// data: event (8), len(8), address(48), channel (8), rfcomm_cid (16) +static void rfcomm_emit_connection_request(rfcomm_channel_t *channel) { + uint8_t event[11]; + event[0] = RFCOMM_EVENT_INCOMING_CONNECTION; + event[1] = sizeof(event) - 2; + bt_flip_addr(&event[2], channel->multiplexer->remote_addr); + event[8] = channel->dlci >> 1; + bt_store_16(event, 9, channel->rfcomm_cid); + hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); + (*app_packet_handler)(channel->connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event)); +} + +// API Change: BTstack-0.3.50x uses +// data: event(8), len(8), status (8), address (48), server channel(8), rfcomm_cid(16), max frame size(16) +// next Cydia release will use SVN version of this +// data: event(8), len(8), status (8), address (48), handle (16), server channel(8), rfcomm_cid(16), max frame size(16) +static void rfcomm_emit_channel_opened(rfcomm_channel_t *channel, uint8_t status) { + uint8_t event[16]; + uint8_t pos = 0; + event[pos++] = RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE; + event[pos++] = sizeof(event) - 2; + event[pos++] = status; + bt_flip_addr(&event[pos], channel->multiplexer->remote_addr); pos += 6; + bt_store_16(event, pos, channel->multiplexer->con_handle); pos += 2; + event[pos++] = channel->dlci >> 1; + bt_store_16(event, pos, channel->rfcomm_cid); pos += 2; // channel ID + bt_store_16(event, pos, channel->max_frame_size); pos += 2; // max frame size + hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); + (*app_packet_handler)(channel->connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, pos); +} + +static void rfcomm_emit_channel_open_failed_outgoing_memory(void * connection, bd_addr_t *addr, uint8_t server_channel){ + uint8_t event[16]; + uint8_t pos = 0; + event[pos++] = RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE; + event[pos++] = sizeof(event) - 2; + event[pos++] = BTSTACK_MEMORY_ALLOC_FAILED; + bt_flip_addr(&event[pos], *addr); pos += 6; + bt_store_16(event, pos, 0); pos += 2; + event[pos++] = server_channel; + bt_store_16(event, pos, 0); pos += 2; // channel ID + bt_store_16(event, pos, 0); pos += 2; // max frame size + hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); + (*app_packet_handler)(connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, pos); +} + +// data: event(8), len(8), creidts incoming(8), new credits incoming(8), credits outgoing(8) +static inline void rfcomm_emit_credit_status(rfcomm_channel_t * channel) { +#ifdef RFCOMM_LOG_CREDITS + uint8_t event[5]; + event[0] = 0x88; + event[1] = sizeof(event) - 2; + event[2] = channel->credits_incoming; + event[3] = channel->new_credits_incoming; + event[4] = channel->credits_outgoing; + hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); +#endif +} + +// data: event(8), len(8), rfcomm_cid(16) +static void rfcomm_emit_channel_closed(rfcomm_channel_t * channel) { + uint8_t event[4]; + event[0] = RFCOMM_EVENT_CHANNEL_CLOSED; + event[1] = sizeof(event) - 2; + bt_store_16(event, 2, channel->rfcomm_cid); + hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); + (*app_packet_handler)(channel->connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event)); +} + +static void rfcomm_emit_credits(rfcomm_channel_t * channel, uint8_t credits) { + uint8_t event[5]; + event[0] = RFCOMM_EVENT_CREDITS; + event[1] = sizeof(event) - 2; + bt_store_16(event, 2, channel->rfcomm_cid); + event[4] = credits; + hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); + (*app_packet_handler)(channel->connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event)); +} + +static void rfcomm_emit_service_registered(void *connection, uint8_t status, uint8_t channel){ + uint8_t event[4]; + event[0] = RFCOMM_EVENT_SERVICE_REGISTERED; + event[1] = sizeof(event) - 2; + event[2] = status; + event[3] = channel; + hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); + (*app_packet_handler)(connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event)); +} + +// MARK: RFCOMM MULTIPLEXER HELPER + +static uint16_t rfcomm_max_frame_size_for_l2cap_mtu(uint16_t l2cap_mtu){ + + // Assume RFCOMM header with credits and single byte length field + uint16_t max_frame_size = l2cap_mtu - 5; + + // single byte can denote len up to 127 + if (max_frame_size > 127) { + max_frame_size--; + } + + log_info("rfcomm_max_frame_size_for_l2cap_mtu: %u -> %u\n", l2cap_mtu, max_frame_size); + return max_frame_size; +} + +static void rfcomm_multiplexer_initialize(rfcomm_multiplexer_t *multiplexer){ + + memset(multiplexer, 0, sizeof(rfcomm_multiplexer_t)); + + multiplexer->state = RFCOMM_MULTIPLEXER_CLOSED; + multiplexer->l2cap_credits = 0; + multiplexer->send_dm_for_dlci = 0; + multiplexer->max_frame_size = rfcomm_max_frame_size_for_l2cap_mtu(l2cap_max_mtu()); +} + +static rfcomm_multiplexer_t * rfcomm_multiplexer_create_for_addr(bd_addr_t *addr){ + + // alloc structure + rfcomm_multiplexer_t * multiplexer = (rfcomm_multiplexer_t*)btstack_memory_rfcomm_multiplexer_get(); + if (!multiplexer) return NULL; + + // fill in + rfcomm_multiplexer_initialize(multiplexer); + BD_ADDR_COPY(&multiplexer->remote_addr, addr); + + // add to services list + linked_list_add(&rfcomm_multiplexers, (linked_item_t *) multiplexer); + + return multiplexer; +} + +static rfcomm_multiplexer_t * rfcomm_multiplexer_for_addr(bd_addr_t *addr){ + linked_item_t *it; + for (it = (linked_item_t *) rfcomm_multiplexers; it ; it = it->next){ + rfcomm_multiplexer_t * multiplexer = ((rfcomm_multiplexer_t *) it); + if (BD_ADDR_CMP(addr, multiplexer->remote_addr) == 0) { + return multiplexer; + }; + } + return NULL; +} + +static rfcomm_multiplexer_t * rfcomm_multiplexer_for_l2cap_cid(uint16_t l2cap_cid) { + linked_item_t *it; + for (it = (linked_item_t *) rfcomm_multiplexers; it ; it = it->next){ + rfcomm_multiplexer_t * multiplexer = ((rfcomm_multiplexer_t *) it); + if (multiplexer->l2cap_cid == l2cap_cid) { + return multiplexer; + }; + } + return NULL; +} + +static int rfcomm_multiplexer_has_channels(rfcomm_multiplexer_t * multiplexer){ + linked_item_t *it; + for (it = (linked_item_t *) rfcomm_channels; it ; it = it->next){ + rfcomm_channel_t * channel = ((rfcomm_channel_t *) it); + if (channel->multiplexer == multiplexer) { + return 1; + } + } + return 0; +} + +// MARK: RFCOMM CHANNEL HELPER + +static void rfcomm_dump_channels(void){ +#ifndef EMBEDDED + linked_item_t * it; + int channels = 0; + for (it = (linked_item_t *) rfcomm_channels; it ; it = it->next){ + rfcomm_channel_t * channel = (rfcomm_channel_t *) it; + log_info("Channel #%u: addr %p, state %u\n", channels, channel, channel->state); + channels++; + } +#endif +} + +static void rfcomm_channel_initialize(rfcomm_channel_t *channel, rfcomm_multiplexer_t *multiplexer, + rfcomm_service_t *service, uint8_t server_channel){ + + // don't use 0 as channel id + if (rfcomm_client_cid_generator == 0) ++rfcomm_client_cid_generator; + + // setup channel + memset(channel, 0, sizeof(rfcomm_channel_t)); + + channel->state = RFCOMM_CHANNEL_CLOSED; + channel->state_var = RFCOMM_CHANNEL_STATE_VAR_NONE; + + channel->multiplexer = multiplexer; + channel->service = service; + channel->rfcomm_cid = rfcomm_client_cid_generator++; + channel->max_frame_size = multiplexer->max_frame_size; + + channel->credits_incoming = 0; + channel->credits_outgoing = 0; + channel->packets_granted = 0; + + // incoming flow control not active + channel->new_credits_incoming = 0x30; + channel->incoming_flow_control = 0; + + if (service) { + // incoming connection + channel->outgoing = 0; + channel->dlci = (server_channel << 1) | multiplexer->outgoing; + if (channel->max_frame_size > service->max_frame_size) { + channel->max_frame_size = service->max_frame_size; + } + channel->incoming_flow_control = service->incoming_flow_control; + channel->new_credits_incoming = service->incoming_initial_credits; + } else { + // outgoing connection + channel->outgoing = 1; + channel->dlci = (server_channel << 1) | (multiplexer->outgoing ^ 1); + } +} + +// service == NULL -> outgoing channel +static rfcomm_channel_t * rfcomm_channel_create(rfcomm_multiplexer_t * multiplexer, + rfcomm_service_t * service, uint8_t server_channel){ + + log_info("rfcomm_channel_create for service %p, channel %u --- begin\n", service, server_channel); + rfcomm_dump_channels(); + + // alloc structure + rfcomm_channel_t * channel = (rfcomm_channel_t*)btstack_memory_rfcomm_channel_get(); + if (!channel) return NULL; + + // fill in + rfcomm_channel_initialize(channel, multiplexer, service, server_channel); + + // add to services list + linked_list_add(&rfcomm_channels, (linked_item_t *) channel); + + return channel; +} + +static rfcomm_channel_t * rfcomm_channel_for_rfcomm_cid(uint16_t rfcomm_cid){ + linked_item_t *it; + for (it = (linked_item_t *) rfcomm_channels; it ; it = it->next){ + rfcomm_channel_t * channel = ((rfcomm_channel_t *) it); + if (channel->rfcomm_cid == rfcomm_cid) { + return channel; + }; + } + return NULL; +} + +static rfcomm_channel_t * rfcomm_channel_for_multiplexer_and_dlci(rfcomm_multiplexer_t * multiplexer, uint8_t dlci){ + linked_item_t *it; + for (it = (linked_item_t *) rfcomm_channels; it ; it = it->next){ + rfcomm_channel_t * channel = ((rfcomm_channel_t *) it); + if (channel->dlci == dlci && channel->multiplexer == multiplexer) { + return channel; + }; + } + return NULL; +} + +static rfcomm_service_t * rfcomm_service_for_channel(uint8_t server_channel){ + linked_item_t *it; + for (it = (linked_item_t *) rfcomm_services; it ; it = it->next){ + rfcomm_service_t * service = ((rfcomm_service_t *) it); + if ( service->server_channel == server_channel){ + return service; + }; + } + return NULL; +} + +// MARK: RFCOMM SEND + +/** + * @param credits - only used for RFCOMM flow control in UIH wiht P/F = 1 + */ +static int rfcomm_send_packet_for_multiplexer(rfcomm_multiplexer_t *multiplexer, uint8_t address, uint8_t control, uint8_t credits, uint8_t *data, uint16_t len){ + + if (!l2cap_can_send_packet_now(multiplexer->l2cap_cid)) return BTSTACK_ACL_BUFFERS_FULL; + + uint8_t * rfcomm_out_buffer = l2cap_get_outgoing_buffer(); + + uint16_t pos = 0; + uint8_t crc_fields = 3; + + rfcomm_out_buffer[pos++] = address; + rfcomm_out_buffer[pos++] = control; + + // length field can be 1 or 2 octets + if (len < 128){ + rfcomm_out_buffer[pos++] = (len << 1)| 1; // bits 0-6 + } else { + rfcomm_out_buffer[pos++] = (len & 0x7f) << 1; // bits 0-6 + rfcomm_out_buffer[pos++] = len >> 7; // bits 7-14 + crc_fields++; + } + + // add credits for UIH frames when PF bit is set + if (control == BT_RFCOMM_UIH_PF){ + rfcomm_out_buffer[pos++] = credits; + } + + // copy actual data + if (len) { + memcpy(&rfcomm_out_buffer[pos], data, len); + pos += len; + } + + // UIH frames only calc FCS over address + control (5.1.1) + if ((control & 0xef) == BT_RFCOMM_UIH){ + crc_fields = 2; + } + rfcomm_out_buffer[pos++] = crc8_calc(rfcomm_out_buffer, crc_fields); // calc fcs + + int credits_taken = 0; + if (multiplexer->l2cap_credits){ + credits_taken++; + multiplexer->l2cap_credits--; + } else { + log_info( "rfcomm_send_packet addr %02x, ctrl %02x size %u without l2cap credits\n", address, control, pos); + } + + int err = l2cap_send_prepared(multiplexer->l2cap_cid, pos); + + if (err) { + // undo credit counting + multiplexer->l2cap_credits += credits_taken; + } + return err; +} + +// C/R Flag in Address +// - terms: initiator = station that creates multiplexer with SABM +// - terms: responder = station that responds to multiplexer setup with UA +// "For SABM, UA, DM and DISC frames C/R bit is set according to Table 1 in GSM 07.10, section 5.2.1.2" +// - command initiator = 1 /response responder = 1 +// - command responder = 0 /response initiator = 0 +// "For UIH frames, the C/R bit is always set according to section 5.4.3.1 in GSM 07.10. +// This applies independently of what is contained wthin the UIH frames, either data or control messages." +// - c/r = 1 for frames by initiating station, 0 = for frames by responding station + +// C/R Flag in Message +// "In the message level, the C/R bit in the command type field is set as stated in section 5.4.6.2 in GSM 07.10." +// - If the C/R bit is set to 1 the message is a command +// - if it is set to 0 the message is a response. + +// temp/old messge construction + +// new object oriented version +static int rfcomm_send_sabm(rfcomm_multiplexer_t *multiplexer, uint8_t dlci){ + uint8_t address = (1 << 0) | (multiplexer->outgoing << 1) | (dlci << 2); // command + return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_SABM, 0, NULL, 0); +} + +static int rfcomm_send_disc(rfcomm_multiplexer_t *multiplexer, uint8_t dlci){ + uint8_t address = (1 << 0) | (multiplexer->outgoing << 1) | (dlci << 2); // command + return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_DISC, 0, NULL, 0); +} + +static int rfcomm_send_ua(rfcomm_multiplexer_t *multiplexer, uint8_t dlci){ + uint8_t address = (1 << 0) | ((multiplexer->outgoing ^ 1) << 1) | (dlci << 2); // response + return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_UA, 0, NULL, 0); +} + +static int rfcomm_send_dm_pf(rfcomm_multiplexer_t *multiplexer, uint8_t dlci){ + uint8_t address = (1 << 0) | ((multiplexer->outgoing ^ 1) << 1) | (dlci << 2); // response + return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_DM_PF, 0, NULL, 0); +} + +static int rfcomm_send_uih_msc_cmd(rfcomm_multiplexer_t *multiplexer, uint8_t dlci, uint8_t signals) { + uint8_t address = (1 << 0) | (multiplexer->outgoing << 1); + uint8_t payload[4]; + uint8_t pos = 0; + payload[pos++] = BT_RFCOMM_MSC_CMD; + payload[pos++] = 2 << 1 | 1; // len + payload[pos++] = (1 << 0) | (1 << 1) | (dlci << 2); // CMD => C/R = 1 + payload[pos++] = signals; + return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_UIH, 0, (uint8_t *) payload, pos); +} + +static int rfcomm_send_uih_msc_rsp(rfcomm_multiplexer_t *multiplexer, uint8_t dlci, uint8_t signals) { + uint8_t address = (1 << 0) | (multiplexer->outgoing<< 1); + uint8_t payload[4]; + uint8_t pos = 0; + payload[pos++] = BT_RFCOMM_MSC_RSP; + payload[pos++] = 2 << 1 | 1; // len + payload[pos++] = (1 << 0) | (1 << 1) | (dlci << 2); // CMD => C/R = 1 + payload[pos++] = signals; + return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_UIH, 0, (uint8_t *) payload, pos); +} + +static int rfcomm_send_uih_pn_command(rfcomm_multiplexer_t *multiplexer, uint8_t dlci, uint16_t max_frame_size){ + uint8_t payload[10]; + uint8_t address = (1 << 0) | (multiplexer->outgoing << 1); + uint8_t pos = 0; + payload[pos++] = BT_RFCOMM_PN_CMD; + payload[pos++] = 8 << 1 | 1; // len + payload[pos++] = dlci; + payload[pos++] = 0xf0; // pre-defined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM + payload[pos++] = 0; // priority + payload[pos++] = 0; // max 60 seconds ack + payload[pos++] = max_frame_size & 0xff; // max framesize low + payload[pos++] = max_frame_size >> 8; // max framesize high + payload[pos++] = 0x00; // number of retransmissions + payload[pos++] = 0x00; // (unused error recovery window) initial number of credits + return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_UIH, 0, (uint8_t *) payload, pos); +} + +// "The response may not change the DLCI, the priority, the convergence layer, or the timer value." RFCOMM-tutorial.pdf +static int rfcomm_send_uih_pn_response(rfcomm_multiplexer_t *multiplexer, uint8_t dlci, + uint8_t priority, uint16_t max_frame_size){ + uint8_t payload[10]; + uint8_t address = (1 << 0) | (multiplexer->outgoing << 1); + uint8_t pos = 0; + payload[pos++] = BT_RFCOMM_PN_RSP; + payload[pos++] = 8 << 1 | 1; // len + payload[pos++] = dlci; + payload[pos++] = 0xe0; // pre defined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM + payload[pos++] = priority; // priority + payload[pos++] = 0; // max 60 seconds ack + payload[pos++] = max_frame_size & 0xff; // max framesize low + payload[pos++] = max_frame_size >> 8; // max framesize high + payload[pos++] = 0x00; // number of retransmissions + payload[pos++] = 0x00; // (unused error recovery window) initial number of credits + return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_UIH, 0, (uint8_t *) payload, pos); +} + +static int rfcomm_send_uih_rpn_rsp(rfcomm_multiplexer_t *multiplexer, uint8_t dlci, rfcomm_rpn_data_t *rpn_data) { + uint8_t payload[10]; + uint8_t address = (1 << 0) | (multiplexer->outgoing << 1); + uint8_t pos = 0; + payload[pos++] = BT_RFCOMM_RPN_RSP; + payload[pos++] = 8 << 1 | 1; // len + payload[pos++] = (1 << 0) | (1 << 1) | (dlci << 2); // CMD => C/R = 1 + payload[pos++] = rpn_data->baud_rate; + payload[pos++] = rpn_data->flags; + payload[pos++] = rpn_data->flow_control; + payload[pos++] = rpn_data->xon; + payload[pos++] = rpn_data->xoff; + payload[pos++] = rpn_data->parameter_mask_0; + payload[pos++] = rpn_data->parameter_mask_1; + return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_UIH, 0, (uint8_t *) payload, pos); +} + +static int rfcomm_send_uih_data(rfcomm_multiplexer_t *multiplexer, uint8_t dlci, uint8_t *data, uint16_t len){ + uint8_t address = (1 << 0) | (multiplexer->outgoing << 1) | (dlci << 2); + return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_UIH, 0, data, len); +} + +static void rfcomm_send_uih_credits(rfcomm_multiplexer_t *multiplexer, uint8_t dlci, uint8_t credits){ + uint8_t address = (1 << 0) | (multiplexer->outgoing << 1) | (dlci << 2); + rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_UIH_PF, credits, NULL, 0); +} + +// MARK: RFCOMM MULTIPLEXER + +static void rfcomm_multiplexer_finalize(rfcomm_multiplexer_t * multiplexer){ + + // remove (potential) timer + if (multiplexer->timer_active) { + run_loop_remove_timer(&multiplexer->timer); + multiplexer->timer_active = 0; + } + + // close and remove all channels + linked_item_t *it = (linked_item_t *) &rfcomm_channels; + while (it->next){ + rfcomm_channel_t * channel = (rfcomm_channel_t *) it->next; + if (channel->multiplexer == multiplexer) { + // emit appropriate events + if (channel->state == RFCOMM_CHANNEL_OPEN) { + rfcomm_emit_channel_closed(channel); + } else { + rfcomm_emit_channel_opened(channel, RFCOMM_MULTIPLEXER_STOPPED); + } + // remove from list + it->next = it->next->next; + // free channel struct + btstack_memory_rfcomm_channel_free(channel); + } else { + it = it->next; + } + } + + // keep reference to l2cap channel + uint16_t l2cap_cid = multiplexer->l2cap_cid; + + // remove mutliplexer + linked_list_remove( &rfcomm_multiplexers, (linked_item_t *) multiplexer); + btstack_memory_rfcomm_multiplexer_free(multiplexer); + + // close l2cap multiplexer channel, too + l2cap_disconnect_internal(l2cap_cid, 0x13); +} + +static void rfcomm_multiplexer_timer_handler(timer_source_t *timer){ + rfcomm_multiplexer_t * multiplexer = (rfcomm_multiplexer_t *) linked_item_get_user( (linked_item_t *) timer); + if (!rfcomm_multiplexer_has_channels(multiplexer)){ + log_info( "rfcomm_multiplexer_timer_handler timeout: shutting down multiplexer!\n"); + rfcomm_multiplexer_finalize(multiplexer); + } +} + +static void rfcomm_multiplexer_prepare_idle_timer(rfcomm_multiplexer_t * multiplexer){ + if (multiplexer->timer_active) { + run_loop_remove_timer(&multiplexer->timer); + multiplexer->timer_active = 0; + } + if (!rfcomm_multiplexer_has_channels(multiplexer)){ + // start timer for multiplexer timeout check + run_loop_set_timer(&multiplexer->timer, RFCOMM_MULIPLEXER_TIMEOUT_MS); + multiplexer->timer.process = rfcomm_multiplexer_timer_handler; + linked_item_set_user((linked_item_t*) &multiplexer->timer, multiplexer); + run_loop_add_timer(&multiplexer->timer); + multiplexer->timer_active = 1; + } +} + +static void rfcomm_multiplexer_opened(rfcomm_multiplexer_t *multiplexer){ + log_info("Multiplexer up and running\n"); + multiplexer->state = RFCOMM_MULTIPLEXER_OPEN; + + rfcomm_channel_event_t event = { CH_EVT_MULTIPLEXER_READY }; + + // transition of channels that wait for multiplexer + linked_item_t *it; + for (it = (linked_item_t *) rfcomm_channels; it ; it = it->next){ + rfcomm_channel_t * channel = ((rfcomm_channel_t *) it); + if (channel->multiplexer != multiplexer) continue; + rfcomm_channel_state_machine(channel, &event); + } + + rfcomm_run(); + rfcomm_multiplexer_prepare_idle_timer(multiplexer); +} + + +/** + * @return handled packet + */ +static int rfcomm_multiplexer_hci_event_handler(uint8_t *packet, uint16_t size){ + bd_addr_t event_addr; + uint16_t psm; + uint16_t l2cap_cid; + hci_con_handle_t con_handle; + rfcomm_multiplexer_t *multiplexer = NULL; + switch (packet[0]) { + + // accept incoming PSM_RFCOMM connection if no multiplexer exists yet + case L2CAP_EVENT_INCOMING_CONNECTION: + // data: event(8), len(8), address(48), handle (16), psm (16), source cid(16) dest cid(16) + bt_flip_addr(event_addr, &packet[2]); + con_handle = READ_BT_16(packet, 8); + psm = READ_BT_16(packet, 10); + l2cap_cid = READ_BT_16(packet, 12); + + if (psm != PSM_RFCOMM) break; + + multiplexer = rfcomm_multiplexer_for_addr(&event_addr); + + if (multiplexer) { + log_info("INCOMING_CONNECTION (l2cap_cid 0x%02x) for PSM_RFCOMM => decline - multiplexer already exists", l2cap_cid); + l2cap_decline_connection_internal(l2cap_cid, 0x04); // no resources available + return 1; + } + + // create and inititialize new multiplexer instance (incoming) + multiplexer = rfcomm_multiplexer_create_for_addr(&event_addr); + if (!multiplexer){ + log_info("INCOMING_CONNECTION (l2cap_cid 0x%02x) for PSM_RFCOMM => decline - no memory left", l2cap_cid); + l2cap_decline_connection_internal(l2cap_cid, 0x04); // no resources available + return 1; + } + + multiplexer->con_handle = con_handle; + multiplexer->l2cap_cid = l2cap_cid; + multiplexer->state = RFCOMM_MULTIPLEXER_W4_SABM_0; + + log_info("L2CAP_EVENT_INCOMING_CONNECTION (l2cap_cid 0x%02x) for PSM_RFCOMM => accept", l2cap_cid); + l2cap_accept_connection_internal(l2cap_cid); + return 1; + + // l2cap connection opened -> store l2cap_cid, remote_addr + case L2CAP_EVENT_CHANNEL_OPENED: + if (READ_BT_16(packet, 11) != PSM_RFCOMM) break; + log_info("L2CAP_EVENT_CHANNEL_OPENED for PSM_RFCOMM\n"); + // get multiplexer for remote addr + con_handle = READ_BT_16(packet, 9); + l2cap_cid = READ_BT_16(packet, 13); + bt_flip_addr(event_addr, &packet[3]); + multiplexer = rfcomm_multiplexer_for_addr(&event_addr); + if (!multiplexer) { + log_error("L2CAP_EVENT_CHANNEL_OPENED but no multiplexer prepared\n"); + return 1; + } + if (multiplexer->state == RFCOMM_MULTIPLEXER_W4_CONNECT) { + log_info("L2CAP_EVENT_CHANNEL_OPENED: outgoing connection\n"); + // wrong remote addr + if (BD_ADDR_CMP(event_addr, multiplexer->remote_addr)) break; + multiplexer->l2cap_cid = l2cap_cid; + multiplexer->con_handle = con_handle; + // send SABM #0 + multiplexer->state = RFCOMM_MULTIPLEXER_SEND_SABM_0; + } else { // multiplexer->state == RFCOMM_MULTIPLEXER_W4_SABM_0 + + // set max frame size based on l2cap MTU + multiplexer->max_frame_size = rfcomm_max_frame_size_for_l2cap_mtu(READ_BT_16(packet, 17)); + } + return 1; + + // l2cap disconnect -> state = RFCOMM_MULTIPLEXER_CLOSED; + + case L2CAP_EVENT_CREDITS: + // data: event(8), len(8), local_cid(16), credits(8) + l2cap_cid = READ_BT_16(packet, 2); + multiplexer = rfcomm_multiplexer_for_l2cap_cid(l2cap_cid); + if (!multiplexer) break; + multiplexer->l2cap_credits += packet[4]; + + // log_info("L2CAP_EVENT_CREDITS: %u (now %u)\n", packet[4], multiplexer->l2cap_credits); + + // new credits, continue with signaling + rfcomm_run(); + + if (multiplexer->state != RFCOMM_MULTIPLEXER_OPEN) break; + rfcomm_hand_out_credits(); + return 1; + + case DAEMON_EVENT_HCI_PACKET_SENT: + // testing DMA done code + rfcomm_run(); + break; + + case L2CAP_EVENT_CHANNEL_CLOSED: + // data: event (8), len(8), channel (16) + l2cap_cid = READ_BT_16(packet, 2); + multiplexer = rfcomm_multiplexer_for_l2cap_cid(l2cap_cid); + if (!multiplexer) break; + switch (multiplexer->state) { + case RFCOMM_MULTIPLEXER_W4_SABM_0: + case RFCOMM_MULTIPLEXER_W4_UA_0: + case RFCOMM_MULTIPLEXER_OPEN: + rfcomm_multiplexer_finalize(multiplexer); + return 1; + default: + break; + } + break; + default: + break; + } + return 0; +} + +static int rfcomm_multiplexer_l2cap_packet_handler(uint16_t channel, uint8_t *packet, uint16_t size){ + + // get or create a multiplexer for a certain device + rfcomm_multiplexer_t *multiplexer = rfcomm_multiplexer_for_l2cap_cid(channel); + if (!multiplexer) return 0; + + // but only care for multiplexer control channel + uint8_t frame_dlci = packet[0] >> 2; + if (frame_dlci) return 0; + const uint8_t length_offset = (packet[2] & 1) ^ 1; // to be used for pos >= 3 + const uint8_t credit_offset = ((packet[1] & BT_RFCOMM_UIH_PF) == BT_RFCOMM_UIH_PF) ? 1 : 0; // credits for uih_pf frames + const uint8_t payload_offset = 3 + length_offset + credit_offset; + switch (packet[1]){ + + case BT_RFCOMM_SABM: + if (multiplexer->state == RFCOMM_MULTIPLEXER_W4_SABM_0){ + log_info("Received SABM #0\n"); + multiplexer->outgoing = 0; + multiplexer->state = RFCOMM_MULTIPLEXER_SEND_UA_0; + return 1; + } + break; + + case BT_RFCOMM_UA: + if (multiplexer->state == RFCOMM_MULTIPLEXER_W4_UA_0) { + // UA #0 -> send UA #0, state = RFCOMM_MULTIPLEXER_OPEN + log_info("Received UA #0 \n"); + rfcomm_multiplexer_opened(multiplexer); + return 1; + } + break; + + case BT_RFCOMM_DISC: + // DISC #0 -> send UA #0, close multiplexer + log_info("Received DISC #0, (ougoing = %u)\n", multiplexer->outgoing); + multiplexer->state = RFCOMM_MULTIPLEXER_SEND_UA_0_AND_DISC; + return 1; + + case BT_RFCOMM_DM: + // DM #0 - we shouldn't get this, just give up + log_info("Received DM #0\n"); + log_info("-> Closing down multiplexer\n"); + rfcomm_multiplexer_finalize(multiplexer); + return 1; + + case BT_RFCOMM_UIH: + if (packet[payload_offset] == BT_RFCOMM_CLD_CMD){ + // Multiplexer close down (CLD) -> close mutliplexer + log_info("Received Multiplexer close down command\n"); + log_info("-> Closing down multiplexer\n"); + rfcomm_multiplexer_finalize(multiplexer); + return 1; + } + break; + + default: + break; + + } + return 0; +} + +static void rfcomm_multiplexer_state_machine(rfcomm_multiplexer_t * multiplexer, RFCOMM_MULTIPLEXER_EVENT event){ + + // process stored DM responses + if (multiplexer->send_dm_for_dlci){ + rfcomm_send_dm_pf(multiplexer, multiplexer->send_dm_for_dlci); + multiplexer->send_dm_for_dlci = 0; + } + + switch (multiplexer->state) { + case RFCOMM_MULTIPLEXER_SEND_SABM_0: + switch (event) { + case MULT_EV_READY_TO_SEND: + log_info("Sending SABM #0 - (multi 0x%p)\n", multiplexer); + multiplexer->state = RFCOMM_MULTIPLEXER_W4_UA_0; + rfcomm_send_sabm(multiplexer, 0); + break; + default: + break; + } + break; + case RFCOMM_MULTIPLEXER_SEND_UA_0: + switch (event) { + case MULT_EV_READY_TO_SEND: + log_info("Sending UA #0\n"); + multiplexer->state = RFCOMM_MULTIPLEXER_OPEN; + rfcomm_send_ua(multiplexer, 0); + rfcomm_multiplexer_opened(multiplexer); + break; + default: + break; + } + break; + case RFCOMM_MULTIPLEXER_SEND_UA_0_AND_DISC: + switch (event) { + case MULT_EV_READY_TO_SEND: + log_info("Sending UA #0\n"); + log_info("Closing down multiplexer\n"); + multiplexer->state = RFCOMM_MULTIPLEXER_CLOSED; + rfcomm_send_ua(multiplexer, 0); + rfcomm_multiplexer_finalize(multiplexer); + // try to detect authentication errors: drop link key if multiplexer closed before first channel got opened + if (!multiplexer->at_least_one_connection){ + log_info("TODO: no connections established - delete link key prophylactically\n"); + // hci_send_cmd(&hci_delete_stored_link_key, multiplexer->remote_addr); + } + default: + break; + } + break; + default: + break; + } +} + +// MARK: RFCOMM CHANNEL + +static void rfcomm_hand_out_credits(void){ + linked_item_t * it; + for (it = (linked_item_t *) rfcomm_channels; it ; it = it->next){ + rfcomm_channel_t * channel = (rfcomm_channel_t *) it; + if (channel->state != RFCOMM_CHANNEL_OPEN) { + // log_info("RFCOMM_EVENT_CREDITS: multiplexer not open\n"); + continue; + } + if (channel->packets_granted) { + // log_info("RFCOMM_EVENT_CREDITS: already packets granted\n"); + continue; + } + if (!channel->credits_outgoing) { + // log_info("RFCOMM_EVENT_CREDITS: no outgoing credits\n"); + continue; + } + if (!channel->multiplexer->l2cap_credits){ + // log_info("RFCOMM_EVENT_CREDITS: no l2cap credits\n"); + continue; + } + // channel open, multiplexer has l2cap credits and we didn't hand out credit before -> go! + // log_info("RFCOMM_EVENT_CREDITS: 1\n"); + channel->packets_granted += 1; + rfcomm_emit_credits(channel, 1); + } +} + +static void rfcomm_channel_send_credits(rfcomm_channel_t *channel, uint8_t credits){ + rfcomm_send_uih_credits(channel->multiplexer, channel->dlci, credits); + channel->credits_incoming += credits; + + rfcomm_emit_credit_status(channel); +} + +static void rfcomm_channel_opened(rfcomm_channel_t *rfChannel){ + + log_info("rfcomm_channel_opened!\n"); + + rfChannel->state = RFCOMM_CHANNEL_OPEN; + rfcomm_emit_channel_opened(rfChannel, 0); + rfcomm_hand_out_credits(); + + // remove (potential) timer + rfcomm_multiplexer_t *multiplexer = rfChannel->multiplexer; + if (multiplexer->timer_active) { + run_loop_remove_timer(&multiplexer->timer); + multiplexer->timer_active = 0; + } + // hack for problem detecting authentication failure + multiplexer->at_least_one_connection = 1; + + // start next connection request if pending + rfcomm_run(); +} + +static void rfcomm_channel_packet_handler_uih(rfcomm_multiplexer_t *multiplexer, uint8_t * packet, uint16_t size){ + const uint8_t frame_dlci = packet[0] >> 2; + const uint8_t length_offset = (packet[2] & 1) ^ 1; // to be used for pos >= 3 + const uint8_t credit_offset = ((packet[1] & BT_RFCOMM_UIH_PF) == BT_RFCOMM_UIH_PF) ? 1 : 0; // credits for uih_pf frames + const uint8_t payload_offset = 3 + length_offset + credit_offset; + + rfcomm_channel_t * channel = rfcomm_channel_for_multiplexer_and_dlci(multiplexer, frame_dlci); + if (!channel) return; + + // handle new outgoing credits + if (packet[1] == BT_RFCOMM_UIH_PF) { + + // add them + uint16_t new_credits = packet[3+length_offset]; + channel->credits_outgoing += new_credits; + log_info( "RFCOMM data UIH_PF, new credits: %u, now %u\n", new_credits, channel->credits_outgoing); + + // notify channel statemachine + rfcomm_channel_event_t channel_event = { CH_EVT_RCVD_CREDITS }; + rfcomm_channel_state_machine(channel, &channel_event); + } + + // contains payload? + if (size - 1 > payload_offset){ + + // log_info( "RFCOMM data UIH_PF, size %u, channel %p\n", size-payload_offset-1, rfChannel->connection); + + // decrease incoming credit counter + if (channel->credits_incoming > 0){ + channel->credits_incoming--; + } + + // deliver payload + (*app_packet_handler)(channel->connection, RFCOMM_DATA_PACKET, channel->rfcomm_cid, + &packet[payload_offset], size-payload_offset-1); + } + + // automatically provide new credits to remote device, if no incoming flow control + if (!channel->incoming_flow_control && channel->credits_incoming < 5){ + channel->new_credits_incoming = 0x30; + } + + rfcomm_emit_credit_status(channel); + + // we received new RFCOMM credits, hand them out if possible + rfcomm_hand_out_credits(); +} + +static void rfcomm_channel_accept_pn(rfcomm_channel_t *channel, rfcomm_channel_event_pn_t *event){ + // priority of client request + channel->pn_priority = event->priority; + + // new credits + channel->credits_outgoing = event->credits_outgoing; + + // negotiate max frame size + if (channel->max_frame_size > channel->multiplexer->max_frame_size) { + channel->max_frame_size = channel->multiplexer->max_frame_size; + } + if (channel->max_frame_size > event->max_frame_size) { + channel->max_frame_size = event->max_frame_size; + } + +} + +static void rfcomm_channel_finalize(rfcomm_channel_t *channel){ + + rfcomm_multiplexer_t *multiplexer = channel->multiplexer; + + // remove from list + linked_list_remove( &rfcomm_channels, (linked_item_t *) channel); + + // free channel + btstack_memory_rfcomm_channel_free(channel); + + // update multiplexer timeout after channel was removed from list + rfcomm_multiplexer_prepare_idle_timer(multiplexer); +} + +static void rfcomm_channel_state_machine_2(rfcomm_multiplexer_t * multiplexer, uint8_t dlci, rfcomm_channel_event_t *event){ + + // TODO: if client max frame size is smaller than RFCOMM_DEFAULT_SIZE, send PN + + + // lookup existing channel + rfcomm_channel_t * channel = rfcomm_channel_for_multiplexer_and_dlci(multiplexer, dlci); + + // log_info("rfcomm_channel_state_machine_2 lookup dlci #%u = 0x%08x - event %u\n", dlci, (int) channel, event->type); + + if (channel) { + rfcomm_channel_state_machine(channel, event); + return; + } + + // service registered? + rfcomm_service_t * service = rfcomm_service_for_channel(dlci >> 1); + // log_info("rfcomm_channel_state_machine_2 service dlci #%u = 0x%08x\n", dlci, (int) service); + if (!service) { + // discard request by sending disconnected mode + multiplexer->send_dm_for_dlci = dlci; + return; + } + + // create channel for some events + switch (event->type) { + case CH_EVT_RCVD_SABM: + case CH_EVT_RCVD_PN: + case CH_EVT_RCVD_RPN_REQ: + case CH_EVT_RCVD_RPN_CMD: + // setup incoming channel + channel = rfcomm_channel_create(multiplexer, service, dlci >> 1); + if (!channel){ + // discard request by sending disconnected mode + multiplexer->send_dm_for_dlci = dlci; + } + break; + default: + break; + } + + if (!channel) { + // discard request by sending disconnected mode + multiplexer->send_dm_for_dlci = dlci; + return; + } + channel->connection = service->connection; + rfcomm_channel_state_machine(channel, event); +} + +void rfcomm_channel_packet_handler(rfcomm_multiplexer_t * multiplexer, uint8_t *packet, uint16_t size){ + + // rfcomm: (0) addr [76543 server channel] [2 direction: initiator uses 1] [1 C/R: CMD by initiator = 1] [0 EA=1] + const uint8_t frame_dlci = packet[0] >> 2; + uint8_t message_dlci; // used by commands in UIH(_PF) packets + uint8_t message_len; // " + + // rfcomm: (1) command/control + // -- credits_offset = 1 if command == BT_RFCOMM_UIH_PF + const uint8_t credit_offset = ((packet[1] & BT_RFCOMM_UIH_PF) == BT_RFCOMM_UIH_PF) ? 1 : 0; // credits for uih_pf frames + // rfcomm: (2) length. if bit 0 is cleared, 2 byte length is used. (little endian) + const uint8_t length_offset = (packet[2] & 1) ^ 1; // to be used for pos >= 3 + // rfcomm: (3+length_offset) credits if credits_offset == 1 + // rfcomm: (3+length_offest+credits_offset) + const uint8_t payload_offset = 3 + length_offset + credit_offset; + + rfcomm_channel_event_t event; + rfcomm_channel_event_pn_t event_pn; + rfcomm_channel_event_rpn_t event_rpn; + + // switch by rfcomm message type + switch(packet[1]) { + + case BT_RFCOMM_SABM: + event.type = CH_EVT_RCVD_SABM; + log_info("Received SABM #%u\n", frame_dlci); + rfcomm_channel_state_machine_2(multiplexer, frame_dlci, &event); + break; + + case BT_RFCOMM_UA: + event.type = CH_EVT_RCVD_UA; + log_info("Received UA #%u - channel opened\n",frame_dlci); + rfcomm_channel_state_machine_2(multiplexer, frame_dlci, &event); + break; + + case BT_RFCOMM_DISC: + event.type = CH_EVT_RCVD_DISC; + rfcomm_channel_state_machine_2(multiplexer, frame_dlci, &event); + break; + + case BT_RFCOMM_DM: + case BT_RFCOMM_DM_PF: + event.type = CH_EVT_RCVD_DM; + rfcomm_channel_state_machine_2(multiplexer, frame_dlci, &event); + break; + + case BT_RFCOMM_UIH_PF: + case BT_RFCOMM_UIH: + + message_len = packet[payload_offset+1] >> 1; + + switch (packet[payload_offset]) { + case BT_RFCOMM_PN_CMD: + message_dlci = packet[payload_offset+2]; + event_pn.super.type = CH_EVT_RCVD_PN; + event_pn.priority = packet[payload_offset+4]; + event_pn.max_frame_size = READ_BT_16(packet, payload_offset+6); + event_pn.credits_outgoing = packet[payload_offset+9]; + log_info("Received UIH Parameter Negotiation Command for #%u\n", message_dlci); + rfcomm_channel_state_machine_2(multiplexer, message_dlci, (rfcomm_channel_event_t*) &event_pn); + break; + + case BT_RFCOMM_PN_RSP: + message_dlci = packet[payload_offset+2]; + event_pn.super.type = CH_EVT_RCVD_PN_RSP; + event_pn.priority = packet[payload_offset+4]; + event_pn.max_frame_size = READ_BT_16(packet, payload_offset+6); + event_pn.credits_outgoing = packet[payload_offset+9]; + log_info("UIH Parameter Negotiation Response max frame %u, credits %u\n", + event_pn.max_frame_size, event_pn.credits_outgoing); + rfcomm_channel_state_machine_2(multiplexer, message_dlci, (rfcomm_channel_event_t*) &event_pn); + break; + + case BT_RFCOMM_MSC_CMD: + message_dlci = packet[payload_offset+2] >> 2; + event.type = CH_EVT_RCVD_MSC_CMD; + log_info("Received MSC CMD for #%u, \n", message_dlci); + rfcomm_channel_state_machine_2(multiplexer, message_dlci, &event); + break; + + case BT_RFCOMM_MSC_RSP: + message_dlci = packet[payload_offset+2] >> 2; + event.type = CH_EVT_RCVD_MSC_RSP; + log_info("Received MSC RSP for #%u\n", message_dlci); + rfcomm_channel_state_machine_2(multiplexer, message_dlci, &event); + break; + + case BT_RFCOMM_RPN_CMD: + message_dlci = packet[payload_offset+2] >> 2; + switch (message_len){ + case 1: + log_info("Received Remote Port Negotiation for #%u\n", message_dlci); + event.type = CH_EVT_RCVD_RPN_REQ; + rfcomm_channel_state_machine_2(multiplexer, message_dlci, &event); + break; + case 8: + log_info("Received Remote Port Negotiation (Info) for #%u\n", message_dlci); + event_rpn.super.type = CH_EVT_RCVD_RPN_CMD; + event_rpn.data.baud_rate = packet[payload_offset+3]; + event_rpn.data.flags = packet[payload_offset+4]; + event_rpn.data.flow_control = packet[payload_offset+5]; + event_rpn.data.xon = packet[payload_offset+6]; + event_rpn.data.xoff = packet[payload_offset+7]; + event_rpn.data.parameter_mask_0 = packet[payload_offset+8]; + event_rpn.data.parameter_mask_1 = packet[payload_offset+9]; + rfcomm_channel_state_machine_2(multiplexer, message_dlci, (rfcomm_channel_event_t*) &event_rpn); + break; + default: + break; + } + break; + + default: + log_error("Received unknown UIH packet - 0x%02x\n", packet[payload_offset]); + break; + } + break; + + default: + log_error("Received unknown RFCOMM message type %x\n", packet[1]); + break; + } + + // trigger next action - example W4_PN_RSP: transition to SEND_SABM which only depends on "can send" + rfcomm_run(); +} + +void rfcomm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + + // multiplexer handler + int handled = 0; + switch (packet_type) { + case HCI_EVENT_PACKET: + handled = rfcomm_multiplexer_hci_event_handler(packet, size); + break; + case L2CAP_DATA_PACKET: + handled = rfcomm_multiplexer_l2cap_packet_handler(channel, packet, size); + break; + default: + break; + } + + if (handled) { + rfcomm_run(); + return; + } + + // we only handle l2cap packet over open multiplexer channel now + if (packet_type != L2CAP_DATA_PACKET) { + (*app_packet_handler)(NULL, packet_type, channel, packet, size); + return; + } + rfcomm_multiplexer_t * multiplexer = rfcomm_multiplexer_for_l2cap_cid(channel); + if (!multiplexer || multiplexer->state != RFCOMM_MULTIPLEXER_OPEN) { + (*app_packet_handler)(NULL, packet_type, channel, packet, size); + return; + } + + // channel data ? + // rfcomm: (0) addr [76543 server channel] [2 direction: initiator uses 1] [1 C/R: CMD by initiator = 1] [0 EA=1] + const uint8_t frame_dlci = packet[0] >> 2; + + if (frame_dlci && (packet[1] == BT_RFCOMM_UIH || packet[1] == BT_RFCOMM_UIH_PF)) { + rfcomm_channel_packet_handler_uih(multiplexer, packet, size); + rfcomm_run(); + return; + } + + rfcomm_channel_packet_handler(multiplexer, packet, size); +} + +static int rfcomm_channel_ready_for_open(rfcomm_channel_t *channel){ + // log_info("rfcomm_channel_ready_for_open state %u, flags needed %04x, current %04x, rf credits %u, l2cap credits %u \n", channel->state, RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_RSP|RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_RSP|RFCOMM_CHANNEL_STATE_VAR_SENT_CREDITS, channel->state_var, channel->credits_outgoing, channel->multiplexer->l2cap_credits); + if ((channel->state_var & RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_RSP) == 0) return 0; + if ((channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_RSP) == 0) return 0; + if ((channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SENT_CREDITS) == 0) return 0; + if (channel->credits_outgoing == 0) return 0; + + return 1; +} + +static void rfcomm_channel_state_machine(rfcomm_channel_t *channel, rfcomm_channel_event_t *event){ + + // log_info("rfcomm_channel_state_machine: state %u, state_var %04x, event %u\n", channel->state, channel->state_var ,event->type); + + rfcomm_multiplexer_t *multiplexer = channel->multiplexer; + + // TODO: integrate in common switch + if (event->type == CH_EVT_RCVD_DISC){ + rfcomm_emit_channel_closed(channel); + channel->state = RFCOMM_CHANNEL_SEND_UA_AFTER_DISC; + return; + } + + // TODO: integrate in common switch + if (event->type == CH_EVT_RCVD_DM){ + log_info("Received DM message for #%u\n", channel->dlci); + log_info("-> Closing channel locally for #%u\n", channel->dlci); + rfcomm_emit_channel_closed(channel); + rfcomm_channel_finalize(channel); + return; + } + + // remote port negotiation command - just accept everything for now + // + // "The RPN command can be used before a new DLC is opened and should be used whenever the port settings change." + // "The RPN command is specified as optional in TS 07.10, but it is mandatory to recognize and respond to it in RFCOMM. + // (Although the handling of individual settings are implementation-dependent.)" + // + + // TODO: integrate in common switch + if (event->type == CH_EVT_RCVD_RPN_CMD){ + // control port parameters + rfcomm_channel_event_rpn_t *event_rpn = (rfcomm_channel_event_rpn_t*) event; + memcpy(&channel->rpn_data, &event_rpn->data, sizeof(rfcomm_rpn_data_t)); + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_RSP; + return; + } + + // TODO: integrate in common switch + if (event->type == CH_EVT_RCVD_RPN_REQ){ + // default rpn rsp + rfcomm_rpn_data_t rpn_data; + rpn_data.baud_rate = 0xa0; /* 9600 bps */ + rpn_data.flags = 0x03; /* 8-n-1 */ + rpn_data.flow_control = 0; /* no flow control */ + rpn_data.xon = 0xd1; /* XON */ + rpn_data.xoff = 0xd3; /* XOFF */ + rpn_data.parameter_mask_0 = 0x7f; /* parameter mask, all values set */ + rpn_data.parameter_mask_1 = 0x3f; /* parameter mask, all values set */ + memcpy(&channel->rpn_data, &rpn_data, sizeof(rfcomm_rpn_data_t)); + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_RSP; + return; + } + + // TODO: integrate in common swich + if (event->type == CH_EVT_READY_TO_SEND){ + if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_RSP){ + log_info("Sending Remote Port Negotiation RSP for #%u\n", channel->dlci); + channel->state_var &= ~RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_RSP; + rfcomm_send_uih_rpn_rsp(multiplexer, channel->dlci, &channel->rpn_data); + return; + } + } + + rfcomm_channel_event_pn_t * event_pn = (rfcomm_channel_event_pn_t*) event; + + switch (channel->state) { + case RFCOMM_CHANNEL_CLOSED: + switch (event->type){ + case CH_EVT_RCVD_SABM: + log_info("-> Inform app\n"); + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_RCVD_SABM; + channel->state = RFCOMM_CHANNEL_INCOMING_SETUP; + rfcomm_emit_connection_request(channel); + break; + case CH_EVT_RCVD_PN: + rfcomm_channel_accept_pn(channel, event_pn); + log_info("-> Inform app\n"); + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_RCVD_PN; + channel->state = RFCOMM_CHANNEL_INCOMING_SETUP; + rfcomm_emit_connection_request(channel); + break; + default: + break; + } + break; + + case RFCOMM_CHANNEL_INCOMING_SETUP: + switch (event->type){ + case CH_EVT_RCVD_SABM: + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_RCVD_SABM; + if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_CLIENT_ACCEPTED) { + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_UA; + } + break; + case CH_EVT_RCVD_PN: + rfcomm_channel_accept_pn(channel, event_pn); + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_RCVD_PN; + if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_CLIENT_ACCEPTED) { + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_PN_RSP; + } + break; + case CH_EVT_READY_TO_SEND: + if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SEND_PN_RSP){ + log_info("Sending UIH Parameter Negotiation Respond for #%u\n", channel->dlci); + channel->state_var &= ~RFCOMM_CHANNEL_STATE_VAR_SEND_PN_RSP; + rfcomm_send_uih_pn_response(multiplexer, channel->dlci, channel->pn_priority, channel->max_frame_size); + } + else if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SEND_UA){ + log_info("Sending UA #%u\n", channel->dlci); + channel->state_var &= ~RFCOMM_CHANNEL_STATE_VAR_SEND_UA; + rfcomm_send_ua(multiplexer, channel->dlci); + } + if ((channel->state_var & RFCOMM_CHANNEL_STATE_VAR_CLIENT_ACCEPTED) && (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_RCVD_SABM)) { + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_CMD; + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_CREDITS; + channel->state = RFCOMM_CHANNEL_DLC_SETUP; + } + break; + + default: + break; + } + break; + + case RFCOMM_CHANNEL_W4_MULTIPLEXER: + switch (event->type) { + case CH_EVT_MULTIPLEXER_READY: + log_info("Muliplexer opened, sending UIH PN next\n"); + channel->state = RFCOMM_CHANNEL_SEND_UIH_PN; + break; + default: + break; + } + break; + + case RFCOMM_CHANNEL_SEND_UIH_PN: + switch (event->type) { + case CH_EVT_READY_TO_SEND: + log_info("Sending UIH Parameter Negotiation Command for #%u (channel 0x%p)\n", channel->dlci, channel ); + channel->state = RFCOMM_CHANNEL_W4_PN_RSP; + rfcomm_send_uih_pn_command(multiplexer, channel->dlci, channel->max_frame_size); + break; + default: + break; + } + break; + + case RFCOMM_CHANNEL_W4_PN_RSP: + switch (event->type){ + case CH_EVT_RCVD_PN_RSP: + // update max frame size + if (channel->max_frame_size > event_pn->max_frame_size) { + channel->max_frame_size = event_pn->max_frame_size; + } + // new credits + channel->credits_outgoing = event_pn->credits_outgoing; + channel->state = RFCOMM_CHANNEL_SEND_SABM_W4_UA; + break; + default: + break; + } + break; + + case RFCOMM_CHANNEL_SEND_SABM_W4_UA: + switch (event->type) { + case CH_EVT_READY_TO_SEND: + log_info("Sending SABM #%u\n", channel->dlci); + channel->state = RFCOMM_CHANNEL_W4_UA; + rfcomm_send_sabm(multiplexer, channel->dlci); + break; + default: + break; + } + break; + + case RFCOMM_CHANNEL_W4_UA: + switch (event->type){ + case CH_EVT_RCVD_UA: + channel->state = RFCOMM_CHANNEL_DLC_SETUP; + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_CMD; + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_CREDITS; + break; + default: + break; + } + break; + + case RFCOMM_CHANNEL_DLC_SETUP: + switch (event->type){ + case CH_EVT_RCVD_MSC_CMD: + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_CMD; + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP; + break; + case CH_EVT_RCVD_MSC_RSP: + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_RSP; + break; + + case CH_EVT_READY_TO_SEND: + if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_CMD){ + log_info("Sending MSC CMD for #%u\n", channel->dlci); + channel->state_var &= ~RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_CMD; + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_CMD; + rfcomm_send_uih_msc_cmd(multiplexer, channel->dlci , 0x8d); // ea=1,fc=0,rtc=1,rtr=1,ic=0,dv=1 + break; + } + if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP){ + log_info("Sending MSC RSP for #%u\n", channel->dlci); + channel->state_var &= ~RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP; + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_RSP; + rfcomm_send_uih_msc_rsp(multiplexer, channel->dlci, 0x8d); // ea=1,fc=0,rtc=1,rtr=1,ic=0,dv=1 + break; + } + if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SEND_CREDITS){ + log_info("Providing credits for #%u\n", channel->dlci); + channel->state_var &= ~RFCOMM_CHANNEL_STATE_VAR_SEND_CREDITS; + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SENT_CREDITS; + if (channel->new_credits_incoming) { + uint8_t new_credits = channel->new_credits_incoming; + channel->new_credits_incoming = 0; + rfcomm_channel_send_credits(channel, new_credits); + } + break; + + } + break; + default: + break; + } + // finally done? + if (rfcomm_channel_ready_for_open(channel)){ + channel->state = RFCOMM_CHANNEL_OPEN; + rfcomm_channel_opened(channel); + } + break; + + case RFCOMM_CHANNEL_OPEN: + switch (event->type){ + case CH_EVT_RCVD_MSC_CMD: + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP; + break; + case CH_EVT_READY_TO_SEND: + if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP){ + log_info("Sending MSC RSP for #%u\n", channel->dlci); + channel->state_var &= ~RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP; + rfcomm_send_uih_msc_rsp(multiplexer, channel->dlci, 0x8d); // ea=1,fc=0,rtc=1,rtr=1,ic=0,dv=1 + break; + } + if (channel->new_credits_incoming) { + uint8_t new_credits = channel->new_credits_incoming; + channel->new_credits_incoming = 0; + rfcomm_channel_send_credits(channel, new_credits); + break; + } + break; + case CH_EVT_RCVD_CREDITS: { + // notify daemon -> might trigger re-try of parked connections + uint8_t event[1] = { DAEMON_EVENT_NEW_RFCOMM_CREDITS }; + (*app_packet_handler)(channel->connection, DAEMON_EVENT_PACKET, channel->rfcomm_cid, event, sizeof(event)); + break; + } + default: + break; + } + break; + + case RFCOMM_CHANNEL_SEND_DM: + switch (event->type) { + case CH_EVT_READY_TO_SEND: + log_info("Sending DM_PF for #%u\n", channel->dlci); + // don't emit channel closed - channel was never open + channel->state = RFCOMM_CHANNEL_CLOSED; + rfcomm_send_dm_pf(multiplexer, channel->dlci); + rfcomm_channel_finalize(channel); + break; + default: + break; + } + break; + + case RFCOMM_CHANNEL_SEND_DISC: + switch (event->type) { + case CH_EVT_READY_TO_SEND: + channel->state = RFCOMM_CHANNEL_CLOSED; + rfcomm_send_disc(multiplexer, channel->dlci); + rfcomm_emit_channel_closed(channel); + rfcomm_channel_finalize(channel); + break; + default: + break; + } + break; + + case RFCOMM_CHANNEL_SEND_UA_AFTER_DISC: + switch (event->type) { + case CH_EVT_READY_TO_SEND: + log_info("Sending UA after DISC for #%u\n", channel->dlci); + channel->state = RFCOMM_CHANNEL_CLOSED; + rfcomm_send_ua(multiplexer, channel->dlci); + rfcomm_channel_finalize(channel); + break; + default: + break; + } + break; + + default: + break; + } +} + + +// MARK: RFCOMM RUN +// process outstanding signaling tasks +static void rfcomm_run(void){ + + linked_item_t *it; + linked_item_t *next; + + for (it = (linked_item_t *) rfcomm_multiplexers; it ; it = next){ + + next = it->next; // be prepared for removal of channel in state machine + + rfcomm_multiplexer_t * multiplexer = ((rfcomm_multiplexer_t *) it); + + if (!l2cap_can_send_packet_now(multiplexer->l2cap_cid)) { + // log_info("rfcomm_run cannot send l2cap packet for #%u, credits %u\n", multiplexer->l2cap_cid, multiplexer->l2cap_credits); + continue; + } + // log_info("rfcomm_run: multi 0x%08x, state %u\n", (int) multiplexer, multiplexer->state); + + rfcomm_multiplexer_state_machine(multiplexer, MULT_EV_READY_TO_SEND); + } + + for (it = (linked_item_t *) rfcomm_channels; it ; it = next){ + + next = it->next; // be prepared for removal of channel in state machine + + rfcomm_channel_t * channel = ((rfcomm_channel_t *) it); + rfcomm_multiplexer_t * multiplexer = channel->multiplexer; + + if (!l2cap_can_send_packet_now(multiplexer->l2cap_cid)) continue; + + rfcomm_channel_event_t event = { CH_EVT_READY_TO_SEND }; + rfcomm_channel_state_machine(channel, &event); + } +} + +// MARK: RFCOMM BTstack API + +void rfcomm_init(void){ + rfcomm_client_cid_generator = 0; + rfcomm_multiplexers = NULL; + rfcomm_services = NULL; + rfcomm_channels = NULL; +} + +// register packet handler +void rfcomm_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, + uint16_t channel, uint8_t *packet, uint16_t size)){ + app_packet_handler = handler; +} + +// send packet over specific channel +int rfcomm_send_internal(uint16_t rfcomm_cid, uint8_t *data, uint16_t len){ + + rfcomm_channel_t * channel = rfcomm_channel_for_rfcomm_cid(rfcomm_cid); + if (!channel){ + log_error("rfcomm_send_internal cid %u doesn't exist!\n", rfcomm_cid); + return 0; + } + + if (!channel->credits_outgoing){ + log_info("rfcomm_send_internal cid %u, no rfcomm outgoing credits!\n", rfcomm_cid); + return RFCOMM_NO_OUTGOING_CREDITS; + } + + if (!channel->packets_granted){ + log_info("rfcomm_send_internal cid %u, no rfcomm credits granted!\n", rfcomm_cid); + return RFCOMM_NO_OUTGOING_CREDITS; + } + + // log_info("rfcomm_send_internal: len %u... outgoing credits %u, l2cap credit %us, granted %u\n", + // len, channel->credits_outgoing, channel->multiplexer->l2cap_credits, channel->packets_granted); + + + // send might cause l2cap to emit new credits, update counters first + channel->credits_outgoing--; + int packets_granted_decreased = 0; + if (channel->packets_granted) { + channel->packets_granted--; + packets_granted_decreased++; + } + + int result = rfcomm_send_uih_data(channel->multiplexer, channel->dlci, data, len); + + if (result != 0) { + channel->credits_outgoing++; + channel->packets_granted += packets_granted_decreased; + log_info("rfcomm_send_internal: error %d\n", result); + return result; + } + + // log_info("rfcomm_send_internal: now outgoing credits %u, l2cap credit %us, granted %u\n", + // channel->credits_outgoing, channel->multiplexer->l2cap_credits, channel->packets_granted); + + rfcomm_hand_out_credits(); + + return result; +} + +void rfcomm_create_channel2(void * connection, bd_addr_t *addr, uint8_t server_channel, uint8_t incoming_flow_control, uint8_t initial_credits){ + log_info("rfcomm_create_channel_internal to %s, at channel #%02x, flow control %u, init credits %u\n", bd_addr_to_str(*addr), server_channel, + incoming_flow_control, initial_credits); + + // create new multiplexer if necessary + rfcomm_multiplexer_t * multiplexer = rfcomm_multiplexer_for_addr(addr); + if (!multiplexer) { + multiplexer = rfcomm_multiplexer_create_for_addr(addr); + if (!multiplexer){ + rfcomm_emit_channel_open_failed_outgoing_memory(connection, addr, server_channel); + return; + } + multiplexer->outgoing = 1; + multiplexer->state = RFCOMM_MULTIPLEXER_W4_CONNECT; + } + + // prepare channel + rfcomm_channel_t * channel = rfcomm_channel_create(multiplexer, NULL, server_channel); + if (!channel){ + rfcomm_emit_channel_open_failed_outgoing_memory(connection, addr, server_channel); + return; + } + channel->connection = connection; + channel->incoming_flow_control = incoming_flow_control; + channel->new_credits_incoming = initial_credits; + + // start multiplexer setup + if (multiplexer->state != RFCOMM_MULTIPLEXER_OPEN) { + + channel->state = RFCOMM_CHANNEL_W4_MULTIPLEXER; + + l2cap_create_channel_internal(connection, rfcomm_packet_handler, *addr, PSM_RFCOMM, l2cap_max_mtu()); + + return; + } + + channel->state = RFCOMM_CHANNEL_SEND_UIH_PN; + + // start connecting, if multiplexer is already up and running + rfcomm_run(); +} + +void rfcomm_create_channel_with_initial_credits_internal(void * connection, bd_addr_t *addr, uint8_t server_channel, uint8_t initial_credits){ + rfcomm_create_channel2(connection, addr, server_channel, 1, initial_credits); +} + +void rfcomm_create_channel_internal(void * connection, bd_addr_t *addr, uint8_t server_channel){ + rfcomm_create_channel2(connection, addr, server_channel, 0, 0x30); +} + +void rfcomm_disconnect_internal(uint16_t rfcomm_cid){ + rfcomm_channel_t * channel = rfcomm_channel_for_rfcomm_cid(rfcomm_cid); + if (channel) { + channel->state = RFCOMM_CHANNEL_SEND_DISC; + } + + // process + rfcomm_run(); +} + + +void rfcomm_register_service2(void * connection, uint8_t channel, uint16_t max_frame_size, uint8_t incoming_flow_control, uint8_t initial_credits){ + // check if already registered + rfcomm_service_t * service = rfcomm_service_for_channel(channel); + if (service){ + rfcomm_emit_service_registered(connection, RFCOMM_CHANNEL_ALREADY_REGISTERED, channel); + return; + } + + // alloc structure + service = (rfcomm_service_t*)btstack_memory_rfcomm_service_get(); + if (!service) { + rfcomm_emit_service_registered(connection, BTSTACK_MEMORY_ALLOC_FAILED, channel); + return; + } + + // register with l2cap if not registered before, max MTU + if (linked_list_empty(&rfcomm_services)){ + l2cap_register_service_internal(NULL, rfcomm_packet_handler, PSM_RFCOMM, 0xffff); + } + + // fill in + service->connection = connection; + service->server_channel = channel; + service->max_frame_size = max_frame_size; + service->incoming_flow_control = incoming_flow_control; + service->incoming_initial_credits = initial_credits; + + // add to services list + linked_list_add(&rfcomm_services, (linked_item_t *) service); + + // done + rfcomm_emit_service_registered(connection, 0, channel); +} + +void rfcomm_register_service_with_initial_credits_internal(void * connection, uint8_t channel, uint16_t max_frame_size, uint8_t initial_credits){ + rfcomm_register_service2(connection, channel, max_frame_size, 1, initial_credits); +} + +void rfcomm_register_service_internal(void * connection, uint8_t channel, uint16_t max_frame_size){ + rfcomm_register_service2(connection, channel, max_frame_size, 0, 0x30); +} + +void rfcomm_unregister_service_internal(uint8_t service_channel){ + rfcomm_service_t *service = rfcomm_service_for_channel(service_channel); + if (!service) return; + linked_list_remove(&rfcomm_services, (linked_item_t *) service); + btstack_memory_rfcomm_service_free(service); + + // unregister if no services active + if (linked_list_empty(&rfcomm_services)){ + // bt_send_cmd(&l2cap_unregister_service, PSM_RFCOMM); + l2cap_unregister_service_internal(NULL, PSM_RFCOMM); + } +} + +void rfcomm_accept_connection_internal(uint16_t rfcomm_cid){ + log_info("Received Accept Connction\n"); + rfcomm_channel_t * channel = rfcomm_channel_for_rfcomm_cid(rfcomm_cid); + if (!channel) return; + switch (channel->state) { + case RFCOMM_CHANNEL_INCOMING_SETUP: + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_CLIENT_ACCEPTED; + if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_RCVD_PN){ + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_PN_RSP; + } + if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_RCVD_SABM){ + channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_UA; + } + break; + default: + break; + } + + // process + rfcomm_run(); +} + +void rfcomm_decline_connection_internal(uint16_t rfcomm_cid){ + log_info("Received Decline Connction\n"); + rfcomm_channel_t * channel = rfcomm_channel_for_rfcomm_cid(rfcomm_cid); + if (!channel) return; + switch (channel->state) { + case RFCOMM_CHANNEL_INCOMING_SETUP: + channel->state = RFCOMM_CHANNEL_SEND_DM; + break; + default: + break; + } + + // process + rfcomm_run(); +} + +void rfcomm_grant_credits(uint16_t rfcomm_cid, uint8_t credits){ + rfcomm_channel_t * channel = rfcomm_channel_for_rfcomm_cid(rfcomm_cid); + if (!channel) return; + if (!channel->incoming_flow_control) return; + channel->new_credits_incoming += credits; + + // process + rfcomm_run(); +} + +// +void rfcomm_close_connection(void *connection){ + linked_item_t *it; + + // close open channels + for (it = (linked_item_t *) rfcomm_channels; it ; it = it->next){ + rfcomm_channel_t * channel = (rfcomm_channel_t *)it; + if (channel->connection != connection) continue; + channel->state = RFCOMM_CHANNEL_SEND_DISC; + } + + // unregister services + it = (linked_item_t *) &rfcomm_services; + while (it->next) { + rfcomm_service_t * service = (rfcomm_service_t *) it->next; + if (service->connection == connection){ + it->next = it->next->next; + btstack_memory_rfcomm_service_free(service); + } else { + it = it->next; + } + } + + // process + rfcomm_run(); +} + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/rfcomm.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * RFCOMM.h + */ + +#include <btstack/btstack.h> +#include <btstack/utils.h> + +#include <stdint.h> + +#if defined __cplusplus +extern "C" { +#endif + +void rfcomm_init(void); + +// register packet handler +void rfcomm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); +void rfcomm_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, + uint16_t channel, uint8_t *packet, uint16_t size)); + +// BTstack Internal RFCOMM API +void rfcomm_create_channel_internal(void * connection, bd_addr_t *addr, uint8_t channel); +void rfcomm_create_channel_with_initial_credits_internal(void * connection, bd_addr_t *addr, uint8_t server_channel, uint8_t initial_credits); +void rfcomm_disconnect_internal(uint16_t rfcomm_cid); +void rfcomm_register_service_internal(void * connection, uint8_t channel, uint16_t max_frame_size); +void rfcomm_register_service_with_initial_credits_internal(void * connection, uint8_t channel, uint16_t max_frame_size, uint8_t initial_credits); +void rfcomm_unregister_service_internal(uint8_t service_channel); +void rfcomm_accept_connection_internal(uint16_t rfcomm_cid); +void rfcomm_decline_connection_internal(uint16_t rfcomm_cid); +void rfcomm_grant_credits(uint16_t rfcomm_cid, uint8_t credits); +int rfcomm_send_internal(uint16_t rfcomm_cid, uint8_t *data, uint16_t len); +void rfcomm_close_connection(void *connection); + +#define UNLIMITED_INCOMING_CREDITS 0xff + +// private structs +typedef enum { + RFCOMM_MULTIPLEXER_CLOSED = 1, + RFCOMM_MULTIPLEXER_W4_CONNECT, // outgoing + RFCOMM_MULTIPLEXER_SEND_SABM_0, // " + RFCOMM_MULTIPLEXER_W4_UA_0, // " + RFCOMM_MULTIPLEXER_W4_SABM_0, // incoming + RFCOMM_MULTIPLEXER_SEND_UA_0, + RFCOMM_MULTIPLEXER_OPEN, + RFCOMM_MULTIPLEXER_SEND_UA_0_AND_DISC +} RFCOMM_MULTIPLEXER_STATE; + +typedef enum { + MULT_EV_READY_TO_SEND = 1, + +} RFCOMM_MULTIPLEXER_EVENT; + +typedef enum { + RFCOMM_CHANNEL_CLOSED = 1, + RFCOMM_CHANNEL_W4_MULTIPLEXER, + RFCOMM_CHANNEL_SEND_UIH_PN, + RFCOMM_CHANNEL_W4_PN_RSP, + RFCOMM_CHANNEL_SEND_SABM_W4_UA, + RFCOMM_CHANNEL_W4_UA, + RFCOMM_CHANNEL_INCOMING_SETUP, + RFCOMM_CHANNEL_DLC_SETUP, + RFCOMM_CHANNEL_OPEN, + RFCOMM_CHANNEL_SEND_UA_AFTER_DISC, + RFCOMM_CHANNEL_SEND_DISC, + RFCOMM_CHANNEL_SEND_DM, + +} RFCOMM_CHANNEL_STATE; + +/* +typedef enum { + RFCOMM_CHANNEL_STATE_VAR_NONE = 0, + RFCOMM_CHANNEL_STATE_VAR_CLIENT_ACCEPTED = 1 << 0, + RFCOMM_CHANNEL_STATE_VAR_RCVD_PN = 1 << 1, + RFCOMM_CHANNEL_STATE_VAR_RCVD_RPN = 1 << 2, + RFCOMM_CHANNEL_STATE_VAR_RCVD_SABM = 1 << 3, + + RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_CMD = 1 << 4, + RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_RSP = 1 << 5, + RFCOMM_CHANNEL_STATE_VAR_SEND_PN_RSP = 1 << 6, + RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_INFO = 1 << 7, + + RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_RSP = 1 << 8, + RFCOMM_CHANNEL_STATE_VAR_SEND_UA = 1 << 9, + RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_CMD = 1 << 10, + RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP = 1 << 11, + + RFCOMM_CHANNEL_STATE_VAR_SEND_CREDITS = 1 << 12, + RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_CMD = 1 << 13, + RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_RSP = 1 << 14, + RFCOMM_CHANNEL_STATE_VAR_SENT_CREDITS = 1 << 15, +} RFCOMM_CHANNEL_STATE_VAR; +*/ + +enum { + RFCOMM_CHANNEL_STATE_VAR_NONE = 0, + RFCOMM_CHANNEL_STATE_VAR_CLIENT_ACCEPTED = 1 << 0, + RFCOMM_CHANNEL_STATE_VAR_RCVD_PN = 1 << 1, + RFCOMM_CHANNEL_STATE_VAR_RCVD_RPN = 1 << 2, + RFCOMM_CHANNEL_STATE_VAR_RCVD_SABM = 1 << 3, + + RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_CMD = 1 << 4, + RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_RSP = 1 << 5, + RFCOMM_CHANNEL_STATE_VAR_SEND_PN_RSP = 1 << 6, + RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_INFO = 1 << 7, + + RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_RSP = 1 << 8, + RFCOMM_CHANNEL_STATE_VAR_SEND_UA = 1 << 9, + RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_CMD = 1 << 10, + RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP = 1 << 11, + + RFCOMM_CHANNEL_STATE_VAR_SEND_CREDITS = 1 << 12, + RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_CMD = 1 << 13, + RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_RSP = 1 << 14, + RFCOMM_CHANNEL_STATE_VAR_SENT_CREDITS = 1 << 15, +}; +typedef uint16_t RFCOMM_CHANNEL_STATE_VAR; + +typedef enum { + CH_EVT_RCVD_SABM = 1, + CH_EVT_RCVD_UA, + CH_EVT_RCVD_PN, + CH_EVT_RCVD_PN_RSP, + CH_EVT_RCVD_DISC, + CH_EVT_RCVD_DM, + CH_EVT_RCVD_MSC_CMD, + CH_EVT_RCVD_MSC_RSP, + CH_EVT_RCVD_RPN_CMD, + CH_EVT_RCVD_RPN_REQ, + CH_EVT_RCVD_CREDITS, + CH_EVT_MULTIPLEXER_READY, + CH_EVT_READY_TO_SEND, +} RFCOMM_CHANNEL_EVENT; + +typedef struct rfcomm_channel_event { + RFCOMM_CHANNEL_EVENT type; +} rfcomm_channel_event_t; + +typedef struct rfcomm_channel_event_pn { + rfcomm_channel_event_t super; + uint16_t max_frame_size; + uint8_t priority; + uint8_t credits_outgoing; +} rfcomm_channel_event_pn_t; + +typedef struct rfcomm_rpn_data { + uint8_t baud_rate; + uint8_t flags; + uint8_t flow_control; + uint8_t xon; + uint8_t xoff; + uint8_t parameter_mask_0; // first byte + uint8_t parameter_mask_1; // second byte +} rfcomm_rpn_data_t; + +typedef struct rfcomm_channel_event_rpn { + rfcomm_channel_event_t super; + rfcomm_rpn_data_t data; +} rfcomm_channel_event_rpn_t; + +// info regarding potential connections +typedef struct { + // linked list - assert: first field + linked_item_t item; + + // server channel + uint8_t server_channel; + + // incoming max frame size + uint16_t max_frame_size; + + // use incoming flow control + uint8_t incoming_flow_control; + + // initial incoming credits + uint8_t incoming_initial_credits; + + // client connection + void *connection; + + // internal connection + btstack_packet_handler_t packet_handler; + +} rfcomm_service_t; + +// info regarding multiplexer +// note: spec mandates single multplexer per device combination +typedef struct { + // linked list - assert: first field + linked_item_t item; + + timer_source_t timer; + int timer_active; + + RFCOMM_MULTIPLEXER_STATE state; + + uint16_t l2cap_cid; + uint8_t l2cap_credits; + + bd_addr_t remote_addr; + hci_con_handle_t con_handle; + + uint8_t outgoing; + + // hack to deal with authentication failure only observed by remote side + uint8_t at_least_one_connection; + + uint16_t max_frame_size; + + // send DM for DLCI != 0 + uint8_t send_dm_for_dlci; + +} rfcomm_multiplexer_t; + +// info regarding an actual coneection +typedef struct { + // linked list - assert: first field + linked_item_t item; + + rfcomm_multiplexer_t *multiplexer; + uint16_t rfcomm_cid; + uint8_t outgoing; + uint8_t dlci; + + // number of packets granted to client + uint8_t packets_granted; + + // credits for outgoing traffic + uint8_t credits_outgoing; + + // number of packets remote will be granted + uint8_t new_credits_incoming; + + // credits for incoming traffic + uint8_t credits_incoming; + + // use incoming flow control + uint8_t incoming_flow_control; + + // channel state + RFCOMM_CHANNEL_STATE state; + + // state variables used in RFCOMM_CHANNEL_INCOMING + RFCOMM_CHANNEL_STATE_VAR state_var; + + // priority set by incoming side in PN + uint8_t pn_priority; + + // negotiated frame size + uint16_t max_frame_size; + + // rpn data + rfcomm_rpn_data_t rpn_data; + + // server channel (see rfcomm_service_t) - NULL => outgoing channel + rfcomm_service_t * service; + + // internal connection + btstack_packet_handler_t packet_handler; + + // client connection + void * connection; + +} rfcomm_channel_t; + + +#if defined __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/run_loop.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * run_loop.c + * + * Created by Matthias Ringwald on 6/6/09. + */ + +#include <btstack/run_loop.h> + +#include <stdio.h> +#include <stdlib.h> // exit() + +#include "run_loop_private.h" + +#include "debug.h" +#include "config.h" + +static run_loop_t * the_run_loop = NULL; + +extern const run_loop_t run_loop_embedded; + +#ifdef USE_POSIX_RUN_LOOP +extern run_loop_t run_loop_posix; +#endif + +#ifdef USE_COCOA_RUN_LOOP +extern run_loop_t run_loop_cocoa; +#endif + +// assert run loop initialized +void run_loop_assert(void){ +#ifndef EMBEDDED + if (!the_run_loop){ + log_error("ERROR: run_loop function called before run_loop_init!\n"); + exit(10); + } +#endif +} + +/** + * Add data_source to run_loop + */ +void run_loop_add_data_source(data_source_t *ds){ + run_loop_assert(); + the_run_loop->add_data_source(ds); +} + +/** + * Remove data_source from run loop + */ +int run_loop_remove_data_source(data_source_t *ds){ + run_loop_assert(); + return the_run_loop->remove_data_source(ds); +} + +/** + * Add timer to run_loop (keep list sorted) + */ +void run_loop_add_timer(timer_source_t *ts){ + run_loop_assert(); + the_run_loop->add_timer(ts); +} + +/** + * Remove timer from run loop + */ +int run_loop_remove_timer(timer_source_t *ts){ + run_loop_assert(); + return the_run_loop->remove_timer(ts); +} + +void run_loop_timer_dump(){ + run_loop_assert(); + the_run_loop->dump_timer(); +} + +/** + * Execute run_loop + */ +void run_loop_execute() { + run_loop_assert(); + the_run_loop->execute(); +} + +// init must be called before any other run_loop call +void run_loop_init(RUN_LOOP_TYPE type){ +#ifndef EMBEDDED + if (the_run_loop){ + log_error("ERROR: run loop initialized twice!\n"); + exit(10); + } +#endif + switch (type) { +#ifdef EMBEDDED + case RUN_LOOP_EMBEDDED: + the_run_loop = (run_loop_t*) &run_loop_embedded; + break; +#endif +#ifdef USE_POSIX_RUN_LOOP + case RUN_LOOP_POSIX: + the_run_loop = &run_loop_posix; + break; +#endif +#ifdef USE_COCOA_RUN_LOOP + case RUN_LOOP_COCOA: + the_run_loop = &run_loop_cocoa; + break; +#endif + default: +#ifndef EMBEDDED + log_error("ERROR: invalid run loop type %u selected!\n", type); + exit(10); +#endif + break; + } + the_run_loop->init(); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/run_loop.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2009 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * run_loop.h + * + * Created by Matthias Ringwald on 6/6/09. + */ + +#pragma once + +#include "config.h" + +#include <btstack/linked_list.h> + +#include <stdint.h> + +#ifdef HAVE_TIME +#include <sys/time.h> +#endif + +#if defined __cplusplus +extern "C" { +#endif + +typedef enum { + RUN_LOOP_POSIX = 1, + RUN_LOOP_COCOA, + RUN_LOOP_EMBEDDED +} RUN_LOOP_TYPE; + +typedef struct data_source { + linked_item_t item; + int fd; // <-- file descriptor to watch or 0 + int (*process)(struct data_source *ds); // <-- do processing +} data_source_t; + +typedef struct timer { + linked_item_t item; +#ifdef HAVE_TIME + struct timeval timeout; // <-- next timeout +#endif +#ifdef HAVE_TICK + uint32_t timeout; // timeout in system ticks +#endif + void (*process)(struct timer *ts); // <-- do processing +} timer_source_t; + + +// set timer based on current time +void run_loop_set_timer(timer_source_t *a, uint32_t timeout_in_ms); + +// add/remove timer_source +void run_loop_add_timer(timer_source_t *timer); +int run_loop_remove_timer(timer_source_t *timer); + +// init must be called before any other run_loop call +void run_loop_init(RUN_LOOP_TYPE type); + +// add/remove data_source +void run_loop_add_data_source(data_source_t *dataSource); +int run_loop_remove_data_source(data_source_t *dataSource); + + +// execute configured run_loop +void run_loop_execute(void); + +// hack to fix HCI timer handling +#ifdef HAVE_TICK +uint32_t embedded_get_ticks(void); +uint32_t embedded_ticks_for_ms(uint32_t time_in_ms); +#endif +#ifdef EMBEDDED +void embedded_trigger(void); +#endif +#if defined __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/run_loop_embedded.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * run_loop_embedded.c + * + * For this run loop, we assume that there's no global way to wait for a list + * of data sources to get ready. Instead, each data source has to queried + * individually. Calling ds->isReady() before calling ds->process() doesn't + * make sense, so we just poll each data source round robin. + * + * To support an idle state, where an MCU could go to sleep, the process function + * has to return if it has to called again as soon as possible + * + * After calling process() on every data source and evaluating the pending timers, + * the idle hook gets called if no data source did indicate that it needs to be + * called right away. + * + */ + + +#include <btstack/run_loop.h> +#include <btstack/linked_list.h> +#include <btstack/hal_tick.h> +#include <btstack/hal_cpu.h> + +#include "run_loop_private.h" +#include "debug.h" + +#include <stddef.h> // NULL + +// the run loop +static linked_list_t data_sources; + +static linked_list_t timers; + +#ifdef HAVE_TICK +static uint32_t system_ticks; +#endif + +static int trigger_event_received = 0; + +/** + * trigger run loop iteration + */ +void embedded_trigger(void){ + trigger_event_received = 1; +} + +/** + * Add data_source to run_loop + */ +void embedded_add_data_source(data_source_t *ds){ + linked_list_add(&data_sources, (linked_item_t *) ds); +} + +/** + * Remove data_source from run loop + */ +int embedded_remove_data_source(data_source_t *ds){ + return linked_list_remove(&data_sources, (linked_item_t *) ds); +} + +/** + * Add timer to run_loop (keep list sorted) + */ +void embedded_add_timer(timer_source_t *ts){ +#ifdef HAVE_TICK + linked_item_t *it; + for (it = (linked_item_t *) &timers; it->next ; it = it->next){ + if (ts->timeout < ((timer_source_t *) it->next)->timeout) { + break; + } + } + ts->item.next = it->next; + it->next = (linked_item_t *) ts; + // log_info("Added timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec); + // embedded_dump_timer(); +#endif +} + +/** + * Remove timer from run loop + */ +int embedded_remove_timer(timer_source_t *ts){ +#ifdef HAVE_TICK + // log_info("Removed timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec); + return linked_list_remove(&timers, (linked_item_t *) ts); +#else + return 0; +#endif +} + +void embedded_dump_timer(void){ +#ifdef HAVE_TICK +#ifdef ENABLE_LOG_INFO + linked_item_t *it; + int i = 0; + for (it = (linked_item_t *) timers; it ; it = it->next){ + timer_source_t *ts = (timer_source_t*) it; + log_info("timer %u, timeout %u\n", i, (unsigned int) ts->timeout); + } +#endif +#endif +} + +/** + * Execute run_loop + */ +void embedded_execute(void) { + data_source_t *ds; + + while (1) { + + // process data sources + data_source_t *next; + for (ds = (data_source_t *) data_sources; ds != NULL ; ds = next){ + next = (data_source_t *) ds->item.next; // cache pointer to next data_source to allow data source to remove itself + ds->process(ds); + } + +#ifdef HAVE_TICK + // process timers + while (timers) { + timer_source_t *ts = (timer_source_t *) timers; + if (ts->timeout > system_ticks) break; + run_loop_remove_timer(ts); + ts->process(ts); + } +#endif + + // disable IRQs and check if run loop iteration has been requested. if not, go to sleep + hal_cpu_disable_irqs(); + if (trigger_event_received){ + hal_cpu_enable_irqs_and_sleep(); + continue; + } + hal_cpu_enable_irqs(); + } +} + +#ifdef HAVE_TICK +static void embedded_tick_handler(void){ + system_ticks++; + trigger_event_received = 1; +} + +uint32_t embedded_get_ticks(void){ + return system_ticks; +} + +uint32_t embedded_ticks_for_ms(uint32_t time_in_ms){ + return time_in_ms / hal_tick_get_tick_period_in_ms(); +} + +// set timer +void run_loop_set_timer(timer_source_t *ts, uint32_t timeout_in_ms){ + uint32_t ticks = embedded_ticks_for_ms(timeout_in_ms); + if (ticks == 0) ticks++; + ts->timeout = system_ticks + ticks; +} +#endif + +void embedded_init(void){ + + data_sources = NULL; + +#ifdef HAVE_TICK + timers = NULL; + system_ticks = 0; + hal_tick_init(); + hal_tick_set_handler(&embedded_tick_handler); +#endif +} + +extern const run_loop_t run_loop_embedded; +const run_loop_t run_loop_embedded = { + &embedded_init, + &embedded_add_data_source, + &embedded_remove_data_source, + &embedded_add_timer, + &embedded_remove_timer, + &embedded_execute, + &embedded_dump_timer +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/run_loop_private.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * run_loop_private.h + * + * Created by Matthias Ringwald on 6/6/09. + */ + +#pragma once + +#include <btstack/run_loop.h> + +#ifdef HAVE_TIME +#include <sys/time.h> + +// compare timeval or timers - NULL is assumed to be before the Big Bang +int run_loop_timeval_compare(struct timeval *a, struct timeval *b); +int run_loop_timer_compare(timer_source_t *a, timer_source_t *b); + +#endif + +// +void run_loop_timer_dump(void); + +// internal use only +typedef struct { + void (*init)(void); + void (*add_data_source)(data_source_t *dataSource); + int (*remove_data_source)(data_source_t *dataSource); + void (*add_timer)(timer_source_t *timer); + int (*remove_timer)(timer_source_t *timer); + void (*execute)(void); + void (*dump_timer)(void); +} run_loop_t;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/sdp.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,662 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * Implementation of the Service Discovery Protocol Server + */ + +#include "sdp.h" + + +#include <stdio.h> +#include <string.h> + +#include <btstack/sdp_util.h> + +#include "hci_dump.h" +#include "l2cap.h" + +#include "debug.h" + +// max reserved ServiceRecordHandle +#define maxReservedServiceRecordHandle 0xffff + +// max SDP response +#define SDP_RESPONSE_BUFFER_SIZE (HCI_ACL_BUFFER_SIZE-HCI_ACL_HEADER_SIZE) + +static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); + +// registered service records +static linked_list_t sdp_service_records = NULL; + +// our handles start after the reserved range +static uint32_t sdp_next_service_record_handle = maxReservedServiceRecordHandle + 2; + +static uint8_t sdp_response_buffer[SDP_RESPONSE_BUFFER_SIZE]; + +static void (*app_packet_handler)(void * connection, uint8_t packet_type, + uint16_t channel, uint8_t *packet, uint16_t size) = NULL; + +static uint16_t l2cap_cid = 0; +static uint16_t sdp_response_size = 0; + +void sdp_init(){ + // register with l2cap psm sevices - max MTU + l2cap_register_service_internal(NULL, sdp_packet_handler, PSM_SDP, 0xffff); +} + +// register packet handler +void sdp_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, + uint16_t channel, uint8_t *packet, uint16_t size)){ + app_packet_handler = handler; + l2cap_cid = 0; +} + +uint32_t sdp_get_service_record_handle(uint8_t * record){ + uint8_t * serviceRecordHandleAttribute = sdp_get_attribute_value_for_attribute_id(record, SDP_ServiceRecordHandle); + if (!serviceRecordHandleAttribute) return 0; + if (de_get_element_type(serviceRecordHandleAttribute) != DE_UINT) return 0; + if (de_get_size_type(serviceRecordHandleAttribute) != DE_SIZE_32) return 0; + return READ_NET_32(serviceRecordHandleAttribute, 1); +} + +// data: event(8), len(8), status(8), service_record_handle(32) +static void sdp_emit_service_registered(void *connection, uint32_t handle, uint8_t status) { + if (!app_packet_handler) return; + uint8_t event[7]; + event[0] = SDP_SERVICE_REGISTERED; + event[1] = sizeof(event) - 2; + event[2] = status; + bt_store_32(event, 3, handle); + hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); + (*app_packet_handler)(connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event)); +} + +service_record_item_t * sdp_get_record_for_handle(uint32_t handle){ + linked_item_t *it; + for (it = (linked_item_t *) sdp_service_records; it ; it = it->next){ + service_record_item_t * item = (service_record_item_t *) it; + if (item->service_record_handle == handle){ + return item; + } + } + return NULL; +} + +// get next free, unregistered service record handle +uint32_t sdp_create_service_record_handle(void){ + uint32_t handle = 0; + do { + handle = sdp_next_service_record_handle++; + if (sdp_get_record_for_handle(handle)) handle = 0; + } while (handle == 0); + return handle; +} + +#ifdef EMBEDDED + +// register service record internally - this special version doesn't copy the record, it should not be freeed +// pre: AttributeIDs are in ascending order +// pre: ServiceRecordHandle is first attribute and valid +// pre: record +// @returns ServiceRecordHandle or 0 if registration failed +uint32_t sdp_register_service_internal(void *connection, service_record_item_t * record_item){ + // get user record handle + uint32_t record_handle = record_item->service_record_handle; + // get actual record + uint8_t *record = record_item->service_record; + + // check for ServiceRecordHandle attribute, returns pointer or null + uint8_t * req_record_handle = sdp_get_attribute_value_for_attribute_id(record, SDP_ServiceRecordHandle); + if (!req_record_handle) { + log_error("SDP Error - record does not contain ServiceRecordHandle attribute\n"); + return 0; + } + + // validate service record handle is not in reserved range + if (record_handle <= maxReservedServiceRecordHandle) record_handle = 0; + + // check if already in use + if (record_handle) { + if (sdp_get_record_for_handle(record_handle)) { + record_handle = 0; + } + } + + // create new handle if needed + if (!record_handle){ + record_handle = sdp_create_service_record_handle(); + // Write the handle back into the record too + record_item->service_record_handle = record_handle; + sdp_set_attribute_value_for_attribute_id(record, SDP_ServiceRecordHandle, record_handle); + } + + // add to linked list + linked_list_add(&sdp_service_records, (linked_item_t *) record_item); + + sdp_emit_service_registered(connection, 0, record_item->service_record_handle); + + return record_handle; +} + +#else + +// AttributeIDList used to remove ServiceRecordHandle +static const uint8_t removeServiceRecordHandleAttributeIDList[] = { 0x36, 0x00, 0x05, 0x0A, 0x00, 0x01, 0xFF, 0xFF }; + +// register service record internally - the normal version creates a copy of the record +// pre: AttributeIDs are in ascending order => ServiceRecordHandle is first attribute if present +// @returns ServiceRecordHandle or 0 if registration failed +uint32_t sdp_register_service_internal(void *connection, uint8_t * record){ + + // dump for now + // printf("Register service record\n"); + // de_dump_data_element(record); + + // get user record handle + uint32_t record_handle = sdp_get_service_record_handle(record); + + // validate service record handle is not in reserved range + if (record_handle <= maxReservedServiceRecordHandle) record_handle = 0; + + // check if already in use + if (record_handle) { + if (sdp_get_record_for_handle(record_handle)) { + record_handle = 0; + } + } + + // create new handle if needed + if (!record_handle){ + record_handle = sdp_create_service_record_handle(); + } + + // calculate size of new service record: DES (2 byte len) + // + ServiceRecordHandle attribute (UINT16 UINT32) + size of existing attributes + uint16_t recordSize = 3 + (3 + 5) + de_get_data_size(record); + + // alloc memory for new service_record_item + service_record_item_t * newRecordItem = (service_record_item_t *) malloc(recordSize + sizeof(service_record_item_t)); + if (!newRecordItem) { + sdp_emit_service_registered(connection, 0, BTSTACK_MEMORY_ALLOC_FAILED); + return 0; + } + // link new service item to client connection + newRecordItem->connection = connection; + + // set new handle + newRecordItem->service_record_handle = record_handle; + + // create updated service record + uint8_t * newRecord = (uint8_t *) &(newRecordItem->service_record); + + // create DES for new record + de_create_sequence(newRecord); + + // set service record handle + de_add_number(newRecord, DE_UINT, DE_SIZE_16, 0); + de_add_number(newRecord, DE_UINT, DE_SIZE_32, record_handle); + + // add other attributes + sdp_append_attributes_in_attributeIDList(record, (uint8_t *) removeServiceRecordHandleAttributeIDList, 0, recordSize, newRecord); + + // dump for now + // de_dump_data_element(newRecord); + // printf("reserved size %u, actual size %u\n", recordSize, de_get_len(newRecord)); + + // add to linked list + linked_list_add(&sdp_service_records, (linked_item_t *) newRecordItem); + + sdp_emit_service_registered(connection, 0, newRecordItem->service_record_handle); + + return record_handle; +} + +#endif + +// unregister service record internally +// +// makes sure one client cannot remove service records of other clients +// +void sdp_unregister_service_internal(void *connection, uint32_t service_record_handle){ + service_record_item_t * record_item = sdp_get_record_for_handle(service_record_handle); + if (record_item && record_item->connection == connection) { + linked_list_remove(&sdp_service_records, (linked_item_t *) record_item); + } +} + +// remove all service record for a client connection +void sdp_unregister_services_for_connection(void *connection){ + linked_item_t *it = (linked_item_t *) &sdp_service_records; + while (it->next){ + service_record_item_t *record_item = (service_record_item_t *) it->next; + if (record_item->connection == connection){ + it->next = it->next->next; +#ifndef EMBEDDED + free(record_item); +#endif + } else { + it = it->next; + } + } +} + +// PDU +// PDU ID (1), Transaction ID (2), Param Length (2), Param 1, Param 2, .. + +int sdp_create_error_response(uint16_t transaction_id, uint16_t error_code){ + sdp_response_buffer[0] = SDP_ErrorResponse; + net_store_16(sdp_response_buffer, 1, transaction_id); + net_store_16(sdp_response_buffer, 3, 2); + net_store_16(sdp_response_buffer, 5, error_code); // invalid syntax + return 7; +} + +int sdp_handle_service_search_request(uint8_t * packet, uint16_t remote_mtu){ + + // get request details + uint16_t transaction_id = READ_NET_16(packet, 1); + // not used yet - uint16_t param_len = READ_NET_16(packet, 3); + uint8_t * serviceSearchPattern = &packet[5]; + uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern); + uint16_t maximumServiceRecordCount = READ_NET_16(packet, 5 + serviceSearchPatternLen); + uint8_t * continuationState = &packet[5+serviceSearchPatternLen+2]; + + // calc maxumumServiceRecordCount based on remote MTU + uint16_t maxNrServiceRecordsPerResponse = (remote_mtu - (9+3))/4; + + // continuation state contains index of next service record to examine + int continuation = 0; + uint16_t continuation_index = 0; + if (continuationState[0] == 2){ + continuation_index = READ_NET_16(continuationState, 1); + } + + // get and limit total count + linked_item_t *it; + uint16_t total_service_count = 0; + for (it = (linked_item_t *) sdp_service_records; it ; it = it->next){ + service_record_item_t * item = (service_record_item_t *) it; + if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue; + total_service_count++; + } + if (total_service_count > maximumServiceRecordCount){ + total_service_count = maximumServiceRecordCount; + } + + // ServiceRecordHandleList at 9 + uint16_t pos = 9; + uint16_t current_service_count = 0; + uint16_t current_service_index = 0; + uint16_t matching_service_count = 0; + for (it = (linked_item_t *) sdp_service_records; it ; it = it->next, ++current_service_index){ + service_record_item_t * item = (service_record_item_t *) it; + + if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue; + matching_service_count++; + + if (current_service_index < continuation_index) continue; + + net_store_32(sdp_response_buffer, pos, item->service_record_handle); + pos += 4; + current_service_count++; + + if (matching_service_count >= total_service_count) break; + + if (current_service_count >= maxNrServiceRecordsPerResponse){ + continuation = 1; + continuation_index = current_service_index + 1; + break; + } + } + + // Store continuation state + if (continuation) { + sdp_response_buffer[pos++] = 2; + net_store_16(sdp_response_buffer, pos, continuation_index); + pos += 2; + } else { + sdp_response_buffer[pos++] = 0; + } + + // header + sdp_response_buffer[0] = SDP_ServiceSearchResponse; + net_store_16(sdp_response_buffer, 1, transaction_id); + net_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload + net_store_16(sdp_response_buffer, 5, total_service_count); + net_store_16(sdp_response_buffer, 7, current_service_count); + + return pos; +} + +int sdp_handle_service_attribute_request(uint8_t * packet, uint16_t remote_mtu){ + + // get request details + uint16_t transaction_id = READ_NET_16(packet, 1); + // not used yet - uint16_t param_len = READ_NET_16(packet, 3); + uint32_t serviceRecordHandle = READ_NET_32(packet, 5); + uint16_t maximumAttributeByteCount = READ_NET_16(packet, 9); + uint8_t * attributeIDList = &packet[11]; + uint16_t attributeIDListLen = de_get_len(attributeIDList); + uint8_t * continuationState = &packet[11+attributeIDListLen]; + + // calc maximumAttributeByteCount based on remote MTU + uint16_t maximumAttributeByteCount2 = remote_mtu - (7+3); + if (maximumAttributeByteCount2 < maximumAttributeByteCount) { + maximumAttributeByteCount = maximumAttributeByteCount2; + } + + // continuation state contains the offset into the complete response + uint16_t continuation_offset = 0; + if (continuationState[0] == 2){ + continuation_offset = READ_NET_16(continuationState, 1); + } + + // get service record + service_record_item_t * item = sdp_get_record_for_handle(serviceRecordHandle); + if (!item){ + // service record handle doesn't exist + return sdp_create_error_response(transaction_id, 0x0002); /// invalid Service Record Handle + } + + + // AttributeList - starts at offset 7 + uint16_t pos = 7; + + if (continuation_offset == 0){ + + // get size of this record + uint16_t filtered_attributes_size = spd_get_filtered_size(item->service_record, attributeIDList); + + // store DES + de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, filtered_attributes_size); + maximumAttributeByteCount -= 3; + pos += 3; + } + + // copy maximumAttributeByteCount from record + uint16_t bytes_used; + int complete = sdp_filter_attributes_in_attributeIDList(item->service_record, attributeIDList, continuation_offset, maximumAttributeByteCount, &bytes_used, &sdp_response_buffer[pos]); + pos += bytes_used; + + uint16_t attributeListByteCount = pos - 7; + + if (complete) { + sdp_response_buffer[pos++] = 0; + } else { + continuation_offset += bytes_used; + sdp_response_buffer[pos++] = 2; + net_store_16(sdp_response_buffer, pos, continuation_offset); + pos += 2; + } + + // header + sdp_response_buffer[0] = SDP_ServiceAttributeResponse; + net_store_16(sdp_response_buffer, 1, transaction_id); + net_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload + net_store_16(sdp_response_buffer, 5, attributeListByteCount); + + return pos; +} + +static uint16_t sdp_get_size_for_service_search_attribute_response(uint8_t * serviceSearchPattern, uint8_t * attributeIDList){ + uint16_t total_response_size = 0; + linked_item_t *it; + for (it = (linked_item_t *) sdp_service_records; it ; it = it->next){ + service_record_item_t * item = (service_record_item_t *) it; + + if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue; + + // for all service records that match + total_response_size += 3 + spd_get_filtered_size(item->service_record, attributeIDList); + } + return total_response_size; +} + +int sdp_handle_service_search_attribute_request(uint8_t * packet, uint16_t remote_mtu){ + + // SDP header before attribute sevice list: 7 + // Continuation, worst case: 5 + + // get request details + uint16_t transaction_id = READ_NET_16(packet, 1); + // not used yet - uint16_t param_len = READ_NET_16(packet, 3); + uint8_t * serviceSearchPattern = &packet[5]; + uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern); + uint16_t maximumAttributeByteCount = READ_NET_16(packet, 5 + serviceSearchPatternLen); + uint8_t * attributeIDList = &packet[5+serviceSearchPatternLen+2]; + uint16_t attributeIDListLen = de_get_len(attributeIDList); + uint8_t * continuationState = &packet[5+serviceSearchPatternLen+2+attributeIDListLen]; + + // calc maximumAttributeByteCount based on remote MTU, SDP header and reserved Continuation block + uint16_t maximumAttributeByteCount2 = remote_mtu - 12; + if (maximumAttributeByteCount2 < maximumAttributeByteCount) { + maximumAttributeByteCount = maximumAttributeByteCount2; + } + + // continuation state contains: index of next service record to examine + // continuation state contains: byte offset into this service record + uint16_t continuation_service_index = 0; + uint16_t continuation_offset = 0; + if (continuationState[0] == 4){ + continuation_service_index = READ_NET_16(continuationState, 1); + continuation_offset = READ_NET_16(continuationState, 3); + } + + // printf("--> sdp_handle_service_search_attribute_request, cont %u/%u, max %u\n", continuation_service_index, continuation_offset, maximumAttributeByteCount); + + // AttributeLists - starts at offset 7 + uint16_t pos = 7; + + // add DES with total size for first request + if (continuation_service_index == 0 && continuation_offset == 0){ + uint16_t total_response_size = sdp_get_size_for_service_search_attribute_response(serviceSearchPattern, attributeIDList); + de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, total_response_size); + // log_info("total response size %u\n", total_response_size); + pos += 3; + maximumAttributeByteCount -= 3; + } + + // create attribute list + int first_answer = 1; + int continuation = 0; + uint16_t current_service_index = 0; + linked_item_t *it = (linked_item_t *) sdp_service_records; + for ( ; it ; it = it->next, ++current_service_index){ + service_record_item_t * item = (service_record_item_t *) it; + + if (current_service_index < continuation_service_index ) continue; + if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue; + + if (continuation_offset == 0){ + + // get size of this record + uint16_t filtered_attributes_size = spd_get_filtered_size(item->service_record, attributeIDList); + + // stop if complete record doesn't fits into response but we already have a partial response + if ((filtered_attributes_size + 3 > maximumAttributeByteCount) && !first_answer) { + continuation = 1; + break; + } + + // store DES + de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, filtered_attributes_size); + pos += 3; + maximumAttributeByteCount -= 3; + } + + first_answer = 0; + + // copy maximumAttributeByteCount from record + uint16_t bytes_used; + int complete = sdp_filter_attributes_in_attributeIDList(item->service_record, attributeIDList, continuation_offset, maximumAttributeByteCount, &bytes_used, &sdp_response_buffer[pos]); + pos += bytes_used; + maximumAttributeByteCount -= bytes_used; + + if (complete) { + continuation_offset = 0; + continue; + } + + continuation = 1; + continuation_offset += bytes_used; + break; + } + + uint16_t attributeListsByteCount = pos - 7; + + // Continuation State + if (continuation){ + sdp_response_buffer[pos++] = 4; + net_store_16(sdp_response_buffer, pos, (uint16_t) current_service_index); + pos += 2; + net_store_16(sdp_response_buffer, pos, continuation_offset); + pos += 2; + } else { + // complete + sdp_response_buffer[pos++] = 0; + } + + // create SDP header + sdp_response_buffer[0] = SDP_ServiceSearchAttributeResponse; + net_store_16(sdp_response_buffer, 1, transaction_id); + net_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload + net_store_16(sdp_response_buffer, 5, attributeListsByteCount); + + return pos; +} + +static void sdp_try_respond(void){ + if (!sdp_response_size ) return; + if (!l2cap_cid) return; + if (!l2cap_can_send_packet_now(l2cap_cid)) return; + + // update state before sending packet (avoid getting called when new l2cap credit gets emitted) + uint16_t size = sdp_response_size; + sdp_response_size = 0; + l2cap_send_internal(l2cap_cid, sdp_response_buffer, size); +} + +// we assume that we don't get two requests in a row +static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + uint16_t transaction_id; + SDP_PDU_ID_t pdu_id; + uint16_t remote_mtu; + // uint16_t param_len; + + switch (packet_type) { + + case L2CAP_DATA_PACKET: + pdu_id = (SDP_PDU_ID_t) packet[0]; + transaction_id = READ_NET_16(packet, 1); + // param_len = READ_NET_16(packet, 3); + remote_mtu = l2cap_get_remote_mtu_for_local_cid(channel); + // account for our buffer + if (remote_mtu > SDP_RESPONSE_BUFFER_SIZE){ + remote_mtu = SDP_RESPONSE_BUFFER_SIZE; + } + + // printf("SDP Request: type %u, transaction id %u, len %u, mtu %u\n", pdu_id, transaction_id, param_len, remote_mtu); + switch (pdu_id){ + + case SDP_ServiceSearchRequest: + sdp_response_size = sdp_handle_service_search_request(packet, remote_mtu); + break; + + case SDP_ServiceAttributeRequest: + sdp_response_size = sdp_handle_service_attribute_request(packet, remote_mtu); + break; + + case SDP_ServiceSearchAttributeRequest: + sdp_response_size = sdp_handle_service_search_attribute_request(packet, remote_mtu); + break; + + default: + sdp_response_size = sdp_create_error_response(transaction_id, 0x0003); // invalid syntax + break; + } + + sdp_try_respond(); + + break; + + case HCI_EVENT_PACKET: + + switch (packet[0]) { + + case L2CAP_EVENT_INCOMING_CONNECTION: + if (l2cap_cid) { + // CONNECTION REJECTED DUE TO LIMITED RESOURCES + l2cap_decline_connection_internal(channel, 0x0d); + break; + } + // accept + l2cap_cid = channel; + sdp_response_size = 0; + l2cap_accept_connection_internal(channel); + break; + + case L2CAP_EVENT_CHANNEL_OPENED: + if (packet[2]) { + // open failed -> reset + l2cap_cid = 0; + } + break; + + case L2CAP_EVENT_CREDITS: + case DAEMON_EVENT_HCI_PACKET_SENT: + sdp_try_respond(); + break; + + case L2CAP_EVENT_CHANNEL_CLOSED: + if (channel == l2cap_cid){ + // reset + l2cap_cid = 0; + } + break; + + default: + // other event + break; + } + break; + + default: + // other packet type + break; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/sdp.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ +#pragma once + +#include <stdint.h> +#include <btstack/linked_list.h> + +#include "config.h" + +typedef enum { + SDP_ErrorResponse = 1, + SDP_ServiceSearchRequest, + SDP_ServiceSearchResponse, + SDP_ServiceAttributeRequest, + SDP_ServiceAttributeResponse, + SDP_ServiceSearchAttributeRequest, + SDP_ServiceSearchAttributeResponse +} SDP_PDU_ID_t; + +// service record +// -- uses user_data field for actual +typedef struct { + // linked list - assert: first field + linked_item_t item; + + // client connection + void * connection; + + // data is contained in same memory + uint32_t service_record_handle; + uint8_t service_record[0]; +} service_record_item_t; + + +void sdp_init(void); + +void sdp_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, + uint16_t channel, uint8_t *packet, uint16_t size)); + +#ifdef EMBEDDED +// register service record internally - the normal version creates a copy of the record +// pre: AttributeIDs are in ascending order => ServiceRecordHandle is first attribute if present +// @returns ServiceRecordHandle or 0 if registration failed +uint32_t sdp_register_service_internal(void *connection, service_record_item_t * record_item); +#else +// register service record internally - this special version doesn't copy the record, it cannot be freeed +// pre: AttributeIDs are in ascending order +// pre: ServiceRecordHandle is first attribute and valid +// pre: record +// @returns ServiceRecordHandle or 0 if registration failed +uint32_t sdp_register_service_internal(void *connection, uint8_t * service_record); +#endif + +// unregister service record internally +void sdp_unregister_service_internal(void *connection, uint32_t service_record_handle); + +// +void sdp_unregister_services_for_connection(void *connection); + +// +int sdp_handle_service_search_request(uint8_t * packet, uint16_t remote_mtu); +int sdp_handle_service_attribute_request(uint8_t * packet, uint16_t remote_mtu); +int sdp_handle_service_search_attribute_request(uint8_t * packet, uint16_t remote_mtu);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/sdp_util.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,698 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * sdp_util.c + */ + +#include <btstack/sdp_util.h> +#include <btstack/utils.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <inttypes.h> // PRIx32 + +// workaround for missing PRIx32 on mspgcc (16-bit MCU) +#ifndef PRIx32 +#warning Using own: #define PRIx32 "lx" +#define PRIx32 "lx" +#endif + +// date element type names +const char * const type_names[] = { "NIL", "UINT", "INT", "UUID", "STRING", "BOOL", "DES", "DEA", "URL"}; + +// Bluetooth Base UUID: 00000000-0000-1000-8000- 00805F9B34FB +const uint8_t sdp_bluetooth_base_uuid[] = { 0x00, 0x00, 0x00, 0x00, /* - */ 0x00, 0x00, /* - */ 0x10, 0x00, /* - */ + 0x80, 0x00, /* - */ 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }; + +void sdp_normalize_uuid(uint8_t *uuid, uint32_t shortUUID){ + memcpy(uuid, sdp_bluetooth_base_uuid, 16); + net_store_32(uuid, 0, shortUUID); +} + +// MARK: DataElement getter +de_size_t de_get_size_type(uint8_t *header){ + return (de_size_t) (header[0] & 7); +} + +de_type_t de_get_element_type(uint8_t *header){ + return (de_type_t) (header[0] >> 3); +} + +int de_get_header_size(uint8_t * header){ + de_size_t de_size = de_get_size_type(header); + if (de_size <= DE_SIZE_128) { + return 1; + } + return 1 + (1 << (de_size-DE_SIZE_VAR_8)); +} + +int de_get_data_size(uint8_t * header){ + uint32_t result = 0; + de_type_t de_type = de_get_element_type(header); + de_size_t de_size = de_get_size_type(header); + switch (de_size){ + case DE_SIZE_VAR_8: + result = header[1]; + break; + case DE_SIZE_VAR_16: + result = READ_NET_16(header,1); + break; + case DE_SIZE_VAR_32: + result = READ_NET_32(header,1); + break; + default: + // case DE_SIZE_8: + // case DE_SIZE_16: + // case DE_SIZE_32: + // case DE_SIZE_64: + // case DE_SIZE_128: + if (de_type == DE_NIL) return 0; + return 1 << de_size; + } + return result; +} + +int de_get_len(uint8_t *header){ + return de_get_header_size(header) + de_get_data_size(header); +} + +// @returns: element is valid UUID +int de_get_normalized_uuid(uint8_t *uuid128, uint8_t *element){ + de_type_t uuidType = de_get_element_type(element); + de_size_t uuidSize = de_get_size_type(element); + if (uuidType != DE_UUID) return 0; + uint32_t shortUUID; + switch (uuidSize){ + case DE_SIZE_16: + shortUUID = READ_NET_16(element, 1); + break; + case DE_SIZE_32: + shortUUID = READ_NET_32(element, 1); + break; + case DE_SIZE_128: + memcpy(uuid128, element+1, 16); + return 1; + default: + return 0; + } + sdp_normalize_uuid(uuid128, shortUUID); + return 1; +} + +// functions to create record +static void de_store_descriptor(uint8_t * header, de_type_t type, de_size_t size){ + header[0] = (type << 3) | size; +} + +void de_store_descriptor_with_len(uint8_t * header, de_type_t type, de_size_t size, uint32_t len){ + header[0] = (type << 3) | size; + switch (size){ + case DE_SIZE_VAR_8: + header[1] = len; + break; + case DE_SIZE_VAR_16: + net_store_16(header, 1, len); + break; + case DE_SIZE_VAR_32: + net_store_32(header, 1, len); + break; + default: + break; + } +} + +// MARK: DataElement creation + +/* starts a new sequence in empty buffer - first call */ +void de_create_sequence(uint8_t *header){ + de_store_descriptor_with_len( header, DE_DES, DE_SIZE_VAR_16, 0); // DES, 2 Byte Length +}; + +/* starts a sub-sequence, @returns handle for sub-sequence */ +uint8_t * de_push_sequence(uint8_t *header){ + int element_len = de_get_len(header); + de_store_descriptor_with_len(header+element_len, DE_DES, DE_SIZE_VAR_16, 0); // DES, 2 Byte Length + return header + element_len; +} + +/* closes the current sequence and updates the parent sequence */ +void de_pop_sequence(uint8_t * parent, uint8_t * child){ + int child_len = de_get_len(child); + int data_size_parent = READ_NET_16(parent,1); + net_store_16(parent, 1, data_size_parent + child_len); +} + +/* adds a single number value and 16+32 bit UUID to the sequence */ +void de_add_number(uint8_t *seq, de_type_t type, de_size_t size, uint32_t value){ + int data_size = READ_NET_16(seq,1); + int element_size = 1; // e.g. for DE_TYPE_NIL + de_store_descriptor(seq+3+data_size, type, size); + switch (size){ + case DE_SIZE_8: + if (type != DE_NIL){ + seq[4+data_size] = value; + element_size = 2; + } + break; + case DE_SIZE_16: + net_store_16(seq, 4+data_size, value); + element_size = 3; + break; + case DE_SIZE_32: + net_store_32(seq, 4+data_size, value); + element_size = 5; + break; + default: + break; + } + net_store_16(seq, 1, data_size+element_size); +} + +/* add a single block of data, e.g. as DE_STRING, DE_URL */ +void de_add_data( uint8_t *seq, de_type_t type, uint16_t size, uint8_t *data){ + int data_size = READ_NET_16(seq,1); + if (size > 0xff) { + // use 16-bit lengh information (3 byte header) + de_store_descriptor_with_len(seq+3+data_size, type, DE_SIZE_VAR_16, size); + data_size += 3; + } else { + // use 8-bit lengh information (2 byte header) + de_store_descriptor_with_len(seq+3+data_size, type, DE_SIZE_VAR_8, size); + data_size += 2; + } + memcpy( seq + 3 + data_size, data, size); + data_size += size; + net_store_16(seq, 1, data_size); +} + +void de_add_uuid128(uint8_t * seq, uint8_t * uuid){ + int data_size = READ_NET_16(seq,1); + de_store_descriptor(seq+3+data_size, DE_UUID, DE_SIZE_128); + memcpy( seq + 4 + data_size, uuid, 16); + net_store_16(seq, 1, data_size+1+16); +} + +void sdp_add_attribute(uint8_t *seq, uint16_t attributeID, uint8_t attributeValue){ +} + +// MARK: DataElementSequence traversal +typedef int (*de_traversal_callback_t)(uint8_t * element, de_type_t type, de_size_t size, void *context); +static void de_traverse_sequence(uint8_t * element, de_traversal_callback_t handler, void *context){ + de_type_t type = de_get_element_type(element); + if (type != DE_DES) return; + int pos = de_get_header_size(element); + int end_pos = de_get_len(element); + while (pos < end_pos){ + de_type_t elemType = de_get_element_type(element + pos); + de_size_t elemSize = de_get_size_type(element + pos); + uint8_t done = (*handler)(element + pos, elemType, elemSize, context); + if (done) break; + pos += de_get_len(element + pos); + } +} + +// MARK: AttributeList traversal +typedef int (*sdp_attribute_list_traversal_callback_t)(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *context); +static void sdp_attribute_list_traverse_sequence(uint8_t * element, sdp_attribute_list_traversal_callback_t handler, void *context){ + de_type_t type = de_get_element_type(element); + if (type != DE_DES) return; + int pos = de_get_header_size(element); + int end_pos = de_get_len(element); + while (pos < end_pos){ + de_type_t idType = de_get_element_type(element + pos); + de_size_t idSize = de_get_size_type(element + pos); + if (idType != DE_UINT || idSize != DE_SIZE_16) break; // wrong type + uint16_t attribute_id = READ_NET_16(element, pos + 1); + pos += 3; + if (pos >= end_pos) break; // array out of bounds + de_type_t valueType = de_get_element_type(element + pos); + de_size_t valueSize = de_get_size_type(element + pos); + uint8_t done = (*handler)(attribute_id, element + pos, valueType, valueSize, context); + if (done) break; + pos += de_get_len(element + pos); + } +} + +// MARK: AttributeID in AttributeIDList +// attribute ID in AttributeIDList +// context { result, attributeID } +struct sdp_context_attributeID_search { + int result; + uint16_t attributeID; +}; +static int sdp_traversal_attributeID_search(uint8_t * element, de_type_t type, de_size_t size, void *my_context){ + struct sdp_context_attributeID_search * context = (struct sdp_context_attributeID_search *) my_context; + if (type != DE_UINT) return 0; + switch (size) { + case DE_SIZE_16: + if (READ_NET_16(element, 1) == context->attributeID) { + context->result = 1; + return 1; + } + break; + case DE_SIZE_32: + if (READ_NET_16(element, 1) <= context->attributeID + && context->attributeID <= READ_NET_16(element, 3)) { + context->result = 1; + return 1; + } + break; + default: + break; + } + return 0; +} +int sdp_attribute_list_constains_id(uint8_t *attributeIDList, uint16_t attributeID){ + struct sdp_context_attributeID_search attributeID_search; + attributeID_search.result = 0; + attributeID_search.attributeID = attributeID; + de_traverse_sequence(attributeIDList, sdp_traversal_attributeID_search, &attributeID_search); + return attributeID_search.result; +} + +// MARK: Append Attributes for AttributeIDList +// pre: buffer contains DES with 2 byte length field +struct sdp_context_append_attributes { + uint8_t * buffer; + uint16_t startOffset; // offset of when to start copying + uint16_t maxBytes; + uint16_t usedBytes; + uint8_t *attributeIDList; +}; + +static int sdp_traversal_append_attributes(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *my_context){ + struct sdp_context_append_attributes * context = (struct sdp_context_append_attributes *) my_context; + if (sdp_attribute_list_constains_id(context->attributeIDList, attributeID)) { + // DES_HEADER(3) + DES_DATA + (UINT16(3) + attribute) + uint16_t data_size = READ_NET_16(context->buffer, 1); + int attribute_len = de_get_len(attributeValue); + if (3 + data_size + (3 + attribute_len) <= context->maxBytes) { + // copy Attribute + de_add_number(context->buffer, DE_UINT, DE_SIZE_16, attributeID); + data_size += 3; // 3 bytes + memcpy(context->buffer + 3 + data_size, attributeValue, attribute_len); + net_store_16(context->buffer,1,data_size+attribute_len); + } else { + // not enought space left -> continue with previous element + return 1; + } + } + return 0; +} + +// maxBytes: maximal size of data element sequence +uint16_t sdp_append_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint8_t *buffer){ + struct sdp_context_append_attributes context; + context.buffer = buffer; + context.maxBytes = maxBytes; + context.usedBytes = 0; + context.startOffset = startOffset; + context.attributeIDList = attributeIDList; + sdp_attribute_list_traverse_sequence(record, sdp_traversal_append_attributes, &context); + return context.usedBytes; +} + +// MARK: Filter attributes that match attribute list from startOffset and a max nr bytes +struct sdp_context_filter_attributes { + uint8_t * buffer; + uint16_t startOffset; // offset of when to start copying + uint16_t maxBytes; + uint16_t usedBytes; + uint8_t *attributeIDList; + int complete; +}; + +// copy data with given start offset and max bytes, returns OK if all data has been copied +static int spd_append_range(struct sdp_context_filter_attributes* context, uint16_t len, uint8_t *data){ + int ok = 1; + uint16_t remainder_len = len - context->startOffset; + if (context->maxBytes < remainder_len){ + remainder_len = context->maxBytes; + ok = 0; + } + memcpy(context->buffer, &data[context->startOffset], remainder_len); + context->usedBytes += remainder_len; + context->buffer += remainder_len; + context->maxBytes -= remainder_len; + context->startOffset = 0; + return ok; +} + +static int sdp_traversal_filter_attributes(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *my_context){ + struct sdp_context_filter_attributes * context = (struct sdp_context_filter_attributes *) my_context; + + if (!sdp_attribute_list_constains_id(context->attributeIDList, attributeID)) return 0; + + // { Attribute ID (Descriptor, big endian 16-bit ID), AttributeValue (data)} + + // handle Attribute ID + if (context->startOffset >= 3){ + context->startOffset -= 3; + } else { + uint8_t idBuffer[3]; + de_store_descriptor(idBuffer, DE_UINT, DE_SIZE_16); + net_store_16(idBuffer,1,attributeID); + + int ok = spd_append_range(context, 3, idBuffer); + if (!ok) { + context->complete = 0; + return 1; + } + } + + // handle Attribute Value + int attribute_len = de_get_len(attributeValue); + if (context->startOffset >= attribute_len) { + context->startOffset -= attribute_len; + return 0; + } + + int ok = spd_append_range(context, attribute_len, attributeValue); + if (!ok) { + context->complete = 0; + return 1; + } + return 0; +} + +int sdp_filter_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint16_t *usedBytes, uint8_t *buffer){ + + struct sdp_context_filter_attributes context; + context.buffer = buffer; + context.maxBytes = maxBytes; + context.usedBytes = 0; + context.startOffset = startOffset; + context.attributeIDList = attributeIDList; + context.complete = 1; + + sdp_attribute_list_traverse_sequence(record, sdp_traversal_filter_attributes, &context); + + *usedBytes = context.usedBytes; + return context.complete; +} + +// MARK: Get sum of attributes matching attribute list +struct sdp_context_get_filtered_size { + uint8_t *attributeIDList; + uint16_t size; +}; + +static int sdp_traversal_get_filtered_size(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *my_context){ + struct sdp_context_get_filtered_size * context = (struct sdp_context_get_filtered_size *) my_context; + if (sdp_attribute_list_constains_id(context->attributeIDList, attributeID)) { + context->size += 3 + de_get_len(attributeValue); + } + return 0; +} + +int spd_get_filtered_size(uint8_t *record, uint8_t *attributeIDList){ + struct sdp_context_get_filtered_size context; + context.size = 0; + context.attributeIDList = attributeIDList; + sdp_attribute_list_traverse_sequence(record, sdp_traversal_get_filtered_size, &context); + return context.size; +} + +// MARK: Get AttributeValue for AttributeID +// find attribute (ELEMENT) by ID +struct sdp_context_attribute_by_id { + uint16_t attributeID; + uint8_t * attributeValue; +}; +static int sdp_traversal_attribute_by_id(uint16_t attributeID, uint8_t * attributeValue, de_type_t attributeType, de_size_t size, void *my_context){ + struct sdp_context_attribute_by_id * context = (struct sdp_context_attribute_by_id *) my_context; + if (attributeID == context->attributeID) { + context->attributeValue = attributeValue; + return 1; + } + return 0; +} + +uint8_t * sdp_get_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID){ + struct sdp_context_attribute_by_id context; + context.attributeValue = NULL; + context.attributeID = attributeID; + sdp_attribute_list_traverse_sequence(record, sdp_traversal_attribute_by_id, &context); + return context.attributeValue; +} + +// MARK: Set AttributeValue for AttributeID +struct sdp_context_set_attribute_for_id { + uint16_t attributeID; + uint32_t attributeValue; + uint8_t attributeFound; +}; +static int sdp_traversal_set_attribute_for_id(uint16_t attributeID, uint8_t * attributeValue, de_type_t attributeType, de_size_t size, void *my_context){ + struct sdp_context_set_attribute_for_id * context = (struct sdp_context_set_attribute_for_id *) my_context; + if (attributeID == context->attributeID) { + context->attributeFound = 1; + switch (size){ + case DE_SIZE_8: + if (attributeType != DE_NIL){ + attributeValue[1] = context->attributeValue; + } + break; + case DE_SIZE_16: + net_store_16(attributeValue, 1, context->attributeValue); + break; + case DE_SIZE_32: + net_store_32(attributeValue, 1, context->attributeValue); + break; + // Might want to support STRINGS to, copy upto original length + default: + break; + } + return 1; + } + return 0; +} +uint8_t sdp_set_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID, uint32_t value){ + struct sdp_context_set_attribute_for_id context; + context.attributeID = attributeID; + context.attributeValue = value; + context.attributeFound = 0; + sdp_attribute_list_traverse_sequence(record, sdp_traversal_set_attribute_for_id, &context); + return context.attributeFound; +} + +// MARK: ServiceRecord contains UUID +// service record contains UUID +// context { normalizedUUID } +struct sdp_context_contains_uuid128 { + uint8_t * uuid128; + int result; +}; +int sdp_record_contains_UUID128(uint8_t *record, uint8_t *uuid128); +static int sdp_traversal_contains_UUID128(uint8_t * element, de_type_t type, de_size_t size, void *my_context){ + struct sdp_context_contains_uuid128 * context = (struct sdp_context_contains_uuid128 *) my_context; + uint8_t normalizedUUID[16]; + if (type == DE_UUID){ + uint8_t uuidOK = de_get_normalized_uuid(normalizedUUID, element); + context->result = uuidOK && memcmp(context->uuid128, normalizedUUID, 16) == 0; + } + if (type == DE_DES){ + context->result = sdp_record_contains_UUID128(element, context->uuid128); + } + return context->result; +} +int sdp_record_contains_UUID128(uint8_t *record, uint8_t *uuid128){ + struct sdp_context_contains_uuid128 context; + context.uuid128 = uuid128; + context.result = 0; + de_traverse_sequence(record, sdp_traversal_contains_UUID128, &context); + return context.result; +} + +// MARK: ServiceRecord matches SearchServicePattern +// if UUID in searchServicePattern is not found in record => false +// context { result, record } +struct sdp_context_match_pattern { + uint8_t * record; + int result; +}; +int sdp_traversal_match_pattern(uint8_t * element, de_type_t attributeType, de_size_t size, void *my_context){ + struct sdp_context_match_pattern * context = (struct sdp_context_match_pattern *) my_context; + uint8_t normalizedUUID[16]; + uint8_t uuidOK = de_get_normalized_uuid(normalizedUUID, element); + if (!uuidOK || !sdp_record_contains_UUID128(context->record, normalizedUUID)){ + context->result = 0; + return 1; + } + return 0; +} +int sdp_record_matches_service_search_pattern(uint8_t *record, uint8_t *serviceSearchPattern){ + struct sdp_context_match_pattern context; + context.record = record; + context.result = 1; + de_traverse_sequence(serviceSearchPattern, sdp_traversal_match_pattern, &context); + return context.result; +} + +// MARK: Dump DataElement +// context { indent } +static int de_traversal_dump_data(uint8_t * element, de_type_t de_type, de_size_t de_size, void *my_context){ + int indent = *(int*) my_context; + int i; + for (i=0; i<indent;i++) printf(" "); + int pos = de_get_header_size(element); + int end_pos = de_get_len(element); + printf("type %5s (%u), element len %2u ", type_names[de_type], de_type, end_pos); + if (de_type == DE_DES) { + printf("\n"); + indent++; + de_traverse_sequence(element, de_traversal_dump_data, (void *)&indent); + } else if (de_type == DE_UUID && de_size == DE_SIZE_128) { + printf(", value: "); + printUUID(element+1); + printf("\n"); + } else if (de_type == DE_STRING) { + int len = 0; + switch (de_size){ + case DE_SIZE_VAR_8: + len = element[1]; + break; + case DE_SIZE_VAR_16: + len = READ_NET_16(element, 1); + break; + default: + break; + } + printf("len %u (0x%02x)\n", len, len); + hexdump(&element[pos], len); + } else { + uint32_t value = 0; + switch (de_size) { + case DE_SIZE_8: + if (de_type != DE_NIL){ + value = element[pos]; + } + break; + case DE_SIZE_16: + value = READ_NET_16(element,pos); + break; + case DE_SIZE_32: + value = READ_NET_32(element,pos); + break; + default: + break; + } + printf(", value: 0x%08" PRIx32 "\n", value); + } + return 0; +} + +void de_dump_data_element(uint8_t * record){ + int indent = 0; + // hack to get root DES, too. + de_type_t type = de_get_element_type(record); + de_size_t size = de_get_size_type(record); + de_traversal_dump_data(record, type, size, (void*) &indent); +} + +void sdp_create_spp_service(uint8_t *service, int service_id, const char *name){ + + uint8_t* attribute; + de_create_sequence(service); + + // 0x0000 "Service Record Handle" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceRecordHandle); + de_add_number(service, DE_UINT, DE_SIZE_32, 0x10001); + + // 0x0001 "Service Class ID List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceClassIDList); + attribute = de_push_sequence(service); + { + de_add_number(attribute, DE_UUID, DE_SIZE_16, 0x1101 ); + } + de_pop_sequence(service, attribute); + + // 0x0004 "Protocol Descriptor List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ProtocolDescriptorList); + attribute = de_push_sequence(service); + { + uint8_t* l2cpProtocol = de_push_sequence(attribute); + { + de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, 0x0100); + } + de_pop_sequence(attribute, l2cpProtocol); + + uint8_t* rfcomm = de_push_sequence(attribute); + { + de_add_number(rfcomm, DE_UUID, DE_SIZE_16, 0x0003); // rfcomm_service + de_add_number(rfcomm, DE_UINT, DE_SIZE_8, service_id); // rfcomm channel + } + de_pop_sequence(attribute, rfcomm); + } + de_pop_sequence(service, attribute); + + // 0x0005 "Public Browse Group" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BrowseGroupList); // public browse group + attribute = de_push_sequence(service); + { + de_add_number(attribute, DE_UUID, DE_SIZE_16, 0x1002 ); + } + de_pop_sequence(service, attribute); + + // 0x0006 + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_LanguageBaseAttributeIDList); + attribute = de_push_sequence(service); + { + de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x656e); + de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x006a); + de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x0100); + } + de_pop_sequence(service, attribute); + + // 0x0009 "Bluetooth Profile Descriptor List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BluetoothProfileDescriptorList); + attribute = de_push_sequence(service); + { + uint8_t *sppProfile = de_push_sequence(attribute); + { + de_add_number(sppProfile, DE_UUID, DE_SIZE_16, 0x1101); + de_add_number(sppProfile, DE_UINT, DE_SIZE_16, 0x0100); + } + de_pop_sequence(attribute, sppProfile); + } + de_pop_sequence(service, attribute); + + // 0x0100 "ServiceName" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); + de_add_data(service, DE_STRING, strlen(name), (uint8_t *) name); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/sdp_util.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2010 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * sdp_util.h + */ + +#pragma once + +#include <stdint.h> + +#if defined __cplusplus +extern "C" { +#endif + +typedef enum { + DE_NIL = 0, + DE_UINT, + DE_INT, + DE_UUID, + DE_STRING, + DE_BOOL, + DE_DES, + DE_DEA, + DE_URL +} de_type_t; + +typedef enum { + DE_SIZE_8 = 0, + DE_SIZE_16, + DE_SIZE_32, + DE_SIZE_64, + DE_SIZE_128, + DE_SIZE_VAR_8, + DE_SIZE_VAR_16, + DE_SIZE_VAR_32 +} de_size_t; + +// UNIVERSAL ATTRIBUTE DEFINITIONS +#define SDP_ServiceRecordHandle 0x0000 +#define SDP_ServiceClassIDList 0x0001 +#define SDP_ServiceRecordState 0x0002 +#define SDP_ServiceID 0x0003 +#define SDP_ProtocolDescriptorList 0x0004 +#define SDP_BrowseGroupList 0x0005 +#define SDP_LanguageBaseAttributeIDList 0x0006 +#define SDP_ServiceInfoTimeToLive 0x0007 +#define SDP_ServiceAvailability 0x0008 +#define SDP_BluetoothProfileDescriptorList 0x0009 +#define SDP_DocumentationURL 0x000a +#define SDP_ClientExecutableURL 0x000b +#define SDP_IconURL 0x000c +#define SDP_AdditionalProtocolDescriptorList 0x000d +#define SDP_SupportedFormatsList 0x0303 + +// SERVICE CLASSES +#define SDP_OBEXObjectPush 0x1105 +#define SDP_OBEXFileTransfer 0x1106 +#define SDP_PublicBrowseGroup 0x1002 + +// PROTOCOLS +#define SDP_SDPProtocol 0x0001 +#define SDP_UDPProtocol 0x0002 +#define SDP_RFCOMMProtocol 0x0003 +#define SDP_OBEXProtocol 0x0008 +#define SDP_L2CAPProtocol 0x0100 + +// OFFSETS FOR LOCALIZED ATTRIBUTES - SDP_LanguageBaseAttributeIDList +#define SDP_Offest_ServiceName 0x0000 +#define SDP_Offest_ServiceDescription 0x0001 +#define SDP_Offest_ProviderName 0x0002 + +// OBEX +#define SDP_vCard_2_1 0x01 +#define SDP_vCard_3_0 0x02 +#define SDP_vCal_1_0 0x03 +#define SDP_iCal_2_0 0x04 +#define SDP_vNote 0x05 +#define SDP_vMessage 0x06 +#define SDP_OBEXFileTypeAny 0xFF + +// MARK: DateElement +void de_dump_data_element(uint8_t * record); +int de_get_len(uint8_t *header); +de_size_t de_get_size_type(uint8_t *header); +de_type_t de_get_element_type(uint8_t *header); +int de_get_header_size(uint8_t * header); +void de_create_sequence(uint8_t *header); +void de_store_descriptor_with_len(uint8_t * header, de_type_t type, de_size_t size, uint32_t len); +uint8_t * de_push_sequence(uint8_t *header); +void de_pop_sequence(uint8_t * parent, uint8_t * child); +void de_add_number(uint8_t *seq, de_type_t type, de_size_t size, uint32_t value); +void de_add_data( uint8_t *seq, de_type_t type, uint16_t size, uint8_t *data); + +int de_get_data_size(uint8_t * header); +void de_add_uuid128(uint8_t * seq, uint8_t * uuid); + +// MARK: SDP +uint16_t sdp_append_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint8_t *buffer); +uint8_t * sdp_get_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID); +uint8_t sdp_set_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID, uint32_t value); +int sdp_record_matches_service_search_pattern(uint8_t *record, uint8_t *serviceSearchPattern); +int spd_get_filtered_size(uint8_t *record, uint8_t *attributeIDList); +int sdp_filter_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint16_t *usedBytes, uint8_t *buffer); + +void sdp_create_spp_service(uint8_t *service, int service_id, const char *name); + +#if defined __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/utils.c Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at btstack@ringwald.ch + * + */ + +/* + * utils.c + * + * General utility functions + * + * Created by Matthias Ringwald on 7/23/09. + */ + +#include "config.h" +#include <btstack/utils.h> +#include <stdio.h> +#include "debug.h" + +void bt_store_16(uint8_t *buffer, uint16_t pos, uint16_t value){ + buffer[pos++] = value; + buffer[pos++] = value >> 8; +} + +void bt_store_32(uint8_t *buffer, uint16_t pos, uint32_t value){ + buffer[pos++] = value; + buffer[pos++] = value >> 8; + buffer[pos++] = value >> 16; + buffer[pos++] = value >> 24; +} + +void net_store_16(uint8_t *buffer, uint16_t pos, uint16_t value){ + buffer[pos++] = value >> 8; + buffer[pos++] = value; +} + +void net_store_32(uint8_t *buffer, uint16_t pos, uint32_t value){ + buffer[pos++] = value >> 24; + buffer[pos++] = value >> 16; + buffer[pos++] = value >> 8; + buffer[pos++] = value; +} + +void bt_flip_addr(bd_addr_t dest, bd_addr_t src){ + dest[0] = src[5]; + dest[1] = src[4]; + dest[2] = src[3]; + dest[3] = src[2]; + dest[4] = src[1]; + dest[5] = src[0]; +} + +void hexdump(void *data, int size){ + int i; + for (i=0; i<size;i++){ + log_info("%02X ", ((uint8_t *)data)[i]); + } + log_info("\n"); +} + +void printUUID(uint8_t *uuid) { + log_info("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); +} + +static char bd_addr_to_str_buffer[6*3]; // 12:45:78:01:34:67\0 +char * bd_addr_to_str(bd_addr_t addr){ + sprintf(bd_addr_to_str_buffer, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + return (char *) bd_addr_to_str_buffer; +} + +void print_bd_addr( bd_addr_t addr){ + log_info("%s", bd_addr_to_str(addr)); +} + +#ifndef EMBEDDED +int sscan_bd_addr(uint8_t * addr_string, bd_addr_t addr){ + unsigned int bd_addr_buffer[BD_ADDR_LEN]; //for sscanf, integer needed + // reset result buffer + int i; + for (i = 0; i < BD_ADDR_LEN; i++) { + bd_addr_buffer[i] = 0; + } + + // parse + int result = sscanf( (char *) addr_string, "%2x:%2x:%2x:%2x:%2x:%2x", &bd_addr_buffer[0], &bd_addr_buffer[1], &bd_addr_buffer[2], + &bd_addr_buffer[3], &bd_addr_buffer[4], &bd_addr_buffer[5]); + // store + if (result == 6){ + for (i = 0; i < BD_ADDR_LEN; i++) { + addr[i] = (uint8_t) bd_addr_buffer[i]; + } + } + return (result == 6); +} +#endif + +/* + * CRC (reversed crc) lookup table as calculated by the table generator in ETSI TS 101 369 V6.3.0. + */ +static const uint8_t crc8table[256] = { /* reversed, 8-bit, poly=0x07 */ + 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, + 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, + 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43, + 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, + 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, + 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, + 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33, + 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, + 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B, + 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, + 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, + 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, + 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, + 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, + 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, + 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF +}; + +#define CRC8_INIT 0xFF // Initial FCS value +#define CRC8_OK 0xCF // Good final FCS value +/*-----------------------------------------------------------------------------------*/ +uint8_t crc8(uint8_t *data, uint16_t len) +{ + uint16_t count; + uint8_t crc = CRC8_INIT; + for (count = 0; count < len; count++) + crc = crc8table[crc ^ data[count]]; + return crc; +} + +/*-----------------------------------------------------------------------------------*/ +uint8_t crc8_check(uint8_t *data, uint16_t len, uint8_t check_sum) +{ + uint8_t crc; + + crc = crc8(data, len); + + crc = crc8table[crc ^ check_sum]; + if (crc == CRC8_OK) + return 0; /* Valid */ + else + return 1; /* Failed */ + +} + +/*-----------------------------------------------------------------------------------*/ +uint8_t crc8_calc(uint8_t *data, uint16_t len) +{ + /* Ones complement */ + return 0xFF - crc8(data, len); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/btstack/utils.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2009 by Matthias Ringwald + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * utils.h + * + * General utility functions + * + * Created by Matthias Ringwald on 7/23/09. + */ + +#pragma once + + +#if defined __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +/** + * @brief hci connection handle type + */ +typedef uint16_t hci_con_handle_t; + +/** + * @brief Length of a bluetooth device address. + */ +#define BD_ADDR_LEN 6 +typedef uint8_t bd_addr_t[BD_ADDR_LEN]; + +/** + * @brief The link key type + */ +#define LINK_KEY_LEN 16 +typedef uint8_t link_key_t[LINK_KEY_LEN]; + +/** + * @brief The device name type + */ +#define DEVICE_NAME_LEN 248 +typedef uint8_t device_name_t[DEVICE_NAME_LEN+1]; + + +// helper for BT little endian format +#define READ_BT_16( buffer, pos) ( ((uint16_t) buffer[pos]) | (((uint16_t)buffer[pos+1]) << 8)) +#define READ_BT_24( buffer, pos) ( ((uint32_t) buffer[pos]) | (((uint32_t)buffer[pos+1]) << 8) | (((uint32_t)buffer[pos+2]) << 16)) +#define READ_BT_32( buffer, pos) ( ((uint32_t) buffer[pos]) | (((uint32_t)buffer[pos+1]) << 8) | (((uint32_t)buffer[pos+2]) << 16) | (((uint32_t) buffer[pos+3])) << 24) + +// helper for SDP big endian format +#define READ_NET_16( buffer, pos) ( ((uint16_t) buffer[pos+1]) | (((uint16_t)buffer[pos ]) << 8)) +#define READ_NET_32( buffer, pos) ( ((uint32_t) buffer[pos+3]) | (((uint32_t)buffer[pos+2]) << 8) | (((uint32_t)buffer[pos+1]) << 16) | (((uint32_t) buffer[pos])) << 24) + +// HCI CMD OGF/OCF +#define READ_CMD_OGF(buffer) (buffer[1] >> 2) +#define READ_CMD_OCF(buffer) ((buffer[1] & 0x03) << 8 | buffer[0]) + +// check if command complete event for given command +#define COMMAND_COMPLETE_EVENT(event,cmd) ( event[0] == HCI_EVENT_COMMAND_COMPLETE && READ_BT_16(event,3) == cmd.opcode) +#define COMMAND_STATUS_EVENT(event,cmd) ( event[0] == HCI_EVENT_COMMAND_STATUS && READ_BT_16(event,4) == cmd.opcode) + +// Code+Len=2, Pkts+Opcode=3; total=5 +#define OFFSET_OF_DATA_IN_COMMAND_COMPLETE 5 + +// ACL Packet +#define READ_ACL_CONNECTION_HANDLE( buffer ) ( READ_BT_16(buffer,0) & 0x0fff) +#define READ_ACL_FLAGS( buffer ) ( buffer[1] >> 4 ) +#define READ_ACL_LENGTH( buffer ) (READ_BT_16(buffer, 2)) + +// L2CAP Packet +#define READ_L2CAP_LENGTH(buffer) ( READ_BT_16(buffer, 4)) +#define READ_L2CAP_CHANNEL_ID(buffer) ( READ_BT_16(buffer, 6)) + +void bt_store_16(uint8_t *buffer, uint16_t pos, uint16_t value); +void bt_store_32(uint8_t *buffer, uint16_t pos, uint32_t value); +void bt_flip_addr(bd_addr_t dest, bd_addr_t src); + +void net_store_16(uint8_t *buffer, uint16_t pos, uint16_t value); +void net_store_32(uint8_t *buffer, uint16_t pos, uint32_t value); + +void hexdump(void *data, int size); +void printUUID(uint8_t *uuid); + +// @deprecated please use more convenient bd_addr_to_str +void print_bd_addr( bd_addr_t addr); +char * bd_addr_to_str(bd_addr_t addr); + +int sscan_bd_addr(uint8_t * addr_string, bd_addr_t addr); + +uint8_t crc8_check(uint8_t *data, uint16_t len, uint8_t check_sum); +uint8_t crc8_calc(uint8_t *data, uint16_t len); + +#define BD_ADDR_CMP(a,b) memcmp(a,b, BD_ADDR_LEN) +#define BD_ADDR_COPY(dest,src) memcpy(dest,src,BD_ADDR_LEN) + +#if defined __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,382 @@ + +#include "mbed.h" +#include <btstack/hci_cmds.h> +#include <btstack/run_loop.h> +#include <btstack/sdp_util.h> +#include "hci.h" +#include "l2cap.h" +#include "btstack_memory.h" +#include "remote_device_db.h" +#include "rfcomm.h" +extern "C" { +#include "sdp.h" +} +#include "config.h" +#include "debug.h" +#include "bd_addr.h" // class bd_addr + +#include "Wiimote.h" +#include "TB6612.h" + +// ----- Wallbot I/O Setting ----- +// Motor +TB6612 right(p21,p12,p11); +TB6612 left(p22,p14,p13); + +#define WII_REMOTE_CONNECTION_SYNC 0 // 0:old Wii Remote 1:New Wii Remote +#define REPORTMODE_TT_BIT 0x00 +#define WII_REMOTE_REPORT_MODE 0x31 +#define MAX_SPEED 100 + +#define HEARTBEAT_PERIOD_MS 1000 +#define INQUIRY_INTERVAL 15 + +Serial pc(USBTX, USBRX); +DigitalOut ActLED(LED1); + + + +static bd_addr_t keyboard; +static int haveKeyboard = 0; +static char lineBuffer[80]; +static short wii_remocon_channel_id=0; + +int DirectMode( Wiimote* wii, int stat ) +{ + + if( wii->left ) + { + right = -MAX_SPEED; + left = MAX_SPEED; + } + else if( wii->right ) + { + right = MAX_SPEED; + left = -MAX_SPEED; + } + else if( wii->up ) + { + right = MAX_SPEED; + left = MAX_SPEED; + } + else if( wii->down ) + { + right = -MAX_SPEED; + left = -MAX_SPEED; + } + else + { + right = 0; + left = 0; + } + + float factor = wii->wheel * 1.5f; + + if(factor > 100.0f ) factor = 100.0f; + if(factor < -100.0f ) factor = -100.0f; + + printf("%f\t%f\r\n",wii->wheel,factor); + + int left_factor = (int)((factor <= 0.0) ? 100.0 : 100.0 - factor); + int right_factor = (int)((factor >= 0.0) ? 100.0 : 100.0 - (-factor)); + + if( wii->one ) + { + right = right_factor; + left = left_factor; + } + if( wii->two ) + { + right = -left_factor; + left = -right_factor; + } + + return(stat); +} + +int wall_bot_remote(char *c) +{ + Wiimote wii; + int ret = 0; + + wii.decode(c); + + ret = DirectMode( &wii ,ret ); + + return(ret); +} + +void wii_remote_report_mode(uint16_t channel,uint8_t tt,uint8_t mm) +{ + lineBuffer[0]=0xa2; + lineBuffer[1]=0x12; + lineBuffer[2]=tt; + lineBuffer[3]=mm; + l2cap_send_internal(channel,(uint8_t*)lineBuffer,4); +} + +void wii_remote_request_status_report(uint16_t channel) +{ + lineBuffer[0]=0xa2; + lineBuffer[1]=0x15; + lineBuffer[2]=0x00; + l2cap_send_internal(channel,(uint8_t*)lineBuffer,3); +} + +void hid_process_packet(uint16_t channel,unsigned char *hid_report,uint16_t size) +{ + if(hid_report[0]!=0xa1){ + return; + } + + if(hid_report[1]==0x30){ + wii_remote_report_mode(channel,REPORTMODE_TT_BIT,WII_REMOTE_REPORT_MODE); + wii_remote_request_status_report(channel); + } + + switch(hid_report[1]){ + case 0x30: //0x30:Core Buttons + case 0x31: //0x31:Core Buttons and Accelerometer + case 0x35: //0x35:Core Buttons and Accelerometer with 16 Extension Bytes + wall_bot_remote( (char*)&hid_report[2] ); + break; + default: + break; + } + return; +} + +static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) +{ + if (packet_type == HCI_EVENT_PACKET){ + switch(packet[0]){ + case L2CAP_EVENT_INCOMING_CONNECTION: + log_info("L2CAP_EVENT_INCOMING_CONNECTION\n"); + l2cap_accept_connection_internal(channel); + break; + case L2CAP_EVENT_CHANNEL_OPENED: + if (packet[2]) { + log_info("Connection failed\n"); + return; + } + log_info("interrupt channel connected\n"); + wii_remocon_channel_id=channel; + break; + case L2CAP_EVENT_CHANNEL_CLOSED: + wii_remocon_channel_id=0; + // use_extension=0; + haveKeyboard = 0; + hci_send_cmd(&hci_inquiry, HCI_INQUIRY_LAP, INQUIRY_INTERVAL, 0); + break; + default: + break; + } + } + if (packet_type == L2CAP_DATA_PACKET){ + hid_process_packet(channel,packet,size); + } +} + +//static uint16_t control_channel=0; +static void l2cap_control_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) +{ + if (packet_type == HCI_EVENT_PACKET){ + switch(packet[0]){ + case L2CAP_EVENT_INCOMING_CONNECTION: + log_info("L2CAP_EVENT_INCOMING_CONNECTION\n"); + l2cap_accept_connection_internal(channel); + break; + case L2CAP_EVENT_CHANNEL_OPENED: + if (packet[2]) { + // xputs("control Connection failed\n"); + return; + } + // xputs("control channel connected\n"); + l2cap_create_channel_internal(NULL, l2cap_packet_handler, keyboard, PSM_HID_INTERRUPT, 150); + //control_channel=channel; + break; + case L2CAP_EVENT_CHANNEL_CLOSED: + log_info("L2CAP_CHANNEL_CLOSED\n"); + //control_channel=0; + break; + case L2CAP_EVENT_CREDITS: + break; + default: + log_info("l2cap:unknown(%02x)\n",packet[0]); + break; + } + } +} + +// Bluetooth logic +static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + bd_addr_t event_addr; + + switch (packet_type) { + case HCI_EVENT_PACKET: + switch (packet[0]) { + case BTSTACK_EVENT_STATE: + // bt stack activated, get started - set local name + if (packet[2] == HCI_STATE_WORKING) { + hci_send_cmd(&hci_write_local_name, "mbed"); + } + break; + + case HCI_EVENT_COMMAND_COMPLETE: + if (COMMAND_COMPLETE_EVENT(packet, hci_read_bd_addr)){ + bt_flip_addr(event_addr, &packet[6]); + log_info("BD-ADDR: %s\n", bd_addr_to_str(event_addr)); + break; + } + if (COMMAND_COMPLETE_EVENT(packet, hci_write_local_name)){ +// hci_discoverable_control(1); +// hci_send_cmd(&hci_write_authentication_enable, 1); + hci_send_cmd(&hci_write_authentication_enable, 0); + break; + } + if (COMMAND_COMPLETE_EVENT(packet, hci_write_authentication_enable)){ + inquiry: + + log_info("Inquiry\n"); + hci_send_cmd(&hci_inquiry, HCI_INQUIRY_LAP, INQUIRY_INTERVAL, 0); + + break; + } + if (COMMAND_COMPLETE_EVENT(packet, hci_inquiry_cancel) ) { + //l2cap_create_channel_internal(NULL, l2cap_packet_handler, keyboard, PSM_HID_INTERRUPT, 150); + l2cap_create_channel_internal(NULL, l2cap_control_packet_handler, keyboard, PSM_HID_CONTROL, 150); + break; + } + break; + case HCI_EVENT_INQUIRY_RESULT: + + // ignore further results + if (haveKeyboard) break; + + // ignore none wii remocon + if( + !(packet[12]==0x04 && packet[13]==0x25 && packet[14]==0x00) + && !(packet[12]==0x08 && packet[13]==0x05 && packet[14]==0x00) //RVL-CNT-01-TR + ){ + break; + } + + // flip addr + bt_flip_addr(keyboard, &packet[3]); + + // show + log_info("found wii remocon:%s\n",bd_addr_to_str(keyboard)); + + haveKeyboard = 1; + hci_send_cmd(&hci_inquiry_cancel); + break; + + case HCI_EVENT_INQUIRY_COMPLETE: + log_info("No wii remocon\n"); + goto inquiry; + //break; + case HCI_EVENT_LINK_KEY_REQUEST: + // deny link key request + log_info("Link key request\n"); + bt_flip_addr(event_addr, &packet[2]); + hci_send_cmd(&hci_link_key_request_negative_reply, &event_addr); + break; + + case HCI_EVENT_PIN_CODE_REQUEST: + // inform about pin code request + bt_flip_addr(event_addr, &packet[2]); + //wii remocon +#if WII_REMOTE_CONNECTION_SYNC + bt_flip_addr(packet+2, addr_global); +#endif + log_info("Pin code request - using %02x%02x%02x%02x%02x%02x\n",packet[2],packet[3],packet[4],packet[5],packet[6],packet[7]); + memset(packet+2+6,0x00,10); + hci_send_cmd(&hci_pin_code_request_reply, &event_addr, 6, &packet[2]); + break; + case HCI_EVENT_CONNECTION_COMPLETE: + //log_info("connection complete: status=%d\n",packet[2]); + if(packet[2]){ + haveKeyboard=0; + goto inquiry; + } + break; + default: + break; + } + break; + default: + break; + } +} + +static void heartbeat_handler(struct timer *ts){ + static int counter = 0; + counter++; + + if(wii_remocon_channel_id){ + lineBuffer[0]=0xa2; + lineBuffer[1]=0x11; + lineBuffer[2]=0x10<<(counter%4); //LED Player1 + if(l2cap_send_internal(wii_remocon_channel_id,(uint8_t*)lineBuffer,3)<0){ + //error control init + } + ActLED = !ActLED; + } + run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS); + run_loop_add_timer(ts); +} + +// main +int main(void) +{ + pc.baud(921600); + log_info("%s\n", __FILE__); + + // init LEDs + ActLED = 0; + + /// GET STARTED with BTstack /// + btstack_memory_init(); + run_loop_init(RUN_LOOP_EMBEDDED); + + // init HCI + hci_transport_t* transport = hci_transport_usb_instance(); + remote_device_db_t * remote_db = (remote_device_db_t *) &remote_device_db_memory; + hci_init(transport, NULL, NULL, remote_db); + + // init L2CAP + l2cap_init(); + l2cap_register_packet_handler(packet_handler); + +#if 0 + // init RFCOMM + rfcomm_init(); + rfcomm_register_packet_handler(packet_handler); + rfcomm_register_service_internal(NULL, rfcomm_channel_nr, 100); // reserved channel, mtu=100 + + + // init SDP, create record for SPP and register with SDP + sdp_init(); + memset(spp_service_buffer, 0, sizeof(spp_service_buffer)); + service_record_item_t * service_record_item = (service_record_item_t *) spp_service_buffer; + sdp_create_spp_service( (uint8_t*) &service_record_item->service_record, 1, "loopback"); + log_info("SDP service buffer size: %u\n\r", (uint16_t) (sizeof(service_record_item_t) + de_get_len((uint8_t*) &service_record_item->service_record))); + sdp_register_service_internal(NULL, service_record_item); +#endif + + // set one-shot timer + timer_source_t heartbeat; + heartbeat.process = &heartbeat_handler; + run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS); + run_loop_add_timer(&heartbeat); + + log_info("SPP loopback demo...\n\r"); + + // turn on! + hci_power_control(HCI_POWER_ON); + + // go! + run_loop_execute(); + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/5e5da4a5990b \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usbbt/bd_addr.cpp Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,33 @@ +#include "mbed.h" +#include "bd_addr.h" +bd_addr::bd_addr() +{ + +} + +bd_addr::bd_addr(char* s) +{ + for(int i = 0; i <= 5; i++) { + ad[i] = strtol(s+i*3, NULL, 16); + } +} + +uint8_t* bd_addr::data(uint8_t* addr) +{ + if (addr != NULL) { + ad[5] = addr[0]; + ad[4] = addr[1]; + ad[3] = addr[2]; + ad[2] = addr[3]; + ad[1] = addr[4]; + ad[0] = addr[5]; + } + return ad; +} + +char* bd_addr::to_str() +{ + snprintf(ad_str, sizeof(ad_str), "%02X:%02X:%02X:%02X:%02X:%02X", + ad[0], ad[1], ad[2], ad[3], ad[4], ad[5]); + return ad_str; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usbbt/bd_addr.h Mon Jun 10 16:01:50 2013 +0000 @@ -0,0 +1,13 @@ +#ifndef BD_ADDR_H +#define BD_ADDR_H +class bd_addr { +public: + bd_addr(); + bd_addr(char* s); + uint8_t* data(uint8_t* addr = NULL); + char* to_str(); +private: + char ad_str[18]; + uint8_t ad[6]; +}; +#endif //BD_ADDR_H