diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..b68024fe --- /dev/null +++ b/.clang-format @@ -0,0 +1,246 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseColons: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +KeepEmptyLinesAtEOF: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +PPIndentWidth: -1 +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +... + diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 00000000..85d50b18 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,17 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "macFrameworkPath": [], + "compilerPath": "/usr/bin/gcc", + "intelliSenseMode": "linux-gcc-x64", + "cStandard": "c17", + "cppStandard": "c++17" + } + ], + "version": 4 +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..25e2d155 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "editor.formatOnSave": true, + "editor.formatOnPaste": false, + "clang-format.executable": "/usr/bin/clang-format", + "clangd.path": "/usr/bin/clangd", + "[c]": { + "editor.defaultFormatter": "xaver.clang-format", + "editor.tabSize": 2, + "editor.indentSize": "tabSize", + } +} diff --git a/Makefile b/Makefile index f5f40c10..af06d668 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,13 @@ -APPS = +APPS = -DRIVERS = +DRIVERS = driver/dummy.o OBJS = util.o \ + net.o \ TESTS = test/step0.exe \ + test/step1.exe \ + test/step2.exe \ CFLAGS := $(CFLAGS) -g -W -Wall -Wno-unused-parameter -iquote . @@ -12,6 +15,7 @@ ifeq ($(shell uname),Linux) # Linux specific settings BASE = platform/linux CFLAGS := $(CFLAGS) -pthread -iquote $(BASE) + OBJS := $(OBJS) $(BASE)/intr.o endif ifeq ($(shell uname),Darwin) @@ -21,7 +25,7 @@ endif .SUFFIXES: .SUFFIXES: .c .o -.PHONY: all clean +.PHONY: all clean format all: $(APPS) $(TESTS) @@ -36,3 +40,6 @@ $(TESTS): %.exe : %.o $(OBJS) $(DRIVERS) test/test.h clean: rm -rf $(APPS) $(APPS:.exe=.o) $(OBJS) $(DRIVERS) $(TESTS) $(TESTS:.exe=.o) + +format: + find . -name "*.c" -o -name "*.h" | xargs clang-format -i --sort-includes diff --git a/docs/microps-architecture.excalidraw b/docs/microps-architecture.excalidraw new file mode 100644 index 00000000..401338e7 --- /dev/null +++ b/docs/microps-architecture.excalidraw @@ -0,0 +1,14 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff", + "lockedMultiSelections": {} + }, + "files": {} +} \ No newline at end of file diff --git a/driver/dummy.c b/driver/dummy.c new file mode 100644 index 00000000..20201dec --- /dev/null +++ b/driver/dummy.c @@ -0,0 +1,92 @@ +#include +#include +#include + +#include "platform.h" + +#include "net.h" +#include "util.h" + +#define DUMMY_MTU UINT16_MAX /* maximum size of IP datagram (0 ~ 65535) */ + +// ダミーデバイスが使う IRQ 番号 +// microps の IRQ 番号は 35 始まり +/* platform/linux/platform.h +#define INTR_IRQ_BASE (SIGRTMIN+1) +*/ +#define DUMMY_IRQ INTR_IRQ_BASE + +static int dummy_transmit(struct net_device *dev, uint16_t type, + const uint8_t *data, size_t len, const void *dst) { + debugf("dummy transmit to device: dev=%s, type=0x%04x, len=%zu", dev->name, + type, len); + debugdump(data, len); + + // データを破棄 + // ダミーデバイスなのでデータに対しては何もしない + + // テスト用に割り込みを発生させる + intr_raise_irq(DUMMY_IRQ); + + return 0; +} + +// デバイスドライバに実装されている関数へのポインタを格納する構造体 +// ダミーデバイス用に transmit のみ登録 +static struct net_device_ops dummy_ops = { + .transmit = dummy_transmit, +}; + +static int dummy_isr(unsigned int irq, void *id) { + // 現段階ではダミーデバイス用の割り込みハンドラが呼び出されたことがわかれば良いのでデバッグ出力のみ + // FIXME: 本来は net_device 以外にも対応するため、キャストは行わない。 + // あくまで現段階ではデバッグ用 + struct net_device *dev = (struct net_device *)id; // 無理やりキャスト + debugf("ISR called: irq=%u, dev=%s", irq, dev->name); + + return 0; +} + +/* ダミーデバイスの初期化 + +## ダミーデバイスの仕様 + +- 入力:なし(データを受信することはない) +- 出力:データを破棄 +*/ +struct net_device *dummy_init(void) { + struct net_device *dev; + + // ダミーデバイスを生成 + dev = net_device_alloc(); + if (!dev) { + errorf("net_device_alloc() failure on initializing dummy device"); + return NULL; + } + + // ダミーデバイスの設定 + // note: dev->name は net_device_register 内で更新されるのでここでは設定しない + dev->type = NET_DEVICE_TYPE_DUMMY; + dev->mtu = DUMMY_MTU; + // ヘッダもアドレスも存在しない + dev->hlen = 0; + dev->alen = 0; + // デバイスドライバに実装されている関数へのポインタを格納する構造体 + dev->ops = &dummy_ops; + + // デバイスを登録 + if (net_device_register(dev) == -1) { + debugf("net_device_register() failure on initializaing dummy device: " + "dev=%s, type=0x%04x", + dev->name, dev->type); + return NULL; + } + debugf("dummy device initialized: dev=%s", dev->name); + + // デバイスの割り込みハンドラとして dummy_isr を登録する + infof("register interrupt handler for dummy device"); + intr_register_irq_entry(DUMMY_IRQ, dummy_isr, INTR_IRQ_SHARED, dev->name, + dev); + + return dev; +} diff --git a/driver/dummy.h b/driver/dummy.h new file mode 100644 index 00000000..6a4fe9ec --- /dev/null +++ b/driver/dummy.h @@ -0,0 +1,8 @@ +#ifndef DUMMY_H +#define DUMMY_H + +#include "net.h" + +extern struct net_device *dummy_init(void); + +#endif diff --git a/net.c b/net.c new file mode 100644 index 00000000..2e75d5db --- /dev/null +++ b/net.c @@ -0,0 +1,259 @@ +#include +#include +#include + +#include "platform.h" + +#include "net.h" +#include "util.h" + +/* NOTE: if you want to add/delete the entries after net_run(), you need to + * protect these lists with a mutex. */ +// デバイスリスト(リストの先頭を指すポインタ) +static struct net_device *devices; + +struct net_device *net_device_alloc(void) { + // pointer to new net_device + struct net_device *dev; + + // デバイス構造体のサイズのメモリを確保 + // - memory_alloc() で確保したメモリ領域は 0 で初期化されている + // - メモリが確保できなかったらエラーとして NULL を返す + // - メモリの確保と解放には memory_alloc() と memory_free() を使う + dev = memory_alloc(sizeof(*dev)); + if (!dev) { + errorf("memory_alloc() failure"); + return NULL; + } + + return dev; +} + +/* NOTE: must not be call after net_run() */ +// デバイスの登録 +int net_device_register(struct net_device *dev) { + // (関数の初回実行時だけ動く)デバイスのインデックス番号の初期化 + // note: 関数内で static な変数を一度だけ初期化し、関数呼出し間で保持できる。 + // note: 関数に副作用を入れることになる。 + // note: マルチスレッド環境でのデータ競合に注意。 + static unsigned int index = 0; + + // デバイスのインデックス番号の設定 + // note: 後置インクリメントなので現在の値を代入してからインクリメントされる + dev->index = index++; // set, then increment + + // デバイス名を生成する(net0, net1, net2, ...) + snprintf(dev->name, sizeof(dev->name), "net%d", dev->index); + + // デバイスリストの先頭にデバイスを追加 + // ref: Day 1 - デバイスの生成と登録 + // https://docs.google.com/presentation/d/1ID6ggxASfc_1bWiJfDy1IFKIwzxvfYy8rUBrWYTRFj8/edit?slide=id.gd328c3072b_0_1179#slide=id.gd328c3072b_0_1179 + // 新規登録するデバイスの次のデバイスを、現在のデバイスリストの先頭のデバイスに設定する + dev->next = devices; + + // 現在のデバイスリストの先頭のデバイスを新規登録するデバイスに設定する + devices = dev; + infof("registered device: dev=%s, type=0x%04x", dev->name, dev->type); + + return 0; +} + +static int net_device_open(struct net_device *dev) { + // デバイスの状態を確認(既に UP 状態の場合はエラーを返す) + if (NET_DEVICE_IS_UP(dev)) { + errorf("device is already opened: dev=%s", dev->name); + return -1; + } + + // デバイスドライバのオープン関数を呼び出す + // - オープン関数が設定されていない場合は呼び出しをスキップ + // - エラーが返されたらこの関数もエラーを返す + if (dev->ops->open) { + if (dev->ops->open(dev) == -1) { + errorf("failed to open device: dev=%s", dev->name); + return -1; + } + } + + // デバイスのオープンに成功したら UP フラグを立てる + dev->flags |= NET_DEVICE_FLAG_UP; + infof("opened device status: dev=%s, state=%s", dev->name, + NET_DEVICE_STATE(dev)); + + return 0; +} + +static int net_device_close(struct net_device *dev) { + // デバイスの状態を確認 + // UP 状態でないデバイスを close しようとしたときはエラーを返す + if (!NET_DEVICE_IS_UP(dev)) { + errorf("device is not opened: dev=%s", dev->name); + } + + // デバイスドライバのクローズ関数を呼び出す + // - クローズ関数が設定されていない場合は呼び出しをスキップ + // - エラーが返されたらこの関数もエラーを返す + if (dev->ops->close) { + if (dev->ops->close(dev) == -1) { + errorf("failed to close device: dev=%s", dev->name); + return -1; + } + } + + // UP フラグを落とす + dev->flags &= ~NET_DEVICE_FLAG_UP; + infof("closed device status: dev=%s, state=%s", dev->name, + NET_DEVICE_STATE(dev)); + + return 0; +} + +// デバイスへの出力 +int net_device_output(struct net_device *dev, uint16_t type, + const uint8_t *data, size_t len, const void *dst) { + // デバイスの状態を確認 + // UP 状態でないデバイスに出力する場合はエラーを返す + if (!NET_DEVICE_IS_UP(dev)) { + errorf("device is not opened on transmit: dev=%s", dev->name); + return -1; + } + + // データのサイズを確認 + // デバイスの MTU を超えるサイズのデータは送信できないのでエラーを返す + if (len > dev->mtu) { + errorf("data is too long on transmit: dev=%s, mtu=%u, len=%zu", dev->name, + dev->mtu, len); + return -1; + } + /* note: フォーマット指定子 %04x について + + - `%04x`: 値を 16 進数で出力する。ゼロ 4 桁で padding。 + - 幅を 4 桁に指定し、ゼロで埋める (`%0` + `4` + `x`) + - 16 進数で足りない桁は先頭を 0 で埋めて 4 桁で出力する + */ + debugf("output to device: dev=%s, type=0x%04x, len=%zu", dev->name, type, + len); + debugdump(data, len); + + // デバイスドライバの出力関数を呼び出す + // エラーが返されたらこの関数もエラーを返す + if (dev->ops->transmit(dev, type, data, len, dst) == -1) { + errorf("device transmit failure on output: dev=%s, len=%zu", dev->name, + len); + return -1; + } + + return 0; +} + +/* デバイスからの入力: デバイスが受信したパケットをプロトコルスタックに渡す関数 + +・プロトコルスタックへのデータの入口であり、デバイスドライバから呼び出されることを想定している +・複数のデバイスドライバからの入力を一手に受け取る + + デバイスドライからこの関数が呼び出されるイメージ: + + +----------------------+ + | net_input_handler() | + +-----------^----------+ + | + +------------+------------+ + | | + +--------------+ +--------------+ + | Device Driver| | Device Driver| + +------^-------+ +------^-------+ + | | + Device Device +*/ +int net_input_handler(uint16_t type, const uint8_t *data, size_t len, + struct net_device *dev) { + // TODO: 今の段階では呼び出されたことがわかればよいのでデバッグ出力のみ + debugf("received data from device: dev=%s, type=0x%04x, len=%zu", dev->name, + type, len); + debugdump(data, len); + + return 0; +} + +/* プロトコルスタックの起動 + +## 呼び出しのコード例: net_run / net_shutdown + +```c +int main(void) { + if (net_init() == -1) { + return -1; + } + + // デバイスの登録処理 + // ... + + // プロトコルスタックの起動 + if (net_run() == -1) { + return -1; + } + + // アプリケーションの処理 + // ... + + net_shutdown(); + + return 0; +} +``` +*/ +int net_run(void) { + struct net_device *dev; + + // 割り込み機構の起動 + debugf("start interrupt processing..."); + if (intr_run() == -1) { + errorf("intr_run() failure"); + return -1; + } + + // 登録済みの全デバイスをオープン + debugf("open all devices..."); + for (dev = devices; dev; dev = dev->next) { + if (net_device_open(dev) == -1) { + errorf("failed to open device on net_run: dev=%s"); + // 失敗しても for 文を抜けはしない + } + } + debugf("running..."); + + return 0; +} + +// プロトコルスタックの停止 +void net_shutdown(void) { + struct net_device *dev; + + // 登録済みの全デバイスをクローズ + debugf("close all devices..."); + for (dev = devices; dev; dev = dev->next) { + if (net_device_close(dev) == -1) { + errorf("failed to close device on shutting down: dev=%s"); + // 失敗しても for 文を抜けはしない + } + } + + // 割り込み機構の終了 + debugf("shutdown interrupt processing..."); + intr_shutdown(); + + debugf("shutting down"); +} + +int net_init(void) { + // 割り込み機構の初期化 + debugf("initialize interrupt processing..."); + if (intr_init() == -1) { + errorf("intr_init() failure"); + return -1; + } + + infof("initialized protocol stack"); + + return 0; +} diff --git a/net.h b/net.h new file mode 100644 index 00000000..a24f0faf --- /dev/null +++ b/net.h @@ -0,0 +1,92 @@ +#ifndef NET_H +#define NET_H + +#include +#include + +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif + +#define NET_DEVICE_TYPE_DUMMY 0x0000 +#define NET_DEVICE_TYPE_LOOPBACK 0x0001 +#define NET_DEVICE_TYPE_ETHERNET 0x0002 + +// NET_DEVICE_FLAG_UP: 0000_0000_0000_0001 = 1 +#define NET_DEVICE_FLAG_UP 0x0001 +// NET_DEVICE_FLAG_LOOPBACK: 0000_0000_0001_0000 = 16 +#define NET_DEVICE_FLAG_LOOPBACK 0x0010 +// NET_DEVICE_FLAG_BROADCAST: 0000_0000_0010_0000 = 32 +#define NET_DEVICE_FLAG_BROADCAST 0x0020 +// NET_DEVICE_FLAG_P2P: 0000_0000_0100_0000 = 64 +#define NET_DEVICE_FLAG_P2P 0x0040 +// NET_DEVICE_FLAG_NEED_ARP: 0000_0001_0000_0000 = 256 (= 16^2 * 1) +#define NET_DEVICE_FLAG_NEED_ARP 0x0100 + +#define NET_DEVICE_ADDR_LEN 16 + +// UP flag が立っていればビット演算の論理積 AND の値が非 0 になり、 +// truthy な値となる +#define NET_DEVICE_IS_UP(x) ((x)->flags & NET_DEVICE_FLAG_UP) +#define NET_DEVICE_STATE(x) (NET_DEVICE_IS_UP(x) ? "up" : "down") + +// ネットワークデバイスの定義 +// ref: +// https://docs.google.com/presentation/d/1ID6ggxASfc_1bWiJfDy1IFKIwzxvfYy8rUBrWYTRFj8/edit?slide=id.gd328c3072b_0_1161#slide=id.gd328c3072b_0_1161 +struct net_device { + // 次のデバイスへのポインタ + struct net_device *next; + unsigned int index; + char name[IFNAMSIZ]; + + // デバイスの種別(net.h に NET_DEVICE_TYPE_XXX として定義) + uint16_t type; + + // ----- デバイスの種別によって変化する値 --------------- + uint16_t mtu; // デバイスの MTU (Maximum Transmission Unit) の値 + uint16_t flags; + uint16_t hlen; /* header length */ + uint16_t alen; /* address length */ + // ------------------------------------------------ + + // デバイスのハードウェアアドレス + // - デバイスによってアドレスサイズが異なるので大きめのバッファを用意 + // - アドレスを持たないデバイスでは値は設定されない + uint8_t addr[NET_DEVICE_ADDR_LEN]; + union { + uint8_t peer[NET_DEVICE_ADDR_LEN]; + uint8_t broadcast[NET_DEVICE_ADDR_LEN]; + }; + + // デバイスドライバに実装されている関数を + // 格納している構造体 struct net_device_ops へのポインタ + struct net_device_ops *ops; + + // デバイスドライバが使用するプライベートなデータへのポインタ + void *priv; +}; + +// デバイスドライバに実装されている関数へのポインタを格納 +struct net_device_ops { + // optional + int (*open)(struct net_device *dev); + // optional + int (*close)(struct net_device *dev); + // required: 送信関数 transmit は必須 + int (*transmit)(struct net_device *dev, uint16_t type, const uint8_t *data, + size_t len, const void *dst); +}; + +extern struct net_device *net_device_alloc(void); +extern int net_device_register(struct net_device *dev); +extern int net_device_output(struct net_device *dev, uint16_t type, + const uint8_t *data, size_t len, const void *dst); + +extern int net_input_handler(uint16_t type, const uint8_t *data, size_t len, + struct net_device *dev); + +extern int net_run(void); +extern void net_shutdown(void); +extern int net_init(void); + +#endif diff --git a/platform/linux/intr.c b/platform/linux/intr.c new file mode 100644 index 00000000..78b8df18 --- /dev/null +++ b/platform/linux/intr.c @@ -0,0 +1,328 @@ +#include +#include +#include +#include + +#include "platform.h" + +#include "util.h" + +// 割り込み要求(IRQ, Interrupt Request)の構造体 +// ネットワークデバイス(net_device 構造体)と同様に linked list 構造で管理する +struct irq_entry { + // 次の IRQ 構造体 + struct irq_entry *next; + // 割り込み番号(IRQ 番号) + unsigned int irq; + // 割り込みハンドラ(割り込みが発生した際に呼び出す関数へのポインタ) + int (*handler)(unsigned int irq, void *dev); + // フラグ(INTR_IRQ_SHARED が指定された場合は IRQ 番号を共有可能) + int flags; + // デバッグ出力で識別するための名前 + char name[16]; + /* 割り込みの発生元となるデバイス + + ---------- + + struct net_device 以外にも対応できるように void * で保持 + */ + void *dev; +}; + +/* NOTE: if you want to add/delete the entries after intr_run(), you need to + * protect these lists with a mutex. */ +// IRQ リスト(リストの先頭を指すポインタ) +static struct irq_entry *irqs; + +// シグナルマスク用のシグナル集合 +static sigset_t sigmask; + +// 割り込みスレッドのスレッド ID +static pthread_t tid; +// スレッド間の同期のためのバリア +static pthread_barrier_t barrier; +// バリアで同期するスレッドの数 +// (今回はメインスレッド、割り込み処理スレッドの 2 つ) +static const int NUM_THREADS_FOR_BARRIER = 2; + +/* 割り込み要求エントリ(IRQ エントリ)を IRQ リストに登録する + * + * ---------- + * + * ## microps における「割り込み」の実装 + * + * microps では、IRQ 番号にシグナル番号を使用する。 + * IRQ 番号は、ハードウェア割り込みにおいては、 + * 「どのハードウェアによって割り込み要求(IRQ)が発生したか」を識別するための数値。 + * microsはユーザ空間で動くので、カーネル空間で動くハードウェア割り込みの仕組みは使えない。 + * そこで処理の割り込みを行うためにシグナルを使ったソフトウェア割り込みを実装する。 + * ハードウェア割り込みを模倣した割り込みの仕組みをユーザ空間で実装する。 + * + * ## シグナルによりソフトウェア割り込みにマルチスレッドを使用する理由 + * + * シグナル受信時に非同期に実行されるシグナルハンドラでは実行できる処理が大きく制限される。 + * シグナル安全(signal-safe)、非同期安全な処理の範囲で処理を実装しなければならない。 + * + * ref: https://www.jpcert.or.jp/sc-rules/c-sig30-c.html + * + * そのため、割り込み処理のために専用のスレッドを起動してシグナルの発生を待ち受けて処理する。 + * 割り込みの仕組みをマルチスレッドで実装する理由はこれ。 + * + */ +int intr_register_irq_entry(unsigned int irq, + int (*handler)(unsigned int irq, void *dev), + int flags, const char *name, void *dev) { + struct irq_entry *entry; + + debugf("registering new IRQ: irq=%u, flags=%d, name=%s", irq, flags, name); + + // -------------------------------------------------- + // IRQ 番号が既に登録されている場合、 + // IRQ 番号の共有が許可されているかどうかチェック + // どちらかが共有を許可していない場合はエラーを返す + // -------------------------------------------------- + + // IRQ リストを走査 + for (entry = irqs; entry; entry = entry->next) { + // IRQ リストに新規 IRQ エントリの IRQ 番号が既に登録されている場合、 + // その IRQ 番号に対して追加のハンドラを登録できるかどうか確認する。 + if (entry->irq == irq) { + /* + XOR(^) はビット単位の排他的論理和(2 つの値が異なるとき 1 になる) + 真理値表 (A ^ B): + A B | A^B + 0 0 | 0 + 0 1 | 1 + 1 0 | 1 + 1 1 | 0 + */ + /* + ・既存の IRQ のフラグが `INTR_IRQ_SHARED` と完全に一致しているか + - flags と INTR_IRQ_SHARED が一致している場合は XOR の結果は偽になる + - e.g. flags ^ INTR_IRQ_SHARED = 0001 ^ 0001 = 0000 -> 偽 + - flags と INTR_IRQ_SHARED が一致していない場合は XOR の結果は真 + - e.g. flags ^ INTR_IRQ_SHARED = 0000 ^ 0001 = 0001 -> 真 + ・新しく登録しようとしているフラグが`INTR_IRQ_SHARED`と完全に一致しているか + - + + をチェックしていて、どちらか一方でも値が違えば衝突とみなす。 + + 注意点として、XOR は「`INTR_IRQ_SHARED` + ビットが立っているかどうか」ではなく + 「値がまるごと同一かどうか」を判定している。 + フラグにほかのビットが含まれる場合は真になってしまうので、共有可否をビット単位で見る意図なら + `flags & INTR_IRQ_SHARED` のようにマスクした方が適切かもしれない。 + */ + + const int is_existing_irq_allows_share = + (entry->flags ^ INTR_IRQ_SHARED) == 0; + const int is_new_irq_allows_share = (flags ^ INTR_IRQ_SHARED) == 0; + const int is_irq_allows_share = + is_existing_irq_allows_share || is_new_irq_allows_share; + if (!is_irq_allows_share) { + errorf("conflicts with already registered IRQs"); + return -1; + } + } + } + + // -------------------------------------------------- + // IRQ リストへ新しいエントリを追加 + // -------------------------------------------------- + + // 新しい IRQ エントリのメモリを確保 + entry = memory_alloc(sizeof(*entry)); + if (!entry) { + errorf("memory_alloc() on registering IRQ entry failure"); + return -1; + } + + // IRQ エントリ構造体に値を設定 + entry->irq = irq; + entry->handler = handler; + entry->flags = flags; + strncpy(entry->name, name, sizeof(entry->name) - 1); + entry->dev = dev; + + // 新しい IRQ エントリを IRQ リストの先頭へ挿入 + entry->next = irqs; + irqs = entry; + + // シグナル集合へ新しいシグナルを追加 + // ここって既存のシグナルの被らないの? + // -> 被らない。既存のシグナルとの OR 演算なので。 + sigaddset(&sigmask, irq); + debugf("registered new IRQ entry: irq=%u, name=%s", entry->irq, entry->name); + + return 0; +} + +int intr_raise_irq(unsigned int irq) { + // 割り込み処理用のスレッドへシグナルを送信 + // microps では、IRQ 番号にシグナル番号を使用する + // 後続の処理では、 + // 発生したシグナルのシグナル番号と IRQ エントリの IRQ 番号が一致したときに + // IRQ エントリに登録されているハンドラが実行される + return pthread_kill(tid, (int)irq); +} + +// 割り込み処理を実行するスレッドのエントリーポイント +static void *intr_thread(void *arg) { + int terminate = 0; + int sig; + int err; + + struct irq_entry *entry; + + infof("start thread for interrupt"); + + // メインスレッドと同期を取るための処理 + pthread_barrier_wait(&barrier); + debugf("barrier unlocked on intr_thread!"); + + // ハードウェア割り込みに見立てたシグナルを処理するループ + while (!terminate) { + // ハードウェア割り込みに見立てたシグナルが発生するまで待機 + err = sigwait(&sigmask, &sig); + if (err) { + errorf("sigwait() failure: %s", strerror(err)); + break; + } + + // 発生したシグナルの種類に応じて処理 + switch (sig) { + // SIGHUP シグナルの処理: + // 割り込み処理用のスレッドへ終了を通知するためのシグナル + case SIGHUP: + terminate = 1; + break; + // デバイス割り込み用のシグナルを処理 + default: + // IRQ リストを巡回 + for (entry = irqs; entry; entry = entry->next) { + // IRQ 番号が一致するエントリの割り込みハンドラを呼び出す + // microps では、IRQ 番号にシグナル番号を使用する + if (entry->irq == (unsigned int)sig) { + debugf("call ISR: irq=%d, name=%s", entry->irq, entry->name); + entry->handler(entry->irq, entry->dev); + } + } + break; + } + } + warnf("terminated thread for interrupt"); + + return NULL; +} + +// 割り込み機構を起動する +// intr_init が呼ばれている状態でこの関数を呼ぶこと +int intr_run(void) { + int err; + + // シグナルマスクの設定 + // intr_init 内で sigmask の設定ができていることが前提 + err = pthread_sigmask(SIG_BLOCK, &sigmask, NULL); + if (err) { + errorf("pthread_sigmask"); + return -1; + } + + // 割り込み処理スレッドの起動 + err = pthread_create(&tid, NULL, intr_thread, NULL); + if (err) { + errorf("pthread_create() failed on intr_run: %s", strerror(err)); + return -1; + } + + // スレッドが動き出すまで待つ + // 他のスレッドが同じように pthread_barrier_wait() を呼び出し、 + // バリアのカウントが指定の数になるまでスレッドを停止する + infof("waiting for starting thread for interrupt with barrier..."); + pthread_barrier_wait(&barrier); + infof("thread for interrupt started!"); + + return 0; +} + +void intr_shutdown(void) { + int err; + + // 割り込み処理スレッドが起動済みかどうか確認 + /* Q. なぜ以下のコードで割り込み処理スレッドが起動済みかどうか確認できるのか? + * 前提:関数の実行順序は intr_init -> intr_run -> intr_shutdown + * + * `pthread_equal(tid, pthread_self())`は + *「今の呼び出し元スレッド(メインスレッド)の + * ID」と、グローバルに保持している `tid` を比較している。 + * + * ここの比較は `intr_init` の段階で `tid` に「メインスレッド(=呼び出し元) + * の `pthread_t`」を代入しておく前提になっている。 + * `intr_run` で割り込み処理スレッドを起動するときに + * `pthread_create(&tid, …)` として `tid` を新しいスレッドの ID で上書きする。 + * + * つまり、`intr_shutdown` をメインスレッドから呼ぶときに、 + * `tid` がまだメインスレッドと同じ ID のままなら + * 「新しいスレッドに置き換わっていない → スレッドを作れていない」= + *`pthread_equal` が真になる、という判定になる。 + * + * 逆に、スレッドが正常に起動済みなら `tid` は別の ID に差し替わっているので、 + * `pthread_equal` は偽になり、 `pthread_kill` や `pthread_join` + * に進む、という流れとなっている。 + + * 注意点として、このロジックは + * + * - `intr_init` をメインスレッドで呼ぶこと + * - 初期値として `tid = pthread_self()` をセットしていること + * - `intr_shutdown` をメインスレッドで呼ぶこと + * + * の 3 つが前提なので、別スレッドから呼び出したり、 + * 初期化を忘れたりすると意図通りにならない点には気をつける必要がある。 + */ + if (pthread_equal(tid, pthread_self()) != 0) { + warnf("thread not created"); + return; + } + + // 割り込み処理スレッドにシグナル(SIGHUP)を送信 + err = pthread_kill(tid, SIGHUP); + if (err) { + warnf("failed to kill process with SIGHUP: tid=%d, err=%s", tid, + strerror(err)); + return; + } + + // 割り込み処理スレッドが完全に終了するのを待つ + err = pthread_join(tid, NULL); + if (err) { + warnf("failed to pthread_join: tid=%d, err=%s", tid, strerror(err)); + return; + } + + infof("shutdown interrupt successfully!"); + + return; +} + +int intr_init(void) { + infof("init interrupt"); + + // スレッド ID の初期値にメインスレッドの ID を設定する + tid = pthread_self(); + + // pthread_barrier の初期化(カウントを 2 に設定) + infof("init pthread barrier for %d threads", NUM_THREADS_FOR_BARRIER); + pthread_barrier_init(&barrier, NULL, NUM_THREADS_FOR_BARRIER); + debugf("barrier unlocked on intr_init!"); + + // グローバルな割り込み処理用のシグナル集合(シグナルマスク)を初期化(空にする) + infof("init sigmask"); + sigemptyset(&sigmask); + + // シグナル集合に SIGHUP を追加(割り込みスレッド終了通知用) + sigaddset(&sigmask, SIGHUP); + // 注意:この段階ではまだプロセス(メインスレッド)にシグナルマスクは設定されていない + // pthread_sigmask が実行されて初めてシグナルマスクがプロセスに適用される + + return 0; +} diff --git a/platform/linux/platform.h b/platform/linux/platform.h index 0b8bce2c..3b70715d 100644 --- a/platform/linux/platform.h +++ b/platform/linux/platform.h @@ -1,25 +1,18 @@ #ifndef PLATFORM_H #define PLATFORM_H +#include +#include #include #include -#include /* * Memory */ -static inline void * -memory_alloc(size_t size) -{ - return calloc(1, size); -} +static inline void *memory_alloc(size_t size) { return calloc(1, size); } -static inline void -memory_free(void *ptr) -{ - free(ptr); -} +static inline void memory_free(void *ptr) { free(ptr); } /* * Mutex @@ -29,22 +22,48 @@ typedef pthread_mutex_t mutex_t; #define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER -static inline int -mutex_init(mutex_t *mutex) -{ - return pthread_mutex_init(mutex, NULL); +static inline int mutex_init(mutex_t *mutex) { + return pthread_mutex_init(mutex, NULL); } -static inline int -mutex_lock(mutex_t *mutex) -{ - return pthread_mutex_lock(mutex); +static inline int mutex_lock(mutex_t *mutex) { + return pthread_mutex_lock(mutex); } -static inline int -mutex_unlock(mutex_t *mutex) -{ - return pthread_mutex_unlock(mutex); +static inline int mutex_unlock(mutex_t *mutex) { + return pthread_mutex_unlock(mutex); } +/* + * Interrupt + */ + +// IRQ 番号の開始番号 +// +/* + +microps では IRQ 番号としてシグナル番号を使用する。 + +Linux では、SIGRTMIN ~ SIGRTMAX (34 ~ 64)までのシグナルを +アプリケーションが任意の目的で利用できる。 + +ただし、SIGRTMIN (34) に関しては、glibc が内部的に利用しているため、 ++1 した番号から利用するようにしている。 + +そのため、microps の IRQ 番号は 35 始まり。 + +*/ +#define INTR_IRQ_BASE (SIGRTMIN + 1) + +#define INTR_IRQ_SHARED 0x0001 + +extern int intr_register_irq_entry(unsigned int irq, + int (*handler)(unsigned int irq, void *id), + int flags, const char *name, void *dev); +extern int intr_raise_irq(unsigned int irq); + +extern int intr_run(void); +extern void intr_shutdown(void); +extern int intr_init(void); + #endif diff --git a/test/step0.c b/test/step0.c index 80f28c3f..59f9f548 100644 --- a/test/step0.c +++ b/test/step0.c @@ -1,12 +1,10 @@ -#include "util.h" #include "test.h" +#include "util.h" -int -main(void) -{ - debugf("Hello, World!"); +int main(void) { + debugf("Hello, World!"); - debugdump(test_data, sizeof(test_data)); + debugdump(test_data, sizeof(test_data)); - return 0; + return 0; } diff --git a/test/step1.c b/test/step1.c new file mode 100644 index 00000000..d30bb589 --- /dev/null +++ b/test/step1.c @@ -0,0 +1,91 @@ +#include +#include +#include + +#include "net.h" +#include "util.h" + +#include "driver/dummy.h" + +#include "test.h" + +static volatile sig_atomic_t terminate; + +/* シグナルハンドラ + +原則、シグナルハンドラの中では下記以外のことはしない: + +- 非同期安全な関数の呼び出し + - ref: https://www.jpcert.or.jp/sc-rules/c-sig30-c.html +- volatile sig_atomic_t 型の変数への書き込み +*/ +static void on_signal(int signal) { + /* + 一般に、I/O 関数をシグナルハンドラ内で呼び出すのは安全ではない。 + シグナルハンドラ内で I/O 関数を使用する場合は、 + 使用するシステムの非同期安全な関数を事前に確認すること。 + */ + // warnf("on_signal: signal received %d", signal); + + /* `(void)signal;` は必要? + `(void)signal;` は「この引数は使っていない」ことを意図的に示す + ためのお決まりの書き方。多くのコンパイラは未使用引数に警告を出すので、 + それを抑止する目的で入れている。 + + - 既にハンドラ内で `signal` を使って何かするなら不要。 + - 使わないならそのまま残しておけば警告を避けられる。 + - 別の書き方として、 + - `__attribute__((unused))` + - `void on_signal(int \/\*signal\*\/)` + - のようにコメントで示す例もある。 + - 単に `(void)signal;` がもっとも広く使われる方法です。 + */ + + // (void)signal; + terminate = 1; +} + +int main(int argc, char *argv[]) { + struct net_device *dev; + + // シグナルハンドラの設定 + // Ctrl + C が押された際に行儀よく終了するようにする + signal(SIGINT, on_signal); + + // プロトコルスタックの初期化 + if (net_init() == -1) { + errorf("net_init() failure"); + return -1; + } + + // ダミーデバイスの初期化(ダミーデバイスの生成、設定、登録) + dev = dummy_init(); + + // プロトコルスタックの起動 + if (net_run() == -1) { + errorf("net_run() failure"); + return -1; + } + + // 1 秒おきにデバイスにパケットを書き込む + // まだパケットを自力で生成できないのでテストデータを用いる + uint16_t type = 0x0800; + size_t len = sizeof(test_data); + // Ctrl + C が押されるとシグナルハンドラ on_signal() の中で + // terminate に 1 が設定され、while ループを抜ける + while (!terminate) { + // Internet Layer -> Network Interface Layer への書き込み + // Internet Layer から来たパケットをデバイスに書き込む + if (net_device_output(dev, type, test_data, len, NULL) == -1) { + errorf("net_device_output() failure"); + break; + } + + sleep(1); + } + + // プロトコルスタックの停止 + net_shutdown(); + + return 0; +} diff --git a/test/step2.c b/test/step2.c new file mode 100644 index 00000000..d30bb589 --- /dev/null +++ b/test/step2.c @@ -0,0 +1,91 @@ +#include +#include +#include + +#include "net.h" +#include "util.h" + +#include "driver/dummy.h" + +#include "test.h" + +static volatile sig_atomic_t terminate; + +/* シグナルハンドラ + +原則、シグナルハンドラの中では下記以外のことはしない: + +- 非同期安全な関数の呼び出し + - ref: https://www.jpcert.or.jp/sc-rules/c-sig30-c.html +- volatile sig_atomic_t 型の変数への書き込み +*/ +static void on_signal(int signal) { + /* + 一般に、I/O 関数をシグナルハンドラ内で呼び出すのは安全ではない。 + シグナルハンドラ内で I/O 関数を使用する場合は、 + 使用するシステムの非同期安全な関数を事前に確認すること。 + */ + // warnf("on_signal: signal received %d", signal); + + /* `(void)signal;` は必要? + `(void)signal;` は「この引数は使っていない」ことを意図的に示す + ためのお決まりの書き方。多くのコンパイラは未使用引数に警告を出すので、 + それを抑止する目的で入れている。 + + - 既にハンドラ内で `signal` を使って何かするなら不要。 + - 使わないならそのまま残しておけば警告を避けられる。 + - 別の書き方として、 + - `__attribute__((unused))` + - `void on_signal(int \/\*signal\*\/)` + - のようにコメントで示す例もある。 + - 単に `(void)signal;` がもっとも広く使われる方法です。 + */ + + // (void)signal; + terminate = 1; +} + +int main(int argc, char *argv[]) { + struct net_device *dev; + + // シグナルハンドラの設定 + // Ctrl + C が押された際に行儀よく終了するようにする + signal(SIGINT, on_signal); + + // プロトコルスタックの初期化 + if (net_init() == -1) { + errorf("net_init() failure"); + return -1; + } + + // ダミーデバイスの初期化(ダミーデバイスの生成、設定、登録) + dev = dummy_init(); + + // プロトコルスタックの起動 + if (net_run() == -1) { + errorf("net_run() failure"); + return -1; + } + + // 1 秒おきにデバイスにパケットを書き込む + // まだパケットを自力で生成できないのでテストデータを用いる + uint16_t type = 0x0800; + size_t len = sizeof(test_data); + // Ctrl + C が押されるとシグナルハンドラ on_signal() の中で + // terminate に 1 が設定され、while ループを抜ける + while (!terminate) { + // Internet Layer -> Network Interface Layer への書き込み + // Internet Layer から来たパケットをデバイスに書き込む + if (net_device_output(dev, type, test_data, len, NULL) == -1) { + errorf("net_device_output() failure"); + break; + } + + sleep(1); + } + + // プロトコルスタックの停止 + net_shutdown(); + + return 0; +} diff --git a/test/test.h b/test/test.h index 0cc02a54..c8fcbafd 100644 --- a/test/test.h +++ b/test/test.h @@ -3,32 +3,26 @@ #include -/* Scope of Internet host loopback address. see https://tools.ietf.org/html/rfc5735 */ +/* Scope of Internet host loopback address. see + * https://tools.ietf.org/html/rfc5735 */ #define LOOPBACK_IP_ADDR "127.0.0.1" #define LOOPBACK_NETMASK "255.0.0.0" #define ETHER_TAP_NAME "tap0" -/* Scope of EUI-48 Documentation Values. see https://tools.ietf.org/html/rfc7042 */ +/* Scope of EUI-48 Documentation Values. see https://tools.ietf.org/html/rfc7042 + */ #define ETHER_TAP_HW_ADDR "00:00:5e:00:53:01" -/* Scope of Documentation Address Blocks (TEST-NET-1). see https://tools.ietf.org/html/rfc5731 */ +/* Scope of Documentation Address Blocks (TEST-NET-1). see + * https://tools.ietf.org/html/rfc5731 */ #define ETHER_TAP_IP_ADDR "192.0.2.2" #define ETHER_TAP_NETMASK "255.255.255.0" #define DEFAULT_GATEWAY "192.0.2.1" const uint8_t test_data[] = { - 0x45, 0x00, 0x00, 0x30, - 0x00, 0x80, 0x00, 0x00, - 0xff, 0x01, 0xbd, 0x4a, - 0x7f, 0x00, 0x00, 0x01, - 0x7f, 0x00, 0x00, 0x01, - 0x08, 0x00, 0x35, 0x64, - 0x00, 0x80, 0x00, 0x01, - 0x31, 0x32, 0x33, 0x34, - 0x35, 0x36, 0x37, 0x38, - 0x39, 0x30, 0x21, 0x40, - 0x23, 0x24, 0x25, 0x5e, - 0x26, 0x2a, 0x28, 0x29 -}; + 0x45, 0x00, 0x00, 0x30, 0x00, 0x80, 0x00, 0x00, 0xff, 0x01, 0xbd, 0x4a, + 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x08, 0x00, 0x35, 0x64, + 0x00, 0x80, 0x00, 0x01, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x30, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5e, 0x26, 0x2a, 0x28, 0x29}; #endif diff --git a/util.c b/util.c index bd7697be..aba7d5ba 100644 --- a/util.c +++ b/util.c @@ -1,13 +1,13 @@ -#include +#include +#include +#include #include #include +#include #include #include -#include -#include -#include -#include #include +#include #include "platform.h" @@ -17,61 +17,61 @@ * Logging */ -int -lprintf(FILE *fp, int level, const char *file, int line, const char *func, const char *fmt, ...) -{ - struct timeval tv; - struct tm tm; - char timestamp[32]; - int n = 0; - va_list ap; - - flockfile(fp); - gettimeofday(&tv, NULL); - strftime(timestamp, sizeof(timestamp), "%T", localtime_r(&tv.tv_sec, &tm)); - n += fprintf(fp, "%s.%03d [%c] %s: ", timestamp, (int)(tv.tv_usec / 1000), level, func); - va_start(ap, fmt); - n += vfprintf(fp, fmt, ap); - va_end(ap); - n += fprintf(fp, " (%s:%d)\n", file, line); - funlockfile(fp); - return n; +int lprintf(FILE *fp, int level, const char *file, int line, const char *func, + const char *fmt, ...) { + struct timeval tv; + struct tm tm; + char timestamp[32]; + int n = 0; + va_list ap; + + flockfile(fp); + gettimeofday(&tv, NULL); + strftime(timestamp, sizeof(timestamp), "%T", localtime_r(&tv.tv_sec, &tm)); + n += fprintf(fp, "%s.%03d [%c] %s: ", timestamp, (int)(tv.tv_usec / 1000), + level, func); + va_start(ap, fmt); + n += vfprintf(fp, fmt, ap); + va_end(ap); + n += fprintf(fp, " (%s:%d)\n", file, line); + funlockfile(fp); + return n; } -void -hexdump(FILE *fp, const void *data, size_t size) -{ - unsigned char *src; - int offset, index; - - flockfile(fp); - src = (unsigned char *)data; - fprintf(fp, "+------+-------------------------------------------------+------------------+\n"); - for(offset = 0; offset < (int)size; offset += 16) { - fprintf(fp, "| %04x | ", offset); - for(index = 0; index < 16; index++) { - if(offset + index < (int)size) { - fprintf(fp, "%02x ", 0xff & src[offset + index]); - } else { - fprintf(fp, " "); - } +void hexdump(FILE *fp, const void *data, size_t size) { + unsigned char *src; + int offset, index; + + flockfile(fp); + src = (unsigned char *)data; + fprintf(fp, "+------+-------------------------------------------------+------" + "------------+\n"); + for (offset = 0; offset < (int)size; offset += 16) { + fprintf(fp, "| %04x | ", offset); + for (index = 0; index < 16; index++) { + if (offset + index < (int)size) { + fprintf(fp, "%02x ", 0xff & src[offset + index]); + } else { + fprintf(fp, " "); + } + } + fprintf(fp, "| "); + for (index = 0; index < 16; index++) { + if (offset + index < (int)size) { + if (isascii(src[offset + index]) && isprint(src[offset + index])) { + fprintf(fp, "%c", src[offset + index]); + } else { + fprintf(fp, "."); } - fprintf(fp, "| "); - for(index = 0; index < 16; index++) { - if(offset + index < (int)size) { - if(isascii(src[offset + index]) && isprint(src[offset + index])) { - fprintf(fp, "%c", src[offset + index]); - } else { - fprintf(fp, "."); - } - } else { - fprintf(fp, " "); - } - } - fprintf(fp, " |\n"); - } - fprintf(fp, "+------+-------------------------------------------------+------------------+\n"); - funlockfile(fp); + } else { + fprintf(fp, " "); + } + } + fprintf(fp, " |\n"); + } + fprintf(fp, "+------+-------------------------------------------------+------" + "------------+\n"); + funlockfile(fp); } /* @@ -79,83 +79,74 @@ hexdump(FILE *fp, const void *data, size_t size) */ struct queue_entry { - struct queue_entry *next; - void *data; + struct queue_entry *next; + void *data; }; -void -queue_init(struct queue_head *queue) -{ - queue->head = NULL; - queue->tail = NULL; - queue->num = 0; +void queue_init(struct queue_head *queue) { + queue->head = NULL; + queue->tail = NULL; + queue->num = 0; } -void * -queue_push(struct queue_head *queue, void *data) -{ - struct queue_entry *entry; - - if (!queue) { - return NULL; - } - entry = memory_alloc(sizeof(*entry)); - if (!entry) { - return NULL; - } - entry->next = NULL; - entry->data = data; - if (queue->tail) { - queue->tail->next = entry; - } - queue->tail = entry; - if (!queue->head) { - queue->head = entry; - } - queue->num++; - return data; +void *queue_push(struct queue_head *queue, void *data) { + struct queue_entry *entry; + + if (!queue) { + return NULL; + } + entry = memory_alloc(sizeof(*entry)); + if (!entry) { + return NULL; + } + entry->next = NULL; + entry->data = data; + if (queue->tail) { + queue->tail->next = entry; + } + queue->tail = entry; + if (!queue->head) { + queue->head = entry; + } + queue->num++; + return data; } -void * -queue_pop(struct queue_head *queue) -{ - struct queue_entry *entry; - void *data; +void *queue_pop(struct queue_head *queue) { + struct queue_entry *entry; + void *data; - if (!queue || !queue->head) { - return NULL; - } - entry = queue->head; - queue->head = entry->next; - if (!queue->head) { - queue->tail = NULL; - } - queue->num--; - data = entry->data; - memory_free(entry); - return data; + if (!queue || !queue->head) { + return NULL; + } + entry = queue->head; + queue->head = entry->next; + if (!queue->head) { + queue->tail = NULL; + } + queue->num--; + data = entry->data; + memory_free(entry); + return data; } -void * -queue_peek(struct queue_head *queue) -{ - if (!queue || !queue->head) { - return NULL; - } - return queue->head->data; +void *queue_peek(struct queue_head *queue) { + if (!queue || !queue->head) { + return NULL; + } + return queue->head->data; } -void -queue_foreach(struct queue_head *queue, void (*func)(void *arg, void *data), void *arg) -{ - struct queue_entry *entry; +void queue_foreach(struct queue_head *queue, + void (*func)(void *arg, void *data), void *arg) { + struct queue_entry *entry; - if (!queue || !func) { - return; - } - for (entry = queue->head; entry; entry = entry->next) { - func(arg, entry->data); - } + if (!queue || !func) { + return; + } + for (entry = queue->head; entry; entry = entry->next) { + func(arg, entry->data); + } } /* @@ -171,80 +162,66 @@ queue_foreach(struct queue_head *queue, void (*func)(void *arg, void *data), voi static int endian; -static int -byteorder(void) { - uint32_t x = 0x00000001; +static int byteorder(void) { + uint32_t x = 0x00000001; - return *(uint8_t *)&x ? __LITTLE_ENDIAN : __BIG_ENDIAN; + return *(uint8_t *)&x ? __LITTLE_ENDIAN : __BIG_ENDIAN; } -static uint16_t -byteswap16(uint16_t v) -{ - return (v & 0x00ff) << 8 | (v & 0xff00 ) >> 8; +static uint16_t byteswap16(uint16_t v) { + return (v & 0x00ff) << 8 | (v & 0xff00) >> 8; } -static uint32_t -byteswap32(uint32_t v) -{ - return (v & 0x000000ff) << 24 | (v & 0x0000ff00) << 8 | (v & 0x00ff0000) >> 8 | (v & 0xff000000) >> 24; +static uint32_t byteswap32(uint32_t v) { + return (v & 0x000000ff) << 24 | (v & 0x0000ff00) << 8 | + (v & 0x00ff0000) >> 8 | (v & 0xff000000) >> 24; } -uint16_t -hton16(uint16_t h) -{ - if (!endian) { - endian = byteorder(); - } - return endian == __LITTLE_ENDIAN ? byteswap16(h) : h; +uint16_t hton16(uint16_t h) { + if (!endian) { + endian = byteorder(); + } + return endian == __LITTLE_ENDIAN ? byteswap16(h) : h; } -uint16_t -ntoh16(uint16_t n) -{ - if (!endian) { - endian = byteorder(); - } - return endian == __LITTLE_ENDIAN ? byteswap16(n) : n; +uint16_t ntoh16(uint16_t n) { + if (!endian) { + endian = byteorder(); + } + return endian == __LITTLE_ENDIAN ? byteswap16(n) : n; } -uint32_t -hton32(uint32_t h) -{ - if (!endian) { - endian = byteorder(); - } - return endian == __LITTLE_ENDIAN ? byteswap32(h) : h; +uint32_t hton32(uint32_t h) { + if (!endian) { + endian = byteorder(); + } + return endian == __LITTLE_ENDIAN ? byteswap32(h) : h; } -uint32_t -ntoh32(uint32_t n) -{ - if (!endian) { - endian = byteorder(); - } - return endian == __LITTLE_ENDIAN ? byteswap32(n) : n; +uint32_t ntoh32(uint32_t n) { + if (!endian) { + endian = byteorder(); + } + return endian == __LITTLE_ENDIAN ? byteswap32(n) : n; } /* * Checksum */ -uint16_t -cksum16(uint16_t *addr, uint16_t count, uint32_t init) -{ - uint32_t sum; - - sum = init; - while (count > 1) { - sum += *(addr++); - count -= 2; - } - if (count > 0) { - sum += *(uint8_t *)addr; - } - while (sum >> 16) { - sum = (sum & 0xffff) + (sum >> 16); - } - return ~(uint16_t)sum; +uint16_t cksum16(uint16_t *addr, uint16_t count, uint32_t init) { + uint32_t sum; + + sum = init; + while (count > 1) { + sum += *(addr++); + count -= 2; + } + if (count > 0) { + sum += *(uint8_t *)addr; + } + while (sum >> 16) { + sum = (sum & 0xffff) + (sum >> 16); + } + return ~(uint16_t)sum; } diff --git a/util.h b/util.h index ea4e491d..c48e1a33 100644 --- a/util.h +++ b/util.h @@ -1,8 +1,8 @@ #ifndef UTIL_H #define UTIL_H -#include #include +#include #include /* @@ -28,34 +28,38 @@ * Time */ -#define timeval_add_usec(x, y) \ - do { \ - (x)->tv_sec += y / 1000000; \ - (x)->tv_usec += y % 1000000; \ - if ((x)->tv_usec >= 1000000) { \ - (x)->tv_sec += 1; \ - (x)->tv_usec -= 1000000; \ - } \ - } while(0); - -#define timespec_add_nsec(x, y) \ - do { \ - (x)->tv_sec += y / 1000000000; \ - (x)->tv_nsec += y % 1000000000; \ - if ((x)->tv_nsec >= 1000000000) { \ - (x)->tv_sec += 1; \ - (x)->tv_nsec -= 1000000000; \ - } \ - } while(0); +#define timeval_add_usec(x, y) \ + do { \ + (x)->tv_sec += y / 1000000; \ + (x)->tv_usec += y % 1000000; \ + if ((x)->tv_usec >= 1000000) { \ + (x)->tv_sec += 1; \ + (x)->tv_usec -= 1000000; \ + } \ + } while (0); + +#define timespec_add_nsec(x, y) \ + do { \ + (x)->tv_sec += y / 1000000000; \ + (x)->tv_nsec += y % 1000000000; \ + if ((x)->tv_nsec >= 1000000000) { \ + (x)->tv_sec += 1; \ + (x)->tv_nsec -= 1000000000; \ + } \ + } while (0); /* * Logging */ -#define errorf(...) lprintf(stderr, 'E', __FILE__, __LINE__, __func__, __VA_ARGS__) -#define warnf(...) lprintf(stderr, 'W', __FILE__, __LINE__, __func__, __VA_ARGS__) -#define infof(...) lprintf(stderr, 'I', __FILE__, __LINE__, __func__, __VA_ARGS__) -#define debugf(...) lprintf(stderr, 'D', __FILE__, __LINE__, __func__, __VA_ARGS__) +#define errorf(...) \ + lprintf(stderr, 'E', __FILE__, __LINE__, __func__, __VA_ARGS__) +#define warnf(...) \ + lprintf(stderr, 'W', __FILE__, __LINE__, __func__, __VA_ARGS__) +#define infof(...) \ + lprintf(stderr, 'I', __FILE__, __LINE__, __func__, __VA_ARGS__) +#define debugf(...) \ + lprintf(stderr, 'D', __FILE__, __LINE__, __func__, __VA_ARGS__) #ifdef HEXDUMP #define debugdump(...) hexdump(stderr, __VA_ARGS__) @@ -63,10 +67,9 @@ #define debugdump(...) #endif -extern int -lprintf(FILE *fp, int level, const char *file, int line, const char *func, const char *fmt, ...); -extern void -hexdump(FILE *fp, const void *data, size_t size); +extern int lprintf(FILE *fp, int level, const char *file, int line, + const char *func, const char *fmt, ...); +extern void hexdump(FILE *fp, const void *data, size_t size); /* * Queue @@ -75,40 +78,31 @@ hexdump(FILE *fp, const void *data, size_t size); struct queue_entry; struct queue_head { - struct queue_entry *head; - struct queue_entry *tail; - unsigned int num; + struct queue_entry *head; + struct queue_entry *tail; + unsigned int num; }; -extern void -queue_init(struct queue_head *queue); -extern void * -queue_push(struct queue_head *queue, void *data); -extern void * -queue_pop(struct queue_head *queue); -extern void * -queue_peek(struct queue_head *queue); -extern void -queue_foreach(struct queue_head *queue, void (*func)(void *arg, void *data), void *arg); +extern void queue_init(struct queue_head *queue); +extern void *queue_push(struct queue_head *queue, void *data); +extern void *queue_pop(struct queue_head *queue); +extern void *queue_peek(struct queue_head *queue); +extern void queue_foreach(struct queue_head *queue, + void (*func)(void *arg, void *data), void *arg); /* * Byteorder */ -extern uint16_t -hton16(uint16_t h); -extern uint16_t -ntoh16(uint16_t n); -extern uint32_t -hton32(uint32_t h); -extern uint32_t -ntoh32(uint32_t n); +extern uint16_t hton16(uint16_t h); +extern uint16_t ntoh16(uint16_t n); +extern uint32_t hton32(uint32_t h); +extern uint32_t ntoh32(uint32_t n); /* * Checksum */ -extern uint16_t -cksum16(uint16_t *addr, uint16_t count, uint32_t init); +extern uint16_t cksum16(uint16_t *addr, uint16_t count, uint32_t init); #endif