OpenWRT WIFI驱动的iwpriv兼容

最近OpenWRT升级到18.06.2后,内核变为4.14,移植WIFI驱动的过程中遇到了困难,原因是WIFI驱动比较老依赖WEXT的旧接口ndo_do_ioctl方式,导致运行iwpriv出错。

以下是解决问题的思路:

一、用strace调试iwpriv

root@XXX:~# strace iwpriv ra0
execve("/usr/sbin/iwpriv", ["iwpriv", "ra0"], 0x7feeb574 /* 12 vars */) = 0
set_thread_area(0x77fc4dc0)             = 0
set_tid_address(0x77fbdd28)             = 5485
open("/etc/ld-musl-mipsel-sf.path", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/lib/libgcc_s.so.1", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
fcntl64(3, F_SETFD, FD_CLOEXEC)         = 0
fstat64(3, {st_mode=S_IFREG|0644, st_size=78095, ...}) = 0
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\10\0\1\0\0\0p(\0\0004\0\0\0"..., 936) = 936
mmap2(NULL, 147456, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x77ef5000
mmap2(0x77f18000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x13000) = 0x77f18000
close(3)                                = 0
mprotect(0x418000, 4096, PROT_READ)     = 0
socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) = 3
ioctl(3, SIOCGIWPRIV, 0x7f8e699c)       = -1 EOPNOTSUPP (Not supported)
writev(2, [{iov_base="eth0.2    no private ioctls.\n\n", iov_len=30}, {iov_base=NULL, iov_len=0}], 2eth0.2    no private ioctls.

) = 30
close(3)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

分析到iwpriv调用了socket ioctl 获取SIOCGIWPRIV返回的私有ioctl信息,但是返回EOPNOTSUPP不支持,下一步重点就在内核的sock_ioctl是怎么处理SIOCGIWPRIV?

二、查看内核代码(4.14):

    static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
    {
        struct socket *sock;
        struct sock *sk;
        void __user *argp = (void __user *)arg;
        int pid, err;
        struct net *net;

        sock = file->private_data;
        sk = sock->sk;
        net = sock_net(sk);
        if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
            err = dev_ioctl(net, cmd, argp);
        } else
    #ifdef CONFIG_WEXT_CORE
        if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
            err = dev_ioctl(net, cmd, argp); // 看这里
        } else
    #endif
    ......

发现sock_ioctl有WEXT的特殊处理,因为SIOCGIWPRIV是在SIOCIWFIRST和SIOCIWLAST之间,直接调用了dev_ioctl(好像是/dev/下设备文件的ioctl),直接看dev_ioctl:

    int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
    {
        struct ifreq ifr;
        int ret;
        char *colon;

        /* One special case: SIOCGIFCONF takes ifconf argument
           and requires shared lock, because it sleeps writing
           to user space.
         */

        if (cmd == SIOCGIFCONF) {
            rtnl_lock();
            ret = dev_ifconf(net, (char __user *) arg);
            rtnl_unlock();
            return ret;
        }
        if (cmd == SIOCGIFNAME)
            return dev_ifname(net, (struct ifreq __user *)arg);

        /*
         * Take care of Wireless Extensions. Unfortunately struct iwreq
         * isn't a proper subset of struct ifreq (it's 8 byte shorter)
         * so we need to treat it specially, otherwise applications may
         * fault if the struct they're passing happens to land at the
         * end of a mapped page.
         */
        if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
            struct iwreq iwr;

            if (copy_from_user(&iwr, arg, sizeof(iwr)))
                return -EFAULT;

            iwr.ifr_name[sizeof(iwr.ifr_name) - 1] = 0;

            return wext_handle_ioctl(net, &iwr, cmd, arg);  //看这里
        }

这里直接调用了wext_handle_ioctl,注意用了iwreq的结构,这个是和ifreq结构类似的,但是比ifreq小8个字节(这个导致后面的解决补丁有隐患,因为补丁是直接把iwreq转换成了ifreq,如果超过32个字节直接就截掉了)。

中间省略步骤,最后跟踪到wireless_process_ioctl函数里:

/*
 * Main IOCTl dispatcher.
 * Check the type of IOCTL and call the appropriate wrapper...
 */
static int wireless_process_ioctl(struct net *net, struct iwreq *iwr,
                  unsigned int cmd,
                  struct iw_request_info *info,
                  wext_ioctl_func standard,
                  wext_ioctl_func private)
{
    struct net_device *dev;
    iw_handler  handler;

    /* Permissions are already checked in dev_ioctl() before calling us.
     * The copy_to/from_user() of ifr is also dealt with in there */

    /* Make sure the device exist */
    if ((dev = __dev_get_by_name(net, iwr->ifr_name)) == NULL)
        return -ENODEV;

    /* A bunch of special cases, then the generic case...
     * Note that 'cmd' is already filtered in dev_ioctl() with
     * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
    if (cmd == SIOCGIWSTATS)
        return standard(dev, iwr, cmd, info,
                &iw_handler_get_iwstats);

#ifdef CONFIG_WEXT_PRIV
    if (cmd == SIOCGIWPRIV && dev->wireless_handlers) //看这里---------
        return standard(dev, iwr, cmd, info,
                iw_handler_get_private);
#endif

    /* Basic check */
    if (!netif_device_present(dev))
        return -ENODEV;

    /* New driver API : try to find the handler */
    handler = get_handler(dev, cmd);
    if (handler) {
        /* Standard and private are not the same */
        if (cmd < SIOCIWFIRSTPRIV)
            return standard(dev, iwr, cmd, info, handler);
        else if (private)
            return private(dev, iwr, cmd, info, handler);
    }
    return -EOPNOTSUPP;
}

这里压根看不到哪里调用了WIFI驱动配置用的ndo_do_ioctl入口,可以确定的是用了net_device->wireless_handlers获取private和standard的handler方式处理,但没有ndo_do_ioctl什么事情,一番google后发现2017年的时候某一版内核删掉了ndo_do_ioctl支持

diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c
index 1a4db6790e20..24ba8a99b946 100644
--- a/net/wireless/wext-core.c
+++ b/net/wireless/wext-core.c
@@ -957,9 +957,6 @@  static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,
        else if (private)
            return private(dev, iwr, cmd, info, handler);
    }
