blob: 38fdd8a4f82dc146f3e0f8a257692553858c47a1 [file] [log] [blame]
/* Copyright Statement:
*
* This software/firmware and related documentation ("MediaTek Software") are
* protected under relevant copyright laws. The information contained herein
* is confidential and proprietary to MediaTek Inc. and/or its licensors.
* Without the prior written permission of MediaTek inc. and/or its licensors,
* any reproduction, modification, use or disclosure of MediaTek Software,
* and information contained herein, in whole or in part, shall be strictly prohibited.
*
* MediaTek Inc. (C) 2010. All rights reserved.
*
* BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
* THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
* RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
* AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
* NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
* SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
* SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
* THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
* THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
* CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
* SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
* STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
* CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
* AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
* OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
* MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
*
* The following software/firmware and/or related documentation ("MediaTek Software")
* have been modified by MediaTek Inc. All revisions are subject to any receiver's
* applicable license agreements with MediaTek Inc.
*/
/******************************************************************************
* C O M P I L E R F L A G S
*******************************************************************************
*/
/******************************************************************************
* E X T E R N A L R E F E R E N C E S
*******************************************************************************
*/
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
//#include <syslog.h>
#include <termios.h>
#include <time.h>
#include <sys/time.h>
#include <sys/poll.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <linux/serial.h> /* struct serial_struct */
#include "uart_launcher.h"
//---------------------------------------------------------------------------
static int set_speed(int fd, struct termios *ti, int speed);
int setup_uart_param (int hComPort, int iBaudrate, struct UART_CONFIG *sUartConfig);
static int gTtyFd = -1;
static int cont = 1; /** loop continue running */
//---------------------------------------------------------------------------
/* Used as host uart param setup callback */
int setup_uart_param (
int hComPort,
int iBaudrate,
struct UART_CONFIG *sUartConfig)
{
struct termios ti;
int fd;
BPRINT_I("setup_uart_param begin");
if(!sUartConfig){
BPRINT_E("Invalid sUartConfig");
return -2;
}
BPRINT_I("setup_uart_param Baud %d FC %d", iBaudrate, sUartConfig->fc);
fd = hComPort;
if (fd < 0) {
BPRINT_E("Invalid serial port");
return -2;
}
tcflush(fd, TCIOFLUSH);
if (tcgetattr(fd, &ti) < 0) {
BPRINT_E("Can't get port settings");
return -3;
}
cfmakeraw(&ti);
BPRINT_I("ti.c_cflag = 0x%08x", ti.c_cflag);
ti.c_cflag |= CLOCAL;
BPRINT_I("CLOCAL = 0x%x", CLOCAL);
BPRINT_I("(ori)ti.c_iflag = 0x%08x", ti.c_iflag);
BPRINT_I("(ori)ti.c_cflag = 0x%08x", ti.c_cflag);
BPRINT_I("sUartConfig->fc= %d (0:none,sw,hw,linux)", sUartConfig->fc);
switch (sUartConfig->fc) {
/* HW FC Enable */
case UART_HW_FC:
ti.c_cflag |= CRTSCTS;
ti.c_iflag &= ~(NOFLSH);
break;
/* Linux Software FC */
case UART_LINUX_FC:
ti.c_iflag |= (IXON | IXOFF | IXANY);
ti.c_cflag &= ~(CRTSCTS);
ti.c_iflag &= ~(NOFLSH);
break;
/* MTK Software FC */
case UART_MTK_SW_FC:
ti.c_iflag |= CRTSCTS;
ti.c_cflag &= ~(NOFLSH);
break;
/* default disable flow control */
default:
ti.c_cflag &= ~(CRTSCTS);
ti.c_iflag &= ~(NOFLSH|CRTSCTS);
}
BPRINT_D("c_c CRTSCTS = 0x%16x", CRTSCTS);
BPRINT_D("c_i IXON = 0x%08x", IXON);
BPRINT_D("c_i IXOFF = 0x%08x", IXOFF);
BPRINT_D("c_i IXANY = 0x%08x", IXANY);
BPRINT_D("(aft)ti.c_iflag = 0x%08x", ti.c_iflag);
BPRINT_D("(aft)ti.c_cflag = 0x%08x", ti.c_cflag);
if (tcsetattr(fd, TCSANOW, &ti) < 0) {
BPRINT_E("Can't set port settings");
return -4;
}
/* Set baudrate */
if (set_speed(fd, &ti, iBaudrate) < 0) {
BPRINT_E("Can't set initial baud rate");
return -5;
}
tcflush(fd, TCIOFLUSH);
BPRINT_I("%s done", __func__);
return 0;
}
//---------------------------------------------------------------------------
static speed_t get_speed (int baudrate)
{
unsigned int idx;
for (idx = 0; idx < sizeof(speeds)/sizeof(speeds[0]); idx++) {
if (baudrate == (int)speeds[idx].baud) {
return speeds[idx].speed;
}
}
return CBAUDEX;
}
//---------------------------------------------------------------------------
int set_speed(int fd, struct termios *ti, int speed)
{
struct serial_struct ss;
int baudenum = get_speed(speed);
if (speed != CBAUDEX) {
//printf("%s: standard baudrate: %d -> 0x%08x\n", __FUNCTION__, speed, baudenum);
if ((ioctl(fd, TIOCGSERIAL, &ss)) < 0) {
BPRINT_E("%s: BAUD: error to get the serial_struct info:%s\n", __func__, strerror(errno));
return -1;
}
#ifdef ANDROID
ss.flags &= ~ASYNC_SPD_CUST;
#if defined(SERIAL_STRUCT_EXT) /*modified in serial_struct.h*/
memset(ss.reserved, 0x00, sizeof(ss.reserved));
#endif
ss.flags |= (1 << 13); /*set UPFLOWLATENCY flat to tty, or serial_core will reset tty->low_latency to 0*/
/*set standard buadrate setting*/
if ((ioctl(fd, TIOCSSERIAL, &ss)) < 0) {
BPRINT_E("%s: BAUD: error to set serial_struct:%s\n", __func__, strerror(errno));
return -2;
}
#endif
cfsetospeed(ti, baudenum);
cfsetispeed(ti, baudenum);
return tcsetattr(fd, TCSANOW, ti);
}
else {
BPRINT_E("%s: unsupported non-standard baudrate: %d -> 0x%08x\n", __func__, speed, baudenum);
return -3;
}
}
//---------------------------------------------------------------------------
void init_flock(struct flock *flk)
{
flk->l_type = F_WRLCK;
flk->l_whence = SEEK_SET;
flk->l_pid = getpid();
flk->l_start = 0;
flk->l_len = 0;
}
void unlock_flock(int fd, struct flock *fl, int type, int whence)
{
fl->l_type = type;
fl->l_whence = whence;
if (fcntl(fd, F_SETLKW, fl) < 0)
BPRINT_E("%s: fcntl failed(%d)", __func__, errno);
}
//---------------------------------------------------------------------------
int cmd_hdr_baud (struct UART_CONFIG *sUartConfig, int baudrate) {
return (gTtyFd != -1) ? setup_uart_param(gTtyFd, baudrate, sUartConfig) : -1;
}
//---------------------------------------------------------------------------
int osi_system(const char *cmd)
{
FILE *fp;
int ret;
if (cmd == NULL) {
BPRINT_E("%s: cmd is NULL", __func__);
return -1;
}
fp = popen(cmd, "w");
if (fp == NULL) {
BPRINT_E("%s: (%s) failed", __func__, cmd);
return -1;
}
BPRINT_I("Command: %s", cmd);
ret = pclose(fp);
if (ret < 0) {
BPRINT_E("%s: pclose ret = %d", __func__, ret);
} else if (ret > 0) {
BPRINT_I("%s: pclose ret = %d", __func__, ret);
}
return ret;
}
//---------------------------------------------------------------------------
static void uart_launcher_sig_handler(int signum)
{
BPRINT_I("%s: %d", __func__, signum);
cont = 0;
}
//---------------------------------------------------------------------------
int main(int argc, char *argv[])
{
BPRINT_I("Bluetooth Uart Launcher Ver %s", VERSION);
int ld = 0;
struct pollfd fds;
int fd_num = 0;
int err = 0;
int opt;
int baudrate = 115200;
int chang_baud_rate = 0;
int retry = 0;
int flow_control = UART_DISABLE_FC;
char *tty_path = "/dev/ttyUSB0";
struct UART_CONFIG sUartConfig;
struct sigaction sigact;
struct flock fl;
memset(&sUartConfig, 0, sizeof(struct UART_CONFIG));
memset(&fds, 0, sizeof(struct pollfd));
sUartConfig.fc = flow_control;
/* Register signal handler */
sigact.sa_handler = uart_launcher_sig_handler;
sigact.sa_flags = 0;
sigemptyset(&sigact.sa_mask);
sigaction(SIGINT, &sigact, NULL);
sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGQUIT, &sigact, NULL);
sigaction(SIGKILL, &sigact, NULL);
init_flock(&fl);
ld = N_MTK;
while ((opt = getopt(argc, argv, "c:f:p:k:l::")) != -1) {
switch (opt) {
/* change baudrate */
case 'c':
baudrate = atoi(optarg);
BPRINT_I("baudrate = %d", baudrate);
/* not need to change if baudrate is same */
if (baudrate == CUST_BAUDRATE_DFT) {
chang_baud_rate = 0;
} else
chang_baud_rate = 1;
break;
/* flow control */
case 'f':
flow_control = atoi(optarg);
BPRINT_I("flow_control = %d", flow_control);
break;
/* tty path */
case 'p':
tty_path = optarg;
BPRINT_I("Log path is %s", tty_path);
break;
/* kill process */
case 'k':
osi_system("killall uart_launcher");
BPRINT_I("Kill uart_launcher");
return 0;
/* set ldisc */
case 'l':
ld = atoi(optarg);
BPRINT_I("set ldisc[%d]", ld);
break;
case '?':
default:
BPRINT_I("set baud:\t uart_launcher -c [baudrate]");
BPRINT_I("flow control:\t uart_launcher -f [flow_control]");
BPRINT_I("\t\t 0:disable FC 1:MTK_SW_FC 2: SW_FC 3: HW_FC");
BPRINT_I("tty path:\t uart_launcher -p [path]");
BPRINT_I("kill process:\t uart_launcher -k");
goto exit;
break;
}
}
/* open ttyUSB */
BPRINT_I("Running...");
/* node may not ready, retry 20 times */
while (1) {
gTtyFd = open(tty_path, O_RDWR | O_NOCTTY | O_NONBLOCK);
BPRINT_I("open done ttyfd %d", gTtyFd);
if (gTtyFd < 0) {
if (retry > 20) {
BPRINT_E("ttyfd %d, error", gTtyFd);
goto exit;
} else {
retry++;
(void)usleep(1000 * 1000);
}
} else
break;
}
/* flock the device node */
BPRINT_I("flock the device node");
if (fcntl(gTtyFd, F_SETLK, &fl) < 0) {
BPRINT_E("lock device node failed, uart_launcher already running.");
goto exit;
}
/* to ensure driver register TIOCSETD, retry 20 times */
retry = 0;
while (1) {
if (ioctl(gTtyFd, TIOCSETD, &ld) < 0) {
if (retry > 20) {
BPRINT_E("set TIOCSETD N_MTK error");
goto exit;
} else {
retry++;
(void)usleep(1000 * 1000);
}
} else
break;
}
restart:
/* Set default Baud rate */
sUartConfig.iBaudrate = CUST_BAUDRATE_DFT;
BPRINT_I("set baudtate = %d", CUST_BAUDRATE_DFT);
cmd_hdr_baud(&sUartConfig, CUST_BAUDRATE_DFT);
fds.fd = gTtyFd;
fds.events = POLLIN;
++fd_num;
err = ioctl(gTtyFd, HCIUARTINIT, NULL);
if (err < 0) {
BPRINT_E("set HCIUARTINIT error %d", err);
return 0;
}
err = ioctl(gTtyFd, HCIUARTGETBAUD, NULL);
if (err < 0) {
BPRINT_E("set HCIUARTGETBAUD error %d", err);
return 0;
}
/* chang baud rate */
if (chang_baud_rate | flow_control) {
sUartConfig.iBaudrate = baudrate;
sUartConfig.fc = flow_control;
err = ioctl(gTtyFd, HCIUARTSETBAUD, &sUartConfig);
if (err < 0) {
BPRINT_E("set HCIUARTSETBAUD error %d", err);
return 0;
}
BPRINT_I("set baudtate %d", baudrate);
cmd_hdr_baud(&sUartConfig, baudrate);
err = ioctl(gTtyFd, HCIUARTSETWAKEUP, NULL);
if (err < 0) {
BPRINT_E("set HCIUARTSETWAKEUP error %d", err);
return 0;
}
}
if (ioctl(gTtyFd, HCIUARTLOADPATCH, NULL) < 0) {
BPRINT_E("set HCIUARTLOADPATCH error");
return 0;
}
while (cont) {
err = poll(&fds, fd_num, 2000);
if (err < 0) {
if (errno == EINTR) {
continue;
}
else {
BPRINT_E("poll error:%d errno:%d, %s", err, errno, strerror(errno));
break;
}
} else if (!err) {
if (fds.revents & POLLIN) {
goto restart;
} else {
continue;
}
}
goto restart;
}
/* before exit daemon, return baud to default */
if (chang_baud_rate | flow_control) {
sUartConfig.iBaudrate = CUST_BAUDRATE_DFT;
sUartConfig.fc = UART_DISABLE_FC;
err = ioctl(gTtyFd, HCIUARTSETBAUD, &sUartConfig);
if (err < 0) {
BPRINT_E("set HCIUARTSETBAUD error %d", err);
return 0;
}
BPRINT_I("set baudtate %d", CUST_BAUDRATE_DFT);
cmd_hdr_baud(&sUartConfig, CUST_BAUDRATE_DFT);
err = ioctl(gTtyFd, HCIUARTSETWAKEUP, NULL);
if (err < 0) {
BPRINT_E("set HCIUARTSETWAKEUP error %d", err);
return 0;
}
}
exit:
/* unlock ttyFd */
if (gTtyFd > 0) {
BPRINT_I("unlock_flock");
unlock_flock(gTtyFd, &fl, F_UNLCK, SEEK_SET);
close(gTtyFd);
}
BPRINT_I("uart_launcher stop");
return 0;
}
//---------------------------------------------------------------------------