How to enable hci0 for bcm4356 chip on htc m9?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Dear community,

I am trying to enable hci0 on the htc m9 phone. Of course, the
bluetooth functionality on that phone with the original CyanogenMod
Android works well. To my surprise, after I mounted a yocto
rootfs(with bluez built in), setup needed pseudo filesystems such as
proc  and chroot into it, the hciconfig finds no hci0 device at all.

So how could I enable the hci0 interface for the bcm4356 chipset? Also
doesn't bluedroid manipulate it at all?

BTW, the /dev/ttyHS0 is available on CyanogenMod Android, I also found
how bluedroid and broadcom's libbt repo (implements the
libbt-vendor.so library) setup its baud rate and writes into it the
content of a firmware patchfile, so I tried to abstract relevant logic
into the attached c file and run it on my own Linux (derived from 3.10
mainline kernel) with bluetooth and /dev/ttyHS0 drivers all enabled,
however, again, I failed to run hciattach to attach /dev/ttyHS0 to
further bring about hci0, despite that this simple c file can achieve
similar result.

The results of bluetooth related commands on my tailored linux on htc
m9 are as followed.

Many thanks for any advice or comment!

Cheers,
Harry



/ # hciconfig hci0
Can't get device info: No such device
/ #

/ # ls -l /dev/ttyHS0
crw-rw----    1 root     dialout   236,   0 May 13 04:50 /dev/ttyHS0
/ # grep 236 /proc/devices
236 ttyHS
/ #

/ # rfkill list
0: bt_power: bluetooth
    Soft blocked: no
    Hard blocked: no
1: bcm4356: bluetooth
    Soft blocked: no
    Hard blocked: no
2: phy0: wlan
    Soft blocked: no
    Hard blocked: no
3: brcmfmac-wifi: wlan
    Soft blocked: no
    Hard blocked: no
/ #

/ # ./bcm4356_init
userial vendor open: opening /dev/ttyHS0
device fd = 3 open
bt vendor lib: set UART baud 4000000
HCI_VSC_LAUNCH_RAM received!
bt vendor lib: set UART baud 115200
bt vendor lib: set UART baud 4000000
Setting local bd addr to 90:E7:C4:F3:F7:09
vendor lib fwcfg completed
/ #

/ # hciattach -n /dev/ttyHS0 any 4000000 90:E7:C4:F3:F7:09
Device setup complete
(blocked, pressed ctrl + c to terminate)
#include <stdint.h>
#include <stdbool.h>
#include <termios.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/**** baud rates ****/
#define USERIAL_BAUD_300        0
#define USERIAL_BAUD_600        1
#define USERIAL_BAUD_1200       2
#define USERIAL_BAUD_2400       3
#define USERIAL_BAUD_9600       4
#define USERIAL_BAUD_19200      5
#define USERIAL_BAUD_57600      6
#define USERIAL_BAUD_115200     7
#define USERIAL_BAUD_230400     8
#define USERIAL_BAUD_460800     9
#define USERIAL_BAUD_921600     10
#define USERIAL_BAUD_1M         11
#define USERIAL_BAUD_1_5M       12
#define USERIAL_BAUD_2M         13
#define USERIAL_BAUD_3M         14
#define USERIAL_BAUD_4M         15
#define USERIAL_BAUD_AUTO       16

/* Stop Bits */
#define USERIAL_STOPBITS_1      1
#define USERIAL_STOPBITS_1_5    (1<<1)
#define USERIAL_STOPBITS_2      (1<<2)

/* Parity Bits */
#define USERIAL_PARITY_NONE     (1<<3)
#define USERIAL_PARITY_EVEN     (1<<4)
#define USERIAL_PARITY_ODD      (1<<5)

/* Data Bits */
#define USERIAL_DATABITS_5      (1<<6)
#define USERIAL_DATABITS_6      (1<<7)
#define USERIAL_DATABITS_7      (1<<8)
#define USERIAL_DATABITS_8      (1<<9)

#define H4_TYPE_COMMAND         1

#define MSG_STACK_TO_HC_HCI_CMD	0x2000