-   /* Old driver API : call driver ioctl handler */
-   if (dev->netdev_ops->ndo_do_ioctl)
-       return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd);
    return -EOPNOTSUPP;
 }

最后的解决办法为了兼容,加上上面删掉的代码,不过要把ifr替换成(struct iwreq *)iwr,隐患是毕竟iwreq和ifreq相似但是大小不同,一个是32个字节,一个是40个,可能会有问题,不过我测试了一下貌似OK,那就不管了。

翻译Dewey的最后一篇游记

前篇说道Dewey的最后一篇游记追到了xiujuan,那我就试试翻译一下,说不定很有趣:),英语水平很烂,各位少安毋躁。

下文翻译自David Dewey Stories of my travels through China and the adventures I had along the way的最后一篇,[Xiujuan] Finally getting the Girl

BTW: Xiujuan我不知道真实姓名,暂时写作秀娟吧。

沙子作为礼物

到了晚上我去秀娟的宿舍去看她,但很难说服她下来见我。 我发短信告诉她我有些东西给她,但不管怎么样她说不想要。

最终过了一小时她下来了。我原以为她会怪我让她下楼来见我,但她最终跳着走到我身边并对我笑脸相迎。她说她并不知道我在这里等她,管她呢,我咳嗽着跟她讲话,并给她我从内蒙古沙漠里收集的沙子。她确实被我意想不到的礼物和我收集沙子的经历感动了。然后,她帮她的室友买了些冰淇淋就回宿舍了,但她说可以在第二天早上9点来看我,并要我帮她指导英语功课。

那天晚上我的朋友Alex从西雅图打电话给我,我和他聊了很长时间。星期天早上,我到秀娟的宿舍去接她,但我手机坏了,好像是与Alex打电话把我的手机话费用完了。所以我用宿舍楼的电话打电话给秀娟,希望他不要对直接打电话给她感到生气,因为在中国手机话费很贵,但短信费则便宜很多。

