2014/10/27

Android並無提供FM Radio的官方API, 只能vendor自己新增API and APK

最近的工作要研究Qualcomm WCNXXX晶片
WCNXXX是一個結合 BT/WLAN/FM 三個功能的整合性晶片, 而且必須搭配Qualcomm SOC使用

重點來了,
今天如果消費者要使用Android手機聽廣播, 有兩種方法

1. 網路串流的APK
這類的App超多的, 因為資料是透過網路傳輸
所以一定要連上網路才能使用

2. 真正透過FM訊號收聽廣播
就像收音機一樣, 不需要連上網路即可依照頻率收聽
只要手機有電就可以啦

方法一的APP只要上google play隨便找都有,
但方法二的app通常百分之9成9只能使用手機廠商內建的Radio廣播APK

原因是:

Google 原本的 framwork並不提供 FM Radio 的 API (official API)
所以即便Chip支援FM, 但傳輸到 APK之間的路徑沒有標準做法API
只能系統廠與晶片Vendor自己做中間的API (vendor-specific API)
因此各家的做法都不同, 更不可以做出通用版FM Radio APK 上google Play囉

2014/10/22

linux - 網路設備 ioctl 命令參數範圍:SIOCDEVPRIVATE(0-15)

struct iwreq iwr;
s = socket(AF_INET, SOCK_DGRAM, 0);

ioctl(s, (SIOCDEVPRIVATE+15), &iwr) 正常调用
ioctl(s, (SIOCDEVPRIVATE+16), &iwr) 返回错误,应为内核代码只分配了0-15的空间为用户私有可用的。

见代码:
linux/net/code/dev.c

 /*   * Unknown or private ioctl.   */  default:   if (cmd == SIOCWANDEV ||       (cmd >= SIOCDEVPRIVATE &&        cmd <= SIOCDEVPRIVATE + 15)) {    dev_load(net, ifr.ifr_name);    rtnl_lock();    ret = dev_ifsioc(net, &ifr, cmd);    rtnl_unlock();    if (!ret && copy_to_user(arg, &ifr,        sizeof(struct ifreq)))     ret = -EFAULT;    return ret;   }   /* Take care of Wireless Extensions */   if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)    return wext_handle_ioctl(net, &ifr, cmd, arg);   return -EINVAL;

------------------------------
其中
(cmd >= SIOCDEVPRIVATE && cmd <= SIOCDEVPRIVATE + 15))为用户私有,所以在ioctl调用时,只能使用(SIOCDEVPRIVATE+0)到 (SIOCDEVPRIVATE+15),

2014/10/21

Android - Wifi 初始化過程

一張簡圖就是最基本的初始化過程啦!

稍微描述如下列各項:

1) 透過 new WifiService(context) 開啟wifi的服務, 也透過ConnectivityService() 開啟 connectivity服務
位置: android/frameworks/base/services/java/com/android/server/SystemServer.java

               try {                    Slog.i(TAG, "Wi-Fi Service");                    wifi = new WifiService(context);                    ServiceManager.addService(Context.WIFI_SERVICE, wifi);                } catch (Throwable e) {                    reportWtf("starting Wi-Fi Service", e);                }               try {                   int enableCne = 1;                   if (!deviceHasSufficientMemory()) {                       enableCne = SystemProperties.getInt("persist.cne.override.memlimit", 0);                   }                   int cneFeature = (enableCne == 1) ?                       SystemProperties.getInt("persist.cne.feature", 0) : 0;                   if ( cneFeature > 0 && cneFeature < 10 ) {                       Slog.i(TAG, "QcConnectivity Service");                       PathClassLoader qcsClassLoader =                           new PathClassLoader("/system/framework/services-ext.jar",                                   ClassLoader.getSystemClassLoader());                       Class qcsClass =                           qcsClassLoader.loadClass("com.android.server.QcConnectivityService");                       Constructor qcsConstructor = qcsClass.getConstructor                           (new Class[] {Context.class, INetworkManagementService.class,                            INetworkStatsService.class, INetworkPolicyManager.class});                       qcCon = qcsConstructor.newInstance(                               context, networkManagement, networkStats, networkPolicy);                       connectivity = (ConnectivityService) qcCon;                   } else {                       Slog.i(TAG, "Connectivity Service");                       connectivity = new ConnectivityService( context, networkManagement,                               networkStats, networkPolicy);                   }                   if (connectivity != null) {                       ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);                       networkStats.bindConnectivityManager(connectivity);                       networkPolicy.bindConnectivityManager(connectivity);                       wifi.checkAndStartWifi();                       wifiP2p.connectivityServiceReady();                   }               } catch (Throwable e) {                   reportWtf("starting Connectivity Service", e);               }