#define VND_PORT_NAME_MAXLEN    256
#define BT_HS_UART_DEVICE "/dev/ttyHS0"


#define FW_PATCHFILE_PATH	"/system/etc/firmware/BCM4354A2_001.003.015.0064.0175.hcd"

#define HCI_CMD_MAX_LEN             258

/* Common HCI commands */
#define HCI_RESET                               0x0C03
#define HCI_READ_LOCAL_NAME                     0x0C14
#define HCI_READ_LOCAL_BDADDR                   0x1009

/* Vendor Specific HCI commands */
#define HCI_VSC_WRITE_UART_CLOCK_SETTING        0xFC45
#define HCI_VSC_UPDATE_BAUDRATE                 0xFC18
#define HCI_VSC_DOWNLOAD_MINIDRV                0xFC2E
#define HCI_VSC_WRITE_BD_ADDR                   0xFC01
#define HCI_VSC_WRITE_SLEEP_MODE                0xFC27
#define HCI_VSC_WRITE_SCO_PCM_INT_PARAM         0xFC1C
#define HCI_VSC_WRITE_PCM_DATA_FORMAT_PARAM     0xFC1E
#define HCI_VSC_WRITE_I2SPCM_INTERFACE_PARAM    0xFC6D
#define HCI_VSC_ENABLE_WBS                      0xFC7E
#define HCI_VSC_LAUNCH_RAM                      0xFC4E

#define HCI_EVT_CMD_CMPL_STATUS_RET_BYTE        5
#define HCI_EVT_CMD_CMPL_LOCAL_NAME_STRING      6
#define HCI_EVT_CMD_CMPL_LOCAL_BDADDR_ARRAY     6
#define HCI_EVT_CMD_CMPL_OPCODE                 3
#define LPM_CMD_PARAM_SIZE                      12
#define UPDATE_BAUDRATE_CMD_PARAM_SIZE          6
#define HCI_CMD_PREAMBLE_SIZE                   3
#define HCD_REC_PAYLOAD_LEN_BYTE                2
#define BD_ADDR_LEN                             6
#define LOCAL_NAME_BUFFER_LEN                   32
#define LOCAL_BDADDR_PATH_BUFFER_LEN            256

#define STREAM_TO_UINT16(u16, p) {u16 = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1))) << 8)); \
		(p) += 2;}
#define UINT8_TO_STREAM(p, u8)   {*(p)++ = (uint8_t)(u8);}
#define UINT16_TO_STREAM(p, u16) {*(p)++ = (uint8_t)(u16); *(p)++ = (uint8_t)((u16) >> 8);}
#define UINT32_TO_STREAM(p, u32) {*(p)++ = (uint8_t)(u32); *(p)++ = (uint8_t)((u32) >> 8); \
		*(p)++ = (uint8_t)((u32) >> 16); *(p)++ = (uint8_t)((u32) >> 24);}


typedef struct
{
    uint16_t          event;
    uint16_t          len;
    uint16_t          offset;
    uint16_t          layer_specific;
    uint8_t           data[];
} HC_BT_HDR;

/* Hardware Configuration State */
enum {
    HW_CFG_START = 1,
    HW_CFG_SET_UART_CLOCK,
    HW_CFG_SET_UART_BAUD_1,
    HW_CFG_READ_LOCAL_NAME,
    HW_CFG_DL_MINIDRIVER,
    HW_CFG_DL_FW_PATCH,
    HW_CFG_SET_UART_BAUD_2,
    HW_CFG_SET_BD_ADDR
#if (USE_CONTROLLER_BDADDR == TRUE)
    , HW_CFG_READ_BD_ADDR
#endif
};

/* Structure used to configure serial port during open */
typedef struct
{
    uint16_t fmt;       /* Data format */
    uint8_t  baud;      /* Baud rate */
} tUSERIAL_CFG;


typedef struct
{
    int fd;                     /* fd to Bluetooth device */
    struct termios termios;     /* serial terminal of BT port */
    char port_name[VND_PORT_NAME_MAXLEN];
} vnd_userial_cb_t;