秀娟下来了,我试图用英语跟她说话,我认为应该这样,因为我今天本来应该教她英语的嘛。我试图降低语速并尽量用简单语句,但她还是无法理解我说的话并拒绝同我说英语,最后我放弃了尝试。晓娟已经学习英语9年了,但她的英语会话能力少的可怜,她英语几乎只学了读和写。

我们去教室学习,但我们不能说话,因为周围都是同样学习的学生。我建议我们去外面学习,但她不想去。她平时很喜欢去外面学习,而且今天天气很好,可能她担心去外面我的感冒会加重。我只能建议去我住的旅馆学习。

一旦到了旅馆,我们就用功的学习英语。她想通过一个高年级英语考试(译著:senior-year应该是四级英语吧),可是学习用的文章对她而言太难了,里面夹杂着许多生词和许多她不懂的复杂语法。因为她不理解所有这些语法和单词所以就更无法理解这些文章了,也就意味着她无法正确的回答这些多选题。

此外,所有晓娟要学的文章都是从西方的资料上摘抄下来的,都关于英美文化、地理、政府、政治等等。就算晓娟的英文能力再好,她也可能不能理解这些文章,因为她不熟悉这些西方的东西。比如,她不懂基督信仰、盎司、呼气测试、福利,这些都在那些文章里有所涉及。雪上加霜的是她的英文水平远远不够,理解那些文章对她来说是个噩梦。我关心的是她确实需要帮助,我会尽我所能一点一点的帮她理解每篇文章的背景。幸运的是秀娟是个超级聪明的女孩,她学的很快。我确信她如果在美国待几个月就能学会说流利的英语。

手机卡

那天晚上我试着充值手机卡,但不成功。这涉及到一些复杂的因素:我有一个非本地手机号码并且欠费了,但为了拨通手机卡充值号码,我必须手机上有卡 ,因为要漫游。我被告知必须用本机打充值电话,所以我被困住了。我买了个新的北京号码和手机卡。

我花了很长时间写下我的手机里的号码并放到新的卡上。我发现我买的新卡有点烂,一旦涉及到卡的flash读写将会死机。我怀疑手机店里的人利用了我作为一个无知的老外这点卖给我不想要的烂卡。应该有其他方法充值不是本地的手机卡,或许他们不想告诉我。我没时间想这些,我一门心思都在想秀娟。

现在我的手机又好了,我发了个短信给秀娟。我跟她说我病的很厉害(这些都是真的which is all too true),并告诉她应该来看我照顾我。第二早上(星期一早上)她带着给我的一包食物来看我,还带了些感冒药。接下来我们在我的旅馆房间了学习英语,最终耗费了几个小时的时间。

接下来她就到Minqi家教中文去了。她回来后我发短信给她,但她说今天晚上太晚了,不能去看我了。

月亮和火星

星期二我再次跟她一起学英语,她晚上必须去Minqi的朋友那里教中文。晚上的晚些时候,我就去了她的宿舍找她,但碰到了她的室友,她说她认识我,我却不认识她。当你是广大中国人中唯一的外国白人时,我猜很容易被记住。她说秀娟可能不在,但她上楼去看了看,不久秀娟就高兴的下来了。我带她回我的旅馆,我们拥抱并亲吻了一会儿,但秀娟不习惯跟一个男人躺在床上,所以睡不着。旅馆的房间有两张床,所以我就让她睡在旁边那张床上了。

星期三晚上秀娟发短信给我,让我去外面看月亮和火星,因为在一万年中两者之间的距离在今天是最近的。我正在睡觉,但还是起来到外面去看。她又发短信过来,问我看到月亮是否想家。我说没有,只是让我想你了。接下来她第三次发短信过来, 问我,是什么事情使我这么爱他。我误解了,以为她怀疑我对她的爱,就像她一贯如此的那样。所以我回了条短信,告诉她算了睡觉吧。令我惊奇的是,显然这使她感到不快,或许这是个好兆头。她一定是开始喜欢我了,否则她是不好这么在乎我的。

中秋节