2) 由第一項SystemServer.java裡呼叫 WifiService()定義
位置: android/frameworks/base/services/java/com/android/server/wifi

/** * WifiService handles remote WiFi operation requests by implementing * the IWifiManager interface. * * @hide */public final class WifiService extends IWifiManager.Stub {    private static final String TAG = "WifiService";    private static final boolean DBG = false;    final WifiStateMachine mWifiStateMachine;    private final Context mContext;   ....

3) 由第一項SystemServer.java裡呼叫 ConnectivityService()
位置: android/frameworks/base/services/java/com/android/server/ConnectivityService.java

/** * @hide */public class ConnectivityService extends IConnectivityManager.Stub {    private static final String TAG = "ConnectivityService";    protected static final boolean DBG = true;    protected static final boolean VDBG = false;    protected static final boolean LOGD_RULES = false;    ...

4) 由第三項ConnectivityService.java裡呼叫 WifiStateTracker()
位置: android/frameworks/base/services/java/com/android/server/wifi/WifiStateTracker

    public WifiStateTracker(int netType, String networkName) {        mNetworkInfo = new NetworkInfo(netType, 0, networkName, "");        mLinkProperties = new LinkProperties();        mLinkCapabilities = new LinkCapabilities();        mNetworkInfo.setIsAvailable(false);        setTeardownRequested(false);    }

以上的話, 大致wifi的初始化ok

2014/10/20

Linux- 如何由User Space透過ioctl得知MAC Address

可以直接寫個程式碼透過 SIOCGIFHWADDR 這個關鍵字,
使用方法 ioctl( SIOCGIFHWADDR );
去Kernel Space得到資訊再傳回User Space

範例如下:

#include <stdio.h>#include <string.h>#include <unistd.h>#include <net/if.h>#include <sys/ioctl.h>#include <sys/types.h>#include <sys/socket.h>int GetMac( const char *ifname, unsigned char *mac ){	int	sock, ret;	struct	ifreq	ifr;	sock = socket( AF_INET, SOCK_STREAM, 0 );	if ( sock < 0 ) {		perror( "socket" );		return -1;	}	memset( &ifr, 0, sizeof(ifr) );	strcpy( ifr.ifr_name, ifname );	ret = ioctl( sock, SIOCGIFHWADDR, &ifr, sizeof(ifr) );	if ( ret == 0 ) {		memcpy( mac, ifr.ifr_hwaddr.sa_data, 6 );	} else {		perror( "ioctl" );	}	close( sock );	return ret;}int main( int argc, char **argv ){	int	ret;	char	ifname[IFNAMSIZ];	unsigned char	mac[6];	if ( argc == 1 ) {		strcpy( ifname, "eth0" );	} else {		strcpy( ifname, argv[1] );	}	memset( mac, 0, sizeof(mac) );	ret = GetMac( ifname, mac );	if ( ret == 0 ) {		printf( "%s mac address is: [%02X:%02X:%02X:%02X:%02X:%02X]\n", ifname, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] );	} else {		fprintf( stderr, "Can't get %s's mac address\n", ifname );	}	return 0;} 

2014/10/16

[轉]基礎 Linux Device Driver 驅動程式#10 (select/poll)

備份好文章
http://csw-dawn.blogspot.tw/2012/01/linux-device-driver-10-selectpoll.html


基礎 Linux Device Driver 驅動程式#10 (select/poll)

相信各位都有在Linux上寫程式的經驗,
當您程式裡呼叫 open 時,Linux 預設會以 blocking mode的方式開啟,
block 是指當 process 為了等待某件事的發生,而進入 sleep 狀態的情形。

像 read 就是其中一種,當沒資料讀取時,process 就會被 block。
write 也是一樣,在寫入資料時,寫入對象還無法處理資料時,一樣會 block。

對某些程式來說,如果 read 系統呼叫被 block 的話,有時就會有設計上的問題,
所以為了避免這種問題發生,Linux 就準備了以下方式。

1. Non-blocking 模式

啟用 non-blocking 模式後,不管是 read 還是 write 就不會被 block住。但會傳回 errno 錯誤碼,
這時就必須自已再做讀寫的動作。
想使用 non-blocking 模式的話,可在 open() 開檔時指定 O_NONBLOCK。

2. 同時執行多個同步 I/O 工作

同時執行多個同步 I/O 工作 指的是使用 select 系統呼叫的做法。
select 系統呼叫本身會被 block, 但可指定 timeout。

使用 select() 的時候,有幾個常用的函式巨集,可參考 man 2 select。