vnd_userial_cb_t vnd_userial;

static const tUSERIAL_CFG userial_init_cfg =
{
    (USERIAL_DATABITS_8 | USERIAL_PARITY_NONE | USERIAL_STOPBITS_1),
    USERIAL_BAUD_115200
};


void userial_vendor_init(void)
{
	vnd_userial.fd = -1;
	snprintf(vnd_userial.port_name, VND_PORT_NAME_MAXLEN, "%s",
			BT_HS_UART_DEVICE);
}


void userial_to_tcio_baud(uint8_t cfg_baud, uint32_t *baud)
{
    if (cfg_baud == USERIAL_BAUD_115200)
        *baud = B115200;
    else if (cfg_baud == USERIAL_BAUD_4M)
        *baud = B4000000;
    else if (cfg_baud == USERIAL_BAUD_3M)
        *baud = B3000000;
    else if (cfg_baud == USERIAL_BAUD_2M)
        *baud = B2000000;
    else if (cfg_baud == USERIAL_BAUD_1M)
        *baud = B1000000;
    else if (cfg_baud == USERIAL_BAUD_921600)
        *baud = B921600;
    else if (cfg_baud == USERIAL_BAUD_460800)
        *baud = B460800;
    else if (cfg_baud == USERIAL_BAUD_230400)
        *baud = B230400;
    else if (cfg_baud == USERIAL_BAUD_57600)
        *baud = B57600;
    else if (cfg_baud == USERIAL_BAUD_19200)
        *baud = B19200;
    else if (cfg_baud == USERIAL_BAUD_9600)
        *baud = B9600;
    else if (cfg_baud == USERIAL_BAUD_1200)
        *baud = B1200;
    else if (cfg_baud == USERIAL_BAUD_600)
        *baud = B600;
    else
        *baud = B115200;
}


int userial_vendor_open(tUSERIAL_CFG *p_cfg)
{
    uint32_t baud;
    uint8_t data_bits;
    uint16_t parity;
    uint8_t stop_bits;

	userial_to_tcio_baud(p_cfg->baud, &baud);

    if(p_cfg->fmt & USERIAL_DATABITS_8)
        data_bits = CS8;
    else if(p_cfg->fmt & USERIAL_DATABITS_7)
        data_bits = CS7;
    else if(p_cfg->fmt & USERIAL_DATABITS_6)
        data_bits = CS6;
    else if(p_cfg->fmt & USERIAL_DATABITS_5)
        data_bits = CS5;
    else
        return -1;

    if(p_cfg->fmt & USERIAL_PARITY_NONE)
        parity = 0;
    else if(p_cfg->fmt & USERIAL_PARITY_EVEN)
        parity = PARENB;
    else if(p_cfg->fmt & USERIAL_PARITY_ODD)
        parity = (PARENB | PARODD);
    else
        return -1;

    if(p_cfg->fmt & USERIAL_STOPBITS_1)
        stop_bits = 0;
    else if(p_cfg->fmt & USERIAL_STOPBITS_2)
        stop_bits = CSTOPB;
    else
        return -1;

    printf("userial vendor open: opening %s\n", vnd_userial.port_name);

    if ((vnd_userial.fd = open(vnd_userial.port_name, O_RDWR)) == -1) {
        printf("userial vendor open: unable to open %s\n",
			vnd_userial.port_name);
        return -1;
    }

    tcflush(vnd_userial.fd, TCIOFLUSH);

    tcgetattr(vnd_userial.fd, &vnd_userial.termios);
    cfmakeraw(&vnd_userial.termios);
    vnd_userial.termios.c_cflag |= (CRTSCTS | stop_bits);
    tcsetattr(vnd_userial.fd, TCSANOW, &vnd_userial.termios);
    tcflush(vnd_userial.fd, TCIOFLUSH);

    tcsetattr(vnd_userial.fd, TCSANOW, &vnd_userial.termios);
    tcflush(vnd_userial.fd, TCIOFLUSH);
    tcflush(vnd_userial.fd, TCIOFLUSH);

    /* set input/output baudrate */
    cfsetospeed(&vnd_userial.termios, baud);
    cfsetispeed(&vnd_userial.termios, baud);
    tcsetattr(vnd_userial.fd, TCSANOW, &vnd_userial.termios);

    printf("device fd = %d open\n", vnd_userial.fd);

    return vnd_userial.fd;
}