9月11号星期四是中秋节,那晚我去了秀娟那里,我们去了外面,坐在草地上欣赏月亮,这是原本中秋节该干的事情。我们拥抱在一起谈了好一阵子话。接下来我肚子就饿了,我们去了通宵营业的小店买了些小吃。我明白今天没月饼可吃了,这原本应该中秋节吃的,但我们就是找不到月饼,月饼全都买完了。最终,过了午夜,吃月饼已经太迟了,我们回了旅馆,秀娟跟我待了一晚上。这次我们一起睡在一张床上。

第二天我们见面继续学英语,但她不想学,而且她给我带了个月饼。她在读一本她最喜欢的中国小说家的传记,里面包含了许多那个小说家写给他妻子的情书,秀娟一边看书,一边说我永远不会给她写这些情书,因为我的中文水平不够。我没法反驳她。她想回她的宿舍,并想跟我永远的分别。

当我们回到她宿舍时,我情不自禁的抱着她哭了。我猜她也对我感到难过,所以又跟我回到了旅馆房间,而不是回宿舍。但她马上要和她的朋友去听音乐会,所以我又陪她回宿舍,在那里她再次说她不属于我。我又哭了起来并跟她道别。我感到很糟糕,我意识到我必须尽量用英语跟她沟通,她可能感到她在跟一个三岁小孩约会,因为我的中文就那个水平,而且我一直都用中文跟她说话。

忘记钥匙

当我回到旅馆时,欣喜的发现秀娟忘记钥匙了。这给了我完美的再约她见面的机会。所以我用英文发了条关于钥匙的短信给她,并且用了些难懂的词汇,这样确保她无法理解。我认为这样就可以反驳她抱怨我的烂中文了。那天深夜我去她的宿舍找她,我发现她还没从音乐会回来,但很快她出现了。我告诉她我给她带来了她的钥匙。她跟她的朋友说了再见,所以我让她坐自行车后面带她转了一圈。我们坐在长凳上谈了很多事情,我告诉她所有我爱他的理由,并恳求她做我的女朋友,但这都是徒劳。

最终我们回到了旅馆房间,她又跟我度过了一个晚上。由于我的疏忽,我没把窗户关牢,房间里满是蚊子。我把蚊帐覆盖在我们身上,但我愚蠢的在黑暗中盖了一下,但蚊子早就钻进了蚊帐,早上醒来,它们都吸饱了我们的血。

给男孩的信

接下来的晚上,我给秀娟买了朵玫瑰,去她宿舍找她。可是她宿舍那里有大群人,有些男生在给所有女生唱歌。秀娟拒绝下楼见我,我发了很多不同的短信给她,并固执的等了她三个小时。最终一些女孩在阳台上跟我说话,并扔给我个折成纸飞机的信,信是用荧光笔写的,而且用的是英文:

嗨, 小子:

中国女孩可不吃这套的,你今晚最好回去睡觉了。

事实上这幢楼内的所有女孩对你所做的都很感动,但是继续待在这儿确实不是个好办法,听懂了吗?

或许你可以慢慢来,给她些时间来接受你。

祝你好运 : )

这封信写的很好, 但这不适用与秀娟。我了解她,我知道如果我做了些貌似很傻的事,比如说整晚站在外面等她下来,她会很感动的。我知道她爱我,但她在试探我,确信我是否忠于她。

我对晓娟让我在外面整整等了3个小时感到恼火,所以我回去了。她答应第二天带我去吃早餐,并跟我一起学习英语。我发了条短信给她,她第二天早上开手机时应该会看到,在这条短信中我说她辜负了我的心,我决定忘了她。

最后一天

第二天早上,她早早的用短信叫醒了我,她对我前天晚上发的短信感到生气。我道了歉,我们在吃早餐时见了面。那束玫瑰现在看起来有点破败不堪了,但我不管怎么样还是给了她。她并不想要,但我坚持着让她拿着,她被感动到了。我告诉了她等她3小时的事情,她原本希望我会生气,但我告诉她我爱她以至于能原谅她所有事情。那确实是真的,我太爱她了,以至于见到她的瞬间我就忘了所有她对我的伤害。我了解秀娟,她喜欢听这些话,确实是这样的。这天接下来的时间里她对我难以置信的好,她用双手牵着我的手去逛街。在我在中国的最后24小时里,她紧紧的跟着我。