int select(int nfds, fd_set *readfds, fd_set *writefds,    fd_set *exceptfds, struct timeval *timeout);void FD_CLR(int fd, fd_set *set);int  FD_ISSET(int fd, fd_set *set);void FD_SET(int fd, fd_set *set);void FD_ZERO(fd_set *set);

還有一個和 select()很類似的 poll()系統呼叫,以基本功能來說,select()與poll()都一樣,
但指定的 file handler的方式不一樣,且指定多個 file handler 的時候,poll()走訪所有
file handler的速度比較快。 可參考 man 2 poll。

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

應用程式想同時執行多個同步 I/O 工作時,可使用的系統呼叫有好幾個,
但在驅動程式,只需要準備一個函式即可。
不管 user process 用了哪個系統呼叫,kernel 都只會呼叫驅動程式提供的這個函式。

Character 類型的裝置想支援同時執行多個同步 I/O 工作的話,只要在驅動程式準備 poll方法即可。
poll方法會收到 file_operations 結構。

unsigned int (*poll) (struct file *, struct poll_table_struct *);

poll方法會在kernel 處理 select 與 poll 之類的系統呼叫時用到。它必須的執行工作如下:
1. 在 wait queue 豋記。
2. 傳回目前可以操作的狀態。

在呼叫同時執行多個同步 I/O 工作的系統呼叫時,block 直到狀態變化的動作,指的是在 kernel裡面 sleep。
如果要 sleep的話,要先準備 wait queue(wait_queue_head_t),這個由驅動程式負責提供。
豋記 wait queue 的工作可透過 poll_wait()完成,它定義在 include/linux/poll.h

void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p);// filp:執行操作的設備文件指針// wait_address:設備驅動的等待隊列頭// p:內核傳遞的輪詢表指針 調用poll_wait()函數將等待隊列添加到poll_table輪詢表 (出處參考)

在呼叫同時執行多個同步 I/O 工作的系統呼叫時,解除 block 的時機,是驅動程式透過傳給 poll_wait()
的 wait queue 被喚醒的時候。Kernel 在被 wait queue 喚醒之後,會再次呼叫驅動程式的 poll()
確認是否成為等待中的狀態(可寫入或可讀出),如果是的話,FD_ISSET 巨集就會成立。
想判斷是不是這種狀態的話,還是需要驅動程式提供資訊才行,這個資訊就是透過poll()方法的傳回值來表示。
傳回值要透過 include/linux/poll.h 定義的巨集 OR 起來表示,下面是常用到的組合。
數值定義於 android/bionic/libc/kernel/arch-arm/asm/poll.h

POLLIN|POLLRDNORM                     可讀取POLLOUT|POLLWRNORM                    可寫入POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM  可讀寫POLLERR                               發生錯誤POLLHUP                               裝置離線(EOF)

說了那麼多,還倒不如寫個程式比較好了解。

poll操作一般結構:

xxx_poll() {   unsigned int mask = 0;//定義返回值   ...   //調用poll_wait()把進程添加到輪詢表   ...   if(device is ready for read){     mask = POLLIN | POLLRDNORM;   }   ...   return mask; } 

剛試了一下,果然新舊版核心還是有差,幾個問題,稍為提出來討論一下:
1. 要用 kmalloc 及 kfree 的話,要 include 註:其實在上一個示範程式就已有這問題了。
請參考: 基礎 Linux Device Driver 驅動程式#9 (IOCTL)
2. void init_MUTEX (struct semaphore *sem); /* 新版 kernel 已不適用 */
改用
void sema_init (struct semaphore *sem, int val);

test_select.c 原始碼如下:

/*****************************************************************************/#include #include #include #include #include #include #include #include #include #include #include #define DRIVER_NAME "test_select"static unsigned int test_select_major = 0;static unsigned int num_of_dev = 1;static struct cdev test_select_cdev;static unsigned int timeout_value = 10;struct test_select_data { struct timer_list timeout; spinlock_t lock; wait_queue_head_t read_wait; int timeout_done; struct semaphore sem;};unsigned int test_select_poll(struct file *filp, poll_table *wait){ struct test_select_data *data = filp->private_data; unsigned int mask = POLLOUT|POLLWRNORM; printk(KERN_ALERT "Call test_select_poll.\n"); if (data == NULL)  return -EBADFD; down(&data->sem); poll_wait(filp, &data->read_wait, wait); if (data->timeout_done == 1) {    /* readable */  mask |= POLLIN|POLLRDNORM; } up(&data->sem); printk(KERN_ALERT "%s returned (mask 0x%x)\n", __func__,  mask);}static void test_select_timeout(unsigned long arg){ struct test_select_data *data = (struct test_select_data*)arg; unsigned long flags; printk(KERN_ALERT "Call test_select_timeout.\n"); spin_lock_irqsave(&data->lock, flags); data->timeout_done = 1; wake_up_interruptible(&data->read_wait); spin_unlock_irqrestore(&data->lock, flags);}ssize_t test_select_write (struct file *filp, const char __user *buf, size_t count, loff_t *pos){ return -EFAULT;}ssize_t test_select_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos){ struct test_select_data *data = filp->private_data; int i; unsigned char val; int retval; if (down_interruptible(&data->sem))  return -ERESTARTSYS; if (data->timeout_done == 0) {    /* no read */  up(&data->sem);  if (filp->f_flags & O_NONBLOCK)    /* non-blocking mode */   return -EAGAIN;  do {   retval = wait_event_interruptible_timeout(       data->read_wait,       data->timeout_done == 1,       1*HZ);   if (retval == -ERESTARTSYS)    return -ERESTARTSYS;  } while (retval == 0);    /* timeout elapsed */  if (down_interruptible(&data->sem))   return -ERESTARTSYS; } val = 0xff; for (i = 0; i < count; i++) {  if (copy_to_user(&buf[i], &val, 1)) {   retval = -EFAULT;   goto out;  } } retval = count;out: data->timeout_done = 0;  /* restart timer */ mod_timer(&data->timeout, jiffies + timeout_value*HZ); up(&data->sem); return retval;}static int test_select_close(struct inode *inode, struct file *filp){ struct test_select_data *data = filp->private_data; printk(KERN_ALERT "Call test_select_close.\n"); if (data) {  del_timer_sync(&data->timeout);  kfree(data); } return 0;}static int test_select_open(struct inode *inode, struct file *filp){ struct test_select_data *data; printk(KERN_ALERT "Call test_select_open.\n"); data = kmalloc(sizeof(struct test_select_data), GFP_KERNEL); if (data == NULL)  return -ENOMEM; /* initialize members */ spin_lock_init(&data->lock); init_waitqueue_head(&data->read_wait); // init_MUTEX(&data->sem); /* 新版 kernel 已不適用 */ sema_init(&data->sem, 1);  /* 改用 sema_init */ init_timer(&data->timeout); data->timeout.function = test_select_timeout; data->timeout.data = (unsigned long)data; filp->private_data = data; /* start timer */ data->timeout_done = 0; mod_timer(&data->timeout, jiffies + timeout_value*HZ); return 0;}struct file_operations fops = { .owner = THIS_MODULE, .open = test_select_open, .release = test_select_close, .read = test_select_read, .write = test_select_write, .poll = test_select_poll,};static int test_select_init(void){ dev_t dev = MKDEV(test_select_major, 0); int alloc_ret = 0; int cdev_ret = 0; alloc_ret = alloc_chrdev_region(&dev, 0, num_of_dev, DRIVER_NAME); if (alloc_ret)  goto error; test_select_major = MAJOR(dev); cdev_init(&test_select_cdev, &fops); cdev_ret = cdev_add(&test_select_cdev, dev, num_of_dev); if (cdev_ret)  goto error; printk(KERN_ALERT "%s driver(major: %d) installed.\n", DRIVER_NAME, test_select_major);  return 0;error: if (cdev_ret == 0)  cdev_del(&test_select_cdev); if (alloc_ret == 0)  unregister_chrdev_region(dev, num_of_dev);  return -1;}static void test_select_exit(void){ dev_t dev = MKDEV(test_select_major, 0); cdev_del(&test_select_cdev); unregister_chrdev_region(dev, num_of_dev);  printk(KERN_ALERT "%s driver removed\n", DRIVER_NAME);}module_init(test_select_init);module_exit(test_select_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Wang Chen Shu");MODULE_DESCRIPTION("This is test_select module.");/*****************************************************************************/

Makefile 如下:

/*****************************************************************************/KDIR="/opt/linux-source-2.6.38"PWD=$(shell pwd)obj-m := test_select.oall: $(MAKE) -C ${KDIR} M=${PWD} modulesclean: $(MAKE) -C ${KDIR} M=${PWD} clean

/*****************************************************************************/

test.c 如下:
/*****************************************************************************/

#include #include #include #define DEVFILE "/dev/test_select0"int main(){ int fd;        fd_set rfds;        struct timeval tv;        int retval;        unsigned char buf;        ssize_t sz;        int i;        fd = open(DEVFILE, O_RDWR);        if (fd == -1) {                perror("open");                return -1;        }        do {                FD_ZERO(&rfds);                FD_SET(fd, &rfds);                tv.tv_sec = 5;                tv.tv_usec = 0;                printf("select() ...\n");                retval = select(fd + 1, &rfds, NULL, NULL, &tv);                if (retval == -1) {                        perror("select");                        break;                }                if (retval) {                        break;                }        } while (retval == 0);   /* timeout elapsed */        if (FD_ISSET(fd, &rfds)) {                printf("read() ...\n");                sz = read(fd, &buf, 1);                printf("read() %d\n", sz);                printf("%02x ", buf);                printf("\n");        }        close(fd);        return 0;}

/*****************************************************************************/

執行結果如下:

# ls
Makefile test_code test_select.c
# make
make -C “/opt/linux-source-2.6.38” M=/opt/test_driver/my_driver/test_select modules
make[1]: Entering directory `/opt/linux-source-2.6.38′
CC [M] /opt/test_driver/my_driver/test_select/test_select.o
/opt/test_driver/my_driver/test_select/test_select.c: In function ‘test_select_poll’:
/opt/test_driver/my_driver/test_select/test_select.c:44:1: warning: control reaches end of non-void function
Building modules, stage 2.
MODPOST 1 modules
CC /opt/test_driver/my_driver/test_select/test_select.mod.o
LD [M] /opt/test_driver/my_driver/test_select/test_select.ko
make[1]: Leaving directory `/opt/linux-source-2.6.38′
# ls
Makefile Module.symvers test_select.c test_select.mod.c test_select.o
modules.order test_code test_select.ko test_select.mod.o
# cd test_code/
# ls
test.c
# gcc test.c -o test
# ls
test test.c
# cd ..
# insmod ./test_select.ko
test_select driver(major: 250) installed.
# mknod /dev/test_select0 c 250 0
# ./test_code/test
select() …
read() …
… 經過 10秒 …
read() 1
ff
# dmesg | tail
… 以上略過 …
Call test_select_open.
Call test_select_poll.
test_select_poll returned (mask 0x104)
Call test_select_timeout.
Call test_select_close.

# rm -rf test_code/test
# rm /dev/test_select0
# rmmod test_select
# make clean
make -C “/opt/linux-source-2.6.38” M=/opt/test_driver/my_driver/test_select clean
make[1]: Entering directory `/opt/linux-source-2.6.38′
CLEAN /opt/test_driver/my_driver/test_select/.tmp_versions
CLEAN /opt/test_driver/my_driver/test_select/Module.symvers
make[1]: Leaving directory `/opt/linux-source-2.6.38′

OK, 一切就是如此的順利,世界就是如此的美好 ^^

註記及聲明:
本教學,是參考Linux Device Driver Programming驅動程式設計由平田豐著的這本書。

寫了一個修改route的batch檔 for Windows O.S

在windows寫一個batch檔控制route table路由
紀錄如下,

我會將電腦連接到另一外網, 而原本預設的gateway 10.55.11.253 要清掉
改成 10.x.x.x 與 110.x.x.x 的IP網址才經由 10.55.11.253 gateway出去
其他預設則皆由外網出去 (未加入batch裡)

@echo offcls:startecho ----------------------------------echo delete 0.0.0.0 mask 0.0.0.0 10.55.11.253route delete 0.0.0.0 mask 0.0.0.0 10.55.11.253echo delete 10.0.0.0 mask 255.0.0.0 10.55.11.253route delete 10.0.0.0 mask 255.0.0.0 10.55.11.253echo delete 110.0.0.0 mask 255.0.0.0 10.55.11.253route delete 110.0.0.0 mask 255.0.0.0 10.55.11.253echo add 10.0.0.0 mask 255.0.0.0 10.55.11.253route add 10.0.0.0 mask 255.0.0.0 10.55.11.253echo add 110.0.0.0 mask 255.0.0.0 10.55.11.253route add 110.0.0.0 mask 255.0.0.0 10.55.11.253echo ----------------------------------echo Press any key to leftecho ----------------------------------pauseecho Bye!exit
  • echo off 是將這個 把bat位置和指令暴露出來的設定 取消
  • echo on 則是把這個設定開啟
  • CLS : Clears the video display screen, setting the cursor in the upper left-hand corner.
  • pause 是用來暫停程式,不然沒指令後程式會自我關閉 (PAUSE : Pauses the running of a batch file and displays the message “Press any key to continue …” on the screen. If the optional message is included, it will be displayed first. Use pause to optionally terminate the batch file with at a safe place. The optional message is not displayed when echo is OFF, so the message must be echoed on the preceding line.)

更多batch關鍵字查詢