アットウィキロゴ

Bluetooth電源関連

Bluetooth 電源関連


無線LANの時と同様に行くと思いきや全く同様にではなかった。

無線LANの場合
# echo 1 > /sys/devices/platform/bwpm/wifi
で電源onなのだが…、同じsysファイルシステムのディレクトリを参照すると、
# cd /sys/devices/platform/bwpm/wifi
# ls -l
  • rw-rw-rw- root root 4096 2011-09-27 21:55 fm
  • rw-rw-rw- root root 4096 2011-09-27 16:11 bluetooth
  • rw-rw-rw- root root 4096 2011-09-27 21:55 wifi
bluetoothがある、これは!?

# echo 1 > /sys/devices/platform/bwpm/bluetooth

としてみたが、反応無し!

ということで、kernelソースコードに入る。

反応しない訳

echo 1 > /sys/devices/platform/bwpm/bluetooth で反応しない訳

$(KERNEL_SRC)/drivers/sharp/bwpm/bwpm.c
を参照すると、
static
ssize_t show_bluetooth_power(struct device *pdev, struct device_attribute *pattr, char *buf)
{
	bwpm_data_t *p_priv = (bwpm_data_t *)dev_get_drvdata(pdev);

	buf[0] = (char)(p_priv->bluetooth);
	
	return( 1 );
}

static
ssize_t set_bluetooth_power(struct device *pdev, struct device_attribute *pattr, const char *buf, size_t count)
{
	if ( (buf[0]==0) || (buf[0]==1) ){
		bwpm_bluetooth_on( pdev, (int)buf[0] );
        return( count );
	}

	return( 0 );
}

static
ssize_t show_wifi_power(struct device *pdev, struct device_attribute *pattr, char *buf)
{
	bwpm_data_t *p_priv = (bwpm_data_t *)dev_get_drvdata(pdev);
	int status;

	status = p_priv->wifi;
	
	return snprintf( buf, PAGE_SIZE, "%d\n" , status );
}

static
ssize_t set_wifi_power(struct device *pdev, struct device_attribute *pattr, const char *buf, size_t count)
{
	int new_status;

	sscanf( buf, "%d", &new_status );

	if ( (new_status==0) || (new_status==1) ){
		bwpm_wifi_on( pdev, new_status );
	}

	return count;
}

と…、見てみる。
set_wifi_power は buf から sscanf で atoi の様な動作を入れてある。
sscanf( buf, "%d", &new_status );
show_wifi_powerも同様に、bufに文字列が、戻り値にlengthが入るように加工されている。
return snprintf( buf, PAGE_SIZE, "%d\n" , status );

show_bluetooth_power 及び set_bluetooth_power は buf に ASCIIコード文字列ではなく、char型 整数をそのまま受け渡している。対応させるには、open して write でバイナリを書き込むしか無いか??。

kernel 内での Bluetooth HW サスペンドとレジューム

$(KERNEL_SRC)/drivers/serial/serial_core.c
上記のコード内にて、下記のような部分を見つけたIS01 デフォルト1.6でBluetoothをon/offした際に出てくる。
また、デフォルトではタイマーを用いて一定時間経つとレジュームしているような気がする。

デフォルト1.6での挙動を調べるために
printk ("uart_ioctl() >> msm_hs_request_clock_off()\n");
部分の DEBUG 情報に、tty の ネームを出すように変更
printk ("%s:uart_ioctl() >> msm_hs_request_clock_off()\n",tty->name);

この結果から、Suspend the HW driver 及び、 Resume the HW driver は
ttyHS0
に向けて発行されていることが明らかになった。 ttyHS0 は owner が bluetooth になっているので、おそらくこれが、deviceファイルだと思っていたが確信は無かったので^^;

$(KERNEL_SRC)/drivers/serial/serial_core.c
(抜粋&省略)
/* ===== ADDED BY CSR: START ===== */
#define TIOSPSERIAL	        (0x5520)
#define TIORPSERIAL	        (0x5521)

#ifdef CONFIG_SERIAL_MSM_HS
void msm_hs_request_clock_off(struct uart_port *uport);
void msm_hs_request_clock_on(struct uart_port *uport);
#endif
/* ===== ADDED BY CSR: END ===== */
(省略)
	/* ===== ADDED BY CSR: START ===== */
	
	case TIOSPSERIAL: /* Suspend the HW driver */
    {
#ifdef CONFIG_SERIAL_MSM_HS
		struct uart_state *state = tty->driver_data;
		struct uart_port *port = state->port; ret = 0;
		// Customize for Bluetooth
//		gpio_set_value( 155, 0 );
		msm_hs_request_clock_off(port);
		uart_change_pm(state, 3);
#endif
		//printk ("uart_ioctl() >> msm_hs_request_clock_off()\n");
		printk ("%s:uart_ioctl() >> msm_hs_request_clock_off()\n",tty->name);
		break;
    }
	case TIORPSERIAL: /* Resume the HW driver */
	{
#ifdef CONFIG_SERIAL_MSM_HS
		struct uart_state *state = tty->driver_data;
		struct uart_port *port = state->port;

		// Customize for Bluetooth
//		gpio_set_value( 155, 1 );
		uart_change_pm(state, 0);
		msm_hs_request_clock_on(port);
#endif
		//printk ("uart_ioctl() >> msm_hs_request_clock_on()\n");
                printk ("%s:uart_ioctl() >> msm_hs_request_clock_on()\n",tty->name);

		ret = 0;
		break;
    }
	/* ===== ADDED BY CSR: END ===== */