我那天晚上只睡了一个小时,因为必须起来打包准备我的行李。我们临晨4点离开旅馆直奔机场,到机场时发现太早了,她在大庭广众之下毫无羞涩的吻了我,这至少在北京很少见。不幸的是她还没答应做我女朋友,但我一旦回美国就会打电话或者写信、email给她做她的思想工作。

我进入检票区后,秀娟在后面很远的站着看我。她给我的手机发了些温暖的短信,接下来我就登机了,我对我们的未来很有希望。

回到美国

飞机轻松的到了东京,我坐在一个靠窗的位置,那里可以看到所有下面的小船和很多漂亮的云,韩国的大城市,日本的工厂、公园、高尔夫球场、高速公路、房子。在从东京飞西雅图的航班上我座位不靠窗,但飞机靠门的方向上有个座位没人,因为那个人在飞机起飞前没有出现。这恰好是个很好的座位,远离747巨大的机翼。因此,在坐之前我问航班服务员是否可以坐那里,她同意了。我很高兴找了个靠窗的位子。一旦飞过了太平洋,在西雅图着陆之前,我飞过了San Juan Islands, Dungeness spit, Sequim, Port Townsend和华盛顿大学,我看得很清楚。

回美国三个月后,我最终说服了晓娟当我的女朋友。再一个月后我的学习申请被北京师范大学接受,我将在2004年2月2号回北京。

2004年10月更新: 我现在生活在中国浙江省杭州市,晓娟现在已经是杭州这里的高中中文老师了,而且我和我的好朋友开了个公司,ChinaForge.com有限公司。感谢ChinaForge赞助这个网站,谢谢晓娟,作为这个世界上最特别的女孩,也是最好的中文老师。

读老外游记意外收获

我对老外之类游中国之类的游记很有兴趣,在龙腾网上发现这么一篇游记杜大威游中国,看起来Dewey还是比较单纯的人。到了第二篇游记,Dewey已经显示显露出对xiujuan的暗恋了-_-!!!。读到最后,对他和xiujuan的旅游我到不太关心了,我最关心的是他俩以后的事情,毕竟这个游记大概写于2003年(根据这里判断),xiujuan好像还是大二或大三的学生。

OK,按奈不住好奇心寻到了Dewey的游记博客,总共15篇游记,龙腾上只翻译了两篇,所有的游记英文在这里。Dewey与xiujuan分开后独自旅游了很多地方,这些我暂时没工夫看,最最关键的是最后一篇[Xiujuan] Finally getting the Girl。看来Dewey最终抱得美人归了:),有空我试试翻译这篇,我看的很有意思,有点感动。

Dewey的游记博客有点旧了,无意中有找到了比较新的日常博客,从中我知道Dewey在杭州与朋友合伙创业开公司(好熟悉啊),好像是做模具外包的,xiujuan也来到了杭州做了老师。2006年他俩结婚了,这中间有很多波折,你可以自己去看那些博文。我想现在结婚压力都这么大,何况是老外呢,文化差异、工作、手续问题,特别是xiujuan父母有点反对(他们想至少也的嫁个有房有车的小伙吧-_-!!!),但最后他俩熬过来了,皆大欢喜,end story :)。

BTW: Dewey是美国西雅图人,xiujuan是湖州人,他俩的照片去Dewey博客里找吧:),我是不是太八卦了啊-_-!!!。

mini2440上实现网络电台播放

上次说到要实现一个无线鼠标控制的网络电台播放程序(远程控制:)),今天有空就实现了。主要的难点就是读取鼠标数据,昨天花了一天时间看了linux kernel的driver/input/mousedev.c源代码,看懂了数据格式和控制原理,就很简单了:)。

鼠标的设备文件是/dev/input/mice,默认读取的是PS2数据,分3个字节,分别是3个按键、X、Y坐标。不过要注意的是XY坐标都是相对坐标(不超过127,有符号),如果要获得滚轮数据需要打开im扩展,直接向mice写数据串{ 0xf3, 200, 0xf3, 100, 0xf3, 80 }就行了,这个在mousedev.c写的很清楚了,PS2格式参考看这里

解决了鼠标控制,具体播放就开个进程放mplayer,把所有电台URL放在playlist里就行了,mplayer支持从标准输入的按键控制,0和9控制音量,<和>对应前进和后退,p是暂停。进程间通信就用pipe,不过建pipe,绑定stdin有点麻烦,有现成的API做了这些了,popen和pclose就可以搞定这些。最后用鼠标左键前翻,右键后翻,中键暂停,滚轮控制音量。

Talk is cheap, show me the code: github

// @author: bl4kraven
// @brief:  just a network radio player and control by mouse.
//          using mplayer as backend, left button previous, right button forward and middle button pause.
// @see  linux kernel drivers/input/mousedev.c
//       http://www.computer-engineering.org/ps2mouse/
//       
#include <cstdio>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>

typedef unsigned char BYTE;

#pragma pack(1)
struct imps2_data
{
    BYTE btn_left:1;
    BYTE btn_right:1;
    BYTE btn_middle:1;
    BYTE NONE:1;
    BYTE x_sign:1;  // x offset sign
    BYTE y_sign:1;  // y offset sign
    BYTE x_overflow:1; // x offset is overflow
    BYTE y_overflow:1; // y offset is overflow

    // x/y movement offset relative to its position
    signed char x;
    signed char y;
    signed char z;
};
#pragma pack()

int main(int argc, char *argv[])
{
    BYTE mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };
    int mice_fd = open("/dev/input/mice", O_RDWR|O_NONBLOCK);
    if (mice_fd == -1)
    {
        fprintf(stderr, "Open mice fail");
        return 1;
    }

    // set mice mode, so can read rolling wheels
    int nRet = write(mice_fd, mousedev_imps_seq, sizeof(mousedev_imps_seq));
    if (nRet < 0)
    {
        fprintf(stderr, "set mice imps2 fail\n");
        return 1;
    }


    FILE *stream = popen("mplayer -quiet -softvol -softvol-max 300 -playlist channel.txt", "w");
    if (stream == NULL || stream < 0)
    {
        fprintf(stderr, "popen mplayer fail\n");
        return 1;
    }

    while (true)
    {
        fd_set fdset;
        //struct timeval tv;
        //tv.tv_sec = 0;
        //tv.tv_usec = 200000;

        FD_ZERO(&fdset);
        FD_SET(mice_fd, &fdset);

        int ret = select(mice_fd+1, &fdset, NULL, NULL, NULL);
        if (ret < 0)
        {
            fprintf(stderr, "select return error\n");
            return 1;
        }
        else
        {
            if (FD_ISSET(mice_fd, &fdset))
            {
                imps2_data data;
                while (true)
                {
                    int nLen = read(mice_fd, &data, sizeof(data));
                    if (nLen == 0)
                    {
                        fprintf(stderr, "End of file \n");
                        return 1;
                    }
                    else if (nLen == -1)
                    {
                        if (errno != EAGAIN)
                        {
                            fprintf(stderr, "read fail\n");
                            return 1;
                        }
                        break;
                    }
                    else
                    {
                        // nLen = 1 is ack
                        if (nLen == sizeof(data))
                        {
                            //printf("nLen:%d, left:%d right:%d middle:%d X:%d Y:%d Z:%d\n",
                            //        nLen, data.btn_left, data.btn_right, data.btn_middle, data.x, data.y, data.z);
                            if (data.btn_left)
                            {
                                if (data.btn_right)
                                {
                                    // OK quit
                                    close(mice_fd);

                                    fprintf(stream, "q");
                                    fflush(stream);

                                    // wait process exit
                                    pclose(stream);
                                    return 0;
                                }

                                // preivous
                                fprintf(stream, "<");
                                fflush(stream);
                            }
                            else if (data.btn_right)
                            {
                                // forword
                                fprintf(stream, ">");
                                fflush(stream);
                            }
                            else if (data.btn_middle)
                            {
                                // pause
                                fprintf(stream, "p");
                                fflush(stream);
                            }
                            else if (data.z != 0)
                            {
                                if (data.z > 0)
                                {
                                    // rolling down
                                    fprintf(stream, "9");
                                    fflush(stream);
                                }
                                else
                                {
                                    // rolling up
                                    fprintf(stream, "0");
                                    fflush(stream);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    return 0;
}

天线电磁辐射

最近在搞射频方面的工作,发现普通RFID读头RF的输出功率最高可达到32.5dBm(1.7W),天线增益假如是6dBi,线路损耗为2dBm的话,最终天线表面的等效辐射功率达32.5+6-2=36.5dBm(等效功率接近4W-_-!!!,这个功率就比较危险了,国标是33dBm和功率密度2W/平方米),也就是说要达到国标RF的最大输出功率要设置33-9+1=25dBm才可以,为安全其间尽量设小点:)。

说几点想到的:

  1. 就算不考虑天线也有最高也有1.7W,这个相比普通路由器的几个毫瓦高了1000倍。
  2. 尽量离天线越远越好。
  3. 不知生活中各种电磁辐射(手机、无线路由、电脑等等)对人体的影响,看到美国FCC的相关电磁对人体的报告,但没细看。
  4. 想起之前看新闻看到美国有些人患有电磁辐射过敏,没办法只能住在山洞里-_-!!!。
  5. 看了电磁炉其实也是用电磁感应加热金属,只不过大部分能量被金属吸收变成了热能,如果质量不好的话对周围的辐射也比较大吧。

最近睡眠不足,早点睡觉吧:(。

天线增益:http://baike.soso.com/v406959.htm

teppoman2刺客游戏技巧

我以前一直玩teppoman2刺客游戏,一个日本人写的游戏,但最多只能玩到第5关。这个游戏难玩的原因之一是复杂的键盘操作:

  • 短跑(dash) – 快速按两次左/右方向键
  • 空中滑行(glide) – 短跑之后跳跃
  • 刹住(skid) – 滑行之后,按下相反的方向键
  • 反跳(reverse jump) – 刹住之后跳跃
  • 身体冲浪(body surf)- 在高处使出空中滑行,然后按住 Shift

我觉得最有用的就是反跳,但恰恰按键最复杂。如向前反跳为:快速按两次右方向键,马上再按一次左方向键,再按Shift。这一连串按键需一气呵成,我等懒人实在没办法只能借助Python的威力搞定这些按键了。

基本思路是Hook键盘keyup事件,然后用Win32 API SendInput或keybd_event函数模拟键盘输入,需要的Python第三方库有:pywin32、pyHook。

具体实现是用’z’键作向前反跳,用’x’键作向后反跳。

Python语言: Codee#23900
#-*- coding:UTF-8 -*-
import time
import win32gui
import win32api
import win32con
import pyHook  

# global
g_hwndWind = None

def SimulateKey(vk, t = 0.05):
    scan = win32api.MapVirtualKey(vk, 0)
    # down
    win32api.keybd_event(vk, scan, 0, 0)
    time.sleep(t)
    # up
    win32api.keybd_event(vk, scan, win32con.KEYEVENTF_KEYUP, 0)
    # simulate time delay
    time.sleep(0.05)

def OnKeyboardEvent(event):
    global g_hwndWind
    print event.MessageName
    if event.Window == g_hwndWind:
        if event.Ascii == ord(‘z’):
            # ‘z’ simulate jump back (right+right+left+shift)
            SimulateKey(win32con.VK_RIGHT)
            SimulateKey(win32con.VK_RIGHT)
            SimulateKey(win32con.VK_LEFT)
            SimulateKey(win32con.VK_SHIFT, 0.5)
        elif event.Ascii == ord(‘x’):
            # ‘x’ simulate jump front
            SimulateKey(win32con.VK_LEFT)
            SimulateKey(win32con.VK_LEFT)
            SimulateKey(win32con.VK_RIGHT)
            SimulateKey(win32con.VK_SHIFT, 0.5)
    return True

def main():
    try:
        # find teppoman2 window handle
        global g_hwndWind
        g_hwndWind = win32gui.FindWindow(“Mf2MainClassTh”, “偰偭傐儅儞俀”)

        # create the hook mananger
        hm = pyHook.HookManager()
        hm.KeyUp = OnKeyboardEvent

        # hook into the and keyboard events
        hm.HookKeyboard()

        # message poll
        win32gui.PumpMessages()
    except:
        print “can’t find window”

if __name__ == “__main__”:
    main()