void userial_vendor_set_baud(uint8_t userial_baud)
{
    uint32_t tcio_baud;

    userial_to_tcio_baud(userial_baud, &tcio_baud);

    cfsetospeed(&vnd_userial.termios, tcio_baud);
    cfsetispeed(&vnd_userial.termios, tcio_baud);
    tcsetattr(vnd_userial.fd, TCSANOW, &vnd_userial.termios);
}

uint8_t userial_write(const uint8_t *p_data, uint8_t len)
{
	uint8_t total = 0;
	uint8_t type = H4_TYPE_COMMAND;

	ssize_t ret = write(vnd_userial.fd, &type, 1);
	if (ret < 1) {
                printf("%s error writing to serial port: %s\n", __func__,
				strerror(errno));
		return 0;
	}

	while (len) {
		ret = write(vnd_userial.fd, p_data + total, len);
		switch (ret) {
		case -1:
			printf("%s error writing to serial port: %s\n",
					__func__, strerror(errno));
			return total;
		case 0:  // don't loop forever in case write returns 0.
			return total;
		default:
			total += ret;
			len -= ret;
		break;
		}
	}

	return total;
}

uint8_t vnd_local_bd_addr[6]={0x90, 0xE7, 0xC4, 0xF3, 0xF7, 0x09};

void ms_delay (uint32_t timeout)
{
    struct timespec delay;
    int err;

    if (timeout == 0)
        return;

    delay.tv_sec = timeout / 1000;
    delay.tv_nsec = 1000 * 1000 * (timeout%1000);

    /* [u]sleep can't be used because it uses SIGALRM */
    do {
        err = nanosleep(&delay, &delay);
    } while (err < 0 && errno ==EINTR);
}