(省略)



電源を on/off する(していると思われる)。

ということで、bluetooh を on/off するだけの toggle コードを書いた。
先に記述したカーネルの
/sys/devices/platform/bwpm/bluetooth
部分と、
$(KERNEL_SRC)/drivers/serial/serial_core.c
部分に対応させてみた。

使い方

ソースコードは  btsw.zip  からDLできます。
ANDROIDソースの system/extras に btsw ディレクトリを解答して下さい。
ソースのルートで
$ source ./build/envsetup.sh
ターゲットボードの指定のある方は指定してから
$ cd system/extras/btsw/
$ mm
とすると、ターゲットの /system/bin/ に btsw と btstatus が出来ているかと思います。

btsw 結果

# btsw
bluetooth now status=(on)
open(/sys/devices/platform/bwpm/bluetooth) write (0) off success
# btsw
bluetooth now status=(off)
open(/sys/devices/platform/bwpm/bluetooth) write (1) on success
とするだけで、Bluetoothのon/offをしているつもり
hciattachを起動していると、off→on に変わったときbluetoothで書いた h4_recv: Unknown HCI packet type が大量に出てくるので動いているような気がする。

btstatus 結果

# btstatus
bluetooth now status=(on)
# btstatus
bluetooth now status=(off)
となる。

少なくとも、dmesg で得られる結果はそうなっているが実際の動作がどうなのかは未検証。

dmesg 結果


off の結果
<3>[24260.192424] [bwpm] bwpm_bluetooth_on: Low-Power-mode
<3>[24260.192444] [bwpm] bwpm_bluetooth_on: RF OFF
<3>[24260.192450] [bwpm] bwpm_bluetooth_on: change status (1->0)
<4>[24260.192617] ttyHS0:uart_ioctl() >> msm_hs_request_clock_off()

on の結果
<3>[24320.804430] [bwpm] bwpm_bluetooth_on: RF ON
<3>[24320.805377] [bwpm] bwpm_bluetooth_on: Normal-Power-mode
<3>[24320.805390] [bwpm] bwpm_bluetooth_on: change status (0->1)


コメント
名前:
コメント:

すべてのコメントを見る

btsw ソース

/* IS01 Bluetooth power switch test */
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#define BT_SWITCH_FILE  "/sys/devices/platform/bwpm/bluetooth"
#define BT_UART_FILE  "/dev/ttyHS0"
/* ===== ADDED BY CSR: START ===== */
#define TIOSPSERIAL	        (0x5520)
#define TIORPSERIAL	        (0x5521)
/* ===== ADDED BY CSR: END ===== */

int main(int argc, char **argv)
{
    int sz;
    int fd = -1;
    int ret = -1;
    char buffer = 0;

    // 現在の状態確認
    fd=open(BT_SWITCH_FILE, O_RDONLY );
    if(fd != -1 ){
        sz=read(fd, &buffer, 1);
        if( sz == 1 ){
            printf("bluetooth now status=(%s)\n",(buffer? "on" : "off" ) );
        }
        close(fd);
    }

    // 状態設定
    buffer = (buffer==1 ? 0 : 1 );
    
    // BT UART のレジューム設定
    if( buffer == 1 ) {
        fd=open(BT_UART_FILE, O_RDWR );
        if( fd < 0 )
            return -1;

        if( ioctl(fd, TIORPSERIAL, NULL) ) {
            close(fd);
            perror("ioctl failed");
            return ret;
        }
        close(fd);
    }

   // 電源系
    fd=open(BT_SWITCH_FILE, O_WRONLY );
    if( fd != -1 ){
        sz=write(fd, &buffer, 1);
        if( sz < 0 ){
            printf("write(%s) failed: %s (%d)", BT_SWITCH_FILE,
                 strerror(errno), errno);
        }else{
            ret=0;
            printf("open(%s) write (%d) %s success\n",BT_SWITCH_FILE,buffer,(buffer? "on" : "off" ));
        }
        close(fd);
    }
    else {
        printf("open(%s) for write failed: %s (%d)", BT_SWITCH_FILE,
             strerror(errno), errno);
    }

    // BT UART のレジューム設定
    int cmd=TIOSPSERIAL;
    if( buffer == 1 ) {
        cmd=TIORPSERIAL;
    }

    if( cmd == TIOSPSERIAL ){
        fd=open(BT_UART_FILE, O_RDWR );
        if( fd < 0 )
            return -1;

        if( ioctl(fd, cmd, NULL) ) {
            close(fd);
            perror("ioctl failed");
            return -1;
        }
        close(fd);
    }

    return ret;
}


(2011/09/27 22:44 公開)
最終更新:2011年09月27日 23:15
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。