Walrus,Visit. | 一覧 | 検索 | 更新履歴(RSS) | 新規作成
はてなブックマークに追加 はてなブックマークを表示 編集 | 編集(管理者用) | 差分

Linuxザウルス開発メモ/デバイス

編集

デバイスの叩き方、マシン相互の互換性など。

ページ作成: 神木一也

各カーネルソースのありか

編集

各デバイスの詳細

タッチパネル

  • カーネルソースファイル名:
    • SL-5x00: drivers/char/ucb1200_ts.c
    • SL-A300, SL-B500, SL-C7x0: drivers/char/ads7846_ts.c
  • デバイス名:
    • iPAQ: /dev/touchscreen/0raw
    • SL-5x00, SL-A300: /dev/ts
    • デバイスの実体は /dev/touchscreen/0raw.REAL, /dev/sharp_ts で、/dev/ts 等は symlink になっている。
  • デバイスが返す構造体:
    • iPAQ:
              typedef struct {
                 short pressure;          // タップ中は 1.
                 short x;
                 short y;
                 short millisecs;         // ポーリング時刻 (10ms 単位)
              } TS_EVENT;
      • Qte を build する時、-DQT_QWS_IPAQ する。
    • SL-5x00:
              typedef struct {
                 long y;
                 long x;
                 long pressure;            // Qte は 500 以上でタップと解釈。
                 long long millisecs;      // ポーリング時刻 (10ms 単位)
              } TS_EVENT;
      • Qte を build する時、-DQT_QWS_EBX -DQT_QWS_CUSTOM とする。
      • 筆圧は、カーネルは 350 以上でタップと解釈することを推奨しているが、
      • Qte は 500 以上でタップと解釈している。

      • 上の構造体は Qte 表記である。カーネル内では
      • 以下のように表記されている。x, y が逆になることに注意!

                    typedef struct {
                       long x;           // 長軸
                       long y;           // 短軸
                       long pressure;
                       long long millisecs;
                    } TS_EVENT;
    • SL-A300:
              typedef struct {
                 short pressure;          // タップ中は 1.
                 short x;                 // 短軸
                 short y;                 // 長軸
                 short millisecs;         // ポーリング時刻 (10ms 単位)
              } TS_EVENT;
      • Qte を build する時、-DQT_QWS_EBX する。タッチパネル実装周りでは #undef QT_QWS_CUSTOM しておく。
    • SL-B500:
              typedef struct {
                 long y;
                 long x;
                 long pressure;            // 筆圧。Qte は 500 以上でタップと解釈。
                 long long millisecs;      // ポーリング時刻 (10ms 単位)
              } TS_EVENT;
    • SL-C700:
              typedef struct {
                 short pressure;          // 押下時、筆圧取得モード時(Pressure=1)は筆圧、無視(Pressure=0)時は 1.
                 short x;
                 short y;
                 short millisecs;         // ポーリング時刻 (10ms 単位)
              } TS_EVENT;
  • キャリブレーション法 (カーネル内):
    • SL-5x00, SL-A300:
      • 生データ (t.x, t.y) に対して座標 (r.x, r.y) への変換式:
                 r.x = ((raw_max_x - t.x) * res_x) / (raw_max_x - raw_min_x);
                 r.y = ((raw_max_y - t.y) * res_y) / (raw_max_y - raw_min_y);
      • 変換パラメータ raw_min_x, raw_max_x, res_x 等は ioctl() で直接与える。
      • 変換パラメータを userland から読むことはできない。
      • デフォルトでは (r.x, r.y) はおおむね左上が原点で、横が r.x 軸、縦が r.y 軸の状態になっている。
      • (t.x, t.y) はおおむね右下が原点である。t.x, t.y の値は SL5x00 で 70 〜 944 あたり、SL-A300 では 150 〜 3830 あたり。
    • iPAQ:
  • キャリブレーション法 (Qtopia内):
    • SL-A300:
      • 生データ (x, y) に対して座標 (X, Y) への変換式:
                 X = (a * x +  b * y + c) / s;
                 Y = (d * x +  e * y + f) / s;
      • 変換パラメータ a, b, c, d, e, f, s は QCalibratedMouseHandler? のインスタンスとして与える。
      • /etc/pointercal に printf("%d %d %d %d %d %d %d", a, b, c, d, e, f, s) されている。
      • Qtopia 上の座標 (X, Y) は左下が原点で、横が X 軸、縦が Y 軸になっている。
      • なお、実装上、つねに b = d = 0, s = 65536 になっている。
      

サウンド

  • カーネルソースファイル名:
    • SL-5x00: drivers/sound/collie_ssp.c, collie_tc35143.c
    • SL-A300: drivers/sound/discovery_audio.c
    • SL-B500: drivers/sound/pxa-i2s.c, poodle_i2sc.c, poodle_wm8731.c
    • SL-B500: drivers/sound/pxa-i2s_corgi.c, poodle_i2sc.c, poodle_wm8731.c
  • デバイス名:
    • SL-5x00: /dev/audio, /dev/dsp, /dev/mixer* ?
    • SL-A300: /dev/audio (ROM ver1.20J 以降のみ), /dev/dsp, /dev/mixer

  • サポートするサンプリングレート:
    • SL-5x00: 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000Hz.
    • SL-A300: 8000, 11025, 22050, 44100, 48000Hz.
      • SL-A300 でサポートしないレートを指定すると 8000Hz になるが、

        アプリからは指定したレートに見える。バグである :-)

    • SL-B500: 8000, 44100, 48000Hz.
    • SL-C700: 8000, 44100, 48000Hz.
    • -
    • B, C で 8kHz, 44.1kHz, 48kHz 以外のレートを設定してもカーネル内部ではこれらのレートが使用される。

  • サポートするフォーマット:
    • SL-5x00:
    • SL-A300: u-law, A-law, signed 8bit, unsigned 8bit, signed 16bit, unsigned 16bit.
      • ハードウエアがサポートしているのは signed 16bit だけで、それ以外はカーネル内部で

        signed 16bit と相互変換しているだけ。

      • SL-5x00 は full-duplex サポート。
      • SL-A300 は ROM version 1.5 以上のカーネルで full-duplex をサポートした。
  • カーネル内バッファ:
    • SL-5x00:
    • デフォルトは 8kB x 8, 最小で 16B x 2, 最大 128kB

    • SL-A300:
    • 再生側のデフォルトは 8kB x 8, 最小で 16B x 2, 最大 128kB

    • SL-C700:
    • デフォルトは 8kB x 8, 最小で 32B x 2, 最大 256kB

RTC

  • カーネルソースファイル名:
    • SL-5x00: drivers/char/sa1100-rtc.c ?
    • SL-A300: drivers/char/cotulla-rtc.c
      • ioctl(RTC_ALM_SET) はサスペンドしていても有効。
      • 補足すると、サスペンド前にこれをセットしておくと
      • サスペンドしていても指定時刻に起き出すという ioctl である。

        /dev/rtc は atd が握りっぱなしなので使い辛いのが難点だが。

キーボード

  • カーネルソースファイル名:
    • SL-A300: drivers/char/sharp_logkey.c(実体), discovery_keyb.c(スタブ)
      • MENU, HOME, OK, CANCEL が IRQ の 111 〜 114 に対応し、scancode は 29,40,39,34.
      • カーソルキーが IRQ の 115 〜 118 に対応し、scancode は 0x23,0x24,0x25,0x26.
      • 決定キーが IRQ の 119 に対応し、scancode は 91.
      • keycode の実装はない。keycode を得るとでたらめな値が返る。
      • 決定キーを含めて void sharppda_scan_key_gpio(int irq) が本体で、
      • (きわめて紛らわしいが) void sharppda_get_actkey_gpio(void* dummy) は全く呼ばれていない。

      • カーソルキーに N キーロールオーバーの実装はない。
      • だけでなく、連続する N キーに対してキーアップイベントは最後の一つにしかない。

      • キーリピートのデフォルトは delay 2s, rate 1cps という非常識な値になっているが、
      • Qt はカーネルレベルのリピートを無視していて、自前で実装しなおしている。

      • カーソルキーと割り込みの対応は一対一でない:
                   115: 右下方向 
                   116: 左上方向 
                   117: 右上方向 
                   118: 左下方向 
      • たとえば右矢印に対しては 115, 117 の割り込みが順不同で発生する。
      • 右矢印、左矢印の順に押すと、おそらく 115(make),117(make), 117(break), 115(break),

        116(make), 118(make), 118(break), 116(break) のようなシーケンスの割り込みが発生する。

        かなり冗長に思えるが、この割り込み(115 〜 118) は 20% 近く落ちるので、

        これでも十分でない。break の発生しそこないが出ることがある。

  • キー同時入力数拡張パッチ
       以下のパッチは、最大6個のキーが同時に押せるようになるように修正したものです。
       #実際には5個までしか押せませんが…。ハード的な制約かな?
       これを使えばエミュレータでのゲームが快適に!?
--- linux.orig/drivers/char/corgi_logkey.c  2003-06-18 16:12:26.000000000 +0900
+++ linux/drivers/char/corgi_logkey.c   2004-01-28 15:05:24.000000000 +0900
@@ -605,6 +605,7 @@

 #if CHECK_ROLLOVER
 #define MAX_KEY_PRESS (6)
+#define MAX_KEY_PRESS_THRESHOLD (6 + 1)
 #endif

 void sharppda_scan_key_gpio(void* dummy)
@@ -707,8 +708,8 @@
        if (ROLLOVER_CHECK_FLAG(rollover_flag, ROLLOVER_FUNCTION)) {
            rollover_count--;
        }
-       if ((rollover_count > 3) ||
-           ((rollover_count == 3) &&
+       if ((rollover_count > MAX_KEY_PRESS_THRESHOLD) ||
+           ((rollover_count == MAX_KEY_PRESS_THRESHOLD) &&
             (!ROLLOVER_CHECK_FLAG(rollover_flag, ROLLOVER_TAB) ||
              ROLLOVER_CHECK_FLAG(rollover_flag, ROLLOVER_DIRECT_ON)) &&
             (!ROLLOVER_CHECK_FLAG(rollover_flag, ROLLOVER_VERTICAL) ||

フレームバッファ

  • カーネルソースファイル名:
    • SL-5x00: drivers/video/colliefb.c
    • SL-A300: drivers/video/cotulla_fb.c
  • サポートするフォーマット:
    • SL-5x00:
    • 320x240, 16bpp, packed pixel, true color のみ。

    • SL-A300:
    • 240x320 16bpp, packed pixel, true color のみ。

    • 16bpp であるにもかかわらず、ioctl(FBIOGET_FSCREENINFO) は direct color でなく true color を返す。

  • ATI W100に関する未整理メモ (by ヤマケン)
    • SL-C700に搭載されているビデオチップ。カーネルソースで解る事を実験
    • FB領域へのアクセスのパフォーマンスを上げる
      • XScaleのwrite buffer有効化。PCで言うところのwrite-combiningと同様の効果を狙う
      • cache on(かつwrite-back)に設定してみる。FBに対して直接読み書きを繰り返すアプリケーション(あるか?)には有効かも
      • page attributeでwtかwbかを設定する。XScale Developer's manualの6.2.3.4参照
      • 動的に切り換えできるとなお良い。w100_init_from_hwtab()あたりに仕込める?
      • 既に同様の試みがあった
      • Write_buffered_SA-1100_framebuffer

      • linux 2.5にはwrite buffer関係のサポートがあるらしい
      • http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2002-November/012137.html

    • fast clock(50-100MHz)とnorm clock(12.5MHz)があるらしい
      • VGA表示時でも75MHz設定にしかなってない
      • w100_init_sharp_lcd()でw100_SetFastSysClk?(100)を試してみた
    • ATIドライバでW100とレジスタ構成の近いものを探そう
      • RADEONと似てる。linux/drivers/video/radeon.h
      • XFree86は未確認
      • アクセラレーション機能をQt/EのGfxドライバとして使えるようにしたい(ソース込で)
    • ioctl動作確認
      • W100FB_CONFIG(設定ファイル読み込み)
      • W100FB_POWERDOWN(メディアプレイヤーが使ってる?)
    • W100内蔵RAMのみで表示してみよう
      • 容量が足りないのでVGA表示は無理
      • スピード、消費電力はどうなる?
    • 実験的パッチ
    • 注意: メーカー設定に対してオーバークロック動作になるため、ハードウェアにダメージを与える恐れがあります

      • write buffer有効化
      • fast clockを75MHzから100MHzに変更
      • パフォーマンスは未測定。体感では速くなったような気がする。CONFIG_XSCALE_CACHE_ERRATA=nと同時に有効にしているので、何がどれだけ効いているのか(または気のせいなのか)不明
--- w100fb.c.orig	Wed Jan 22 14:03:57 2003
+++ w100fb.c	Sun May 11 10:15:29 2003
@@ -282,7 +282,11 @@
       // remap the areas we're going to use
       remapped_base = ioremap_nocache(W100_PHYS_ADDRESS, REMAPPED_CFG_LEN);
       remapped_regs = ioremap_nocache(W100_REG_BASE, REMAPPED_MMR_LEN);
+#if 0
       remapped_fbuf = ioremap_nocache(W100_FB_BASE, REMAPPED_FB_LEN);
+#else
+      remapped_fbuf = __ioremap(W100_FB_BASE, REMAPPED_FB_LEN, L_PTE_BUFFERABLE);
+#endif
 
       isRemapped = 1;
   }
@@ -2584,7 +2588,11 @@
             break;
         case LCD_SHARP_VGA:
             w100_SetSlowSysClk(12);  // use crystal -- 12.5MHz
+#if 0
             w100_SetFastSysClk(75);  // use PLL     -- 75.0MHz
+#else
+            w100_SetFastSysClk(100);  // use PLL    -- 100.0MHz
+#endif
             gPowerState.pclk_cntl.f.pclk_src_sel  = 0x1;
             gPowerState.pclk_cntl.f.pclk_post_div = 0x2;
             writel((u32)(gPowerState.pclk_cntl.val), remapped_regs+mmPCLK_CNTL);

シリアルポート (/dev/ttyS0)

オプションポート16 に出ているシリアル。

  • カーネルソースファイル名:
    • SL-5x00: drivers/char/serial_sa1100.c
    • SL-A300: drivers/char/serial_pxa200.c
    • SL-B500,C700: drivers/char/serial_pxa200.c
  • UART ハードウエア
    • SL-5x00:
      • SA1x00 内 UART で、モデム制御線は無い。
    • SL-A300:
      • XScale PXA-210 内の Bluetooth UART.
      • 920kbps まで、制御線として RTS/CTS のみをもつ。
      • DSR/RI/DCD を受け取ることはできない。また、オプションポート上の DTR は常に 3.3V (スペース)。
      • GPIO 上 (具体的には ASIC3_GPIO_PSTS_D & COM_DCD 上) に DCD の実装があるようにみえるが、これは /dev/ttyS0 の DCD として見えているわけではない。どこにどのように繋がっているかは不明。
    • SL-B500, C700:
      • XScale PXA-250 内の Full function UART.
      • 230kbps まで、制御線として RTS/CTS/DTR/DSR/RI/DCD をもつ。
      • 実際には DCD はオプションポートに出ていないため、受け取ることはできない。
  • Power management
    • SL-A300:
      • なにもしていない。常に有効になっている。
    • SL-B500:
      • シリアルコンソールがオンになっている場合、シリアルは常に有効。
      • デバイスの open/close に連動してデバイスの出力ドライバ段とクロックがオン/オフされるため、非 open 状態では使うことはできない (たぶん ...)。
    • SL-C7x0:
      • シリアルコンソールがオンになっている場合、シリアルは常に有効。
      • デバイスの open/close に連動してクロックがオン/オフされているため、非 open 状態では入力ピンは使うことはできない (ラッチされないから) が、出力ピンは使うことができる。
    • なお、B500, C7x0 では APM の支配下にある。カーネル内でシリアルデバイスをバイパスして使う場合は CKEN (Clock Enable Register) の CKEN6_FFUART ビットを上げ下げするだけでなく、change_power_mode() を叩いて APM の使用ビットを立てること。
                 xpa210_discovery_serial_power_on();               // B ではドライバ?の電源オン。C ではなにもしない。
                 if (xpa2X0_is_ff_uart(info->iomem_base)) {        // ポートが FFUART であるか?
                     CKEN |= CKEN6_FFUART;                         // FFUART にクロック注入開始。
                     if (!change_power_mode(LOCK_FCS_FFUART, 1)){  // APM に FFUART の使用を宣言。
                          return -EAGAIN;
                     }
                 }

IrDA (/dev/ttyS1)

IrDA のハードウエア層について。IrCOMM などデータリンク〜トランスポート層より上については別項。 SL-A300 以降では、本質的に同じもの。

  • カーネルソースファイル名:
    • SL-A300,B500,C700: drivers/char/serial_pxa200.c
  • UART ハードウエア
    • SL-A300:
      • XScale PXA-210 内の Standard UART.
      • 上限 230kbps にみえるが、FIR (4Mbps) の実装もあるように見える。
    • SL-B500, C700:
      • XScale PXA-250 内の Standard UART.
      • 上限 230kbps にみえるが、FIR (4Mbps) の実装もあるように見える。
  • Power management
    • SL-A300:
      • ポートの open と同時にドライバ段がオンになる。
    • SL-B500, C700:
      • ポートの open と同時にドライバ段がオンになる。また、close 時にはクロックがオフされる。

電源管理

  • カーネルソースファイル名(SL-C700,C750,C760):
    • arch/arm/mach-pxa/pxa_ssp.c
    • include/asm-arm/arch-pxa/sharpsl_battery.h
    • arch/arm/mach-pxa/sharpsl_battery.c
    • arch/arm/mach-pxa/sharpsl_apm.c
  • この辺のコードは罠だらけなので身構えて読む事
    • 関数名、変数名は実体を表していないので鵜呑みにしない事
    • 関数毎の責任範囲は不明確なので、周囲の状況から事前に機能を予想したりせず、コードの字面から意味をつかむ事
      • 特に関数名から機能を予想しない方がよい
      • 途中で担当者の交代があったような形跡がありコードがつぎはぎだらけなので、一つの処理が一つの関数で完結していると思わない方がよい
      • 末端の関数からボトムアップ式に機能を解読して理解していった方がよい。トップダウンは無理
    • グローバル変数多用。変数を保守する責任は多くの関数に分散しているのでそのつもりでコードを読む事
    • 実際には使われていない関数等の残骸あり。同等の機能が別の関数で実装されている可能性を忘れない事
    • シンボリックな名前がdefineされているのに数値で参照する事あり。名前が検索にかからないからといって安心しない事
  • バッテリ状態監視
    • C700/C750/C760(3機種ともCONFIG_ARCH_PXA_CORGIが定義される)ではMAX1111(4ch * 8bit ADC, 16pin QSOP)をSSP経由で接続
    • B500(POODLE)ではADS7846をSSP経由で接続
    • バッテリ残量に応じたおせっかいな挙動(バックライト制御等)や大雑把な残量表示を変更できそうだが、OpenZaurusのカーネルもこの辺を改善しているはずなので、何かいじる前に見てみた方がよいと思われる
    • include/asm-arm/arch-pxa/sharpsl_battery.h
      • ADC内のチャネル定義
#if defined(CONFIG_ARCH_PXA_POODLE)
#define		MUX_CHL		6u		/* channel of MUX */
#define		BATT_CHL	2u		/* channel of BATTery */
#elif defined(CONFIG_ARCH_PXA_CORGI)
#define		BATT_AD		4u		/* channel of BATTery */
#define		BATT_THM	2u		/* channel of BATTery */
#define		JK_VAD		6u		/* channel of BATTery */
#endif
    • arch/arm/mach-pxa/pxa_ssp.c
      • 本来はssp_get_dac_val()でMAX1111とADS7846の叩き方を抽象化するはずだったらしいが、使われていない。現在はssp_get_max1111_val(), ads7846_rw_cli(), Get_DAC_Value()に機能が分散している(やっつけ仕事っぽい。途中で担当者交代でもあった?)
unsigned long ssp_get_dac_val(ulong data, int cs)
int ssp_get_max1111_val(ulong data)
    • arch/arm/mach-pxa/sharpsl_battery.c
      • int Get_DAC_Value(int channel)
      • 異なるADCの叩き方を抽象化(POODLEのADS7846とCORGIのMAX1111)。ここでは生の値を取るだけで、値の補正は別関数の仕事
/* 
 * Translate Analog signal to Digital data
 */
int Get_DAC_Value(int channel)
{
	unsigned long cmd;
	unsigned int dummy;
	int voltage;

#if defined(CONFIG_ARCH_PXA_POODLE)
	/* translate ADC */
	cmd = (1u << ADSCTRL_PD0_SH) | (1u << ADSCTRL_PD1_SH) |(1u << ADSCTRL_DFR_SH)|
		(channel << ADSCTRL_ADR_SH) | (1u << ADSCTRL_STS_SH);
	dummy = ads7846_rw_cli(cmd);
	voltage = ads7846_rw_cli(cmd);

	cmd &= ~(ADSCTRL_PD0_MSK | ADSCTRL_PD1_MSK);
	dummy = ads7846_rw_cli(cmd);

#elif defined(CONFIG_ARCH_PXA_CORGI)
#if 1
	cmd = (1u << MAXCTRL_PD0_SH) | (1u << MAXCTRL_PD1_SH) |
	    (1u << MAXCTRL_SGL_SH) | (1u << MAXCTRL_UNI_SH) |
	    (channel << MAXCTRL_SEL_SH) | (1u <<MAXCTRL_STR_SH);

	voltage = ssp_get_max1111_val(cmd);
#else
...
#endif
	return voltage;
}
      • int sharpsl_get_MainBattery?(void)
      • どこからも呼ばれていないダミー関数。sharpsl_read_MainBattery?()他に置き換わって放棄されたと思われる。この手の罠は他にもあるので注意
#if defined(CONFIG_ARCH_PXA_CORGI)
int sharpsl_get_MainBattery(void)
{
	...
	voltage = Get_DAC_Value(BATT_AD);
	...
	return voltage;
}
#endif
      • int sharpsl_read_MainBattery?(void)
      • 機種に関係なくバッテリ電圧の値を取得。この段階でも値は生のまま。POODLEとCORGIでは電圧値のスケールが全く違うので、以後の処理で個別対応が必要
int sharpsl_read_MainBattery(void)
{
...
#if defined(CONFIG_ARCH_PXA_POODLE)
	voltage = Get_DAC_Value(BATT_CHL);
#elif defined(CONFIG_ARCH_PXA_CORGI)
	voltage = Get_DAC_Value(BATT_AD);
#endif
...
	return voltage;
}
      • int sharpsl_cnv_value(int ad)
      • 関数名からしてADCからの生の値→実際の電圧の変換と誤解してしまいそうだが、直近に計測した生の値を平均化するだけ。バッテリ残量が十分な状態(SHARPSL_BATTERY_STATUS_HIGH)では平均化せず、入力値をそのまま返す。バッテリ残量が少ない状態では電圧の変化が大きいのでならしているものと思われる
      • sharpsl_ad[]は過去計測値のバッファ。sharpsl_cnv_value()が呼ばれる度に新しい値(int ad)が詰め込まれる
#define SHARPSL_CNV_VALUE_NUM	10
static int sharpsl_ad[SHARPSL_CNV_VALUE_NUM+1];
static int sharpsl_ad_index = 0;

/*** get adc *********************************************************************/
int sharpsl_cnv_value(int ad)
{
...
}
      • BATTERY_THRESH *GetMainLevel?( int Volt )
      • ADCから取得した生の電圧値に応じたバッテリ情報構造体(struct BatteryThresh? *)を返す
      • 機種、フロントライトON/OFF、充電中 or notの組み合わせに応じたstruct BatteryThresh?のテーブルが用意されており、ADCからの生電圧値をキーにしてstruct BatteryThresh? *を返す
typedef struct BatteryThresh {
    int voltage;     //ADCから取得できる生の電圧値
    int percentage;  //ユーザに見せる残量(100%,75%,50%,25%,5%,0%)
    int status;      //バッテリ残量区分のシンボル(HIGH,LOW,VERYLOW,CRITICAL)
} BATTERY_THRESH;
...
BATTERY_THRESH sharpsl_main_battery_thresh_charge_nofl[] = {
    { 200, 100, SHARPSL_BATTERY_STATUS_HIGH},
    { 196,  75, SHARPSL_BATTERY_STATUS_HIGH},
    { 192,  50, SHARPSL_BATTERY_STATUS_HIGH},
    { 187,  25, SHARPSL_BATTERY_STATUS_LOW},
    { 171,   5, SHARPSL_BATTERY_STATUS_VERYLOW},
    {   0,   0, SHARPSL_BATTERY_STATUS_CRITICAL},
};
...
BATTERY_THRESH *GetMainLevel( int Volt )
{
    int i;
    BATTERY_THRESH *thresh;

    DPRINTK("  volt = %d  \n",Volt);

    if (counter_step_contrast)
	thresh = sharpsl_main_battery_thresh_fl;
    else
	thresh = sharpsl_main_battery_thresh_nofl;
    for (i = 0; thresh[i].voltage > 0; i++) {
	if (Volt >= thresh[i].voltage)
	    return &thresh[i];
    }
    return &thresh[i];
}
      • int sharpsl_get_main_battery(void)
      • 上記の関数群を使ってバッテリ状態を取得し、グローバル変数に保存する。また、バッテリ残量のシンボル(SHARPSL_BATTERY_STATUS_HIGH等)を返す
      • バッテリ残量が少ない時のバックライト制御の一部をここで行っている。sharpsl_main_bk_flagが真の時の制御責任はsharpsl_battery_thread_main()に分割されている。理由は不明
int sharpsl_main_battery   = SHARPSL_BATTERY_STATUS_HIGH;
int sharpsl_main_battery_percentage = 100;
int sharpsl_main_charge_battery = 100;
int sharpsl_ac_status = APM_AC_OFFLINE;

static int back_ac_status = -1;
static int back_battery_status = -1;
static int sharpsl_check_time = SHARPSL_BATCHK_TIME;

static int sharpsl_main_status_bk = SHARPSL_BATTERY_STATUS_HIGH;
static int sharpsl_main_percent_bk = 100;
static int sharpsl_check_ac_err = 0;
int sharpsl_main_bk_flag = 0;

int sharpsl_get_main_battery(void)
{
	...
	BATTERY_THRESH *thresh;
		...
			voltage = sharpsl_read_MainBattery();
		...
		voltage = sharpsl_cnv_value(voltage);

		thresh = GetMainLevel(voltage);

		// if battery is low , backlight driver become to save power.
		if ( ( ( thresh->status == SHARPSL_BATTERY_STATUS_VERYLOW  ) ||
		       ( thresh->status == SHARPSL_BATTERY_STATUS_CRITICAL ) ||
		       ( thresh->status == SHARPSL_BATTERY_STATUS_LOW      ) ) &&
		       ( !sharpsl_main_bk_flag ) ) {
		  SHARPSL_LIMIT_CONTRAST(SHARPSL_CAUTION_CONTRAST);
		}

		if ( sharpsl_main_bk_flag == 0 ) {
		  return sharpsl_main_battery;
		}
		sharpsl_main_battery = thresh->status;
		sharpsl_main_battery_percentage = thresh->percentage;
		sharpsl_main_charge_battery = GetMainChargePercent(voltage);
	...
	return sharpsl_main_battery;
}
      • void sharpsl_kick_battery_check(int before_waitms,int after_waitms,int flag)
      • バッテリ状態の更新を明示的に指示する関数。ドライバ等からも呼ばれる
      • sharpsl_main_bk_flagはこことdo_ioctl()のcase APM_IOC_RESET_PM:で立てている。bkはbattery kickの略?
      • せめてマジックナンバはやめて欲しかった
      • flag = 2とすると、バッテリ残量状態が変化した時と同じ処理が強制的に走る。次項のsharpsl_battery_thread_main()の解説参照
/*** battery main thread  ***********************************************************/
// waitms : waitms*10 msec wait
// flag   : 0 : check battery w/o reflcting battery status.
//          1 : check battery w/  reflcting battery status.
//          2 : check battery w/  refresh battery status.
void sharpsl_kick_battery_check(int before_waitms,int after_waitms,int flag)
{
  switch (flag) {
  case 0:
    sharpsl_battery_thread_main();
    break;
  case 1:
    if ( sharpsl_main_bk_flag == 0 ) {
      sharpsl_main_bk_flag = 1;
      sharpsl_battery_thread_main();
      sharpsl_main_bk_flag = 0;
    } else {
      sharpsl_battery_thread_main();
    }
    break;
  case 2:
    back_battery_status = -1;
    sharpsl_battery_thread_main();
    break;
  }

  // after check battery
  mdelay(after_waitms*10);
}
      • static int sharpsl_battery_thread_main(void)
      • この関数自体はカーネルスレッドsharpsl_batとは関係ない汎用コードで、sharpsl_battery_thread外の多くのコードから呼ばれる。sharpsl_battery_threadから呼ばれるのが主な使われ方なので"thread_main"という名前が付いているだけ
      • back_battery_statusはprev_battery_statusの意。sharpsl_main_battery(バッテリ状態シンボル)更新前の値が保存されている。以下のようなコードで、バッテリ状態遷移によるエッジトリガを実現している
if ( sharpsl_main_battery != back_battery_status ) {
  状態遷移時の処理
}
      • この関数を呼び出す前にback_battery_status = -1としておき、強制的にトリガをかけるコードがいくつかある。
      • 上述のback_battery_statusを使ったトリガはバックライト制御(低残量時に輝度を落とす)に使われているが、これを制御する責任の一部がsharpsl_get_main_battery()に分散している。理由は不明
      • どうやってカーネルスレッドsharpsl_batとぶつからないようにしているのかは未調査。この関数を呼ぶいくつかの処理にlockらしきものが見当たらないので、さらに外側でうまい事やって競合しないようになってるのかもしれない。Linuxカーネルの事は全然知らないので恥ずかしい事を書いてるかも
      • AC検出等もやっているが、この辺の調査はまたの機会に
static int sharpsl_battery_thread_main(void)
{
...
  // get main battery
  sharpsl_get_main_battery();

  // if battery is low , backlight driver become to save power.
  if ( ( ( sharpsl_main_battery == SHARPSL_BATTERY_STATUS_VERYLOW  ) ||
       ( sharpsl_main_battery == SHARPSL_BATTERY_STATUS_CRITICAL ) ) &&
       ( sharpsl_main_battery != back_battery_status ) &&
       ( sharpsl_main_bk_flag ) ) {
    SHARPSL_LIMIT_CONTRAST(SHARPSL_CAUTION_CONTRAST);
  } else if ( sharpsl_main_battery != back_battery_status ) {
    SHARPSL_LIMIT_CONTRAST(SHARPSL_RESET_CONTRAST);
  }
  back_battery_status = sharpsl_main_battery;
...
}
      • static int sharpsl_battery_thread(void* unused)
      • カーネルスレッドsharpsl_batの本体。処理の中核はsharpsl_battery_thread_main()が担う
static int sharpsl_battery_thread(void* unused)
{
	strcpy(current->comm, "sharpsl_bat");
	sharpsl_check_time = SHARPSL_BATCHK_TIME;

	while(1) {
		interruptible_sleep_on_timeout((wait_queue_head_t*)&battery_queue, sharpsl_check_time );

#if !defined(CONFIG_ARCH_PXA_POODLE) || defined(CONFIG_ARCH_SHARP_SL_J)
...
#endif

		// check battery !
		sharpsl_battery_thread_main();
	}
	return 0;
}
      • void battery_init(void)
      • 電源関係の初期化。電源管理周りのカーネルスレッドを生成する
      • int __init Sharpsl_battery_init(void)から呼ばれる
/*** Config & Setup **********************************************************/
void battery_init(void)
{
...
	/* Make threads */
	kernel_thread(sharpsl_charge_on,      NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
	kernel_thread(sharpsl_charge_off,     NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
	kernel_thread(sharpsl_battery_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);

}
      • int sharpsl_read_Temp(void)
      • 温度取得らしい。詳しくは調べていない
int sharpsl_read_Temp(void)
{
	int temp;

	battery_off_flag = 0;

#if defined(CONFIG_ARCH_PXA_POODLE)
	...
	temp = Get_DAC_Value(MUX_CHL);
	...
#elif defined(CONFIG_ARCH_PXA_CORGI)
	...
	temp = Get_DAC_Value(BATT_THM);
	...
#endif
	if ( battery_off_flag )
		temp = -1;

	return temp;
}

コメント

情報提供や「こんな事が分からない」等のリクエストがあったら気軽に書き込んでください。


お名前: コメント: 更新