int main(int argc, char *argv[])
{
	HC_BT_HDR *p_buf = NULL;
	uint8_t *p;
	uint16_t opcode;
	int fd, fw_fd;
	int ret = -1;
	int delay = 100;

	userial_vendor_init();

	fd = userial_vendor_open((tUSERIAL_CFG *)&userial_init_cfg);
	if (fd < 0) {
		printf("Failed to open firmware patch file\n");
		return -1;
	}

	p_buf = malloc(sizeof(*p_buf) + HCI_CMD_MAX_LEN);
	if (!p_buf) {
		printf("Failed to malloc p_buf\n");
		goto alloc_failed;
	}


        p_buf->event = MSG_STACK_TO_HC_HCI_CMD;
        p_buf->offset = 0;
        p_buf->layer_specific = 0;

	// HCI_RESET
        p = (uint8_t *) (p_buf + 1);
        UINT16_TO_STREAM(p, HCI_RESET);
        *p = 0; /* parameter length */
        p_buf->len = HCI_CMD_PREAMBLE_SIZE;
        userial_write(p_buf->data, p_buf->len);


	// HCI_VSC_WRITE_UART_CLOCK_SETTING - 48M for 4M baudrate
        p = (uint8_t *) (p_buf + 1);
        UINT16_TO_STREAM(p, HCI_VSC_WRITE_UART_CLOCK_SETTING);
	*p++ = 1; /* parameter length */
	*p = 1; /* (1,"UART CLOCK 48 MHz")(2,"UART CLOCK 24 MHz") */

	p_buf->len = HCI_CMD_PREAMBLE_SIZE + 1;
        userial_write(p_buf->data, p_buf->len);

	// HCI_VSC_UPDATE_BAUDRATE	- 4M
        p = (uint8_t *) (p_buf + 1);
	UINT16_TO_STREAM(p, HCI_VSC_UPDATE_BAUDRATE);
	*p++ = UPDATE_BAUDRATE_CMD_PARAM_SIZE; /* parameter length */
	*p++ = 0; /* encoded baud rate */
	*p++ = 0; /* use encoded form */

	UINT32_TO_STREAM(p, 4000000);

	p_buf->len = HCI_CMD_PREAMBLE_SIZE + UPDATE_BAUDRATE_CMD_PARAM_SIZE;
        userial_write(p_buf->data, p_buf->len);

	printf("bt vendor lib: set UART baud 4000000\n");
	userial_vendor_set_baud(USERIAL_BAUD_4M);

	// SKIP HCI_READ_LOCAL_NAME

	fw_fd = open(FW_PATCHFILE_PATH, O_RDONLY);
	if (fw_fd < 0) {
		printf("Failed to open %s\n", FW_PATCHFILE_PATH);
		goto open_failed;
	}

	// HCI_VSC_DOWNLOAD_MINIDRV - download entire patchfile in this state
        p = (uint8_t *) (p_buf + 1);
	UINT16_TO_STREAM(p, HCI_VSC_DOWNLOAD_MINIDRV);
	*p = 0; /* parameter length */
	p_buf->len = HCI_CMD_PREAMBLE_SIZE;

        userial_write(p_buf->data, p_buf->len);
	ms_delay(50);

	int j = 0;

read_again:
        p = (uint8_t *) (p_buf + 1);
	p_buf->len = read(fw_fd, p, HCI_CMD_PREAMBLE_SIZE);
	opcode = (*(p + 1) << 8) | *p;	
	if (p_buf->len == HCI_CMD_PREAMBLE_SIZE) {
		p_buf->len += read(fw_fd, p+HCI_CMD_PREAMBLE_SIZE,
				*(p+HCD_REC_PAYLOAD_LEN_BYTE));
/*

		printf("HCI command[%i] - opcode: %x, len: %d\n", j++,
				opcode, p_buf->len);
		for (int i = 0; i < p_buf->len; i++) {
			printf("%x ", p_buf->data[i]);
		}
		printf("\n");
*/

		if (opcode != HCI_VSC_LAUNCH_RAM)
			userial_write(p_buf->data, p_buf->len);
		else
			printf("HCI_VSC_LAUNCH_RAM received!\n");

		goto read_again;
	}

	printf("bt vendor lib: set UART baud 115200\n");
	userial_vendor_set_baud(USERIAL_BAUD_115200);

	ms_delay(100);

	// HCI_RESET
        p = (uint8_t *) (p_buf + 1);
        UINT16_TO_STREAM(p, HCI_RESET);
        *p = 0; /* parameter length */
        p_buf->len = HCI_CMD_PREAMBLE_SIZE;
        userial_write(p_buf->data, p_buf->len);

	printf("bt vendor lib: set UART baud 4000000\n");
	userial_vendor_set_baud(USERIAL_BAUD_4M);

	// HW_VSC_WRITE_BD_ADDR

	printf("Setting local bd addr to %02X:%02X:%02X:%02X:%02X:%02X\n",
		vnd_local_bd_addr[0], vnd_local_bd_addr[1],
		vnd_local_bd_addr[2], vnd_local_bd_addr[3],
		vnd_local_bd_addr[4], vnd_local_bd_addr[5]);

        p = (uint8_t *) (p_buf + 1);
	UINT16_TO_STREAM(p, HCI_VSC_WRITE_BD_ADDR);
	*p++ = BD_ADDR_LEN; /* parameter length */
	*p++ = vnd_local_bd_addr[5];
	*p++ = vnd_local_bd_addr[4];
	*p++ = vnd_local_bd_addr[3];
	*p++ = vnd_local_bd_addr[2];
	*p++ = vnd_local_bd_addr[1];
	*p = vnd_local_bd_addr[0];

	p_buf->len = HCI_CMD_PREAMBLE_SIZE + BD_ADDR_LEN;
        userial_write(p_buf->data, p_buf->len);

	printf("vendor lib fwcfg completed\n");
	close(fw_fd);

	ret = 0;

open_failed:

	free(p_buf);

alloc_failed:
	close(fd);

	return ret;

}

[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux