コグノスケ


link 未来から過去へ表示  link 過去から未来へ表示(*)

link もっと前
2018年7月6日 >>> 2018年8月5日
link もっと後

2018年7月6日

AndroidとMPEG2-TSその2

その1その2その3その4

昨日の続き。スクランブルの掛かったストリームはmParser->mCasManagerに任せていました。mCasManagerはATSParser::CasManagerでしたので、実装を見てみます。

CasManager

//frameworks/av/media/libstagefright/mpeg2ts/CasManager.cpp

bool ATSParser::CasManager::parsePID(ABitReader *br, unsigned pid) {
    ssize_t index = mCAPidToSessionIdMap.indexOfKey(pid);
    if (index < 0) {
        return false;
    }
    hidl_vec<uint8_t> ecm;
    ecm.setToExternal((uint8_t*)br->data(), br->numBitsLeft() / 8);
    auto returnStatus = mICas->processEcm(mCAPidToSessionIdMap[index], ecm); //★★★★processEcm()
    if (!returnStatus.isOk() || (Status) returnStatus != Status::OK) {
        ALOGE("Failed to process ECM: trans=%s, status=%d",
                returnStatus.description().c_str(), (Status) returnStatus);
    }
    return true; // handled
}

謎のmICasがどこから来るかは、後で調べるとして、関数名processEcm() で探してみると、HALの方にコードがあります。

processEcm() の実装

//hardware/interfaces/cas/1.0/default/CasImpl.cpp

Return<Status> CasImpl::processEcm(
        const HidlCasSessionId &sessionId, const HidlCasData& ecm) {
    ALOGV("%s: sessionId=%s", __FUNCTION__,
            sessionIdToString(sessionId).string());
    std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder); //★★★★CasPlugin
    if (holder.get() == nullptr) {
        return toStatus(INVALID_OPERATION);
    }

    return toStatus(holder->processEcm(sessionId, ecm));
}

想像するにCasPluginというクラスを派生させて処理を実装するのでしょう。探してみるとframeworks/av/drm/mediacas/plugins以下にclearkeyとmockという実装があります。

ClearKeyの実装

//frameworks/av/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h

class ClearKeyCasPlugin : public CasPlugin {
...

//frameworks/av/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp

status_t ClearKeyCasPlugin::processEcm(
        const CasSessionId &sessionId, const CasEcm& ecm) {
    ALOGV("processEcm: sessionId=%s", sessionIdToString(sessionId).string());
    sp<ClearKeyCasSession> session =
            ClearKeySessionLibrary::get()->findSession(sessionId);
    if (session == NULL) {
        return ERROR_CAS_SESSION_NOT_OPENED;
    }

    Mutex::Autolock lock(mKeyFetcherLock);

    return session->updateECM(mKeyFetcher.get(), (void*)ecm.data(), ecm.size()); //★★★★mKeyFetcher
}

status_t ClearKeyCasSession::updateECM(
        KeyFetcher *keyFetcher, void *ecm, size_t size) {
//...
    uint64_t asset_id;
    std::vector<KeyFetcher::KeyInfo> keys;
    status_t err = keyFetcher->ObtainKey(mEcmBuffer, &asset_id, &keys); //★★★★keyFetcher
    if (err != OK) {
        ALOGE("updateECM: failed to obtain key (err=%d)", err);
        return err;
    }

    ALOGV("updateECM: %zu key(s) found", keys.size());
    for (size_t keyIndex = 0; keyIndex < keys.size(); keyIndex++) {
        String8 str;

        const sp<ABuffer>& keyBytes = keys[keyIndex].key_bytes;
        CHECK(keyBytes->size() == kUserKeyLength);

        int result = AES_set_decrypt_key(
                reinterpret_cast<const uint8_t*>(keyBytes->data()),
                AES_BLOCK_SIZE * 8, &mKeyInfo[keyIndex].contentKey); //★★★★libsslの関数に渡して鍵を生成している?ようだ
//...


//frameworks/av/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp

status_t ClearKeyFetcher::ObtainKey(const sp<ABuffer>& buffer,
        uint64_t* asset_id, std::vector<KeyInfo>* keys) {
//...

引数に渡しているmKeyFetcher(とget() が返すkeyFetcherも同様に)はKeyFetcher型のポインタでした。KeyFetcherを継承したClearKeyFetcher型のオブジェクトが格納されていました。

ClearKeyの仕組みは詳しく知りませんが、ClearKeyCasSession::updateECM() でAESの復号などをしていることと、AESの復号鍵はClearKeyFetcher::ObtainKey() がECMを読んで復号鍵を取得してくれるように見えました。

AndroidでECMの解読を行っている箇所が見つけられました。エレメンタリストリームのデスクランブルはどこで行っているのでしょうね…??

編集者:すずき(2018/07/17 22:45)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2018年7月7日

AndroidとMPEG2-TSその3

その1その2その3その4

デスクランブル処理はどこにあるんでしょう??それらしい処理を辿ってみましたが、本当にこの処理が動くのか、何を切っ掛けにデスクランブル処理が働き始めるのか、動かしてみないとわからなさそうです。うーん……。

デスクランブル処理Java側(たぶん)

//frameworks/base/media/java/android/media/MediaDescrambler.java

    public final int descramble(
            @NonNull ByteBuffer srcBuf, @NonNull ByteBuffer dstBuf,
            @NonNull MediaCodec.CryptoInfo cryptoInfo) {
//...
        try {
            return native_descramble(
                    cryptoInfo.key[0],
                    cryptoInfo.numSubSamples,
                    cryptoInfo.numBytesOfClearData,
                    cryptoInfo.numBytesOfEncryptedData,
                    srcBuf, srcBuf.position(), srcBuf.limit(),
                    dstBuf, dstBuf.position(), dstBuf.limit());
        } catch (ServiceSpecificException e) {
            MediaCasStateException.throwExceptionIfNeeded(e.errorCode, e.getMessage());
        } catch (RemoteException e) {
            cleanupAndRethrowIllegalState();
        }
        return -1;
    }

ここで呼び出しているnative_descramble() はnativeであると宣言されています。つまりJNI経由で呼び出します。メディアフレームワーク(android.media)のJNI実装はframeworks/base/media/jniに配置されているようです。

デスクランブル処理JNI側

//frameworks/base/media/jni/android_media_MediaDescrambler.cpp

static jint android_media_MediaDescrambler_native_descramble(
        JNIEnv *env, jobject thiz, jbyte key, jint numSubSamples,
        jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
        jobject srcBuf, jint srcOffset, jint srcLimit,
        jobject dstBuf, jint dstOffset, jint dstLimit) {
    sp<JDescrambler> descrambler = getDescrambler(env, thiz); //★★★★descramblerはJDescrambler型
    if (descrambler == NULL) {
        jniThrowException(env, "java/lang/IllegalStateException",
                "Invalid descrambler object!");
        return -1;
    }

    hidl_vec<SubSample> subSamples;
    ssize_t totalLength = getSubSampleInfo(
            env, numSubSamples, numBytesOfClearDataObj,
            numBytesOfEncryptedDataObj, &subSamples);
    if (totalLength < 0) {
        jniThrowException(env, "java/lang/IllegalArgumentException",
                "Invalid subsample info!");
        return -1;
    }
//...
    err = descrambler->descramble(
            key, totalLength, subSamples,
            srcPtr, srcOffset, dstPtr, dstOffset,
            &status, &bytesWritten, &detailedError); //★★★★
//...


status_t JDescrambler::descramble(
        jbyte key,
        ssize_t totalLength,
        const hidl_vec<SubSample>& subSamples,
        const void *srcPtr,
        jint srcOffset,
        void *dstPtr,
        jint dstOffset,
        Status *status,
        uint32_t *bytesWritten,
        hidl_string *detailedError) {
//...
    auto err = mDescrambler->descramble(
            (ScramblingControl) key,
            subSamples,
            mDescramblerSrcBuffer,
            0,
            dstBuffer,
            0,
            [&status, &bytesWritten, &detailedError] (
                    Status _status, uint32_t _bytesWritten,
                    const hidl_string& _detailedError) {
                *status = _status;
                *bytesWritten = _bytesWritten;
                *detailedError = _detailedError;
            }); //★★★★
//...

ここで出てくるmDescramblerはIDescrambler型のポインタです。パッと見では、何がセットされているのか良くわかりませんが、このインタフェースを実装しているのは下記しかなさそうです。

デスクランブラ

//hardware/interfaces/cas/1.0/default/DescramblerImpl.h

class DescramblerImpl : public IDescrambler {
//...

//hardware/interfaces/cas/1.0/default/DescramblerImpl.cpp

Return<void> DescramblerImpl::descramble(
        ScramblingControl scramblingControl,
        const hidl_vec<SubSample>& subSamples,
        const SharedBuffer& srcBuffer,
        uint64_t srcOffset,
        const DestinationBuffer& dstBuffer,
        uint64_t dstOffset,
        descramble_cb _hidl_cb) {
    ALOGV("%s", __FUNCTION__);

    // Get a local copy of the shared_ptr for the plugin. Note that before
    // calling the HIDL callback, this shared_ptr must be manually reset,
    // since the client side could proceed as soon as the callback is called
    // without waiting for this method to go out of scope.
    std::shared_ptr<DescramblerPlugin> holder = std::atomic_load(&mPluginHolder); //★★★★holder = mPluginHolder
    if (holder.get() == nullptr) {
        _hidl_cb(toStatus(INVALID_OPERATION), 0, NULL);
        return Void();
    }

//...

    // Casting hidl SubSample to DescramblerPlugin::SubSample, but need
    // to ensure structs are actually idential

    int32_t result = holder->descramble(
            dstBuffer.type != BufferType::SHARED_MEMORY,
            (DescramblerPlugin::ScramblingControl)scramblingControl,
            subSamples.size(),
            (DescramblerPlugin::SubSample*)subSamples.data(),
            srcPtr,
            srcOffset,
            dstPtr,
            dstOffset,
            NULL);
//...

このmPluginHolderはDescramblerPlugin型です。DescramblerImplが生成される時に設定されます。DescramblerImplを生成するのはMediaCasService::createDescrambler() のみ?のように見えます。

次に出てくるholderにはDescramblerPluginのポインタが入ります。Pluginの実装を探してみるとClearKeyにそれらしき処理があります。

Descrambler pluginの実装

//frameworks/av/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp

ssize_t ClearKeyDescramblerPlugin::descramble(
        bool secure,
        ScramblingControl scramblingControl,
        size_t numSubSamples,
        const SubSample *subSamples,
        const void *srcPtr,
        int32_t srcOffset,
        void *dstPtr,
        int32_t dstOffset,
        AString *errorDetailMsg) {

    ALOGV("descramble: secure=%d, sctrl=%d, subSamples=%s, "
            "srcPtr=%p, dstPtr=%p, srcOffset=%d, dstOffset=%d",
          (int)secure, (int)scramblingControl,
          subSamplesToString(subSamples, numSubSamples).string(),
          srcPtr, dstPtr, srcOffset, dstOffset);

    if (mCASSession == NULL) {
        ALOGE("Uninitialized CAS session!");
        return ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED;
    }

    return mCASSession->decrypt(
            secure, scramblingControl,
            numSubSamples, subSamples,
            (uint8_t*)srcPtr + srcOffset,
            dstPtr == NULL ? NULL : ((uint8_t*)dstPtr + dstOffset),
            errorDetailMsg);
}

ここで出てくるmCASSessionはClearKeyCasSessionを指すようです。setMediaCasSession() にて設定されています。これは後で追ってみます。

ClearKeyによるデスクランブル

//av/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp

// Decryption of a set of sub-samples
ssize_t ClearKeyCasSession::decrypt(
        bool secure, DescramblerPlugin::ScramblingControl scramblingControl,
        size_t numSubSamples, const DescramblerPlugin::SubSample *subSamples,
        const void *srcPtr, void *dstPtr, AString * /* errorDetailMsg */) {
        return ERROR_CAS_CANNOT_HANDLE;
    

    AES_KEY contentKey;

        // Hold lock to get the key only to avoid contention for decryption
        Mutex::Autolock _lock(mKeyLock);

        int32_t keyIndex = (scramblingControl & 1);
            ALOGE("decrypt: key %d is invalid", keyIndex);
            return ERROR_CAS_DECRYPT;
        
        contentKey = mKeyInfo[keyIndex].contentKey; //★★★★ClearKeyCasSession::updateECM() で設定する鍵だと思う
    
//...
        // Don't decrypt if len < AES_BLOCK_SIZE.
        // The last chunk shorter than AES_BLOCK_SIZE is not encrypted.
        if (scramblingControl != DescramblerPlugin::kScrambling_Unscrambled
                && subSamples[i].mNumBytesOfEncryptedData >= AES_BLOCK_SIZE) {
            err = decryptPayload(
                    contentKey,
                    numBytesinSubSample,
                    subSamples[i].mNumBytesOfClearData,
                    (char *)dst);
        }
//...

// Decryption of a TS payload
status_t ClearKeyCasSession::decryptPayload(
        const AES_KEY& key, size_t length, size_t offset, char* buffer) const {
    CHECK(buffer);

    // Invariant: only call decryptPayload with TS packets with at least 16
    // bytes of payload (AES_BLOCK_SIZE).

    CHECK(length >= offset + AES_BLOCK_SIZE);

    return TpBlockCtsDecrypt(key, length - offset, buffer + offset);
}

// AES-128 CBC-CTS decrypt optimized for Transport Packets. |key| is the AES
// key (odd key or even key), |length| is the data size, and |buffer| is the
// ciphertext to be decrypted in place.
status_t TpBlockCtsDecrypt(const AES_KEY& key, size_t length, char* buffer) {
    CHECK(buffer);
//...

CBCモードでAES暗号を復号しているようです。

まとめると、MediaDescrambler(ドキュメントはここ)のdescrambler() によって、スクランブルされたデータのデスクランブルができそう、ということがわかりました。

ドキュメントにそう書いてあるし、当たり前なんですけどね。新たにデスクランブラを足そうと思う人は、中身も知らないと足せないです。

編集者:すずき(2018/07/17 22:46)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2018年7月8日

AndroidとMPEG2-TSその4

その1その2その3その4

もはや自分以外の誰得の内容なのか、わかりませんが、気にせず書きます。

その3にて「DescramblerImplを生成するのはMediaCasService::createDescrambler() のみ?のように見えます。」と書きました。この関数に至る経路がわかると、デスクランブラがどの暗号系を選択するのか?いつ選択するのか?などがわかるようになります。

で、今回はcreateDescrambler() が呼ばれる経路を見つけたのでメモします。しかしながらExtractorのsetMediaCas() を呼び出すのは誰なのか?まだ謎のままなので、いまいちスッキリしませんけども。

ExtractorのsetMediaCas()

//frameworks/av/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp

status_t MPEG2TSExtractor::setMediaCas(const HInterfaceToken &casToken) {
    HalToken halToken;
    halToken.setToExternal((uint8_t*)casToken.data(), casToken.size());
    sp<ICas> cas = ICas::castFrom(retrieveHalInterface(halToken));
    ALOGD("setMediaCas: %p", cas.get());

    status_t err = mParser->setMediaCas(cas); //★★★★mParser
    if (err == OK) {
        ALOGI("All tracks now have descramblers");
        init();
    }
    return err;
}

ここで出てくるmParserの型はATSParserでしたので、ATSParserの実装を見てみます。

ATSParser setMediaCas()

//frameworks/av/media/libstagefright/mpeg2ts/ATSParser.cpp

status_t ATSParser::setMediaCas(const sp<ICas> &cas) {
    status_t err = mCasManager->setMediaCas(cas); //★★★★mCasManager
    if (err != OK) {
        return err;
    }
    for (size_t i = 0; i < mPrograms.size(); ++i) {
        mPrograms.editItemAt(i)->updateCasSessions();
    }
    return OK;
}

ここで出てくるmCasManagerの型はATSParser::CasManagerでした。CasManagerを見てみましょう。

CasManager setMediaCas()

//frameworks/av/media/libstagefright/mpeg2ts/CasManager.cpp

status_t ATSParser::CasManager::setMediaCas(const sp<ICas> &cas) {
    if (cas == NULL) {
        ALOGE("setMediaCas: received NULL object");
        return BAD_VALUE;
    }
    if (mICas != NULL) {
        ALOGW("setMediaCas: already set");
        return ALREADY_EXISTS;
    }
    for (size_t index = 0; index < mProgramCasMap.size(); index++) {
        status_t err;
        if ((err = mProgramCasMap.editValueAt(
                index)->setMediaCas(cas, mCAPidToSessionIdMap)) != OK) { //★★★★mProgramCasMap
            return err;
        }
    }
    mICas = cas; //★★★★mICas
    return OK;
}

Extractorから渡されてきたcasは、CasManagerのメンバ変数mICasに保存されるようです。ちなみにこのmICasはその2のCasManager::parsePID() にて、mICas->processEcm(mCAPidToSessionIdMap[index], ecm); の呼び出し時に出てきました。ここで保存されていたんですね。

話を戻してmProgramCasMapはunsignedがキー、ProgramCasManagerのポインタが値のKeyedVectorです。

ProgramCasManager setMediaCas()

status_t ATSParser::CasManager::ProgramCasManager::setMediaCas(
        const sp<ICas> &cas, PidToSessionMap &sessionMap) {
    if (mHasProgramCas) {
        return initSession(cas, sessionMap, &mProgramCas); //★★★★
    }
    // TODO: share session among streams that has identical CA_descriptors.
    // For now, we open one session for each stream that has CA_descriptor.
    for (size_t index = 0; index < mStreamPidToCasMap.size(); index++) {
        status_t err = initSession(
                cas, sessionMap, &mStreamPidToCasMap.editValueAt(index)); //★★★★
        if (err != OK) {
            return err;
        }
    }
    return OK;
}

status_t ATSParser::CasManager::ProgramCasManager::initSession(
         const sp<ICas>& cas,
         PidToSessionMap &sessionMap,
         CasSession *session) {
    sp<IMediaCasService> casService = IMediaCasService::getService("default"); //★★★★IMediaCasService型
    if (casService == NULL) {
        ALOGE("Cannot obtain IMediaCasService");
        return NO_INIT;
    }
//...
    returnDescrambler = casService->createDescrambler(descriptor.mSystemID); //★★★★createDescrambler()
    if (!returnDescrambler.isOk()) {
        ALOGE("Failed to create descrambler: trans=%s",
                returnDescrambler.description().c_str());
        goto l_fail;
    }
    descramblerBase = (sp<IDescramblerBase>) returnDescrambler;
    if (descramblerBase == NULL) {
        ALOGE("Failed to create descrambler: null ptr");
        goto l_fail;
    }

やっとcreateDescrambler() が出てきました。casServiceはIMediaCasService型ですが、このインタフェースを実装しているクラスは1つしかなさそうです。

MediaCasService setMediaCas()

//hardware/interfaces/cas/1.0/default/MediaCasService.h

class MediaCasService : public IMediaCasService {
//...


//hardware/interfaces/cas/1.0/default/MediaCasService.cpp

Return<sp<IDescramblerBase>> MediaCasService::createDescrambler(int32_t CA_system_id) {

    ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);

    sp<IDescrambler> result;

    DescramblerFactory *factory;
    sp<SharedLibrary> library;
    if (mDescramblerLoader.findFactoryForScheme(
            CA_system_id, &library, &factory)) { //★★★★DescramblerPluginを探す処理はこの辺にありそう
        DescramblerPlugin *plugin = NULL;
        if (factory->createPlugin(CA_system_id, &plugin) == OK
                && plugin != NULL) {
            result = new DescramblerImpl(library, plugin); //★★★★DescramblerImplを生成している箇所があった
        }
    }

    return result;
}

ここでゴールのようです。まとめるとExtractorのsetMediaCas() を呼ぶと、

  • ATSParser::CasManager::parsePID() が呼び出しているmICas->processEcm() のmICasが設定される。
    つまりECMの処理方法が決まる
  • DescramblerImpl::mPluginHolderが設定される。
    つまりデスクランブルの処理方法が決まる
編集者:すずき(2018/07/17 22:44)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2018年7月15日

AArch64向けLinux開発環境の構築 その1

目次: GCC

目次: Linux

AArch64(64ビットARM)向けのLinux開発環境を構築しました。超有名ツールの組み合わせだし、簡単だろうと思っていたのですが、意外とハマって1日掛かってしまったのでメモしておきます。使用するツールは下記の通りです。

  • クロスコンパイラ: crosstool-NG
  • カーネル: linux-next
  • ルートファイルシステム: buildroot
  • エミュレータ: qemu

クロスコンパイラ

クロスコンパイラを生成するためcrosstool-NG(GitHubへのリンク)を使います。crosstool-NGはARM向け以外にもクロスコンパイラやツールチェインの構築が簡単にできて便利です。

私の環境(Debian Testing)だとlibtool-binパッケージがインストールされていなくてハマったので、インストールしておくと良いかもしれません。

crosstool-NGコード取得、セットアップ
$ git clone https://github.com/crosstool-ng/crosstool-ng
$ cd crosstool-ng

$ ./bootstrap
INFO  :: *** Generating package version descriptions
INFO  :: Master packages: android-ndk autoconf automake avr-libc binutils cloog duma elf2flt expat gcc gdb gettext glibc-ports glibc gmp isl libelf libiconv libtool linux ltrace m4 make mingw-w64 mpc mpfr musl ncurses newlib strace uClibc zlib
INFO  :: Generating 'config/versions/android-ndk.in'
INFO  :: Generating 'config/versions/autoconf.in'
...(snip)...
INFO  :: Generating comp_libs.in (menu)
INFO  :: *** Gathering the list of data files to install
INFO  :: *** Running autoreconf
INFO  :: *** Done!

$ ./configure --enable-local
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
...(snip)...
config.status: creating config.h
config.status: config.h is unchanged
config.status: executing depfiles commands

$ make
/usr/bin/make  all-recursive
make[1]: Entering directory '/home/katsuhiro/share/projects/oss/crosstool-ng'
Making all in kconfig
make[2]: Entering directory '/home/katsuhiro/share/projects/oss/crosstool-ng/kconfig'
bison -y -l -b zconf -p zconf  -ozconf.c zconf.y
...(snip)...
/bin/mkdir -p docs && ( /bin/sed -e 's,[@]docdir[@],/usr/local/share/doc/crosstool-ng,g' -e 's,[@]pkgdatadir[@],/usr/local/share/crosstool-ng,g' -e 's,[@]pkglibexecdir[@],/usr/local/libexec/crosstool-ng,g' -e 's,[@]progname[@],'`echo ct-ng | sed 's,x,x,'`',g' | /bin/bash config.status --file=- ) < docs/ct-ng.1.in >docs/ct-ng.1-t && mv -f docs/ct-ng.1-t docs/ct-ng.1
make[2]: Leaving directory '/home/katsuhiro/share/projects/oss/crosstool-ng'
make[1]: Leaving directory '/home/katsuhiro/share/projects/oss/crosstool-ng'

カレントディレクトリにct-ngという名前のファイルが生成されます。通常はクロスコンパイラをインストールして使いますが、私はインストールしないで欲しい(適宜入れ替えたいから)ので、--enable-localオプションを付けています。

crosstool-NGビルド
$ ./ct-ng menuconfig
- Target options  --->
    Target Architecture (alpha)  --->
      armに変更する

    Bitness: (32-bit)  --->
      64-bitに変更する

- Operating System  --->
    Target OS (bare-metal)  --->
      linuxに変更する

- C compiler  --->
    C++ (NEW) を選択する

$ ./ct-ng build
[00:34] /

ビルド中は多少メッセージも出ますが、基本的に経過時間と棒がくるくる回るだけです。ログが見たい方は、ct-ngと同じディレクトリにあるbuild.logをtail -fなどで表示すると良いでしょう。

マシン性能によりますが、./ct-ng buildによるクロスコンパイラのビルドは1時間くらい掛かると思います。ビルドが終わると、ホームディレクトリにx-toolsというディレクトリが作られていると思います。AArch64用のクロスコンパイラ(gcc 8)をビルドした場合、x-tools以下は下記のようになっているはずです。

crosstool-NG生成物
$ cd ~/x-tools

$ ls
aarch64-unknown-linux-gnu

$ ls aarch64-unknown-linux-gnu
aarch64-unknown-linux-gnu  bin  build.log.bz2  include  lib  libexec  share

$ ls aarch64-unknown-linux-gnu/bin
aarch64-unknown-linux-gnu-addr2line     aarch64-unknown-linux-gnu-gcov-dump
aarch64-unknown-linux-gnu-ar            aarch64-unknown-linux-gnu-gcov-tool
aarch64-unknown-linux-gnu-as            aarch64-unknown-linux-gnu-gfortran
aarch64-unknown-linux-gnu-c++           aarch64-unknown-linux-gnu-gprof
aarch64-unknown-linux-gnu-c++filt       aarch64-unknown-linux-gnu-ld
aarch64-unknown-linux-gnu-cc            aarch64-unknown-linux-gnu-ld.bfd
aarch64-unknown-linux-gnu-cpp           aarch64-unknown-linux-gnu-ld.gold
aarch64-unknown-linux-gnu-ct-ng.config  aarch64-unknown-linux-gnu-ldd
aarch64-unknown-linux-gnu-dwp           aarch64-unknown-linux-gnu-nm
aarch64-unknown-linux-gnu-elfedit       aarch64-unknown-linux-gnu-objcopy
aarch64-unknown-linux-gnu-g++           aarch64-unknown-linux-gnu-objdump
aarch64-unknown-linux-gnu-gcc           aarch64-unknown-linux-gnu-populate
aarch64-unknown-linux-gnu-gcc-7.3.0     aarch64-unknown-linux-gnu-ranlib
aarch64-unknown-linux-gnu-gcc-ar        aarch64-unknown-linux-gnu-readelf
aarch64-unknown-linux-gnu-gcc-nm        aarch64-unknown-linux-gnu-size
aarch64-unknown-linux-gnu-gcc-ranlib    aarch64-unknown-linux-gnu-strings
aarch64-unknown-linux-gnu-gcov          aarch64-unknown-linux-gnu-strip

クロスコンパイラは~/x-tools/aarch64-unknown-linux-gnu/bin以下にあります。今後、このクロスコンパイラを使います。

カーネル

カーネルはLinuxの開発版linux-nextを使います(Gitリポジトリへのリンク)。StableカーネルやLTSカーネルも同じ手順で良いと思いますが、古いカーネルをビルドするときは、crosstool-NGでgcc 7かgcc 6を選択したほうが良いかもしれません(ビルド時に警告が出てくると邪魔なので…)。

linux-nextコード取得、セットアップ、ビルド
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
$ cd linux-next

$ export ARCH=arm64
$ export CROSS_COMPILE=~/x-tools/aarch64-unknown-linux-gnu/bin/aarch64-unknown-linux-gnu-

$ make defconfig

$ make all
scripts/kconfig/conf  --syncconfig Kconfig
  WRAP    arch/arm64/include/generated/uapi/asm/ioctl.h
  WRAP    arch/arm64/include/generated/uapi/asm/errno.h
  WRAP    arch/arm64/include/generated/uapi/asm/ioctls.h
...(snip)...
  LD [M]  sound/soc/generic/snd-soc-simple-card-utils.ko
  LD [M]  sound/soc/generic/snd-soc-simple-card.ko
  LD [M]  sound/soc/sh/rcar/snd-soc-rcar.ko

マシン性能によりますが、カーネルのビルドは数十分くらい掛かると思います。AArch64用のカーネルをビルドした場合、arch/arm64/boot以下は下記のようになっているはずです。

linux-next生成物
$ cd arch/arm64/boot

$ ls
Image  Image.gz  Makefile  dts  install.sh

ビルドが終わると、arch/arm64/boot以下にImageという名前のファイルが作られていると思います。このイメージファイルを後で使います。

編集者:すずき(2024/07/16 17:51)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2018年7月16日

AArch64向けLinux開発環境の構築 その2

目次: GCC

目次: Linux

昨日の続きです。AArch64(64ビットARM)向けのLinux開発環境を構築しました。使用するツールは下記の通りです。

  • クロスコンパイラ: crosstool-NG
  • カーネル: linux-next
  • ルートファイルシステム: buildroot
  • エミュレータ: qemu

カーネルのビルドまで終わりましたので、次はルートファイルシステムです。

ルートファイルシステム

ルートファイルシステムの構築にはbuildrootを使います(Gitリポジトリへのリンク)。

buildrootコード取得、セットアップ、ビルド
$ git clone https://git.buildroot.net/buildroot
$ cd buildroot

$ make menuconfig
- Target options  --->
    Target Architecture (i386)  --->
      AArch64 (little endian) に変更する

- Toolchain  --->
    Toolchain type (Buildroot toolchain)  --->
      External toolchainに変更する

    Toolchain (Linaro AArch64 2018.05)  --->
      Custom toolchainに変更する

    () Toolchain path (NEW)
      /home/username/x-tools/aarch64-unknown-linux-gnuに変更する(チルダ ~ は使えないので、注意)

    ($(ARCH)-linux) Toolchain prefix
      aarch64-unknown-linux-gnuに変更する

    External toolchain gcc version (4.3.x)  --->
      8.xに変更する(crosstool-NGの設定と合わせる)

    External toolchain kernel headers series (2.6.x)  --->
      4.16.xに変更する(crosstool-NGの設定と合わせる)

    External toolchain C library (uClibc/uClibc-ng)  --->
      glibc/eglibcに変更する(crosstool-NGの設定と合わせる)

    Toolchain has C++ support? (NEW)
      設定する

- Filesystem images  --->
    cpio the root filesystem (for use as an initial RAM filesystem)
      設定する

$ make
/usr/bin/make -j1 O=/home/katsuhiro/share/projects/oss/buildroot/output HOSTCC="/usr/lib/ccache/gcc" HOSTCXX="/usr/lib/ccache/g++" silentoldconfig
...(snip)...

マシン性能によりますが、ほぼbusyboxしかビルドしませんので、カーネルよりは短い時間で終わるはずです。生成されたファイルはoutputディレクトリに集められています。output以下は下記のようになっているはずです。

buildroot生成物
$ ls output
build  host  images  staging  target

$ ls output/images/
rootfs.cpio  rootfs.tar

ディレクトリには2つファイルがありますが、cpioフォーマットの方(rootfs.cpio)を使います。

エミュレータ

エミュレータはqemu(Gitリポジトリへのリンク)を使います。qemuのビルドは後回しにして、とりあえず今までビルドしてきたカーネル+ルートファイルシステムを実行してみます。

Debianの場合はapt-get install qemu-systemでインストール可能です。

qemu実行
$ qemu-system-aarch64 -machine virt -cpu cortex-a57 -kernel linux-next/arch/arm64/boot/Image -initrd buildroot/output/images/rootfs.cpio -serial stdio
[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x411fd070]
[    0.000000] Linux version 4.18.0-rc4-next-20180713 (katsuhiro@blackbird) (gcc version 8.1.0 (crosstool-NG 1.23.0.418-d590)) #1 SMP PREEMPT Mon Jul 16 14:38:51 JST 2018
[    0.000000] Machine model: linux,dummy-virt
[    0.000000] efi: Getting EFI parameters from FDT:
[    0.000000] efi: UEFI not found.
...(snip)...
[    3.409356] ALSA device list:
[    3.410631]   No soundcards found.
[    3.419281] uart-pl011 9000000.pl011: no DMA platform data
[    3.534574] Freeing unused kernel memory: 1344K
Starting logging: OK
Initializing random number generator... [    5.446846] random: dd: uninitialized urandom read (512 bytes read)
done.
Starting network: OK

Welcome to Buildroot
buildroot login: root

# uname -a
Linux buildroot 4.18.0-rc4-next-20180713 #1 SMP PREEMPT Mon Jul 16 14:38:51 JST 2018 aarch64 GNU/Linux

オプションでハマったのは -cpu cortex-a57です。省略するとAArch64に対応していないCPUがデフォルトで選ばれるようで、全く動かないです。明示的にARMv8のCPUを指定してください。また -serial stdioを付けないと、シリアルが出力されません。

このときinitramfsを指定し忘れると、起動はしますが下記のようなメッセージを表示して停止します。

Linuxがpanicで止まる
$ qemu-system-aarch64 -machine virt -cpu cortex-a57 -kernel linux-next/arch/arm64/boot/Image -serial stdio
[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x411fd070]
[    0.000000] Linux version 4.18.0-rc4-next-20180713 (katsuhiro@blackbird) (gcc version 8.1.0 (crosstool-NG 1.23.0.418-d590)) #1 SMP PREEMPT Mon Jul 16 14:38:51 JST 2018
[    0.000000] Machine model: linux,dummy-virt
...(snip)...
[    2.777433] ALSA device list:
[    2.777760]   No soundcards found.
[    2.785662] uart-pl011 9000000.pl011: no DMA platform data
[    2.793530] VFS: Cannot open root device "(null)" or unknown-block(0,0): error -6
[    2.794033] Please append a correct "root=" boot option; here are the available partitions:
[    2.794757] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
[    2.796280] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.18.0-rc4-next-20180713 #1
[    2.796760] Hardware name: linux,dummy-virt (DT)
[    2.797221] Call trace:
[    2.797472]  dump_backtrace+0x0/0x148
[    2.797837]  show_stack+0x14/0x20
[    2.798087]  dump_stack+0x90/0xb4
[    2.798352]  panic+0x120/0x27c
[    2.798577]  mount_block_root+0x1a0/0x250
[    2.798875]  mount_root+0x11c/0x148
[    2.799126]  prepare_namespace+0x128/0x16c
[    2.799423]  kernel_init_freeable+0x208/0x228
[    2.799732]  kernel_init+0x10/0x100
[    2.800005]  ret_from_fork+0x10/0x18
[    2.800694] Kernel Offset: disabled
[    2.801107] CPU features: 0x21806082
[    2.801400] Memory Limit: none
[    2.802136] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---

このようにmount_rootでpanicする場合は、qemuの -initrdオプションか、指定しているファイルをご確認ください。

編集者:すずき(2024/07/16 17:51)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2018年7月17日

エアコンが臭い

「エアコンの嫌なニオイが完全に消えた」 "窓全開、16度で1時間つけっぱなし" で本当にニオイが取れる理由 - ねとらぼ を読んで。

こんなの嘘だろ…、と思ってうちの8年モノ(2010年製)のエアコンにやってみたら、素晴らしい効き目でした。運転開始から冷風がでるまでの、放置した雑巾のような臭いが、完全に消えました。

この効果はいつまで持つのか気になるので、また臭くなった時に備えて、いつ浄化したか思い出せるようにしておきます。

騒音に注意

ちなみにエアコンを16度で全開運転すると、壊れるんじゃないかと思うくらい、室外機が唸り始めて、非常にうるさいです。

暑いけれど、昼にやった方が良いと思います。夜やると近所迷惑です。

メモ: 技術系の話はFacebookから転記しておくことにした。

編集者:すずき(2018/07/17 22:53)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2018年7月21日

Bluetooth UART変換

UARTをBluetoothに変換してくれるHC-06(モジュールの販売サイト、仕様書も置いてある)モジュール搭載ボードを買いました。技適マークが無いように見えます、これ日本で使ったらダメな奴かな…?

電源は5Vで、UARTは3.3Vです。ArduinoとかRaspberry Pi用のUSB - UART変換ケーブルを使えば、電源からUARTまで全ての線が揃っているはずです。Raspberry Piに繋いでも動きます。

私が購入したボード(HiLetgo製ZS-040)の場合、電源を入れるとLEDが点滅し始めます。この状態でBluetoothデバイスの検索を行うとHC-06という名前(変更可)のデバイスが見つかりますので、ペアリングします。PINは1234です(変更可)。

ペアリングするとCOMが2つ増えると思います。なぜ2つなのか良くわかりません。片方はうんともすんとも言いませんので、使いません。もうひとつのやや接続に時間が掛かる方を使います。

2つのCOMのどちらを使うべきかは、LEDで見分けることができます。LEDが点滅→点灯に変わる方が本物(?)です。まあ、両方繋いでも問題は起きないようなので、面倒なら両方繋いでしまえば良いと思います。

設定の変更

ペアリングを解除した状態(LEDが点滅)にすると、HC-06の設定を変更することができます。シリアルの設定は、ボーレート9600、8ビット、パリティなしにします。

このデバイスは送った文字をエコーバックしないので、ローカルエコーを強制的にONにしてください。またキーボードで入力すると、ATコマンド先頭のAT二文字を打った段階で「OK」が返ってきてしまい、コマンド発行ができません。コマンド全体をメモ帳などに打ち、コピペで貼り付けるとうまくいきます。

メモ帳からコピペはイマイチなので、ライン編集モードをONにすれば良いだろと思ったら、行の最後にCRが送られてしまうためか、AT+PINコマンドなどがうまくいかなくなります。

デバイス名、つまりBluetoothデバイスの検索のときに出てくる名前です。デバイス名を変更するにはAT+NAMEの後に名前を打ちます。例えば "HC-06-1" に変えたければAT+NAMEHC-06-1を送ります。大文字と小文字は区別されます。返事にOKsetnameと返ってくれば成功です。

またPINを変える際はAT+PINを送ります。例えば8888に変えたければAT+PIN8888です。返事にOKsetPINと返ってくれば成功です。

シリアルの設定を変更するコマンドもあります。興味があれば仕様書を見てくださいまし。

編集者:すずき(2018/07/22 04:22)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2018年7月22日

ROCK64購入

目次: ROCK64/ROCKPro64

Rockchip RK3328搭載のシングルボードコンピュータ、ROCK64 4GB版を買いました。秋月電子で6,500円くらいでした。メモリの少ない1GB版にすると、さらにお安いです。

ROCK64はSDカードから起動できます。OSイメージは開発元が用意しているEtcherを使うと(GitHubからダウンロードできます)ダウンロード+SDカードへの書き込みが簡単にできます。便利です。

しかし手持ちのSDカードが4GBだったため、Etcherに8GBのカードを入れろと言われてしまい、OSイメージが書き込めませんでした。

最近は暑すぎて、電器屋に行くのさえ辛いですが、こればかりは行くしかないですね。昼の外出は危険だと思い、夕方にしましたが、それでも暑いです。

汗だくになりつつ16GBのSDカードを買ってきてDebianを書き込み、起動しましたがシリアルが出ません。何でだ。

ROCK64の爆速UART

どうしてROCK64のUARTが表示されないのかなと思ってググってみたら、ボーレートがまさかの1.5Mbpsでしたサポートフォーラムへのリンク)。UARTで1.5Mbpsなんてボーレート初めて使いました。

偶然、手持ちのFT232R(USB-UART変換チップ)が1.5Mbpsに対応していてラッキーでした。一方のHC06(Bluetooth-UART変換チップ)は1.5Mbpsに対応しておらず、UARTを無線化しようと思ってせっかく買いましたが撃沈です…。

シリアルを見ていると、独自ローダー(?)、ATF(ARM Trusted Firmware)、U-Bootの順に動いており、U-Bootはdistro bootを使ってLinux 4.4.77をロードしているようです。

リセットからU-Boot手前までのログ
DDR version 1.06 20170424
In
SRX
LPDDR3
786MHz
Bus Width=32 Col=11 Bank=8 Row=15/15 CS=2 Die Bus-Width=32 Size=4096MB
ddrconfig:7
OUT
Boot1 Release Time: 2017-05-18, version: 2.43
ChipType = 0x11, 187
emmc reinit
emmc reinit
SdmmcInit=2 20
SdmmcInit=0 0
BootCapSize=0
UserCapSize=14832MB
FwPartOffset=2000 , 0
StorageInit ok = 48105
Raw SecureMode = 0
SecureInit read PBA: 0x4
SecureInit read PBA: 0x404
SecureInit read PBA: 0x804
SecureInit read PBA: 0xc04
SecureInit read PBA: 0x1004
SecureInit ret = 0, SecureMode = 0
LoadTrustBL
No find bl30.bin
No find bl32.bin
Load uboot, ReadLba = 2000
Load OK, addr=0x200000, size=0x92d74
RunBL31 0x10000
NOTICE:  BL31: v1.3(debug):f947c7e
NOTICE:  BL31: Built : 09:28:45, May 31 2017
NOTICE:  BL31:Rockchip release version: v1.3
INFO:    ARM GICv2 driver initialized
INFO:    Using opteed sec cpu_context!
INFO:    boot cpu mask: 1
INFO:    plat_rockchip_pmu_init: pd status 0xe
INFO:    BL31: Initializing runtime services
WARNING: No OPTEE provided by BL2 boot loader, Booting device without OPTEE initialization. SMC`s destined for OPTEE will return SMC_UNK
ERROR:   Error initializing runtime service opteed_fast
INFO:    BL31: Preparing for EL3 exit to normal world
INFO:    Entry point address = 0x200000
INFO:    SPSR = 0x3c9
U-Bootの起動ログ
U-Boot 2017.09-g5aef9f7 (Oct 12 2017 - 09:11:39 +0000), Build: jenkins-linux-build-rock-64-136

Model: Pine64 Rock64
DRAM: 4 GiB
MMC: rksdmmc@ff500000: 1, rksdmmc@ff520000: 0
*** Warning - bad CRC, using default environment

In: serial@ff130000
Out: serial@ff130000
Err: serial@ff130000
Model: Pine64 Rock64
Net: eth0: ethernet@ff540000
Hit any key to stop autoboot: 0

U-Bootの日付は2017年で、まだ新し目ですが、Linuxはかなり古いですね。後でUpstreamカーネルに入れ替えてみましょうか。

編集者:すずき(2020/10/30 01:19)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2018年7月23日

ROCK64とU-Bootのdistro boot

目次: ROCK64/ROCKPro64

昨日(2018年7月22日の日記参照)に引き続き、ROCK64をいじっています。

私も先日知ったばかりで、さほど詳しくありませんが、distro bootはかなり便利です。ROCK64のSDカードはmmc 1です(※)ので、SDカードにFATかext2のパーティション(例えば6番目だとする)を切ってmkfsしておいて、

sysboot mmc 1:6 fat 0x500000 /extlinux/extlinux.conf

このようなコマンドを実行すると、extlinux.confに書いてある設定に従ってカーネルをロードしてくれます。Debianなどもこの設定ファイルを使えば起動できて便利です。

アプリの開発者ならばdistro bootの方が速いし楽でしょう。カーネルの開発者ならばTFTPブートの方が嬉しいかもしれません。SDカードを抜き差しせずに済みます。

一応ROCK64でカーネルをセルフコンパイルすれば、PC要らずで自己完結できますし、SDカードの抜き差しも要りません。しかしROCK64は、さすがにコンパイルに使うには遅いので、クロスコンパイル+TFTPブートの方が効率は良さそうです……。

(※)ROCK64のU-Bootから見ると、SDカードはmmc 1です。オプションで売っているeMMCボードがmmc 0らしいです。私はeMMCボード持ってないので、詳細はわかりません。

Upstreamカーネル

U-Bootのdistro bootのお陰で、カーネルの入れ替えはとても楽です。ROCK64のカーネルを4.4からupstreamのlinux-nextに入れ替えてみたところ、本当に入れ替えたのか不安になるくらい、何も問題なく起動しました。Rockchipやるなあ…。

RockchipはOpenSourceプロジェクトWiki(Wikiへのリンク)があり、ブートローダーから全てOSSで動かせるようです。ブートローダーは隠されていて、ソースコードを見るなんて有り得なかった時代に比べると、隔世の感です。

Rockchip以外にもAllWinnerもLinuxへのupstream活動は盛んなようで、upstreamカーネルが動く可能性が高そうです。AllWinnerのボードは何が良いんでしょうね?お手頃感ではNanoPi NEO 2でしょうか?

RockchipもAllWinnerもシングルボードコンピュータに多く採用されており、性能もなかなかですし、とても安く手に入ります。決して速くないマイコンボードが1枚何万円もしていた時代に比べると、ありがたい時代になったと思います。

ブートローダーにもチャレンジ

ブートローダーも最新版に入れ替えようと思い、Rockchip Opensource Wikiに乗っていた手順を試しましたが、ちょっと内容が古いのかU-Bootのビルドは通りますが、ATFのビルドはコケてしまいました。

Wiki曰くSDカードの0x40セクタに2ndブートローダーを置けば良いらしいですが、U-Boot SPLは廃止されてしまいましたし、ATFはビルドが通らなかったため、肝心のブートローダー(U-Boot SPLもしくはATF BL31)が作れません。困った。

現状、ブートローダーを変更したい積極的な理由も特にないので、ブートローダーのビルドはまた今度ですね。

編集者:すずき(2020/10/30 01:20)

コメント一覧

  • t4さん(2018/07/29 22:33)
    > U-Boot SPL は廃止されてしまいましたし...

    廃止されてないけど、どこを見たんでしょう?
  • すずきさん(2018/07/30 15:33)
    >t4 さん
    ご指摘ありがとうございます。
    トップディレクトリ下にある spl というディレクトリも、u-boot-spl.bin も生成されないため、廃止されたのかと思いました。
  • t4さん(2018/07/31 10:06)
    https://github.com/rockchip-linux/u-boot
    https://github.com/ayufan-rock64/linux-u-boot
    http://git.denx.de/u-boot.git/

    一応 1番上がこのデバイスの本家だけど
    他所は、本家の品質が悪るすぎて Pull-Request を蹴ってるような状況
    どこからGetしたのかな?
    本家のヤツには tpl/spl はそのまま残ってる(品質は別として)

    本家はものは、ビルドが通らないとか、クラッシュするとか、
    真っ当な issue を報告しても無視して即刻 Close とか
    ハッキリ言って Pull-Request を蹴られても当然の酷いレベル
    linux-kernel とか その他諸々も、 総じてそんな感じ。

    そんな理由もあって
    よほどのアドバンテージが無い限り、大概の処は 古いやつを 使ってる
    んでも、彼らはデバイスメーカーしか知らない情報を持ってるからね
  • すずきさん(2018/07/31 10:36)
    > t4 さん
    使っているのは、
    http://git.denx.de/u-boot.git/
    です。

    > 本家はものは、ビルドが通らないとか、クラッシュするとか、
    > 真っ当な issue を報告しても無視して即刻 Close とか
    > ハッキリ言って Pull-Request を蹴られても当然の酷いレベル
    > linux-kernel とか その他諸々も、 総じてそんな感じ。

    なんと、そうなんですか。情報ありがとうございます。


    > そんな理由もあって
    > よほどのアドバンテージが無い限り、大概の処は 古いやつを 使ってる
    > んでも、彼らはデバイスメーカーしか知らない情報を持ってるからね

    デバイスメーカーしか知らない情報で、公開できる範囲の情報を
    Linux や U-Boot の Upstream に公開してくれている、
    と思っていたのですが、そううまくは行っていないということですね。

    Linux はとりあえず
    git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
    が動作するようなので、ブートローダはそっとしておいて、こちらで遊ぼうかと思っています。
  • t4さん(2018/08/01 12:26)
    誰でも見れる情報として公開されているのはこれぐらい
    http://opensource.rock-chips.com/images/9/97/Rockchip_RK3328TRM_V1.1-Part1-20170321.pdf
    中をみて見てみれば解るけど、この情報で弄れるのはベーシックなI/O程度がせいぜい
    込み入ったI/O usb, ether, gpu, vpu の類は何も無し。

    データシート/アプリケーション・ノートの代わりに、ソース・コードを公開
    って方針なんだろうけど
    まぁ、詳細資料は出したくない ってのは理解できる

    しかし、
    公開のサンプル・コードが上手く動かない場合、リバースしながら尚且つ推測もしなければならない
    リバースは良しとしても、推測には限界があるよね。
    真摯に issue に対応してくれれば、資料非公開でも一向に構わないんだが…

    git.denx.de: 確か昨年蹴られてたね reason: build-error
    issue を報告した人達: オマエの処には2度と報告しない
    Armbianの人: 怒ってた、俺らのポーティング終わる前にぶっ壊れる(新しいヤツが)

    グチっぽくなったけど、
    それでも pi とに比べれば資料は多い方だとは思うな

    ---
    お付き合い、ありがとうございました。
  • すずきさん(2018/08/01 20:28)
    > t4 さん

    > 誰でも見れる情報として公開されているのはこれぐらい
    > http://opensource.rock-chips.com/images/9/97/Rockchip_RK3328TRM_V1.1-Part1-20170321.pdf
    > 中をみて見てみれば解るけど、この情報で弄れるのはベーシックなI/O程度がせいぜい
    > 込み入ったI/O usb, ether, gpu, vpu の類は何も無し。

    私も、ビデオデコーダや映像出力系の仕様を見たかったのですが、
    Rockchip OSS Wiki には、残念ながら載っていないです。

    グラフィクスも Mali Utgard 系ですが、
    ARM は Mali のドライバ(ユーザ空間側)を公開していないので、
    カーネルモジュールだけあっても、どうにもなりません…。


    > データシート/アプリケーション・ノートの代わりに、ソース・コードを公開
    > って方針なんだろうけど
    > まぁ、詳細資料は出したくない ってのは理解できる

    確かに、社内の抵抗は強そうです。


    > しかし、
    > 公開のサンプル・コードが上手く動かない場合、リバースしながら尚且つ推測もしなければならない
    > リバースは良しとしても、推測には限界があるよね。
    > 真摯に issue に対応してくれれば、資料非公開でも一向に構わないんだが…
    >
    > git.denx.de: 確か昨年蹴られてたね reason: build-error
    > issue を報告した人達: オマエの処には2度と報告しない
    > Armbianの人: 怒ってた、俺らのポーティング終わる前にぶっ壊れる(新しいヤツが)

    Rockchip は OSS とのコラボがうまく行っている方だと思っていたのですが、
    実情はなかなか、そううまくは行かないのですね。

    邪推ですが Rockchip 社が OSS 貢献する社員にどれくらい
    好感触を持つか次第で OSS との付き合い方は変わりそうですね。


    > グチっぽくなったけど、
    > それでも pi とに比べれば資料は多い方だとは思うな
    >
    > ---
    > お付き合い、ありがとうございました。

    こちらこそ、貴重な情報ありがとうございました。

    安価なワンボードコンピュータは普及しましたが、
    完全にオープンになった HW はまだ先なのでしょうね。
    いつかどこかが作る(古い SoC を公開するとか、そんな形でも)
    と期待しています。
open/close この記事にコメントする



2018年8月4日

レガシィの4回目の車検

目次:

先週、大阪スバル(高槻店)にレガシィの車検をお願いしていました。今日は納車です。

JR高槻駅からディーラーまで歩きましたが、もう、とにかく暑い暑い。暑くてやってられません。将来、車を買い替えるとしたら、夏には絶対、車は買わないぞ、絶対だ。心に決めました。

料金は自賠責&税金込みで13万円くらいでした。特に大きな故障もなかったし、そんなもんでしょう。ね。

編集者:すずき(2023/09/30 15:07)

コメント一覧

  • hdkさん(2018/08/05 01:16)
    車検の時はいつも代車借りてます。駅やバス停は近いけど、タダで貸してくれるので... トヨタ系ディーラーだからですかね? でも最初中古車買った店も貸してくれていました。駅から 1km もあるのに代車なしだとつらいですね。
  • すずきさん(2018/08/05 01:25)
    > hdk さん
    スバルも代車を貸してくれますが、今回は長期間(1週間)預けていたことと、8月は休みが多いせいか、単に繁忙期なのか、貸せる代車が無いと言われました…。
open/close この記事にコメントする



2018年8月5日

微妙に壊れてるThinkPad E480のキーボード

先日購入(2018年6月14日の日記参照)したThinkPad E480ですが、キーボードのカーソルキー、しかも上カーソルキーだけが微妙に壊れています。

キーを押すとキーが傾いてしまい、引っかかって戻ってこないときがあります。

キーを分解してみた

何が引っかかっているのか調べるために分解してみました。キートップをてこなどで外すと、パンタグラフが入っています。パンタグラフは2つの部品からできています。


キーボードのパンタグラフ、分解

2つの部品を組み合わせると、I字型(畳んでいるとき)もしくはX字型(開いているとき)になります。


キーボードのパンタグラフ、畳んだ状態


キーボードのパンタグラフ、開いた状態

良く見るとこの部品、軸が折れてしまっています。


キーボードのパンタグラフ、分解、軸が折れている

軸が片側なくなっているため、パンタグラフを開いても綺麗なX字型にならず、捻じれてしまいます。


キーボードのパンタグラフ、開いた状態、軸が折れているので歪む

これによってキートップが若干傾いてキーボードのフレームに引っかかり、元の位置に戻らなくなるようです。

対策

修理に出そうかどうか迷って、いろいろ押し方を試しているうちに、比較的引っかかりにくい押し方があることを見つけました。今は押し方を工夫することで凌いでいます。

さらに最近は、普通にキーを押しても引っかかりにくくなった気がします。パンタグラフが壊れているのは相変わらずなので、キートップ側が削れたのか、変形したのかな?

編集者:すずき(2018/08/05 01:20)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
2018年7月6日 >>> 2018年8月5日
link もっと後

管理用メニュー

link 記事を新規作成

<2018>
<<<07>>>
1234567
891011121314
15161718192021
22232425262728
293031----

最近のコメント5件

  • link 02年8月4日
    lxbfYeaaさん (07/12 10:11)
    「555」
  • link 24年6月17日
    すずきさん (06/23 00:12)
    「ありがとうございます。バルコニーではない...」
  • link 24年6月17日
    hdkさん (06/22 22:08)
    「GPSの最初の同期を取る時は見晴らしのい...」
  • link 24年5月16日
    すずきさん (05/21 11:41)
    「あー、確かにdpkg-reconfigu...」
  • link 24年5月16日
    hdkさん (05/21 08:55)
    「システム全体のlocale設定はDebi...」

最近の記事3件

  • link 24年8月10日
    すずき (08/12 16:23)
    「[Linuxを調べる - initrdとカーネル引数] 目次: Linux今どき(?)のinitrdとカーネル引数の渡し方を知...」
  • link 23年4月10日
    すずき (08/12 15:08)
    「[Linux - まとめリンク] 目次: Linuxカーネル、ドライバ関連。Linuxのstruct pageって何?Linu...」
  • link 24年8月5日
    すずき (08/11 23:27)
    「[debootstrapと他アーキテクチャバイナリとbinfmt_misc] 目次: Linux以前、Debianのrootf...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 08/12 16:23