Reading WebRTCBook

webrtcbook-cover

5 月 29 号在美国亚马逊上买了 这本书. 邮件上预计是 7 月 5 日到. 结果今天( 6 月 13 日)就到了, 真是开心. BTW, 这算是我买过的最贵的书了. 购买地址

webrtcbook-order

书里面的图片都是彩印的. 不算太厚, 才 295 页.

Reading ng-book 2

哈哈, 最近在同时学两个东西, 都是 ngxxx 一个是 nginx, 另一个就是 angular.

同样地, 这是一篇无聊的文章, 只是给我个人用来记录进度的.(不记录, 坚持不下来啊)

学习动机

  • 目前工作一个项目用的 angular 1.x
  • data 驱动 view
  • 前端太 tmd 的乱, 框架一大堆, 看别人分析, 都希望出现一个统一的框架. 我希望是 angular.
  • 想摆脱手写html, 手写js, 手写 css 的低端模式, 学个框架, 以后可以装逼说自己会前端了.

文档地址

进度

2016-02-17

  • 当前的 angular 2 状态是 beta.
  • angular: https://angular.io/
  • typescript: http://www.typescriptlang.org/
  • 简单的看了下网上各路人马的评价, 在typescript跟es6中选择了typescript.
  • TypeScript is a superset of JavaScript ES6 that adds types.
  • ES5 == normal JavaScript
  • ES6 == ES2015
  • Angular 2 is written in TypeScript and generally that’s what everyone is using.
  • Angular 2 itself is a javascript file.
  • Shim. A shim is a library that brings a new API to an older environment, using only the means of that environment.
  • Polyfill. A polyfill is a piece of code (or plugin) that provides the technology that you, the developer, expect the browser to provide natively. Flattening the API landscape if you will.

2016-05-18

Angular 2 项目基础

  • package.json
  • tsconfig.json
  • tslint.json

Angular 2 依赖

  • ES6 Shim
  • Zones
  • Reflect Metadata
  • SystemJS

notes

  • CSS 使用 Semantic-UI.
  • reference(in .d.ts) 语句指定 typing 文件的路径.
  • import 语句是来自 ES6, 叫做 destructuring.
  • Component 是 Angular 1里的 directives 的新版本.
  • 一个基础的 Component 包含两个部分: Component annotation 和 component definition class.
  • 把 annotation 看做 metadata added to your code. (? 跟 python 的 docorator 类似吧)
  • selector: angular自己的selector mix, 类似 CSS selector, XPath, JQuery selector.
  • npm run tsc 编译.
  • npm run tsc:w 编译并监听.
  • tsc --watch 每次修改都编译.
  • 读到 Page 16

2016-05-19

  • npm run go 监听修改并serve, 注意 8080 端口不能被占用.
  • array: Angular1 的 ng-repeat 在 Angular2 中类似的指令是 NgFor.
  • #newtitle 语法叫做一个 resolve. 效果是让这个变量在这个 view 范围的的表达式有效.
  • 绑定 input 到 value, newtitle 是一个 object, 代表这个 input DOM 元素, 类型是 HTMLInputElement. 可以访问 newtitle.value.
  • 绑定 actions 到 events, 组件类的一个函数赋值给 (click) 属性.
  • 反引号: ES6 语法, 反引号的字符串将会展开模板变量.
  • 可以在 attribute values 里面使用模板.
  • 在 Angular 1, directives 全局 match, 在 Angular 2, 你需要显式指定你用哪个组件(因此, 哪个 selector).
  • 在 js 里, 默认传播 click 事件给所有的父组件.
  • 一个好的实践是当写 Angular 代码时, 尽量将你使用的数据结构与组件代码隔离.
  • 封装的原则: LoD, https://en.wikipedia.org/wiki/Law_of_Demeter
  • train-wreck: 小心 long-method chaining foo.bar.baz.bam
  • 读到 Page 42

2016-05-20

  • MVC guideline: Skinny Controller, Fat Model
  • 核心理念是: 将大多数我们的 domain logic 移动到我们的 models, 因此我们的 components 尽可能做最少的工作.
  • 数组的两种表示: 1. 普通: Article[]; 2. generics: Array
    .
  • Component 的 attribute: inputs
  • 创建 coimponent 的核心不仅仅是封装, 而且是为了重用.

总结编写 Angular 2 应用步骤

  1. 将你的应用分解成 components.
  2. 创建 view.
  3. 定义你的 model.
  4. 显示你的 model.
  5. 添加互动.

typescript

  • ES6 = ES5 + classes + modules
  • TypeScript = ES6 + types + annotations
  • transpiler / transcompiler : TypeScript -> ES5.
  • TypeScript to ES5 有一个单一的 transpiler, 由核心 TypeScript team 开发.
  • ES6 to ES5 有两个 transpiler: traceur (by google), babel (by js community).
  • REPL: ts-node, tsun
  • number: 在TS中, 所有的number都是浮点数.
  • any: any 是默认类型, 如果我们忽略给一个变量指定类型,
  • classes: 在 ES5 中, OO是通过 prototype-based objects 实现的.
  • 最佳实践, 关于补充js中没有class: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide
  • oo in js: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript
  • class 有 properties, methods, constructors.
  • A void value is also a valid any value.
  • constructor: 必须被命名为: constructor. 每个 class 只能有一个 constructor.
  • inheritance in ES5: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
  • inheritance: 使用 extends 关键字.
  • Utilities(语法糖): ES6 提供一些语法糖: 1. fat arrow function syntax; 2. template strings;
  • Fat Arrow Function (=>): 是一个缩写来写函数.
  • 在 ES5 中, 如果我们想要使用一个函数作为参数, 我们需要使用 function 关键字和 {}.
  • => 语法的一个重要特性是, 他和包围他的代码使用相同的 this. 这与你在 JS 中创建一个普通函数不同.
  • 通常当你在 JS 中写一个函数时, 这个函数被分配他自己的 this.
  • => 函数是一个很好的方法来清理你的 inline 函数. 他使在JS中使用高阶函数更简单.
  • Template Strings: 在 ES6 中, 新的 template strings 被引入. 两个好的特性: 1. Varaibles within strings. 2. Multi-line strings.
  • Variables in strings: 也叫 string interpolation. 要使用反引号, 不能使用单引号或者双引号.
  • Angualr 2应用由 Component 组成(a tree of components), 一种理解 Component 的方式是教会浏览器新 tag.
  • Angular 2 的 Component 和 Angular 1 的 directive 类似. 同时, Angular 2 也有 directive.
  • component 是 composable.
  • Angular 2 没有指定一个 model library.
  • 读到 Page 76.
TypeScript 比 ES5 多的特性
  • types
  • classes
  • annotations
  • imports
  • language utilities (e.g. desctructuring)

2016-05-26

  • componet decorator: 包含一个 selector, 一个 template.
  • component controller: 由一个 class 定义.
  • component selector: 两种写法. <inventory-app></inventory-app>
<div inventory-app></div>
  • 添加实例到组件里来显示子组件.
  • template binding: {{...}}, 里面不仅仅是一个变量, 是一个表达式.
  • Inputs 和 Outputs: [squareBrackets] 传入 inputs, (parenthesis) 处理 outputs.
  • 数据流入你的组件, 通过 input binding, 事件流出你的组件, 通过 output binding.
  • 可以把 input + output bindings 看做你的组件的 public API.
  • component inputs:
  • Observer pattern: https://en.wikipedia.org/wiki/Observer_pattern

Reading Nginx tutorial

这是一篇无聊的文章, 用于记录自己的阅读进度而已.

文档地址

进度

2016-02-16

2016-02-17

2016-02-19

  • ngx_http_proxy_module: http://nginx.org/en/docs/http/ngx_http_proxy_module.html
  • 不是所有的 Nginx 变量都拥有存放值的容器。拥有值容器的变量在 Nginx 核心中被称为“被索引的”(indexed);反之,则被称为“未索引的”(non-indexed)。
  • 读完 Nginx 变量漫谈(三)
  • 读完 Nginx 变量漫谈 (四)
  • Nginx 变量值容器的生命期是与当前请求相关联的。每个请求都有所有变量值容器的独立副本,只不过当前请求既可以是“主请求”,也可以是“子请求”。即便是父子请求之间,同名变量一般也不会相互干扰。
  • Module ngx_http_auth_request_module: http://nginx.org/en/docs/http/ngx_http_auth_request_module.html
  • Nginx 变量漫谈(五)

2016-02-22

*

Learning LuaDist

官网

wiki

如何提交Package到PyPI

PyPI官方文档

Steps

注册账号

填写配置文件~/.pypirc

[distutils]
index-servers=
    pypi
    pypitest

[pypitest]
repository = https://testpypi.python.org/pypi
username = <your user name goes here>
password = <your password goes here>

[pypi]
repository = https://pypi.python.org/pypi
username = <your user name goes here>
password = <your password goes here>

准备你的package

PyPI上的每个package需要一个setup.py文件在项目的根目录, 如果你在一个一个markdown格式的README文件, 你还需要一个setup.cfg文件. 一个LICENSE.txt文件. 假设包名为:mypackage, 目录结构如下.

├── LICENSE.txt
├── README.md
├── mypackage
│   ├── __init__.py
│   ├── bar.py
│   ├── baz.py
│   └── foo.py
├── setup.cfg
└── setup.py

setup.py

from distutils.core import setup

setup(
    name='mypackage',
    packages=['mypackage'],  # this must be the same as the name above
    version='0.1',
    description='A random test lib',
    author='Akagi201',
    author_email='akagi201@gmail.com',
    url='https://github.com/Akagi201/mypackage',  # use the URL to the github repo
    download_url='https://github.com/Akagi201/mypackage/tarball/0.1',  # I'll explain this in a second
    keywords=['testing', 'logging', 'example'],  # arbitrary keywords
    classifiers=[],
)

download_url: repo源码的下载链接, 使用git tag后, github会为你host. 使用命令: git tag 0.1 -m "Adds a tag so that we can put this on PyPI.", 使用 git push --tags origin master 提交到github

setup.cfg

告诉PyPI你的README文件的位置.

[metadata]
description-file = README.md

提交package到PyPI Test

  • 注册: python setup.py register -r pypitest
  • 上传: python setup.py sdist upload -r pypitest
  • 安装: pip install -i https://testpypi.python.org/pypi <package name>

提交package到PyPI Live

  • 注册: python setup.py register -r pypi
  • 上传: python setup.py sdist upload -r pypi
  • web form提交: https://pypi.python.org/pypi?:action=submit_form

不使用setuptools, 使用更安全的twine上传

  • twine upload dist/*

Refs

专业投机原理书评

读这本书的文字就能体会到, 作者那种自信而坚定的行事风格, 很喜欢这种略带装逼的文风. 作者的经历我也很喜欢, 学历不高, 小时候就精通打牌(阅读了所有相关书籍, 并勤于练习), 16-20岁之间就收入颇丰, 主要来自扑克赌局与牌技表演. 为了练习玩牌, 他随身带着一副牌. 有一句话蛮逗的, 哈哈. “我与女朋友在看电影时, 我的左手会练习单手切牌, 而右手就放在每一个16岁男孩与女朋友看电影时所应游走的位置.” 当发现赌博不适合作为一生的职业后, 走上了证劵交易员的道路.

摘录

  • 他年少时精于玩牌, 并从中认识到“胜算”和“自律”的重要性.
  • 要了解市场, 更要了解自己.
  • 在阅读任何领域的书时, 我总是要求该书的作者或讨论的对象拥有工人的成就.
  • 知识本身绝对不是成功的保证. 除了知识, 你还需要一套执行知识的管理计划以及严格遵守计划的心理素质, 这样才可以免除情绪的干扰.
  • 如果你希望获胜, 你就必须了解规则; 另外, 你还必须愿意坐上赌桌, 这样你才有获胜的机会.
  • 赌博必须承担不利胜算的风险, 投机是掌握有利胜算的情况下才承担风险.
  • 赌博从来都不是一种高风险的行为, 输赢的关键是如何管理胜算. 我记住每一种牌型的胜算几率, 并依此决定对策. 这便是风险管理方法的要点所在.
  • 不可基于帮助朋友的立场, 免费提供任何有关市场的建议.
  • 结合技术分析, 统计方法以及经济基本面等因素, 评估任何投机头寸的风险. 唯有这三个因素相互配合时, 我才会在市场中建立重要头寸.
  • 作为交易者, 我的目标始终是: 在经济独立的情况下保有自由, 换句话说, 我的目标是: 经年累月地稳定赚钱.
  • 我的哲学基于三个原则, 按重要性排列如下: 保障资本, 一致性的获利能力以及追求卓越的回报. 这三者是我的基本原则, 因为他们是所有市场决策的最高指导原则.

一些原则

  • 鳄鱼原则: 万一鳄鱼咬住你的脚, 务必记住: 你唯一的机会便是牺牲一只脚.
  • 道氏理论: 长期趋势最为重要, 也最容易被辨认, 归类与了解. 中期趋势对投资者来说较为次要, 但却是投机者的主要考虑因素. 短期趋势最难预测, 唯有交易者才会随时考虑他.
  • 交易准则: 1. 根据计划进行交易, 并严格遵守计划. 2. 顺势交易, “趋势是你的朋友”. 3. 在许可的范围内, 尽可能采用止损单. 4. 一旦心存怀疑, 立即出场. 5. 务必要有耐心, 不可过度扩张交易. 6. 迅速认赔, 让获利头寸持续发展. 7. 不可让获利头寸演变为亏损(或者, 尽可能持有必然). 8. 在弱势中买进, 在强势中卖出. 应该以买进的意愿来同等对待卖出. 9. 在多头市场的初期阶段, 应该扮演投资者的角色. 在多头市场的后期与空头市场中, 应该扮演投机的角色. 10. 不可摊平亏损–亏损头寸不可加码. 11. 不可仅因价格偏低而买进, 不可仅因价格偏高而卖出. 12. 只在流动性高的市场中交易. 13. 在价格变动迅速时, 不可建立头寸.

取代netcat的瑞士军刀socat

desc

  • netcat++
  • Multipurpose relay (SOcket CAT)
  • http://www.dest-unreach.org/socat/
  • 曾经一直纠结netcat这么好用的测试用具怎么就好久不更新了呢, 原来是有更好的取代者.
  • socat相比netcat功能更加强大, 同时也相对复杂了一些.

包含的工具

  • socat: establishes two bidirectional byte streams and transfers data between them.
  • filan: prints information about its active file descriptors to stdout.
  • procan: prints information about process parameters to stdout

工作原理 - life cycle of a socat instance (4 phases)

  1. init phase(初始化阶段), the command line options are parsed and logging is initialized. (解析命令行以及初始化日志系统.)
  2. open phase(打开连接阶段), opens the first address and afterwards the second address. These steps are usually blocking; thus, especially for complex address types like socks, connection requests or authentication dialogs must be completed before the next step is started. (先打开第一个连接, 再打开第二个连接. 这个单步执行的. 如果第一个连接失败, 则会直接退出.)
  3. transfer phase(数据转发阶段), socat watches both streams’ read and write file descriptors via select() , and, when data is available on one side and can be written to the other side, socat reads it, performs newline character conversions if required, and writes the data to the write file descriptor of the other stream, then continues waiting for more data in both directions. (谁有数据就转发到另外一个连接上, read/write互换.)
  4. closing phase(关闭阶段), one of the streams effectively reaches EOF. Socat transfers the EOF condition to the other stream, i.e. tries to shutdown only its write stream, giving it a chance to terminate gracefully. For a defined time socat continues to transfer data in the other direction, but then closes all remaining channels and terminates. (其中一个连接断开, 执行处理另外一个连接.)

Options

  • 命令行参数用来修改程序的行为. 他们与所谓的作为address specifications的一部分的address options无关.
  • 详见: socat -h

Address specifications

  • 一个address specification通常包含一个address type关键字, 0或者更多必要的address parameters与keyword用’:‘分隔以及他们相互之间, 和0或者更多address options用’,‘分隔.
  • keyword指定address type(如: TCP4, OPEN, EXEC). 对于有些keywords有同义词(‘-‘与STDIO, TCP与TCP4). 关键字是大小写敏感的. 对于一些特殊的address type, keyword可以被忽略: Address specifications以数字开头的被认为是FD address(raw file descriptor). 如果一个’/‘被发现, 在第一个’:‘或者’,‘前, GOPEN(generic file open)被认定.
  • 0或者更多address options可以被给到每一个地址上. 他们在一些方式上影响地址. Options由一个option keyword组成或者一个option keyword=value组成. Option keywords是大小写不敏感的.

Address Types

  • socat -h

Address Options

  • socat -h

Data Values

Refs

Build OpenCV With Python Support

steps

  1. Download opencv-2.4.11.zip from http://opencv.org/downloads.html
  2. unzip opencv-2.4.11.zip
  3. cd opencv-2.4.11
  4. mkdir release
  5. cd release
  6. export PYTHONPATH="/path_to_python/lib/python2.6/site-packages/:$PYTHONPATH"
  7. cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D BUILD_PYTHON_SUPPORT=ON ..
  8. 如果上一步找不到python相关路径, ccmake .., 然后手动指定 PYTHON_PACKAGES_PATH, PYTHON_EXECUTABLE, PYTHON_INCLUDE_DIR, PYTHON_LIBRARY, PYTHON_PACKAGES_PATH 这几个变量的位置. PYTHON_EXECUTABLE="/path_to_python/bin/python" PYTHON_INCLUDE_DIR="/path_to_python/include/python2.6" PYTHON_LIBRARY="/path_to_python/lib/python2.6/config/libpython2.6.a" PYTHON_PACKAGES_PATH="/path_to_python/lib/python2.6/site-package"
  9. make
  10. sudo make install

test

  1. python
  2. import cv2

Refs

Make A Live USB For MacBook

live usb/live cd 发行版的选择

  • http://livecdlist.com/
  • 最开始使用的live usb是ubuntu, 因为是我的入门linux发行版. 不过后来越来越不喜欢这个不稳定的发行版了. 接触的有Knoppix(默认桌面有3d特性, 基于debian), kali linux(backtrack的新版本, 基于debian, 还不错哦, 玩的人还蛮多的), System Rescue CD(基于Gentoo是他唯一的优点了吧, 官方太多文档与实际不对应的问题了)
  • 本次的选择, 先试用下System Rescue CD(因为我的项目就是用gentoo来做的, 另外, 我个人所有的linux环境都是gentoo啊), 如果发现使用起来非常不方便, 就转kali linux.

System Rescue CD

  • 官方只支持x86(32bit与64bit合二为一了, 应该是gentoo的multilib版本), 不支持ARM, 在树莓派上让我怎么愉快的玩耍! 不过想想, 对于树莓派的live usb, 应该叫做live sd才对, 只要有个可用的sd卡即可了. 我毕竟只有一个U盘(SLC, 16G), ARM版的live usb属实用的地方不多.

Tools

mac上boot分析

不了解boot过程就搞的话, 很容易把系统搞坏的.

uefi

boot camp

Ref

break GxFxW

GxFxW工作方式

  • GxFxW本身是一个人人都知道他的存在, 却从来不会被官方承认的机构.
  • GxFxW封锁重点: 新闻, 社交, 政治, 色情, 文件共享类网站.
  • DNS劫持和污染: DNS缓存投毒, 虚假IP劫持, 空包劫持, 轻松的扩展污染.
  • 敏感词过滤.
  • IP阻断.

网络工具

  • ping, tcping(可测tcp端口) 测试网络是否连通
  • traceroute 显示路由跳跃情况
  • route (netstat -nr) 打印和修改本地路由表
  • dig/nslookup 解析域名

OpenWrt组件

  • ipset
  • iptables-mod-ipopt, kmod-ipt-ipopt
  • ip
  • iptables-mod-filter, kmod-ipt-filter
  • iptables-mod-u32, kmod-ipt-u32
  • ppp-mod-pptp
  • openvpn-ssl
  • dnsmasq-full
  • bind-dig, bind-libs

技术原理

  • IP命令, opkg install ip table概念, 把数据添加到table, 让table数据走VPN接口.

Refs

移植linux内核的list.h到用户态

以前工作的时候在项目里使用过的, 随着离职, 代码已经无法找到了. :(

没关系这次放到github上面就不会丢失了.

Features

  • Type Oblivious
  • Portable
  • Easy to Use
  • Readable
  • Saves Time

Usages

  • List is inside the data item you want to link together.
  • You can put struct list_head anywhere in your structure.
  • You can name struct list_head variable anything you wish.
  • You can have multiple lists!

Repo

Refs

A File-oriented C/C++ Dependency Manager Biicode

Biicode is a file-oriented Dependencies Manager for C and C++ developers.

Features

  • 在C++中实现Go语言的modularity, 类似低是通过写合适的#include语句实现.
  • 文件级别的依赖管理, 重用已有项目的任何独立文件.
  • 使用一条命令分享和发布到biicode
  • 不用打包, 直接使用源码进行模块管理.
  • 基于CMake编译
  • 支持依赖包的版本控制.
  • biicode保存meta-data在文本文件中.

docs

基础命令

  • 版本: bii version
  • 检查并安装编译工具: bii setup:cpp
  • 搜索依赖: bii find
  • 编译: bii cpp:build
  • 创建项目: bii init myproject, 生成bii目录
  • 创建hello world项目: bii new myuser/myblock --hello=cpp, 生成blocks目录.
  • 发布到biicode: bii publish 默认发布到DEV, 每次默认覆盖上次的DEV代码.
  • Release life-cycle tags: DEV, ALPHA, BETA, STABLE. bii publish --tag=STABLE
  • 清理编译生成的结果: bii clean
  • 下载一个block到本地项目block中: bii open owner/block_name

吐槽

  • 支持nodejs是什么鬼啊! 就算技术上能实现, 可不可以完善好C/C++再搞其他的.

简单的内核态时间片轮转程序

实验环境

lab

test1 环境测试

  • 测试下环境, 仅仅是模拟了时钟中断, 一直都是一个进程在运行.
  • cd LinuxKernel/linux-3.9.4
  • qemu -kernel arch/x86/boot/bzImage

mykernel-start

test2 运行时间片轮转程序

分析

进程的启动

进程从void __init my_start_kernel(void)开始启动

嵌入式汇编代码, 初始化第0号进程

进程的切换

schedule()函数

未完待续.

备注

Simple RTMP Server分析

源码目录分析

  • 整体目录
.
├── 3rdparty/ # 第三方库源码跟补丁
├── Makefile  # 通过configure生成的Makefile
├── auto/     # configure读取执行特定功能的脚本
├── conf/     # srs启动的配置文件*.conf
├── configure # 编译脚本用于根据用户参数生成Makefile跟.h文件
├── doc/      # 协议RFC
├── etc/      # linux启动脚本
├── ide/      # IDE工程文件
├── modules/  # 目前为空
├── objs/     # 编译结果
├── research/ # 预研项目
├── scripts/  # 辅助脚本
└── src/      # 源码
  • 源码目录
.
├── app
│   ├── srs_app_bandwidth.cpp
│   ├── srs_app_bandwidth.hpp
│   ├── srs_app_config.cpp
│   ├── srs_app_config.hpp
│   ├── srs_app_conn.cpp
│   ├── srs_app_conn.hpp
│   ├── srs_app_dvr.cpp
│   ├── srs_app_dvr.hpp
│   ├── srs_app_edge.cpp
│   ├── srs_app_edge.hpp
│   ├── srs_app_empty.cpp
│   ├── srs_app_empty.hpp
│   ├── srs_app_encoder.cpp
│   ├── srs_app_encoder.hpp
│   ├── srs_app_ffmpeg.cpp
│   ├── srs_app_ffmpeg.hpp
│   ├── srs_app_forward.cpp
│   ├── srs_app_forward.hpp
│   ├── srs_app_hds.cpp
│   ├── srs_app_hds.hpp
│   ├── srs_app_heartbeat.cpp
│   ├── srs_app_heartbeat.hpp
│   ├── srs_app_hls.cpp
│   ├── srs_app_hls.hpp
│   ├── srs_app_http.cpp
│   ├── srs_app_http.hpp
│   ├── srs_app_http_api.cpp
│   ├── srs_app_http_api.hpp
│   ├── srs_app_http_client.cpp
│   ├── srs_app_http_client.hpp
│   ├── srs_app_http_conn.cpp
│   ├── srs_app_http_conn.hpp
│   ├── srs_app_http_hooks.cpp
│   ├── srs_app_http_hooks.hpp
│   ├── srs_app_ingest.cpp
│   ├── srs_app_ingest.hpp
│   ├── srs_app_json.cpp
│   ├── srs_app_json.hpp
│   ├── srs_app_kbps.cpp
│   ├── srs_app_kbps.hpp
│   ├── srs_app_listener.cpp
│   ├── srs_app_listener.hpp
│   ├── srs_app_log.cpp
│   ├── srs_app_log.hpp
│   ├── srs_app_mpegts_udp.cpp
│   ├── srs_app_mpegts_udp.hpp
│   ├── srs_app_pithy_print.cpp
│   ├── srs_app_pithy_print.hpp
│   ├── srs_app_recv_thread.cpp
│   ├── srs_app_recv_thread.hpp
│   ├── srs_app_refer.cpp
│   ├── srs_app_refer.hpp
│   ├── srs_app_reload.cpp
│   ├── srs_app_reload.hpp
│   ├── srs_app_rtmp_conn.cpp
│   ├── srs_app_rtmp_conn.hpp
│   ├── srs_app_rtsp.cpp
│   ├── srs_app_rtsp.hpp
│   ├── srs_app_security.cpp
│   ├── srs_app_security.hpp
│   ├── srs_app_server.cpp
│   ├── srs_app_server.hpp
│   ├── srs_app_source.cpp
│   ├── srs_app_source.hpp
│   ├── srs_app_st.cpp
│   ├── srs_app_st.hpp
│   ├── srs_app_st_socket.cpp
│   ├── srs_app_st_socket.hpp
│   ├── srs_app_statistic.cpp
│   ├── srs_app_statistic.hpp
│   ├── srs_app_thread.cpp
│   ├── srs_app_thread.hpp
│   ├── srs_app_utility.cpp
│   └── srs_app_utility.hpp
├── core
│   ├── srs_core.cpp
│   ├── srs_core.hpp
│   ├── srs_core_autofree.cpp
│   ├── srs_core_autofree.hpp
│   ├── srs_core_performance.cpp
│   └── srs_core_performance.hpp
├── kernel
│   ├── srs_kernel_aac.cpp
│   ├── srs_kernel_aac.hpp
│   ├── srs_kernel_buffer.cpp
│   ├── srs_kernel_buffer.hpp
│   ├── srs_kernel_codec.cpp
│   ├── srs_kernel_codec.hpp
│   ├── srs_kernel_consts.cpp
│   ├── srs_kernel_consts.hpp
│   ├── srs_kernel_error.cpp
│   ├── srs_kernel_error.hpp
│   ├── srs_kernel_file.cpp
│   ├── srs_kernel_file.hpp
│   ├── srs_kernel_flv.cpp
│   ├── srs_kernel_flv.hpp
│   ├── srs_kernel_log.cpp
│   ├── srs_kernel_log.hpp
│   ├── srs_kernel_mp3.cpp
│   ├── srs_kernel_mp3.hpp
│   ├── srs_kernel_stream.cpp
│   ├── srs_kernel_stream.hpp
│   ├── srs_kernel_ts.cpp
│   ├── srs_kernel_ts.hpp
│   ├── srs_kernel_utility.cpp
│   └── srs_kernel_utility.hpp
├── libs
│   ├── srs_lib_bandwidth.cpp
│   ├── srs_lib_bandwidth.hpp
│   ├── srs_lib_simple_socket.cpp
│   ├── srs_lib_simple_socket.hpp
│   ├── srs_librtmp.cpp
│   └── srs_librtmp.hpp
├── main
│   └── srs_main_server.cpp
├── protocol
│   ├── srs_raw_avc.cpp
│   ├── srs_raw_avc.hpp
│   ├── srs_rtmp_amf0.cpp
│   ├── srs_rtmp_amf0.hpp
│   ├── srs_rtmp_buffer.cpp
│   ├── srs_rtmp_buffer.hpp
│   ├── srs_rtmp_handshake.cpp
│   ├── srs_rtmp_handshake.hpp
│   ├── srs_rtmp_io.cpp
│   ├── srs_rtmp_io.hpp
│   ├── srs_rtmp_msg_array.cpp
│   ├── srs_rtmp_msg_array.hpp
│   ├── srs_rtmp_sdk.cpp
│   ├── srs_rtmp_sdk.hpp
│   ├── srs_rtmp_stack.cpp
│   ├── srs_rtmp_stack.hpp
│   ├── srs_rtmp_utility.cpp
│   ├── srs_rtmp_utility.hpp
│   ├── srs_rtsp_stack.cpp
│   └── srs_rtsp_stack.hpp
└── utest
    ├── srs_utest.cpp
    ├── srs_utest.hpp
    ├── srs_utest_amf0.cpp
    ├── srs_utest_amf0.hpp
    ├── srs_utest_config.cpp
    ├── srs_utest_config.hpp
    ├── srs_utest_core.cpp
    ├── srs_utest_core.hpp
    ├── srs_utest_kernel.cpp
    ├── srs_utest_kernel.hpp
    ├── srs_utest_protocol.cpp
    ├── srs_utest_protocol.hpp
    ├── srs_utest_reload.cpp
    └── srs_utest_reload.hpp

编译脚本分析

/trunk/configure

configure是个Bash脚本, 根据配置来生成Makefile.h文件.

  1. 如果存在Makefile文件, 则执行make clean
  2. 删除Makefile
  3. 创建objs目录
  4. 导入并执行auto/options.sh用于解析用户的编译参数, 对相应的SRS_XXX的变量进行赋值.
  5. 导入auto/generate-srs-librtmp-project.sh用于生成srs-librtmp项目, 创建项目目录, 拷贝一些文件到对应目录. research/librtmp/*.c research/librtmp/Makefile auto/generate_header.sh auto/generate-srs-librtmp-single.sh src/core/* src/kernel/* src/protocol/* src/libs/*
  6. 导入并执行auto/depends.sh, 检查缺少的依赖工具并安装
  7. 导入并执行auto/auto_headers.sh, 生成srs_auto_headers.hpp, 声明配置的宏.
  8. srs modules相关
  9. 编译工具跟编译参数赋值.
  10. 指定第三方库路径
  11. 调用auto/modules.sh将不同模块写入Makefile中.
  12. 生成srs app的编译目标的相关的Makefile代码. auto/apps.sh
  13. 生成utest的编译目标的相关的Makefile代码. auto/utest.sh
  14. 颜色化输出auto/summary.sh
  15. 生成Makefile

导出srs-librtmp项目源码与编译

学习State Threads

State Threads(简称ST)是一个C语言轻量级用户层的线程库, 总共4631行C代码. 这个线程库有助于开发者实现一个具有高性能和可扩展性的网络应用程序. 是Apache项目里面的一个子项目.(?Apache以前听说是用的select啊, 可能apache最后没用st, 懒得去考证了)

akagi201@akrmbp ~/Documents/state-threads (master) $ cloc .
      41 text files.
      38 unique files.
      11 files ignored.

http://cloc.sourceforge.net v 1.62  T=0.22 s (139.8 files/s, 61376.1 lines/s)
---------------------------------------------------------------------------------------
Language                             files          blank        comment           code
---------------------------------------------------------------------------------------
C                                       16           1046           1174           4631
HTML                                     3            236              0           3822
C/C++ Header                             6            270            288            939
make                                     3            106            205            364
Assembly                                 1             31            161            239
Windows Module Definition                1              0              0             51
Bourne Shell                             1             10              9             26
---------------------------------------------------------------------------------------
SUM:                                    31           1699           1837          10072
---------------------------------------------------------------------------------------

Docs

Refs

一个简单的汇编程序分析

最近打算跟一下这门mooc课程. 刚完成了第一周的内容. mindmup内容开源如下:

这里使用跟linux内核一样的AT&T汇编语法.

AT&T 汇编语法注意事项

  • 大小写: 指令语句使用小写字母.
  • 操作符赋值方向: 第一个为源操作数, 第二个为目的操作数, 方向从左到右, 合乎自然(与C库相反)
  • 前缀: 寄存器需要加前缀”%“, 立即数需要加前缀”$“.
  • 间接寻址语法: 使用 “(”, “)”
  • 后缀: 大部分指令操作码的最后一个字母表示操作数大小, “b”表示byte, “w”表示word(2个字节), “l”表示long(4个字节).
  • 注释: @用于一行代码后面添加注释内容, #是整行注释.

简单的C源程序

/*
 * @file main.c
 * @author Akagi201
 * @date 2015/03/01
 *
 * A simple code to learn how assembly code works.
 * build on linux x64: gcc -S -o main.s main.c -m32
 */

int g(int x) {
  return x + 3;
}

int f(int x) {
  return g(x);
}

int main (void) {
  return f(8) + 1;
}

Linux x64平台汇编命令

asm

  • 我的编译环境是: 物理机是rmbp, 运行的virtualbox, guest OS是gentoo x64.
  • 汇编命令: gcc –S –o main.s main.c -m32
  • 将得到的main.s文件中以.开头的行删掉(用于辅助链接的).
  • 生成的汇编代码中增加了部分注释说明了程序执行的过程.
g:
	pushl	%ebp
	movl	%esp, %ebp
	movl	8(%ebp), %eax # eax = 8
	addl	$3, %eax # eax = 8 + 3 = 11
	popl	%ebp
	ret

f:
	pushl	%ebp
	movl	%esp, %ebp # 每个函数前两句都是这个, 用于保存上一个堆栈的栈顶, 跟清空出一个新的堆栈
	subl	$4, %esp
	movl	8(%ebp), %eax # eax = 8
	movl	%eax, (%esp) # 把8放到栈顶
	call	g # 跳到g标号
	leave
	ret

main:
	pushl	%ebp # 将当前ebp的值压栈, 同时esp的值被修改(即减4)
	movl	%esp, %ebp # 将ebp指向esp
	subl	$4, %esp # 将esp向下移动一个位置
	movl	$8, (%esp) # 将立即数8赋值给esp指向的位置(即当前的栈顶)
	call	f # 等价于两条语句 pushl eip; movl f eip => 当前eip实际指向addl $1, %eax这条指令, 跳转到f标号处执行
	addl	$1, %eax # eax = 11 + 1 = 12
	leave
	ret # 最终ebp, esp回到main函数最初的栈的位置.

备注

教不懂电脑的父母上网

估计跟我一样80后的人都会涉及这个头大的问题, “教父母上网”. 下面整理些要点.

父母知识背景

父母虽然年轻时候也算接受过一些中等教育, 不过工作不是从事脑力方面, 学习能力几乎为零. 电脑跟智能手机方面知识几乎为零. 不手把手教, 一点不肯学, 不学习借口一堆.

想要教的东西

  • 基本的硬件组成跟连接
  • 拼音打字, 基本快捷键
  • 帮助注册邮箱跟必备的账号
  • QQ, 包含语音跟视频
  • 淘宝等网上购物
  • 推荐一些应该上的网站
  • 打免费电话
  • 手机上好用的app

email, qq

  • 要求不需要翻墙, 中文界面
  • 选择qq邮箱(需要手机号才行)
  • 备用邮箱用我的, 密码, 密保跟账号我都保留记录好.
  • 加我的账号为好友

电脑上软件

  • 装了腾讯系列的浏览器, 电脑管家等, 先观察看看, 不行就换360吧.
  • qq, 千牛, 迅雷, 360wifi, winrar, 酷盘, 百度云, 搜狗输入法, 爱奇艺PPS(看综艺节目)

电脑硬件配件

  • 耳机插孔的双音响(不需要配件, 连接不无线的简单)
  • 耳麦, 摄像头(用于远程视频通话)
  • 360随身wifi(家里这边移动宽带限制, 路由器上拨号失败, 只能电脑上拨号, 用于分享网络给手机)

引导使用的网站

  • 我的博客, 我公司的网站, 淘宝, 支付宝, 知乎, 优酷, 爱奇艺PPS, 百度地图, 携程, V2EX, 百度云.

手机app

  • 红米+酷派大神+小米充电宝 两台
  • 微信, 微博, qq, 支付宝, 淘宝, 千牛, 百度地图, qq邮箱, 爱奇艺, 优酷, 携程, 虾米音乐, 易信, 叮叮, 滴滴打车, 手机营业厅

The Missing C Package Manager Clib

web与移动开发领域每种语言都有自己的包管理器. nodejsnpm, golanggo get, lualuarocks, ios&macosx平台的cocoapods, PythonPip&Eggs, TeXCTAN, PerlCPAN, JavaMaven, Haskellcabal, RubyGems, .NetNuGet. 如果有兴趣, 可以看下这个播客https://ipn.li/kernelpanic/7/

不过C语言确一直没有一个这样的工具. 导致的问题就是, 当你需要用功C语言实现一个很通用的功能的时候, 你没有一个合适的地方去找, 只能google, stackoverflow, github去搜, 然后你有发现你要的这个功能在一个很大的项目里面的一个小模块, 然后很难独立提取出来, 然后只好重复造轮子.

还好, nodejs社区的几个人创造了Clib. C语言终于有自己的包管理器啦: Clib. TJ Holowaychuk先用node.js写了Clib, 然后Stephen Mathieson把他port成了C.

Clib developer’s blog

Golang on OpenWrt

Repo

Steps

  • git clone https://github.com/GeertJohan/openwrt-go
  • git checkout add-gccgo-and-libgo
  • make menuconfig
-> Advanced configuration options
-> Toolchain options
....
-> Select Build/Install gccgo
....
-> C library implementation
-> Use eglibc
  • make V=s

result

  • firmware with eglibc: bin/ar71xx-eglibc/openwrt-ar71xx-generic-carambola2-squashfs-sysupgrade.bin
  • add toolchain to PATH: export PATH=/home/akagi201/openwrt-go/staging_dir/toolchain-mips_34kc_gcc-4.8-linaro_eglibc-2.19/bin:$PATH
  • add toolchain alias: alias mips_gccgo='mips-openwrt-linux-gccgo -Wl,-R,/home/akagi201/openwrt-go/staging_dir/toolchain-mips_34kc_gcc-4.8.0_eglibc-2.19/lib/gcc/mips-openwrt-linux-gnu/4.8.3 -L /home/akagi201/openwrt-go/staging_dir/toolchain-mips_34kc_gcc-4.8.0_eglibc-2.19/lib'

test

  • use libgo non-static.
package main
import "fmt"
func main() {
    fmt.Println("hello world")
}
  • mips_gccgo -Wall -o hello_static_libgo hello.go -static-libgo

  • Before stripped

akagi201@akgentoo ~/openwrt-go (add-gccgo-and-libgo*) $ file hello_static_libgo
hello_static_libgo: ELF 32-bit MSB executable, MIPS, MIPS32 rel2 version 1, dynamically linked (uses shared libs), for GNU/Linux 2.6.16, not stripped

akagi201@akgentoo ~/openwrt-go (add-gccgo-and-libgo*) $ ll hello_static_libgo
-rwxr-xr-x 1 akagi201 akagi201 2.6M Feb  6 01:47 hello_static_libgo
  • After stripped
akagi201@akgentoo ~/openwrt-go (add-gccgo-and-libgo*) $ file hello_static_libgo
hello_static_libgo: ELF 32-bit MSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.16, stripped
akagi201@akgentoo ~/openwrt-go (add-gccgo-and-libgo*) $ ll hello_static_libgo
-rwxr-xr-x 1 akagi201 akagi201 1.2M Feb  6 02:02 hello_static_libgo

Refs

Links

Friends

Stream Encoder

OBS

BLE

License

Content license

All non-code blog content is licensed under Creative Commons BY-NC-SA.

Code license

All source code files and snippets found on this blog, unless otherwise explicitly noted, are licensed under the terms below.

Copyright 2013-2014 Bob Liu

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Learning RTP

RTP(Real-time Transport Protocol)是用于Internet上针对多媒体数据流的一种传输协议. RTP被定义为在一对一或一对多的传输情况下工作, 其目的是提供时间信息和实现流同步. RTP通常使用UDP来传送数据, 但RTP也可以在TCPATM等其他协议之上工作.

RTP本身并没有提供按时发送机制或其他服务质量(QoS)保证, 它依赖于底层服务去实现这一过程. RTP并不保证传送或防止无序传送, 也不确定底层网络的可靠性.

book

Learning cURL

cURL全称是”Client for URLs”, 即URL客户端.

Daniel Stenberg的一个个人项目, 就放在个人的一个二级域名http://curl.haxx.se/下, 所以有的地方略显粗糙也可以理解了.

项目历史应该比较久远了, 文档全是用的manpage写的, 不用到处找了, 直接man就可以了.

curlrepo由两部分组成, curl命令行跟libcurl, 其中复杂的东西都在libcurl中了.

源码结构

  • curl命令行的源码在src/, 入口在tool_main.c.
  • libcurl的源码在lib/.
  • API example的源码在docs/examples下.

Learning Google C++ Testing Framework

最近接触不少有趣的小项目, 让我重拾C语言的乐趣了. 现在移动和web发展非常块, web领域技术的发展, 也推进了底层技术的发展. clib就让我对jsnode这帮人的印象大大改观.

跟着牛人的脚本慢慢前进是不会错的. 选择了CLion, 导致选择了CMake, 又由CMakeCLion选择了GTest. 学习下来发现都是好东西.

Google C++ Testing Framework简称GTest, 跟CMake集成的非常好, 而且他本身就是用CMake编译的, 作为一个转向CMake开发者来说, 这是极赞的.

GTest文档相应比较少, 对于新手来说可能有点曲线, 我写了几个集成MakefileCMake的例子, 可以看看: https://github.com/Akagi201/learning-gtest

Learning Libuv

想研究libuv很久了, 一直没有用他的机会, 这次项目中有个多进程管理的地方, 正纠结怎么操作麻烦的信号量时, 想起libuv也可以完成这件事, 所以, 赶紧用起来. 不用白不用.

Lua在嵌入式领域大有可以, 可以编译成一个liblua.a的库静态连接到C程序, 还有很多丰富的工具, 本身语言有非常小巧, 以后还是多用lua写写, 方便移植啊. 多平台各种差异, 搞死人啊.

Learning uC/OS

最近比较粗略的学习了一下uC/OS-II的系统, 感觉设计上中规中矩吧, 很多类似的结构, 应该可以设计得更精简些的, 不过, 在单片机系统上用的还是蛮多的. 其中任务就绪表跟内存管理部分, 还需要有空再详细看看.

About Blog

TODO

  • 在slide部分上方显示我的头像.
  • 在手机跟PC上显示slide部分更优雅一点.
  • 中英文两个版本切换.
  • 不同分类用不同的样式.

Categories

  • blog, tool, book, talk, project

Tags

  • openwrt, kernel, wifi, hardware, macosx, golang, algorithm, git, protocol, security

Markdown Contents

Static Pages

About

Time Line

The important things in my life. http://akagi201.github.io/aklife/

Resume

My Resume on GitHub Pages. http://cv.akagi201.org

About Me

On About.Me page. http://about.me/akagi201

Contact Me

Play With Me

Learning CMake

为了更好的掌握CLion, 最近把CMake Practice看完, 并练习了一下. 完成之后发现内容很少. 老的autotools也是要掌握的, openwrt上编译很多项目还是要用到他.

Github Repo

Learning FFmpeg

我自己购买了mindmup gold的账号, 有钱淫欢迎投资我啊!!

发现个ffmpeg领域的大牛, 他已经在读博士了, 天朝这样做学术的人还是值得赞赏的, ffmpeg中文资料必看: http://blog.csdn.net/leixiaohua1020

Learning Network

Google Drive

Streaming Protocols

整理了一下流媒体相关的协议, 可能部分划分的不科学, 展开的不够细致. 先分享出来好了.

Learning OpenWrt - Outline

FreeRADIUS新手入门 - 我翻译的开源书

最近在搭建CoovaChilli + FreeRadius的认证系统. 顺便把一本英文书翻译一下. 由于时间比较匆忙, 所以还不保证质量, 想尽快翻译完, 然后在慢慢斟酌个别语句. 这个项目本身也是试水作品, 为以后写自己的书做些技术储备.

在写书的过程中发现几个问题: * gitbook中文支持有写问题, 有时候文本最左边字会叠在一起. * 有时候gitbook会崩溃, 有时候搜狗输入法会比较卡, 不知道是否是兼容性问题, 要及时保存. * 原文章节层次结构太乱了, 只有一级跟二级标题, 三级标题, 四级标题跟二级标题字体样式完全一样, 没法区分, 抽空要整理下. * markdown感觉层次太多的时候会记不住自己在哪层了. * github还没同步过来, 国内用gitbook写书的还不多, 问了一圈没得到答复.

在线阅读地址

github地址(还没同步过来, 求助)

将OpenWrt变成完美的BSP

BSP(Board Support Package)对于嵌入式开发者一定不陌生, 就是针对一种板子适配指定的操作系统(常见的是linux)所需要的bootloader, 板上外设的所有驱动, 还有内核, 通常还包括一个根文件系统(里面包含能确保板子能跑起来的基本的一些配置)和toolchain.

随着软件系统的发展, 越来越多的统一化环境配置的工具出现, 像vagrant, docker等, 这样, 将运行环境一起打包就不会出现过去那种, 在我的机器上能运行, 在你的机器上运行不了的情况了.

嵌入式开发也是一样, 每次面对一种新的SOC, 多要进行一些重复工作, 像裁剪系统, 裁剪busybox, 移植各种应用, 各种库等. 对于开发而言, 对于每种平台开发时, 都要有一些细小的差异. 而这些差异是可以统一起来的. 解决方案就是OpenWrt.

用OpenWrt作为BSP, 这使得用户和开发者可以快速熟悉不同的/新的硬件产品. 关于OpenWrt的详细内容, 在OpenWrt的官方文档有非常详细的介绍. http://wiki.openwrt.org/doc/start

那么公司如何用OpenWrt做自己的产品呢? 根据开源项目的特点需要进行一些修改.

OpenWrt Buildroot的Makefile wrapper

  • download tool: 下载指定版本的OpenWrt
  • patchset: 对指定版本的OpenWrt进行打补丁, 确保稳定
  • package feed: 自己软件包的 package feed.
  • dl link directory: 将~/dl链接到openwrt/dl.

使用OpenWrt buildroot过程的技巧

  • OpenWrt的buildroot编译系统, 包含fetching, patching, compiling, packaging的过程. 在fetching阶段会联网下载源码到dl目录, 所以, 一个好的方法是, 将dl目录保存在自己本地机器的一个固定位置, 然后软链接到openwrt/dl目录, 这样就不用每次下载重复的包了, 另外, 有时由于网络原因, 可以手动下载包放到这个目录.

地理围栏(Geo-fencing)

geofencing

Geo-fencing

  • A geo-fence is a virtual perimeter for a real-world geographic area.
  • http://en.wikipedia.org/wiki/Geo-fence
  • 地理围栏(Geo-fencing)是LBS的一种新应用, 就是用一个虚拟的栅栏围出一个虚拟地理边界. 当手机进入, 离开某个特定地理区域, 或在该区域内活动时, 手机可以接收自动通知和警告.
  • 有了地理围栏技术, 位置社交网站就可以帮助用户在进入某一地区时自动登记, 可应用智能购物, 个人助理, 家庭成员/朋友的发现, 智能家居等领域.
  • 地理围栏可通过蓝牙, WIFI, GPS等定位技术完成, 移动设备进入围栏后会自动选择最低功耗方式进行定位, 地理围栏技术为开发者提供全新想象空间.
  • 地理围栏技术均是: 预先划定一些多边形的区域, 一个中心化的设备或者云端知道定位源进入该区域; 定位源知道自己进入该区域; 触发一些响应, 例如消息的push. 在智能硬件时代, 这种能够更加精准地定位, 更加节省功耗, 更加有效率低成本的互联互通方式, 迎来爆发机会.

Geo-fencing vs LBS

  • 地理围栏的地理区域是被网格化的. 网格化的标准是根据一个地理区域内的业务和商业聚类的, 而不是纯粹的经纬度和城市地图的匹配.
  • 实际上地理围栏的各个围栏的区隔是一个个的应用需求群地图. 主要的商业需求聚集在特定区域, 形成的一个聚合信息服务区域.
  • 终端自己或者在网络帮助下能够识别所处的围栏.
  • 用户的围栏信息彼此之间能够共享, 也能够与应用开发商分享.
  • 双向, 互动是关键.
  • 商业群落是地理围栏的核心, 类似城市的商圈.
  • 价值整合, 场景整合, 信息流资金流整合是关键.

Geo-fencing vs Beacons

Geo-fencing Applications

百度的地理围栏技术

百度地理围栏技术是国内首家提供离线+在线地理围栏服务的产品, 基于位置的提醒和离在线结合的方式, 实现了功耗的大幅降低. 用户离关键位置点较远的时候, 会进行距离判断, 在用户到达围栏周围的时候, 再请求在线定位, 询问用户是否触发围栏.由于用户不需要一直打开GPS, 所以在使用该功能时达到了省流量, 省电的目的.

百度地理围栏技术”离线+在线”的技术策略, 能够让在用户体验上更为顺畅, 摆脱网络状况的限制, 避免智能手机普遍待机时间短的缺点, 极大提升了地理围栏的工作效率, 因此使用百度定位SDK提供的地理围栏服务的开发者能够设计开发更适合用户需求的产品. 目前, 已经有大量开发者利用百度地理围栏SDK开发相关应用, 精彩创意层出不穷.

Intel的地理围栏技术

Intel的地理围栏技术采用了有效地将地理信息整合到移动平台的应用中去, 极大提高应用的易用性, 同时权衡系统待机时间与响应时间是Intel地理围栏的关键亮点, 实现了低功耗, 快速响应和高精度三者的完美结合, 其围栏技术的创新点:

1)使用MCU完成连续监测功能.

2)基于情境自动甑选合理的位置提供模块.

3)通过传感器轨迹推算实现实时定位和矫正.

Intel地理围栏技术亮点包括:

1)多定位源GNSS/Modem/WiFi.

2)传感器轨迹推算.

3)多地理围栏.

4)情境感知.

5)自适应.

Apple的地理围栏 - iBeacons技术

iBeacon的出现让地理定位能够更加精确——从几百米的精度提高到了一米甚至半米. 这个精细度非常高的地理围栏, 终于可以让很多的实际物体都有条件具有了定义自己地理位置标识的能力, 例如一张桌子, 一把椅子, 都可以有自己的地理坐标.

苹果在iOS 7中推出的iBeacon协议包含两个部分:

1)按照苹果对iBeacon发射设备的数据流格式, 定制蓝牙设备广播, 那么这台低功耗蓝牙设备就可以被识别为iBeacon协议发射装置.

2)利用iOS设备对蓝牙的广播发出设备进行判断, 如果其发出的广播数据符合iBeacon的协议, 那么就认为这台低功耗蓝牙的发射装置是一台iBeacon基站.

由于iBeacon建立在蓝牙协议的基础上, 所以这个技术天然拥有了两个优势:

1)硬件的无缝过渡, 不需要硬件厂商投入成本进行完全不同的硬件开发.

2)现有数据传输协议对于用户来说没有太大迁移成本, 完全有能力像智能路由(Wi-Fi协议)一样成为物联网的数据中心节点.

其他的地理围栏技术

地理围栏技术在10年前便已出现. 在去年的Google IO大会上便已经有了地理围栏的展览.

Meridian平台便提供Zones地理围栏功能, 这项功能允许用户在开发者推出的室内地图App上随意用多边形圈区域, 而当用户真正到达这些区域时, app会立马推送通知. 除了Meridian, 国外还有几家地理围栏平台, 比如PlaceCast, Digby Localpoint, Wifarer和ShopKick. 和上述几家最大的不同是, Meridian是通过Wifi传感而非GPS定位.

而Google Lititude API也支持开发者在地图上标记多边形的围栏区域, App进入该趋于时便会收到push消息. 与iBeacons等技术不同的是, 这个push消息不是由基站发送, 而是云端推动的. 跟百度地图的地理围栏技术有些相似.

Refs

Learning Makefile

系统的学习了一下Makefile, 隐含规则部分没有详细看, 实际使用过程中也不会依靠这个东西的.

参考资料

  1. 跟我一起学Makefile
  2. GNU Makefile Reference: http://www.gnu.org/software/make/manual/make.html
  3. 另一个淘宝前辈的总结(简单向, 我的更偏向深入一些): http://kenwublog.com/arrange-makefile-knowledges
  4. 调试makefile: http://coolshell.cn/articles/3790.html

偶遇的好东西

自主学习的一个好处就是, 在研究的过程中会发现很多好玩的东西.

Root Nook Simple Touch

nookmanager-success

今天断网了大半天, 然后玩了一会Calibre, 翻出了我的Nook3, 刚好利用这个假期打算把他root掉.

使用NookManager进行root

  1. 固件升级到官方1.2.1(我的原来是1.1.5的, 如果不升级那么root时会ModManager会安装失败)
  2. 下载NookManager.img.
  3. 使用dd命令将NookManager.img写入一个空的sd卡中.(我用Mac OS X系统, linux下类似, win下用相应工具)
akagi201@akrmbp ~ $ ls /dev/disk*
/dev/disk0   /dev/disk0s1 /dev/disk0s2 /dev/disk0s3 /dev/disk1   /dev/disk1s1
akagi201@akrmbp ~ $ sudo diskutil umount force /dev/disk1s1
Volume (null) on disk1s1 force-unmounted
akagi201@akrmbp ~ $ sudo dd if=/Users/akagi201/Downloads/NookManager.img of=/dev/disk1 bs=1m
64+0 records in
64+0 records out
67108864 bytes transferred in 49.549986 secs (1354367 bytes/sec)
  1. 关掉Nook的电源, 插入sd卡, 然后开机, 会显示15秒的NookManager的信息.

nookmanager-start

  1. 选择”No, continue without wireless”, 选yes需要刷机之前设备有连过你附近的ap, 才能验证通过.(这个其实可以改进啦)

nookmanager-wifi

  1. 使用NookManager做一次备份! Rescue -> Backup -> Format remaining space on SD card -> Create backup. (需要花费15~45分钟, 最终备份文件大小是几百M)

nookmanager-backup

  1. 使用USB线连接电脑和nook, 拷贝NookBackup分区下的backup.full.gz和backup.full.md5到电脑, 最好上传到网盘备份好.

  2. Root! 备份之后, Back -> Back -> Main Menu -> Root -> Root my device (然后看到全是成功, 如果不成功说明你用其他方法root过或者没有升级到官方的1.2.1)

nookmanager-root

nookmanager-success

  1. Back -> Exit -> 拔出SD卡 -> 设备自动重启 -> 选择Relaunch. 搞定.

Refs

Wireless Penetration

src=“https://atlas.mindmup.com/akagi201/wireless_penetration/index.html" height=“100%” width = “100%”>

之前知乎上有人私信我, 问我无线安全都需要了解哪些东西, 如何去学, 当时没有给他一个好的答案, 最近在研究无线安全攻防方面的东西, 刚好看到一个不错的东西整理一下框架, 然后, 慢慢补充细节.

Refs

无线基础之无线网卡

今天利用一个上午的时间把gentoo装好了, 昨天因为网线的原因导致我这边网络一直超时, 郁闷死我了, 多亏我今天足智多谋发现了. 由于OpenWrt的代码仓库版本更新非常频繁, 所以开发分支里面的库和内核版本比一般的桌面linux发行版都要新. 有一个基本常识是host开发主机上面的库和编译工具版本要比源码使用的版本新, 否则就会出现一些奇怪的问题, 无法解决. 所以, 选择一个滚动升级的linux发行版用于开发是明智的选择(相信我, 不难的). 这样筛选后就只剩下Arch和Gentoo了, Arch比较不稳定(希望不被喷, Arch的wiki跟Gentoo一样丰富是好东西), 所以Gentoo是你最明智的选择.用Gentoo编译了一下openwrt, 比我之前用debian节省了至少一半的时间, 哈哈, 爽. BTW, 不要给Gentoo安装图形界面, 很废时间, 也会出现很多冲突, 那就需要你身边有个高手了(我还不是要靠低调之神Yokit的帮忙才解决一些问题).

在进入无线研究之前你需要一套趁手的装备, 这套装备包括硬件和软件, 当然这个也就是我们要做的东西, 其中必然涉及一些硬件和软件的选型. 本文重点介绍一下网卡芯片的选型与相关知识.

常见网卡接口

底层芯片组

无论使用哪种接口的网卡, 他们的核心都是”芯片组”. 这采集关键所在, 我们要关注的电气性能也是针对芯片组的. 目前常见的WLAN芯片厂商有:

  1. Atheros(已被高通收购)
  2. Broadcom(博通)
  3. Intel
  4. Ralink(已被联发科收购)
  5. Realtek

驱动程序

由于芯片的性能跟驱动的支持是分不开的, 所以, 良好的驱动支持, 也是我们要重点考虑的一项参数. linux内核当前无线网卡驱动架构说明: mac80211

可以看到linux下的无线驱动程序经过了一段”发展期”, 最终以”mac80211驱动框架”作为最终的”主树结构”. 关于mac80211驱动框架的详细文档请查看: http://wireless.kernel.org/en/developers/Documentation/mac80211. mac80211是一个无线驱动的框架, 它提供了大量的API, 规范, 在这个框架下编写驱动程序能和其他的驱动具有良好的共享性, 兼容性(类似与windows下的NDIS框架的作用).

一般来说, 各家芯片厂商都会提供配套的驱动程序, 并提供更新支持 1. Atheros(AR系列)

<http://www.qca.qualcomm.com/resources/driverdownloads/>
<http://wireless.kernel.org/en/users/Drivers/Atheros>
  1. Broadcom(BCM系列)
<http://zh-cn.broadcom.com/support/802.11/linux_sta.php>
<http://wiki.centos.org/zh/HowTos/Laptops/Wireless/Broadcom>
  1. Intel
<http://wireless.kernel.org/en/users/Drivers/iwlwifi>
  1. Ralink(RT系类)
<http://www.mediatek.com/en/downloads/>

淘宝上卖的很多卡皇的内置芯片就是这种RT型号(所谓卡皇就是无良厂家违规的放大了无线发射功率, 大家还是慎重考虑, wifi近距离接触(贴着身体)还是有危害的, 通常半米到1米左右还是可以认为是安全的)

  1. Realtek(RTL系列)
<http://www.realtek.com.tw/DOWNLOADS/downloadsView.aspx?Langid=1&PNid=14&PFid=7&Level=5&Conn=4&DownTypeID=3&GetDown=false>

需要注意的, 我们在选择驱动的时候需要关注一下当前驱动是否支持USB(因为现在大多数人包括我自己都是使用外置网卡进行实验的).

待续

上面我们提到过, 不同型号的网卡的*主要差别*在于内置的芯片组, 但是, 一个无线网卡的好坏除了和上面说的芯片组, 驱动有关外, 还和他自身的一些物理, 电气特性有关, 下一篇我们会进一步与大家交流.

Refs

Signature

  • Author: Akagi201(我的微信, 加我请注明: 真实姓名-公司/专长)
  • Blog: http://akagi201.org
  • AK创客空间qq群: 212106391 (加群暗号: ak)
  • 请支持本微信公众号, 分享给你的朋友们: AKmaker

Learning Kernel

我是从大二开始使用linux系统的, 当时除了玩单片机之外, 所有工作完全在linux完成, 克服了种种”困难”. 不过现在看来自己除了佩服自己当时的热情之外, 就只觉得自己太幼稚了. “在正确的时间做正确的事情, 用正确的工具做正确的事情.” 就像以前一直很偏爱C语言一样, 一定要写成内核模块, 内核线程… 现在自己变得更加聪明了.

我大学期间一直没有机会碰linux内核, 只有在大三下的时候, 在实验室玩ARM开发板, 才知道, 原来linux内核做了这么多的工作, 一个hello world能够轻松的运行起来, 背后有多少的东西在默默无闻的工作着.

毕业后我按照自己的规划找了一份嵌入式软件开发的工作. 公司也还算给力, 给了我足够的时间来学习. 我当时是从驱动入手的, 主要看了一本书和一个英文文档, 分别是LDD3和LKMPG. 这本书我前段时间又看了一遍, 觉得这个不适合一个新手看, 难怪我当初看的那么累, 原因有是作者在书内容里无缝地介绍了软件架构, 代码复用等等对于新手来说高级的东西, 这些应该属于软件工程的内容, 这样当然有好处, 但是给人感觉就是复杂, 相比之下国内的书会简单直接很多. 另外LDD3是基于2.6内核的API写的驱动, 已经有大牛移植到linux3.x上面了.

其实, 内核代码发展很快, 差几个版本基本就面目全非了, 所以, 要多看, 多思考, 有整体把握. 这样, 过段时间拿到最新的代码了, 自己也有能力跟踪进去.

最近, 在linux社区上面看了几篇有趣的东西, 在HN上貌似也火了一下, 所以, 分享出来给大家玩下.

Eudyptula

模仿Matasano Crypto Challenge(集中48小时的练习, 培训参与者密码系统如何建立以及如何被攻击), 面向linux内核的一系列编程练习, 任务难度逐渐增加. 一切是从给little@eudyptula-challenge.org发一封邮件说你要加入开始的. 由于实际的内核开发就是通过邮件列表沟通的, 所以, 必须要熟悉邮件工具是必备技能.

PS: 由于我发了邮件他还没回我, 呵呵, 后面等做了几个任务之后再跟大家分享一下.

Kernel 101

这篇文章最近在HN上比较火, 教你从0开始写一个kernel, 当然功能仅仅是打印一行信息而已. 对于新手来说还是比较好的学习材料. 源码就2个文件, 一段汇编主要功能就是跳转到C程序的kmain函数; 一段C代码, 将显存内容清空并赋值为一段字符串.

其中几个关键知识点记录一下: 1. x86的CPU启动后从地址[0xFFFFFFF0]处开始执行, 这个是设计CPU时写死的. 从芯片手册上可以查到. 下面提到的一些地址都是芯片手册中规定统一的, 所以x86架构才能够通用, 各种OS都能安装, 不需要繁琐的移植工作. 2. 启动流程: 上电 -> CPU[0xFFFFFFF0] -> 跳转到内存中BIOS代码 -> 根据BIOS配置将物理设备第一个扇区的代码copy到物理内存的[0x7c00]位置(即boot loader的代码) -> bootloader将内核代码加载到物理内存0x100000. 3. 汇编代码中使用了一些nasm的伪指令, 所以, um, 看注释就好. 4. 通常的内核开发还需要提供一个根文件系统文件的, 这个简单例子是不包含文件系统的. 5. grub2下添加引导项的方法, 成败在于此, 需要注意你的/boot分区是否是一个独立分区, 如果不是独立分区那么ok, 安装作者的文章搞起; 如果你像我一样安装系统时候将/boot独立一个256M空间的分区, 那么要内核文件位置使用相对/boot相对地址. 另外注意, 要使用msdos2, 否则会失败.

$sudo vim /boot/grub/grub.conf

//在文件中其他引导项下面添加如下
menuentry 'Akagi201 Lovely Kernel' {
    set root='hd0,msdos2'
    multiboot /kernel-7001 ro
}

What my new-born kernel says

um, 我改了一下颜色, 跟字符内容, 哈哈, 没啥技术含量啦!

akgrub

akkernel

保护你的隐私, 从PGP开始

以前大学还在玩ubuntu的时候, 天天逛ubuntu的中文论坛, 看到这么个家伙, http://adam8157.info/about, 他的about页面一直放了一个My PGP/GPG key ID: 2F39D84D, 我一直不知道是干什么用的, 我还特意到知乎上问了一下, 不过貌似知乎对这么被认为是可google的问题没兴趣(对八卦和吐槽感兴趣?), 没得到满意答案, 今天有个空挡, 还是自己研究下.

PGP(OpenPGP)是一个历史悠久的电子加密和签章系统, 透过public key加密演算法, 保护个人电子资料, 不会在散布过程或存储媒体中被有心者窥视, 破坏或伪装. 不同于一般以CA(Certificate Authority, 认证机构)为基础的签章, 加密系统, PGP是分散式的系统, PGP没有中央的控制或信任机构, 因此不会被政府, 少数机构所控制,入侵. 在这个公权力无法被信任的年代, 我们正需要这样的系统, 保护我们的通讯安全.

Web Of Trust

PGP是透过所谓的web of trust, 建构信任网. 也对于, 透过人际网路, 一对一的交换PGP key(public key), 安全的通讯管道, 建立在人和人之们的信任感上. 相对的, 以CA为基础的系统, 是透过少数集中的组织, 交换public key. 因此, CA容易受政府或少数机构的控制, 而破坏其安全性. 而web of trust则没这样的问题, 没有中央机构可以伪造你的PGP key. PGP的使用者, 透过web of trust确保所使用的 key, 不是政府或第三方所伪造的.

伪造Public Key

CA为基础的系统, Public Key的散布是透过CA对Public Key进行签名. CA会有一份公开的Public Key, 透过使用对应的secret key对某Public Key签名, 由CA保证其正确性. 而使用者, 透过验证通讯对方的Public Key是否有CA的正确签名, 确保使用正确的Public Key. 这样的系统, 建立在对CA的信任, 因此CA必需是公信的第三者. 然而, 事实上没有绝对公信的第三者, 政治力量随时可能入侵CA, 透过CA的Key, 伪造任何人的Public Key. 因此, 像CA这类中央式的系统, 容易受外力影向, 进行大规模隐私侵害, 无法保护通信的自由和隐密. 事实上, 中国某CA运作单位, 就被怀疑有这种可能.

PGP工具

PGP系统的实现, 有两项主要工具, PGP和GPG. PGP是原先的实现, 而GPG则是GNU实现的相容工具, 和PGP相容. 本文介绍GPG的使用.

产生PGP Key

GPG基本上会产生两对key, 一组用来sign(签章), 另一组用来encrypt(加密). Encryption用途的key, 因为比较常被使用, 因此较容易受攻击. (透过分析加密的样本, 数量愈多, 愈可能分析出原本的key.) 因此, 一般建议定期更换加密用的 key. 然而, 更换key非常麻烦, 必需一一重新和拥有你的key的朋友交换. 因此, 签章用途的key通常是和加密用途的key分开的. 签章用途的key较少使用, 因此较不易被破解, 通常是永久使用. 在你更换新加密用途的key时, 能够使用签章用途的key, 为新的key签名. 因此, 你可以透过email或其它网路的方式散布你加密用途的新key. 收到新key的朋友, 就可以使用你签章用途的key, 验证你的新key.

产生新的PGP key的方法:

## 两种安装方法
#1. brew install gnupg # 命令行和linux完全一样
#2. https://gpgtools.org/ # GUI界面, 其实命令行就够了

akagi201@akrmbp ~ $ gpg --gen-key
gpg (GnuPG) 1.4.16; Copyright (C) 2013 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: directory `/Users/akagi201/.gnupg' created
gpg: new configuration file `/Users/akagi201/.gnupg/gpg.conf' created
gpg: WARNING: options in `/Users/akagi201/.gnupg/gpg.conf' are not yet active during this run
gpg: keyring `/Users/akagi201/.gnupg/secring.gpg' created
gpg: keyring `/Users/akagi201/.gnupg/pubring.gpg' created
Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)
Requested keysize is 2048 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

Real name: Akagi201
Email address: akagi201@gmail.com
Comment: Bob Liu
You selected this USER-ID:
    "Akagi201 (Bob Liu) <akagi201@gmail.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
You need a Passphrase to protect your secret key.

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
...+++++
..+++++
gpg: /Users/akagi201/.gnupg/trustdb.gpg: trustdb created
gpg: key BAD7F7A3 marked as ultimately trusted
public and secret key created and signed.

gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
pub   2048R/BAD7F7A3 2014-05-27
      Key fingerprint = E19A 2B9C B30F 8D0E 3F14  8C9F 7BAA 088C BAD7 F7A3
uid                  Akagi201 (Bob Liu) <akagi201@gmail.com>

Note that this key cannot be used for encryption.  You may want to use
the command "--edit-key" to generate a subkey for this purpose.

建议选(3)或者(4)(我选了4), 产生只用来签章的key, 并将有效时间设定为永远. 在产生签章用途的key之后, 该key会存在keyring里, 通常是home目录下的.gnupg 子目录, 你能用gpg列出目前存在的key.

akagi201@akrmbp ~ $ gpg --list-keys
/Users/akagi201/.gnupg/pubring.gpg
----------------------------------
pub   2048R/BAD7F7A3 2014-05-27
uid                  Akagi201 (Bob Liu) <akagi201@gmail.com>

akagi201@akrmbp ~ $ gpg --list-secret-keys
/Users/akagi201/.gnupg/secring.gpg
----------------------------------
sec   2048R/BAD7F7A3 2014-05-27
uid                  Akagi201 (Bob Liu) <akagi201@gmail.com>

这列出你有一把PGP的Public Key, 接着列出你有一把Secret Key. 这两把key的ID都是0xBAD7F7A3(第二栏, 斜线后), 代表他们是同一对key. Secret Key是使用者收藏(前面有sec字样), 不能让别人知道的部分, 用在签名用途. 而public key则是公开给别人知道(pub字样), 用来验证你的签名.

接着我们要产生加密用途的key.

akagi201@akrmbp ~ $ gpg --edit-key BAD7F7A3
gpg (GnuPG) 1.4.16; Copyright (C) 2013 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

pub  2048R/BAD7F7A3  created: 2014-05-27  expires: never       usage: SC
                     trust: ultimate      validity: ultimate
[ultimate] (1). Akagi201 (Bob Liu) <akagi201@gmail.com>

gpg> addkey
Key is protected.

You need a passphrase to unlock the secret key for
user: "Akagi201 (Bob Liu) <akagi201@gmail.com>"
2048-bit RSA key, ID BAD7F7A3, created 2014-05-27

Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
Your selection? 6
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)
Requested keysize is 2048 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 2y
Key expires at Thu May 26 10:59:08 2016 CST
Is this correct? (y/N) y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
..+++++
.+++++

pub  2048R/BAD7F7A3  created: 2014-05-27  expires: never       usage: SC
                     trust: ultimate      validity: ultimate
sub  2048R/6E980A59  created: 2014-05-27  expires: 2016-05-26  usage: E
[ultimate] (1). Akagi201 (Bob Liu) <akagi201@gmail.com>

gpg> quit
Save changes? (y/N) y

请选择(5)或(6)(这里我选择了6), 产生专用来加密用的public key. 这里将有效时间设为两年(2y), 可依据需要设定. 完成之后, 会看到一把subkey(前面有sub字样), 这里得到的key ID为0x6E980A59. 0x6E980A59为0xBAD7F7A3的subkey. 0x6E980A59的有效期限为2年, 因此, 两年之后你必须生成一把新的key, 以取代这把.

kagi201@akrmbp ~ $ gpg --list-sigs
/Users/akagi201/.gnupg/pubring.gpg
----------------------------------
pub   2048R/BAD7F7A3 2014-05-27
uid                  Akagi201 (Bob Liu) <akagi201@gmail.com>
sig 3        BAD7F7A3 2014-05-27  Akagi201 (Bob Liu) <akagi201@gmail.com>
sub   2048R/6E980A59 2014-05-27 [expires: 2016-05-26]
sig          BAD7F7A3 2014-05-27  Akagi201 (Bob Liu) <akagi201@gmail.com>

这里可以看到, 0xBAD7F7A3和0x6E980A59这两把key都用0xBAD7F7A3这把key签章过(前面有sig字样). 也就是0xBAD7F7A3 有一个self-sign, 自已签自己.

akagi201@akrmbp ~ $ gpg --armor --export BAD7F7A3
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1

mQENBFOD/ZQBCADQNTAV2KI+37d/Ep1ginwR2AoMTPe4AbhGVBr5LJsWbrW/y/Ap
Fyar6eAcT2OLAASpAyJNZpGxrG5QmKRjvcC/Bdx4mudWExs1o3aUwGIeCCUBVBdj
r0g2kZji/UbuaArRWVBotl/DIqvYswKM762FnQoOKTlMlj45U1dY1WS2ZP8KFhHV
5RWqJknY8p42QC5Tl09m7TCxkAz7ms+qU8Ya6Af4vLdSo8V7bpbATD2BQtPTpfZt
3z9rezvDRcsWK4O3Cmx5z+Q6HjQZV7Wbg2L3Q0yUzHktMM997WmRlT3zyUYXeAxG
wZSAGKcVd4gvHOLiRB5GyuYr8lIQH0GN/uCzABEBAAG0J0FrYWdpMjAxIChCb2Ig
TGl1KSA8YWthZ2kyMDFAZ21haWwuY29tPokBOAQTAQIAIgUCU4P9lAIbAwYLCQgH
AwIGFQgCCQoLBBYCAwECHgECF4AACgkQe6oIjLrX96OgqggAr21OZvGgqwo59G7H
9QGjwVD5OliJgAUuykEuzCv4ATKm+7Y/g/HgGUqIILKzknOPCe2VGTs+6RA98zWk
9nbdCUEu7oUZTBYq5h1uuPkm4FoJyyrwatSNChqJ4qav6jpZDqGnHwNeEwdY1WNG
uoC+MDM38u5KET6TnqYgmd5B0HWlUuiUx57uKE70vPRpziNDloeLzKdfD0fibKHN
ZFOgJJZkoPhr/yigqmQEfdrajTY4YGEy/2HYIULZYQs0paRe0SVw0UqbDZanOvlr
KOv6XFtH66DjY0pTgUWUXfGylzJAAtgQXQOFDXURC2oYG9DRkvA8q5gaZpqJT4f9
3Awk0bkBDQRTg/98AQgAwTKF9C/72yRmtTVaLziH93eKSc8jKOPt+Ncio5l+Bdhj
lJqJLzQWBB8uRa76wcTKMUpPMOPAMYqZjMekbVIrKFfNHh3pD+y90M/rDkhM2M4Z
gV6XtHnjqhQ4woqejM6k1ADKgndZNoau0TlJ7TagPM33Nay43vHo/BPcx6rs4Ssa
oSnv0sf4PBJeDfhjna+LVAQ18/rPBRijL/Xh6Bn2PWTrmF59g2mmdzV3WMOMsC+I
VkuXP20Vg4B0hFi6t9Hx/B5JL9t/xEEo2YKRyia8F4vtraSlhbMwVnfPfWuG5YSh
AsBa1ByLxCbNdELoLwU69ZDmgFH2x8JdUcm2vslwLwARAQABiQElBBgBAgAPBQJT
g/98AhsMBQkDwmcAAAoJEHuqCIy61/ejD9MH/A3vij2gjaLl3u1CAQB6n8DUSiyr
+bzEkoLHcJDYAM49oM3GPnkCSLJVM7EYmnlR6GOO4PwXlr2GjoxIer+MDCJ8hq98
n+H/2kgV8me/DDSoI1WUiIrcVLBcvylqZv3UWN4Vz+hP48iS/CEtPO6up/l3UAL3
YMWpi96+pnPodjMXL8JcIHlvx8Syxz1J0REI1J7uiGqzvJ9wu4ASYAHtJgi3qsvS
0FvAJcbdWMRNs51Pi0C1SYeztV7yRih8lTjI9ylUoncY5nKPtHrXjCNfpe0fClHx
JLz3WwlAX8gFyM3C+dl/622e7EvK9hO/spn8kS3lHD3q2V3t8GtSM4RzsY4=
=G9Ls
-----END PGP PUBLIC KEY BLOCK-----

透过上面的指令, 你能将你的public key export出来(导出), 印在纸上, 或存在U盘上, 或者是贴在网路上散布. 然而, 别人如何确定这把key是你的?

akagi201@akrmbp ~ $ gpg --fingerprint
/Users/akagi201/.gnupg/pubring.gpg
----------------------------------
pub   2048R/BAD7F7A3 2014-05-27
      Key fingerprint = E19A 2B9C B30F 8D0E 3F14  8C9F 7BAA 088C BAD7 F7A3
uid                  Akagi201 (Bob Liu) <akagi201@gmail.com>
sub   2048R/6E980A59 2014-05-27 [expires: 2016-05-26]

上面指令能列出你的public key的fingerprint(指纹)印出来, 这相当于public key浓缩之后的特征(digest). 因此, 其它使用者只需比对这组fingerprint是否和public key的相符, 就能确定这把key的正确性. 因此, 通常你会把fingerprint 印在纸上, 面对面交给对方. 必要时, 还会检查对方的身份证, 护照或其它身份证明文件.

交换key

在开始交换key之前, 建议先把你的public key上传到key server, 别人只需从key server上下载你的key. key server 是由第三方所维护的, 只负责key的散布, 而不负责签名, 因此不会有前面所述伪造的问题.

如下, 上传 0xBAD7F7A3这把key.(注意, 这里需要把shell的http代理关掉,否则上传失败) 上传完之后可以到http://keys.gnupg.net/查询.

akagi201@akrmbp ~ $ gpg --send-keys BAD7F7A3
gpg: sending key BAD7F7A3 to hkp server keys.gnupg.net

之后, 你只需告诉别人你的key id和fingerprint, 其它使用者就能下载和验证完整的public key.

akagi201@akrmbp ~ $ gpg --recv-keys BAD7F7A3
gpg: requesting key BAD7F7A3 from hkp server keys.gnupg.net
gpg: key BAD7F7A3: "Akagi201 (Bob Liu) <akagi201@gmail.com>" not changed
gpg: Total number processed: 1
gpg:              unchanged: 1
akagi201@akrmbp ~ $ gpg --fingerprint
/Users/akagi201/.gnupg/pubring.gpg
----------------------------------
pub   2048R/BAD7F7A3 2014-05-27
      Key fingerprint = E19A 2B9C B30F 8D0E 3F14  8C9F 7BAA 088C BAD7 F7A3
uid                  Akagi201 (Bob Liu) <akagi201@gmail.com>
sub   2048R/6E980A59 2014-05-27 [expires: 2016-05-26]

例如, 本人的key ID是 0xBAD7F7A3. User ID为”Akagi201 (Bob Liu)“(前有uid字样). 你可以透过上面的指令下载我的 public key, 并验证fingerprint.

在下载和验证完成别人的public key之后, 若对方是你信任的人, 你可以为他的key签名. 当你为别人的key签名之后, 对方可以分布你的签名, 加强他的public key的可信度, 于是认识你的朋友, 可以依据你的签名, 决定是否相信该public key. 你也可以依据朋友的签名, 决定是否相信其它人的 public key.(下面的例子中我自己没法再给我自己签名了)

akagi201@akrmbp ~ $ gpg --sign-key BAD7F7A3

pub  2048R/BAD7F7A3  created: 2014-05-27  expires: never       usage: SC
                     trust: ultimate      validity: ultimate
sub  2048R/6E980A59  created: 2014-05-27  expires: 2016-05-26  usage: E
[ultimate] (1). Akagi201 (Bob Liu) <akagi201@gmail.com>

"Akagi201 (Bob Liu) <akagi201@gmail.com>" was already signed by key BAD7F7A3
Nothing to sign with key BAD7F7A3

Key not changed so no update needed.

上面的指令将使用你的secret key, 为 0xBAD7F7A3这把public key签章. 签完之后, 就可以看到0xBAD7F7A3这把key 多了一个签名. 你能将这个签名后的public key(再次)上传到key server, 或export成档案散布. 通常该public key 的原拥有者, 会希望取得你的签名.

akagi201@akrmbp ~ $ gpg --list-sigs
/Users/akagi201/.gnupg/pubring.gpg
----------------------------------
pub   2048R/BAD7F7A3 2014-05-27
uid                  Akagi201 (Bob Liu) <akagi201@gmail.com>
sig 3        BAD7F7A3 2014-05-27  Akagi201 (Bob Liu) <akagi201@gmail.com>
sub   2048R/6E980A59 2014-05-27 [expires: 2016-05-26]
sig          BAD7F7A3 2014-05-27  Akagi201 (Bob Liu) <akagi201@gmail.com>

加密

当你想要传送机密资料给其它人时, 可使用对方的public key为信件和资料加密. 只有对应的secret key, 才能解开该份信件或资料.

akagi201@akrmbp ~ $ gpg --armor --encrypt --output akmsg.asc akmsg.txt
You did not specify a user ID. (you may use "-r")

Current recipients:

Enter the user ID.  End with an empty line: Akagi201

Current recipients:
2048R/6E980A59 2014-05-27 "Akagi201 (Bob Liu) <akagi201@gmail.com>"

Enter the user ID.  End with an empty line:
akagi201@akrmbp ~ $ cat akmsg.asc
-----BEGIN PGP MESSAGE-----
Version: GnuPG v1

hQEMA4V0fiJumApZAQgAq5In+b2OiG7ruCfhbycUEUKgSTGrcSdErFuWHbWKpcSj
xItKaJX6s5FGGG01p4Q9h/kYzhOzwnxHmMDFszzcW9cxBSPXES5qpGJd5lO0PTCX
t7yma4geYmFG1zajkJzsnVyJl4NTvfnMgRcAKRqD5QZuo6vQNaURGsDtwcqY/iRt
6wD958HnBr3+OGkP6KCsjbhYsOrIaVnnfF0TEK0cxyCgu2743O8F9rwhwwTVBCMU
qHRXaHjRaGysflcnXlqHVRbHFYKTg+Mo2K2avricTKzu/bdV6J1jeCkqfXrbGKqD
aDxCmYZH1y7JmBNJJhYaWlth0b3Ir26+kO/RSOji2NJRAcJ1/bZK6uROY1LeuMps
G0zdH0ODNvV/sAe+9C23CqGFXrN/z4XdEHR1WyU3ZWUrPnEP4Z1hkQ2zy8ncy32I
92E6SyWit6R7KNbhHu8/A7jt
=F1tG
-----END PGP MESSAGE-----
akagi201@akrmbp ~ $ cat akmsg.txt
This is secret

例如上面的指令, 使用0x6E980A59这把key(0xBAD7F7A3的subkey), 将akmsg.txt这份文件加密, 并存成akmsg.asc. 上面指令中, 只需输入对方的email address或者user ID(uid), 就会从你的keyring中, 找出对方用来加密的public key.

解密

在你收到加密的文件时, 只需使用下面指令就可以使用存在keyring里的secret key, 为文件解密.

akagi201@akrmbp ~ $ rm akmsg.txt
akagi201@akrmbp ~ $ gpg --decrypt --output akmsg.txt akmsg.asc

You need a passphrase to unlock the secret key for
user: "Akagi201 (Bob Liu) <akagi201@gmail.com>"
2048-bit RSA key, ID 6E980A59, created 2014-05-27 (main key ID BAD7F7A3)

gpg: encrypted with 2048-bit RSA key, ID 6E980A59, created 2014-05-27
      "Akagi201 (Bob Liu) <akagi201@gmail.com>"
akagi201@akrmbp ~ $ cat akmsg.txt
This is secret

提醒

虽然可能性不是那么高, 但你永远不知道哪一天会需要这样的科技, 以保障你的通讯安全. 若不从现在开始建立你的web of trust, 需要时, 可能为时已晚. 请妥善保管你的secret key, 别让任何人有机可趁. 保护自己, 也保护朋友.

Refs

网络嵌入式设备框架

这不是什么新鲜东西, 无线路由器很早前就开始使用了, 不过最近才慢慢理解其原理. 现在网络嵌入式设备的功能越来越强大了, 各芯片厂商的解决方法支持着这种复杂性.

1. 一个网络芯片架构

network-device-hardware

大家应该看得出来, 这是一个DSL芯片. 其网络部分由一个switch core构成核心, 对外有3个交换口(粉红色的): 左侧为以太网MAC, 可外接以太网芯片MAC或PHY, 称为LAN端;右侧为DSL的TPS子层,按照DSL标准传输ATM/PTM数据,成为WAN端;上面的是一个PDMA片级总线,与片上系统SoC通信.

另外还有几个额外的交换口, 主要是提供额外功能的: 一个QDMA用于扩展core外Mem; LTR和WTR是两个转换引擎, 当LAN或WAN端的数据包需要一些特殊处理时(如VLAN, PPPOE头等), 会被分别交换到这两个口进行转换后, 再发回Queue中. 再另外, LAN, WAN端各有一个classifier, 是预分类器, 在数据包进入switch core前, 先进行一个粗略的划分, 决定发往哪个端口.

以上的这些功能都是switch core独立完成的, SoC系统只需对其进行简单的配置, 控制即可. 所以SoC的性能并不需要太高(CPU大概是130MHz的). 注意, 该switch core和一般以太网switch的区别, 首先其端口类型就不同, 所提供的功能也跟为复杂.

这里给出一个简单的以太网switch芯片的架构, 如下图所示, 其核心就是一个register集, 对它们进行配置(内部EEPROM, 或外部MDIO总线), 可以实现port-VLAN, 二层filter等功能. 其结构相对简单, 因为其所有端口都是以太网口.

network-switch

用该芯片接在上述DSL芯片的LAN端, 构成网络系统如下图所示:

network-switch-lan

以太网switch可以自主实现port-VLAN功能, 并通过一个trunk口与switch core相连, 而switch core有很好的vlan_tag classifier功能. 另外WAN端实现了8个硬件通道PVCs, 且switch core也能对它进行很好的classifier. 综上, 就可以实现所谓的port-mapping功能, 只要一跟DSL接入线, 就可在家庭里实现IPTV, Internet, 可视电话等业务的分离.

注意, 所有这些功能都是switch core自动完成的, 网络数据包不需要进入SoC的协议栈, 这和后面讲的一般的路由器是不同的.

2. 嵌入式片上系统

2.1 与外界的交互方式

这里的SoC系统主要功能有: 与外界用户的交互, 解析用户指令, 配置系统.

解析指令对软件系统来说很简单, 配置系统, 前面也说了, 主要是读写一些register, 也很简单. 关键就在于与外界用户交互.

很容易想到的一种方式是网络, SoC也连在switch core上, 有自己的IP, MAC. 当然它和switch core是片上bus相连的, 通信时并不需要MAC, 这里只是把自己伪装成一个通用的以太网设备, 可以被switch core和外界PC识别.

要通信, 当然就需要协议栈了, 不过, 这里的SoC系统不需要处理额外(正常通信)的数据, 所以协议栈也选择简单的LWIP, 如之前的博文所述. 最常用的网络通信方式就http了, 另外还有telent等.

呵呵, 连接192.168.1.1实际就是去连接其内部的SoC, 一般的交换机, 路由器都是这样的, 而不是什么端口. 当我还是一个超级菜鸟时, 这个问题困扰了好久, 纠结.

另外, 嵌入式系统中, 还有一个最常用的交互方式是串口UART. UART是一个非常简单的I/O设备, 它通过直接读写管脚的电平信号(串行的)来实现输入输出, 没有任何额外的中断, 控制等机制. 虽然简单, 不能用以实现复杂, 可靠的功能, 但用作嵌入式系统的调试方法却非常有效.

UART就像是嵌入式设备的键盘/显示器. 它是一种非常简单的硬件资源, 在它之上可以构建通用的I/O设备tty, 在tty之上, 就可以实现各种应用, 如shell等.

network-uart

硬件资源UART, 虚拟层设备tty都是系统的资源, 在Uc/OS中, 一般作为全局量, 在其上的应用则通过task来完成. 如XSHELL_TASK中, 就是通过一个while(1)循环, 不停地通过tty_get_line()读取命令行. 注意, 该函数已经不是裸的硬件操作了, 而是加上了一个上层操作, 即识别\r\n来作为结束符, 也是通过一个while(1)循环来作的. 读到电平为空, 则忽略, 因为UART太简单了, 没有中断, 缓存机制等(没有详细去考究, 只是粗略地浏览了一下代码, 好像是这样的吧!).

最后, 外界用户读写电平, 当然不同用示波器了. 呵呵, PC上装个串口驱动, 那么PC的键盘/显示器就为嵌入式板子所用啦.

2.2 bootloader

这就像一个心结, 你一天不理解它, 就一天不能安心地开发嵌入式系统, 尽管你可以把软件写得很出色.

传统的PC机上电后, cpu核的指令指针(如cs:ip)会指向系统内某段固化的代码, 如BIOS, 这些代码被烧录在rom存储器中, 断电也不会丢失. 它们会调用我们开发的代码(如操作系统软件、或一些简单的前后台程序).

一个嵌入式怎么启动, 其实大意和PC(所有这种代码机器)差不多. 当然不同厂商的芯片, 也有各自的方式特点, 以公司的这款芯片来说(注意, 这里说的是芯片上集成的SoC子系统), 它有好几种方式启动.

首先, 其芯片内集成了一个BootRom, 它里面的代码(也就是二进制的门电路)是在芯片的一部分, 即芯片生产出来就有的. 芯片对外有个引脚(boot-mode_pin), 把它接低, 则芯片上电后的ip指向该BootRom, 执行里面的代码. 这些代码很简单, 一般会实现BOOTP, tffp等功能, 从网络上下载OS的内核到内存中来运行. 这就是所谓的网络无盘系统的工作方式.

不过现在好像这种方式用的少了, 存储器便宜啊. 一般都会把boot-mode_pin拉高, 这样上电后ip指向外部flash. 很容易想到flash和PC上的硬盘类似, 是差不多, 有点区别. PC上电后先执行BIOS, 由BIOS装载硬盘的bootloader扇区. 而嵌入式系统一般不这么麻烦, 它直接就在flash中运行这些代码.

现在的cpu-core一般都是32位的, 即有4G物理寻址空间, 而嵌入式的SDRAM并不要那么大, 所有可以把外部flash和SDRAM一起编址:

network-storage

系统上电后, ip指向2G处, 则可以直接在flash运行初始的代码, 只是速率比较慢, 所以开始的代码往往是把后面的一个image下载到SDRAM中去, 然后在SDRAM中运行.

至于flash的基址为什么能是2G, 这是由CPU的地址总线和flash的SPI总线的特殊的电路连接方式决定的, 呵呵, 电子出身的应该不难理解. 而且, 有些芯片还提高一些外部引脚pin, 来为地址线加上一个offset(比如1M), 那么两个处理芯片就可以使用同一个flash的不同的部分了(0~1M, 1~2M), 而其内部只觉得都是从2G地址开始的.

下面一个关键问题就是, flash中是什么, 怎么来的, 能改变吗?

一般flash中的东西, 是由具体应用来决定的, 开头一般都是bootloader代码, 后面有一个image文件, 公司的网络系统, 需要一个启动配置文件, 也简单的放在flash中:

network-flash-layout

Bootloaer一般放在开头, 便于执行. 后面的则也可以直接这样按照物理空间分配, 复杂一点, 也可以做成文件系统fs, 如嵌入式linux. 主要包括一些配置文件, log信息文件. 最重要的是IMAGE文件, 一般要被加载到SDRAM中去运行, 这也是我们开发系统功能应用的关键.

那这些内容怎么来的呢? 一般初始时, 会用特定的硬件工具厂商提供的一个初始的文件(格式就是上图所示)烧到flash中, 就像当年我们烧8051单片机一样, 再把flash焊在板子上. 如果运行过程中, 代码被改死了, 系统再也起不来了, 那对不起, 只能重新焊一个新的flash了. 而一个补救的方法是在flash中准备两个image, 板子上预留一个特殊的按键, 按下后启动新的image(前面讲的flash-offset方法). 还有一种方法, 是通过内部bootrom启动, 无盘启动. 实现当然需要一定的硬件支持, 如前面所述的那个boot-mode_pin不能焊死, 用一个跳冒, 或者通过一些特殊电路接在以太网上, 以太网事先有数据传输时, 则使用内启动, 如组播升级. 方法各不一啦.

最后, flash里面的内容怎么变? 很简单, 它既然也在cpu的变址内, 直接用cpu把数据写到flash中不就行了. 一般用http服务, client会post一个image文件上来, server端检查没问题, 就写到flash中, 这就是手动升级.

3. 一个无线路由器的架构分析

前面提到的公司芯片解决方案, SoC子系统的功能很有限, 通信数据包一般不会到SoC的协议栈. 而目前市场上的无线路由器系统, 系统中的CPU功能一般很强大, RAM配置也很强大, 一般都能运行大型系统软件, 如Linux.

network-router-framework

这是一块单独的芯片, 而其几乎包含了一个完整的PC机主板上的所有内容, CPU的功能还是很强劲了, 670MHz的MIPS核, 通过桥片连接数据总线. 这里的sys_bus差不多相当于PC中的PCI总线, 诸如USB控制器等都挂在其下. 这里主要看一下网络设备, 主要有两个, 一个802.11n标准的WLAN收发器, 一个4FE+2GE的以太网交换机.

和之前介绍的DSL芯片不同, 他的两个网络接口都是接在系统总线上, 而没有通过一个switch-core交互. 因此, CPU内必须维护一个功能完整的协议栈, 而且SWC往往也被虚拟化为wan和lan口, 参见下面的结构图:

network-router-struct

首先, 先明确一个概念, 在统计网络设备的端口数, 不要忘了还有一个端口连接CPU的. 比如, SWC对外有4FE+2GE, 而实际上它还有第七个端口连接到CPU. 然后就能理解有些数据传输只在这些只能得网络设备中完成了, 而有些则需要进入CPU的协议栈. 更准确的概念应该是, 片内CPU系统也相当于一个PC, 共同接在交换设备上.

这里比较特殊的是这个SWC设备, 照理它的所有端口应该是等价的, 所处网段也相同, 但通过VLAN技术, 把3, 4, CPU端口化为一个VLAN, 而5, CPU化为另一个VLAN, 这是SWC硬件支持的port-mapping, 它能只允许同一个VLAN下的机器通信. 这里就有一个特殊的CPU端口, 它同属于两个VLAN, 称为trunk, SWC硬件在trunk口下收发数据时, 必须带有vlan_tag(802.1q). 在CPU系统内, 因为物理通路只有一个, 只能通过软件的方式来实现VLAN的划分(如Linux下的VLAN). 另外, 一般把wlan挂在LAN内, 所以在Linux中可以用一个虚拟Bridge设备来连接这两个设备. 下面来看几种数据交换的途径:

  1. 3, 4间通信, 在PC机发起的ARP协议阶段, SWC也学习并记录了MAC表, SWC可以直接交换.
  2. 1, 2间通信, 也像ethernet交换机那样, 直接在wlan设备中交换吗? 没研究过wlan协议啊, 姑且这样认为吧.
  3. 3, 1间通信, 是同一网段的, DMAC为PC1的MAC地址, 在SWC中会将它从CPU端口发出去, 当然会加上lan_vlan-tag, 因此会被netdev_eth设备接收到, 而该设备已经变成了bridge设备的一个端口, 因此bridge设备会接管该pkt, 并根据MAC表, 从netdev_wlan设备(已经是端口啦)发出, wlan设备受到pkt后, 根据802.11n协议, 发给对应的PC.
  4. 5和其它通信, 不管是3, 4还是1, 2, 都是不同网段, 因此在PC的路由系统中, 会将数据发往gw-IP2, 即pkt的DMAC为片上CPU的MAC, 那么在SWC中会将pkt从CPU端口发出去, 当然会打上wan_vlan-tag, 因此会被netdev_eth-vlan设备接收, 并进入片上CPU系统的协议栈(注意, 之前的3种情况都不会进的). 此时, 片上CPU系统就充当了路由功能, 选择bridge设备下发, bridge设备中根据MAC选择对应端口下发.
  5. 其它向5通信, 和上面一样, 只是方向反过来.
  6. 片上CPU系统自身也有MAC, IP(192.168.1.1), 外部PC发送数据, 通过wlan, swc设备到达CPU系统的interface, interface发现是发给自己的, 则会传递给上层协议栈.

当然这里只是最基本的情况, 现在的路由器设备已经集成了非常多的功能, 如DHCP, NAT, DNS等等.

Refs

uhttpd实现框架

uhttpd是一个简单的web服务器程序, 以前没怎么接触过, 所以这里主要是对web服务器设计的一些学习总结. OpenWrt系统中, 真正用到的(需要了解的), 其实不多, 主要就是cgi的处理, 包括与cgi程序的信息交互等, 最后一节详细描述一下.

1. HTTP协议概述

HTTP协议是目前互联网使用最广泛的应用层协议. 其协议框架很简单, 在一个TCP连接中, 以一问一答的方式进行信息交互. 具体讲, 就是客户端(如常见的浏览器)connect服务端的知名端口(通常是80), 建立一个TCP连接, 然后发送一个request; 服务器端对该request解析后, 发回相应的request应答, 并关闭TCP连接. 这就是一次交互, 之后客户端再有请求, 则重复上面的过程.

交互报文格式如下图所示:

uhttpd-protocol

Request报文首行为request-line, 其中, type有GET, POST, HEAD三种方式, 然后最重要的是URL, 他告诉服务器所请求的资源. Response报文首行为responsee-line, 其中最重要的是code, 他告知客户端相应情况(found, redirect, error等), 然后跟一个简单的可读的短语.

两种报文后面具体的内容格式差不多, 都是一些headers(其中, 冒号前的str指明header类型), 然后以一个空行标识header结束, 后面是数据. 对于request, 只有POST类型的请求需要提交数据, 其他类型的是没有数据的. Response报文的数据就是URL所指定的资源文件(HTML, DOC, gif等).

2. 服务器架构

uhttpd作为一个简单的web服务器, 其代码量并不多, 而且组织结构比较清楚. 和其他网络服务器差不多, 其main函数进行一些初始化(首先parse config-file, 然后parse argv), 然后进入一个循环, 不断地监听, 每当有一个客户请求到达时, 则对他进行处理.

对于web服务器, 所要做的处理主要就是分析URL, 判断出是file-request, cgi-request或lua-request, 这主要是根据URL的最前面的字符串(称为前缀prefix)得出的; 然后就用相应的形式进行处理. 如下图所示:

uhttpd-framework

3. cgi-response流程

前面已提到, openwrt系统中使用uhttpd服务, 主要是用cgi方式来回应客户请求的, 下面就对这种方式详细阐述.

3.1 URL解析

由上图红字所示, uh_cgi_request需要两个参数path info和interpreter, 其中, pin是一个struct, 包含了路径中各种有用信息; ipr指明所用的cgi程序, 因为一个服务器中可以有多个cgi程序.

uhttpd-url

如图所示, docroot是服务器的资源目录, 是为了os准确定位资源位置, 由uhttpd的config文件设定, 如openwrt中为/www. 后面的是client传来的url, 开头的为cgi-prefix, 也是由uhttpd的config文件设定的, 它指明server端采用cgi处理方式, 如openwrt中的为/www/cgi-bin; 紧接着的是cgi的程序名, 它指明了使用哪个cgi程序; 再后面就是实际的path信息了, 在cgi方式中, 它会被当成参数供cgi程序使用.

3.2 CGI处理框架

要运行cgi程序, 首先意味着需fork出一个子进程, 并通过execl函数替换进程空间为cgi程序; 其次, 数据传递, 子进程替换了进程空间后, 怎么获得原信息, 又怎么把回馈数据传输给父进程(即uhttpd), 父进程又怎么接收这些数据.

!(uhttpd-cgi)[http://akagi201.qiniudn.com/uhttpd-cgi.png]

首先创建2个pipe, 这实际上是利用AF_UNIX协议域, 创建2个相连的socket_unix, 那么, 他们映射的文件描述符(即这里的fd[0], fd[1])就构成了一个pipe, 且这种关系即使fork后也仍然存在, 因为fork仅是增加了文件的引用次数, 而OS维护的file结构和socket结构都没变, 这就是父子进程间传递数据的方式. 然后fork出一个子进程.

子进程中首先把2个管道的一端close, 注意这仅是使得文件引用次数变为1. 由于子进程待会要excel替换, 替换后rfd, wfd就不存在了, 因此, 先把他们dup2给知名的stdin, stdout, 这样即使execl替换后, ipt->extu程序可以以此来和父进程传递数据. 另外, execl替换后, cgi程序仍需要之前的一些参数信息, 如PATH_INFO等, 这种情况下, 最简单的办法就是setenv, 把需要的参数设为环境变量.

为什么要2个pipe, 因为子进程向父进程传递回馈数据需要一个out-pipe, 而若有post数据, 子进程还需要一个in-pipe, 从父进程读取post数据.

父进程中首先也是close, 同上所述. 若有post数据, 先从http request-header中得到content-length, 为后面传递给子进程做准备. 然后进入一个循环(为什么要循环, 什么时候退出, 后面讲), 通过select轮询io, 超时, 中断的情况就不看了, 轮询的io一个是reader, 即从子进程读取回馈数据, 而若有post数据的话, 还要另一个io, writer, 向子进程写post数据. 主要的处理就是上图中红色字所示, 具体如下:

uhttpd-post

Refs

在虚拟机上搭建OpenWrt平台

1. OpenWrt平台搭建

1.1 环境准备

系统Debian7.4, 安装好官网buildroot说明的必备软件包 http://wiki.openwrt.org/doc/howto/buildroot.exigence

注意, 尽量使用较新的linux发行版, 因为openwrt比较新, 而且通常要在trunk分支下开发, 所以依赖的软件包都比较新, 避免繁琐的依赖关系问题.

1.2 编译固件

使用git或者svn下载源码 修改feeds.conf.default 然后更新软件索引

./scripts/feeds update –a
./scripts/feeds install –a

注意, 这里下载的知识编译固件用的控制文件(索引文件), 不包含源码.

然后运行make menuconfig(省去一些检查测试步骤, 具体可看官网说明), 这里注意3点: 1. Target system, 这个一定要选准. 2. Target Image, 选择固件image的格式, 这里我们准备在x86的VM上运行, 可以选择VMDK, 直接编译出硬盘文件, 并且包含grub. 3. 根据需要选择软件包, 要想通过web登录配置, 4个地方要选: Base system -> uci, Libraries -> libuci-lua, LuCI全选, Network -> uhttpd.

注意软件可以选择m或者y, 前者表示只编译出xxx.ipk安装包文件, 用户需要时将它上载到路由器中, opkg install xxx.ipk 安装, 后者表示直接编译在固件中.

最后运行make, 开始编译(可以使用make -j4 V=s加速编译和增加打印编译出错信息), 自动创建dl, build_dir两个目录, 依次调用tools, toolchain, package, target目录中的Makefile编译(前两者是工具, 然后用这个工具编译后两者). 自动根据指示下载所需的源码包, 放在dl目录下, 然后解压到build_dir目录中进行编译. build_dir中有3个子目录, host是于平台无关的一些工具, toolchain中式特定平台的工具, ulibc中式C库, 应用程序等.

1.3 VMware中运行OpenWrt

启动VMware, 新建虚拟机(custom), 以编译出的VMDK文件为硬盘, 直接power on就可以进入OpenWrt, 在目录/bin, /usr/bin等目录下, 有我们选择安装的应用程序的可执行文件, 运行他们实现各种功能.

2. 网络配置的简单示例

我们在虚拟机下运行, 虚拟机的网络模式有3种: nat, host-only, bridge. 我们安装完VMware后, 主机新增了2快虚拟网卡VMnet1, VMnet8, 前者相当于所有host-only模式的VM的交换机, 所有host-only模式的VM可通过它相互通信, 但不能与外界通信; 后者相当于素有nat模式的VM的交换机, 不同之处在于, 他可以NAT到主机的实际网口, 去访问外网(即相当于有个上行口(WAN口)), bridge模式指利用主机的实际网口与外界通信.

我们为OpenWrt的VM准备2块网卡, 一块是bridge模式, 另一块是host-only模式; 同时运行另一VM(red hat), 只有一块host-only模式的网卡, 如下图:

openwrt-vmnet

OpenWrt的网络配置文件为/etc/config/network, 编辑写入:

configure interface lan
    option ifname eth0
    option proto dhcp
configure interface wan
    option igname eth1
    option proto dhcp

然后重启网络 /etc/init.d/network restart

再写入2条路由

Route add-net 10.10.10.0/24 dev eth0
Route add default dev eth1

再在VM-Redhat写入2条路由(注意: dev和gw的区别)

Route add-net 10.10.10.0/24 dev eth0
Route add default gw 10.10.10.130

这样, 实际局域网的主机就可通过OpenWrt这个路由器与host-only虚拟语句网的主机通信了, 当然前提是各主机应设一条网关路由到182.168.68.187

3. 应用程序开发

应用程序开发有2种方法.

3.1 方法一

利用menuconfig, 直接与固件一起编译, 即预先把相关文件放到 trunk/package/myapp 目录下, make menuconfig时就能找到该软件包, 选择(可以选y编译进固件中, 或m只编译成.ipk包)即可.

比如, 在package目录下mkdir hello world, 里面放些什么则至关重要, 可以参考其他包的内容, 其中的Makefile的最关键的, 其他都可选, 比如我们这里还放了一个src子目录, 里面放helloworld.c, Makefile(它是真正编译用的).

Makefile的内容可参考模板, 一般分为很多节, 第一节一般说明程序名称, 版本等; 第二节说明软件包的基本属性(他是一般包, 还是kernel包, SECTION, CATEGORY字段说明其在menuconfig中的位置, 一定要写准确, 该包的依赖关系等). 注意Makefile中Tab符不能乱用, 一般表示command, 这里的属性字段不用用Tab, 只能用空格.

后面几节描述了他的编译安装方法. 一般就是把src里的内容copy到build_dir/uClib下, 然后用toolchain来编译, 最后install一节可以指明该app在固件中被放在哪个目录下(/bin, 或 /usr/bin等).

3.2 方法二

利用SDK, 就像android开发一样, 利用sdk编译出软件安装包, 上传到设备, 安装即可使用. 首先在make menuconfig的时候, 把build SDK选项选上, 那么, 固件编译好后, 在trunk/bin/x86下就有一个SDK.tar文件, 解压, 进入里面.

可以发现里面的内容和trunk主目录下的非常相似, 也有package, buld_dir, dl等目录, 实际上它正是模拟了一个固件的编译环境, 这样就简单了, 如方法一所述, 在package目录下建立我们的app子目录, 放入必要的文件, 然后退回到SDK目录, 直接make即可, 编译好的.ipk包在SDK/bin/x86/package下.

至于上传.ipk包到路由器上, 对于实际设备, 可以scp工具, 在VMware下没找到更好的方法.

3.3 为什么要用ipk包

ilk包是openwrt特有的, 为什么用这个呢, 直接把可执行文件copy到设备中不就行了吗? 回头看看hello world中的Makefile的最后一节install就ming明白了, ilk包li里不仅有可执行文件, 还有安装该文件的具体指示, 甚至还有其他一些配置, 启动文件, 以及他们的安装方式.

联想network命令就是这样的, 进到package/conf下, 看他的Makefile最后, 其实就是拷贝文件.

Refs

Gdbserver On OpenWrt

编写的程序部署到OpenWrt上出错, 打日志是个好办法, 但是今天遇到的情况, 日志也不能显示出正确的程序流程, 实在诡异, 因此, 决定尝试调试器.

熟悉在普通的电脑上使用gdb调试的基本方法: http://www.ibm.com/developerworks/linux/library/l-gdb/

下载2张纸, 打印出来放在手边备用.

路由器中的存储空间十分有限, OpenWRT的包管理器opkg提供的GDB占用大约1.5MB空间. 路由器本身有8M的存储空间, 目前只剩200KB了, GDB的大小不能接受. 相比之下, GDBServer的大小 不到100KB, 这是可以接受的.

gdbserver远程调试方法

路由器端

  1. 安装gdbserver
opkg install gdbserver
  1. 进入目录, 运行gdbserver, 监听网络端口
gdbserver 192.168.8.1:4455 xxxx

PC端

  1. 根据OpenWrt SDK位置配置好PATH路径
  2. 进入被调试的程序文件, 这是为了向gdb提供程序的调试信息.
  3. 指定被调试的程序文件, 启动gdb.
mips-openwrt-linux-gdb xxxx
  1. 在gdb中连接远程调试器
target remote 192.168.8.1:4455
  1. 等待崩溃
bt

如果连接成功, 则此时就可以像平常一样使用gdb来调试程序了, 不过调试目标是位于路由器的程序. 使用这个方法可以很轻易的定位到程序错误的位置. 毕竟是动态调试.

编译gdb和gdbserver方法

# gdb的编译
cd ~/gdb/gdb-7.3.1
mkdir bin
cd bin
../configure --prefix=/opt/gdb-7.3.1 --host=i686-pc-linux-gnu --target=mips-linux
make
make install

# gdbserver的编译
cd ~/gdb/gdb-7.3.1/gdb/gdbserver
mkdir bin
cd bin
export CC=/opt/openwrt/kamikaze_7.09/staging_dir_mips/bin/mips-linux-gcc
../configure --target=mips-linux --host=mips-linux
make

补充

如果上面的方法不能工作, 有可能是openwrt设备上的库被stripped了, 可以在host主机上使用没有stripped的库来远程调试.

openwrt源码上编译工具链

在menuconfig上enable gdb 和 gdbserver.

Advanced configuration options (for developers) → Toolchain Options → Build gdb
Utilities → gdbserver

给你想调试的包增加调试信息

  1. 添加CFLAGS到你要调试的package的Makefile中.
TARGET_CFLAGS += -ggdb3
  1. 重新编译package带有CONFIG_DEBUG.
make package/busybox/{clean,compile} V=99 CONFIG_DEBUG=y
  1. 或者在menuconfig中使能debug info
Global build settings > Compile packages with debugging info

Refs

git commit 规范

最近发现自己的git log太乱了, 稍微整理一下规范.

基本原则

  • 永远不在git commit后增加-m <msg>来添加日志. 直接git commit [-a]然后会出现一个编辑界面.
  • 在.vimrc中添加一行, 来检查拼写和自动折行.
autocmd Filetype gitcommit setlocal spell textwidth=72
  • 第一行应该少于50个字, 随后一个空行, 然后写具体内容.

举例

  • 总体格式
模块名: msg +补充信息关键字(time/issue/review) 补充信息(#issue号 可被github识别)
  • 第一次提交用 first blood

  • 记录花费时间2d 3h

othermsg +time 2d 3h
  • 修issue: 1234
othermsg +issue #1234
  • 提交的同时, 有代码审查, 审查人user1, user2
othermsg +review @user1 @user2
  • 提交的同时, 和1112号代码审查相关联
othermsg +review 项目代码审查关键字-1112

任何项目管理工具(即使使用文本文件管理)都会很容易解析上述信息, 无论用的是git还是svn.

另外更详细的信息会在代码中或者项目管理工具中出现, 不需要提交太多”othermsg”, 一两句概述的话或单词说清楚就行.

Refs

ARM汇编:MOV, LDR, LDR伪指令之间区别

在编译ARM汇编时, 用MOV指令编译有时会出错, 但是, 有时又对了, 还有LDR, 有时要加个”=“, 有时又不加了, 非常容易搞混.

1. “8位图”数据

8bit

2. MOV指令

MOV指令可以把立即数或者寄存器内容(注意: 这里绝对不可以是内存!!)传递给一个寄存器. 对于立即数是有要求的, 就是上边的”8位图”数据. 只能由一个8bit连续有效位通过偶数次移位得到的数. 它为什么会有这样的限制呢? 原因是, MOV本身就是一个32bit指令, 除了指令码本身, 他不可能再带一个可以表示32bit的数字, 所以用了其中的12bit来表示立即数, 其中4bit表示移位的尾数(循环右移, 且数值*2), 8bit用来表示要移位的一个基数. 如果立即数超过这个范围, 就没有办法用一条MOV指令给寄存器赋值(这里就要用到LDR伪指令了, 查看反汇编指令, 你会看到LDR伪指令此变成了两条指令~~).

3. LDR指令

首先呢, ldr指令既可以是大范围的地址读取伪指令, 也可以内存访问指令. 当它的第二个参数前面有”=“时, 表示伪指令, 否则表示内存访问指令. LDR指令就是个单寄存器存储的ARM存储器访问指令, 补充了MOV指令不能访问内存的缺陷. ARM是RISC结构的, 数据从内存到CPU之间的移动只能通过ldr/str指令(我说的是单个寄存器~~). 想要把数据从内存中某处读取到寄存器中, 只能用ldr.

4. LDR伪指令

  1. LDR伪指令没有立即数范围的限制, 即可以直接赋值, 因为这是一条伪指令. 如果立即数在MOV的要求内, 系统会自动用一条汇编MOV指令来实现. 如果不在MOV的范围内, 就用其它的方式来实现, 比如变成了两条指令, 或者从PC偏移地址读取一个32位的数据给寄存器.

  2. 关于LDR伪指令, 可以装载一个32bit立即数的说法并不正确, 因为在实际中并不是这一条语句装载了32bit立即数(跟上面的貌似一样, 呵呵~~), 比如:

ldr r1,=0x70000000

其实真正的汇编代码是将某个地址的值传递给r1, 就是说需要一个地址存放0x70000000这个立即数, 在反汇编中, 如果仔细看会发现, 如果这个立即数可以用mov指令的表达形式来表达, 编译器就直接用mov了.

Refs

体验设计

active-point

受皓玥学姐的邀请, 酱油了一下bong openday的hackathon, 这不是第一次参加hackathon了, 不过算是第一次实际参与了吧. 虽说, 线上的形式会有点松懈没那么多的竞争的感觉.

收获

  • 认识了几个小伙伴, 还认识了2个岑徐媛, 咳咳.
  • 快速体验了一个开源免费的原型设计工具 pencil, 还是有点略难用, 收费软件应该用起来更加直观.
  • 也算给学姐帮了点小忙吧, 呵呵.

成果物仓库

潘小木的作品

  • bong-open
  • 汇总时光机

Learning Lua

学习的动机

  • 因为TI NSPIRE
  • 因为OpenWrt
  • 因为刚好看到一个不错的课程
  • 因为现在刚好有时间和兴致

Refs

MindMup

因为自己看的一个视频课程使用的是思维导图的方式记录笔记的, 所以决定, 自己也选择一种思维导图吧, 经过多种思维导图软件的比较, 最后选择了mindmup(online)+freemind(local), 完全开源免费的解决方案, 主要是用了一下很顺手. 够智能, 够所见即所得. BTW, 因为知识记录这种长久的东西, 还是自己搭建或者使用开源的东西靠谱.

Features

  • Zero friction: 可以从任何地方, 任何设备访问. 可以使用MindMup Anonymous Public Storage用于公开的mindmap(不需要注册), google drive用于私人的mindmap.

  • Productive: 响应式设计来使用 desktop和mobile, 使用了html5技术.

  • Community-driven and Open: 开发是用户驱动, 新特性的优先级由用户投票决定.

Mouse and touch operations

MindMup有丰富的快捷键接口, 但是大多数操作也同时可以使用鼠标或者触屏操作来实现.

  • click and drag the center node: move map
  • click and drag the background: move map
  • scroll with trackpad/touchpad: move map
  • click a node: change current selection
  • shift+click a node: multi-select, add a node to selection
  • shift+drag a node: manually position a node. Children of the root node can be pulled in any direction, lower level nodes can only be positioned in the direction of its parent relative to the root
  • double-click a node: edit
  • drag and drop a node between its siblings: reorder siblings
  • drag and drop a node on another node: move node to a new parent
  • drag a top level 1 node from left to right side of the root (or right-left): switch the position of the node(脑图只区分左右, 不区分上下)
  • alt+click, ctrl+click and cmd+click: link between the currently selected node and the clicked node (only if they are not already related)
  • drag an image onto a node: associate the image with the node as an icon
  • drag an image onto the background: add the image as a child node of the currently selected node

keyboard shortcuts

MindMup对于所有的编辑操作都有快捷键. 在Mac os x上, Ctrl和Cmd键都被记作Ctrl键. 一些浏览器阻止某些键绑定, 所以, 如果Cmd+Space不工作的话, 尝试Ctrl+Space.

node manipulation

shortcut description
Enter Add sibling
Shift+Enter Add sibling above
Shift+Enter Line break (when editing node text)
Tab Add child
Shift+Tab Insert parent
Space Edit node
Shift+Space Change node color
a View or edit node attachment
i Add an image or change the existing image/icon associated with the node
Backspace or Delete Remove node
Ctrl+Up/Down Arrow keys Move node up/down

editing

shortcut description
Ctrl+s Save
Ctrl+x or c Cut
Ctrl+v or p Paste
Ctrl+Shift+v Paste Style only(粘贴所复制内容的格式)
Ctrl+c or y Copy
u or Ctrl+z Undo
r or Ctrl+y or Ctrl+Shift+z Redo

selection

shortcut description
Arrow keys Select the node up/down/left/right of the currently selected one
Shift + Arrow keys Add node up/down/left/right to selection (useful to multi-select siblings)
{ Multi-select the current node and the entire subtree under it
[ Multi-select only the subtree under the current node (not the node itself)
= Multi-select all the siblings of the current node (that have the same parent)
. Cancel multi-selection and select only the current node again
1 - 9 Select all nodes of a particular level (eg 1 selects all first level nodes)
0 Select the root node and recenter view
Ctrl+f Find node by part of title
shortcut description
/ or f Fold or unfold children
Ctrl + or z Zoom in
Ctrl - or Shift z Zoom out
Esc, Ctrl+0 Reset map view - select root node and bring it to the center of the screen(目前测试效果和0一样)
Ctrl+b Convert floating toolbar to menu (press again to convert back)
Ctrl+Shift+b Hide top menu (press again to show)

Storage options

  1. MindMup Anonymous Public Storage: 免费, 公开, 你们的存储在Amazon’s S3文件存储服务. 一旦保存, 变成只读. 任何对于一个map的修改会创建一个新的map和新的url. url是随机生成的, 只有知道了url, 任何人都可以访问.

  2. MindMup Gold: Amazon’s S3 文件存储服务. 没有匿名存储的很多限制, 我们知道谁拥有这个map, map的url是永久的, 不随着修改而改变.详看mindmup-gold

  3. Google Drive: 修改会保存在原有的文件.详看working-with-google-drive

  4. Browser Storage: 这个是在你的浏览器中的html存储, 与我们的网站连接. 虽然他有一个url, 但是内容只可以在同一个浏览器的profile被访问. 存储是永久的, 所以你可以关掉浏览器和关机, 内容都不会丢失.

  5. Dropbox: 与Google Drive完全一样. 详细working-with-dropbox

  6. GitHub: (需要你激活扩展)

Refs

Learning Embedded System Note

换工作这几个月的时间, 还是挺爽的, 时间完全自由支配. 把之前因为工作比较忙的原因没空学习的东西都恶补了一下, 这几个月太赞了, 积累了不少资源, 把openwrt玩遍了, 将ARM-linux嵌入式完整的学习了一遍, 并用思维导图做了笔记.

精华部分

  • 应用层所有的东西包括系统调用, C库, pthread库, socket等等全都在glibc的官方文档里面了http://www.gnu.org/software/libc/manual/.
  • 很多东西linux和android是相通的, 很多android的东西已经融入到了底层驱动中. 所以, 小米有android的深厚技术基础来发展linux, 相当easy.
  • 思维导图是个好东西, 用来索引记忆还是不错的. 长篇大论还是去别的地方找好了, 没必要记录太多东西.
  • ARM交叉编译工具链下载地址 http://www.linaro.org/

RTC驱动框图

学习了一种工具画流程图工具.

无线产品开发流程概述

本人做无线产品设计已经好多年, 做过的产品大概有三, 四十种, 简单的, 复杂的都有. 现如今我的设计一版成功的概率很大, 即使不能一版成功, 也可以在第一版中实现90%以上的功能, 性能, 朋友们因此还送给我了一下绰号”一版达人”. 我想其实我也不是有多么深不可测的技术水平, 可能跟别人相比, 我最大的优点就是细心, 并善于思考. 我想在这里简单地分享一下我的心得体会, 我在这里想大概讲述一下无线产品的开发流程.

无线产品就是把物理接口上的数据转为在空间传输的电磁波. 物理接口就是CVBS, S/PDIF, HDMI, 网口, 串口, E1, T1, ADSL等, 具体采用什么接口由产品形态决定. 电磁波的特征, 如频率, 频宽, 调制方式等由相关的协议标准决定. 如蓝牙, Zigbee就是指定在2.4GHz频段运行的. 无线产品的硬件设计决定了产品的接口形式, 传输距离, 传输速度的问题. 软件设计决定了数据如传输. 这里主要探讨的是无线产品的硬件设计.

无线产品的七大功能模块

有了前面对无线产品的理解, 我把每个硬件产品都分为七大部分, 我想这应该是我首创的, 目前为止我还没有听过别的工程师这样说过.

wireless-hardware

  1. CPU+RAM+Flash, 又可以称为最小系统, 是整个产品的核心, 负责所有部分的正常运转.

  2. 接口, 接口可以分为内部接口和外部接口. 内部接口用于产品内部不同器件之间的连接, 如PCI, MII, I2S, LVDS等. 外部接口是产品与外部进行通信的接口, 如网口, 音视频接口, 天线接口等.

  3. 射频, 无线产品的射频设计是至关重要的, 整个产品的最重要的性能几乎全部体现在这里, 这部分在根本上决定了无线传输的距离, 可靠性.

  4. 电源, 电源是整个产品的”营养供给”, 保证了电源的”营养丰富, 健康”, 才能确保产品整体的稳定性.

  5. 时钟, 在大多数人眼中, 时钟可能都是一个不起眼的小角色, 但是, 在无线产品中, 很多实际问题都是由时钟及其附属电路引起的, 所以我向来都足够重视时钟电路.

  6. 复位, 复位在整个产品中也许随处可见, 大部分芯片都需要进行复位, 可靠的复位是产品稳定工作的又一重要保证.

  7. 按键, LED, GPIO, 有人可能会说, 这部分还值得一提吗? 我认为值得. 按键与LED是最直观的UI, 无线产品中的其他部分几乎都与UI无关, 同时, 按键, LED一般都是连接在GPIO上的, GPIO往往又起到上电配置的作用, 因此, 我认为GPIO很重要.

一般开发流程

wireless-hardware-designflow

Refs

802.11ac路由选购技术攻略 -- 不花冤枉钱的ac网络架设指南

802.11ac虽然还在草拟草案的阶段(IEEE的时间表), 但科技不待人. 对于高速WiFi高速无线传输的殷切需求, 各大厂家已经陆续推出旗舰无线路由, 价钱还不断下降, 连双核AC路由也进入千元以内消费级别之列. 802.11ac制式的无线路由应该如何选择?

为什么要买ac路由? 3天线的N900路由确实不够用吗?

为什么开头要先问这个问题, 因为购买一套802.11ac上网设备价钱不菲! 以旗舰1750ac为例, 很多人讨论的Asus ac66u要1050RMB, 双核D-Link DIR-868L要900RMB, 而匹配AC1750的PCI-E卡插网卡(3天线)要600RMB, 也就是说总的成本很可能在1500RMB以上!

换句话说, 你确定用超过1500元去突破802.11n的速度极限值得吗?

不卖关子了, 802.11n的WiFi无线的实际速度极限是: 200Mbps. 升级802.11ac是笔大投资, 要慎重考虑.

AC1200, AC1300, AC1750…购买前先了解AC制式

AC路由的最高理论速度有好几种, 譬如说D-Link就已经有7款AC路由, 当然把自己的产品线过度复杂化未必是明智选择, Asus现在有3款, ac66, ac56u和ac68u. Asus的新款AC68也即将推出, 速度能达到AC1900.

ASUS AC products

先说这些1200, 1750数值是怎么来的, 是不是1750的理论连接最高速度是1750Mbps? 不是的. 根据SmallnetBuilder总结的行业现行命名准则:

ac路由的标称速度 = n制式的理论最高速度 + ac制式的理论最高速度

Proposed router class summary

至于为什么ac制式会有不同速度和天线数量, 与下文要解释的QAM Coding传输模式有关.

应该如何选择哪个速度的ac路由器? 除了​​考虑价格和家里上网接入的带宽之外, 十分重要的一点是接收端是否匹配. 例如说ac68是MIMO 3X3也就是3天线的产品, 它的ac理论极限速度是1300Mbps, 要匹配3天线的接收器才能发挥这个速度. 那3天线的接收器要多少钱? Asus自己出的要600多RMB.

acnet 3天线ac接收器的价钱不菲, ASUS的这款要600多RMB.

请注意了! 现在很多商家夸大USB AC接收器的接收能力! 例如这款Netgear的A6200, 在官网写明了是867Mbps的速率(ac双天线的极限速度), 但是不少商家却标称其是AC1750 USB网卡!

netgear a6200

这款Netgear A6200的真面目是2×2 MIMO的USB网卡.

总的来说, 3天线的ac路由器就需要搭配3天线的接收网卡, 双天线就搭配双天线. 那么手机呢?和n制式时代一样, 都是单天线的天下. 例如Samsung Galaxy S4是用Broadcom 4335 WiFi芯片单天线.

802.11ac Coding和极限速度的关系

对比起现行主流的802.11n, 802.11ac提供更加复杂的数模转换无线传输方式(Modulation), 这是ac跑得快的核心因素. 什么是Modulation? 从Wiki借用一张图解释:

QAM16_Demonstration

16 QAM 的解码模式

众所周知, WiFi信号是无线电波, 无线电波有相位和强弱的特征值. QAM的转换模式就是把数据包放到不同相位和不同强弱的电波上. 16-QAM就是有16个相位和强弱的信号点, 所以, 接收端通过一次读取16个信号点就可以获得16个数据包.

modulation-data-rate

802.11n和ac Modulation的关系,现行的ac最主要在80MHz上运行

从上图可以看出n和ac的编码差别: 802.11n支持64-QAM, 跑在40MHz, 而802.11ac可以支持256-QAM, 跑在160MHz. 简单说明, 就是n制式是64部车走在40米宽的路上, 802.11ac是256部车跑在160米宽的路上. 所以ac制式的理论速度比n快上几倍. 现行的ac最主要在80MHz上运行, 所以单天线ac的理论速度能跑到433Mbps, 双天线867Mbps, 三天线1300Mbps.

上图的QAM速度仅仅是标准的QAM速度, 不同厂家可以作出优化, 例如ASUS的AC68声称能达到AC1900, Smallnetbuilder写了一篇分析文章分析ASUS无线路由的AC1900是怎么来的, 最后ASUS, Broadcom官方的解释是作出了一定程度的修改. 这又为大家选购AC路由增加了麻烦, ASUS的加速算法Buffalo未必支持, Broadcom的算法Atheros未必支持, 在选购时候特别要留意路由器和接收网卡的匹配. Upsangel和SmallNetBuider更加觉得, AC1900只是”作秀科技”的产物, 营销意义大于速度的实质提升:

ac实际传输速度到底比n快多少?

什么N450, AC1750这些都是标称速度, 我们用户关心的是实际速度! 下面就列一下802.11ac和802.11n的无线路由器实际速度差距(约数, 不为精准参考):

  • N制式:双天线(N300/N600) – 80Mbps, 三天线(N450/N900) – 200Mbps
  • AC制式:双天线(AC1200) – 300Mbps,三天线(AC1600/AC1750) – 350Mbps

也就是说, 200Mb带宽的家庭用3天线的n制式可能吃饱, 稳定地吃饱的话要用ac. ac现时的极限速度在350Mbps左右, 200Mb以上的宽带上网很难用ac WiFi吃饱. 接着分析802.11ac的不足之处, 不要买了后悔.

802.11ac的弱项不足

高频段传输 = 穿墙能力减弱

频率越高穿墙性能越低(在WiFi这个频段适用, 如果在X射线的频段频率越高就穿透力越强, 见多普勒的光粒二像性). 还记得香港4G刚刚出炉的时候, 数码通打着的旗号就是信号好, 因为其用的4G频谱频率比其他电讯商的低. 上面说了ac由于要跑256-QAM, 在宽频段(80MHz)才能跑得到, 而80MHz只能在5GHz的高频段跑, 因为2.4GHz已经拥挤不堪互相干扰严重(不信你搜一下家里收到多少个邻居的2.4G信号, 我搜到20个). 所以如果你家很多墙, ac未必能发挥其快速性能.

占用频宽倍增 = 更容易受干扰

由于要占用80MHz~160MHz的频宽作传输, 使得信号有更加大的机会被干扰. 例如最典型的例子是USB 3.0和2.4GHz的频率冲突:

usbwifi5g

USB3.0和WiFi 5GHz的频率重叠干扰

欠缺第三方Firmware支援

如果你是DD-WRT/OpenWRT的玩家, 那么选购ac路由要特别注意了, 因为第三方软件和驱动的开发未必跟得上官方新硬件的开发. 可能没有第三方Firmware的支援, 不能刷ROM.(主要是硬件平台model太新, 开源界跟进还需要时间, 而不是ac路由器不容易支持开源固件)

AC路由的选购

本文集中谈到了802.11ac制式所引伸的选购问题, 而基本的无线路由器的选购原则绝对不能忽略, 包括路由CPU和内存的选择, 天线的选择等, 一定要参阅本站最热门的文章(台湾upsangel的博客):

说到怎么比较AC路由器的性能, 著名的网站SmallNetBuilder可以给我们不少参考, 其中最多人关心的AC WiFi速度比较图:

1750ac

AC1750的速度比较图, 上传+下载

SmallNetBuider有很多有用的资讯, 对路由的评测比较专业和客观. 不要单看这些速度指标来买路由, 因为这些测试是在关闭所有防火墙和应用的情况下进行的测试, 实际用起来未必一样.

Upsangel最后列一下主流的AC路由器, 如有遗漏请告知:

  • ASUS:RT-AC66 (AC1750), AC68 (AC1900)、AC56u
  • Belkin:AC1200 DB
  • Buffalo:WZR-1166DHP (AC1200) 、WZR-1750DHP (AC 1750)
  • D-Link:AC750、AC1000、AC1200、AC1300、AC1750
  • Linksys:EA6300、EA6400、EA6500、EA6700,除第一款其他3款为AC1300
  • NetGear:R6200、R6250、R6300
  • TP-Link:Archer C7、TL-WDR7500

**最后, Upsangel为大家奉上玩转路由器附加强大功能的攻略(架设VPN Server、共享Printer、Scanner、离线BT等),请参考: ** 普通視像頭+OpenWRT變 IP CAM - OpenWRT, DD-WRT資源補完

Refs

About

如果不是为了写这篇文章, 笔者还真没意识到OpenWrt这个项目已经10年了.

在这个重新强调人工智能, 机器学习, 重新重视物理机械交互的新兴智能机器人的时代, 我们有理由相信因为其纯正的Linux味道, 小型化, 亲近物理交互的特征, 身为Linux社区与物理交互的最佳桥梁, OpenWrt会迎来新一轮的发展.

一切都始于2002年12月, Linksys发布了定义家用无线路由器产品形态的WRT54G, 由于成本的原因, Linksys使用Linux作为固件而不是授权费用很高的VxWorks. 根据GPL条款, 据称是哥伦比亚大学法学院教授Eben Moglen向Linksys提出了开源要求, Linksys随即照办, 之后在一堆各种hack WRT54G固件中, 2004年生长出来了OpenWrt. 2005年到2007年, 最初的稳定版叫White Russian, 之后的Kamikaze延续到2010年, Backfire到2013年, 随后Attitude Adjustment发布, 而最新版的Barrier Breaker也已经在持续开发中, 据称将很快发布.

从一开始, OpenWrt就是各类路由器hack固件中的领头羊, 并成为嵌入式Linux系统的核心贡献者之一, 特别是Linux on mips, 最近的内核更新代码有很大部分是由OpenWrt社区贡献的.

在各种路由器的hack固件中, OpenWrt为什么能脱颖而出? 笔者认为关键原因是OpenWrt社区彻底的开源精神, 不要忽视http://openwrt.org上面明晃晃的”Wireless Freedom”几个字. OpenWrt社区的组织者Gregers Petersen第一title是人类学家, 专注于自由软件及相关社会学研究. 以此为基因, OpenWrt社区聚集了一大批纯正的Linux各个方向的死忠级专家, 从而使OpenWrt具备了如下与传统nor flash嵌入式Linux截然不同的高级特征:

1.SquashFS与JFFS2文件系统的整合形成的overlayfs机制

对用户而言, OpenWrt的整个文件系统是完全动态可读写的, 而其中的固件部分是用SquashFS实施的只读压缩文件系统, 而用户所有的对文件系统的增删改都是用类似”差值”的形态存储在JFFS2文件系统中的, 二者用overlayfs机制黏合, 对用户完全透明. 因此我们可以在文件系统中肆意发挥, 随便折腾, 出现任何问题则可像手机一样恢复出厂设置, 并提供fail-safe模式帮助用户修复系统.

而在传统的嵌入式Linux里, 固件是静态的, 对系统做任何一点与可运行程序相关的变动, 比如增加一个模块, 删除一个应用程序, 都要重新编译全部固件, 并重新刷写, 就好比你一个Android手机要升级微信就要重新刷机. 这种反人类的传统文件系统完全阻挡了非专业爱好者进入嵌入式Linux这一领域.

2. UCI(Unified Configuration Interface)

帮助用户在任何平台的OpenWrt上用同样的方法配置系统, 网络和应用. 在Boardcom的平台上, 在Atheros的平台上, 甚至x86的平台上, 修改系统配置均为同样的命令. 而UCI的机制并不是二进制硬件虚拟层实现的, 是由Linux shell脚本实现的. 这毫无疑问是一种别致的创新, 比Android来的轻巧得多. OpenWrt里的Linux shell脚本用得很帅很高端, 那种感觉怎么形容呢? 就好像精通十八般武艺的高手有一天特别复古地拿起铅笔刀在硬盘上刻出来了系统, 就是这种感觉.

3. Opkg包管理系统与丰富的软件源

是一个与桌面级Linux使用的apt-get, yum等同级别的包管理系统, 使用形如: opkg install xxxx-app 的命令从互联网软件源中安装大约3000余种各种软件. 软件数量虽然没法跟手机的应用市场比, 但是要知道, 这里头的任何一个软件都来头不小, 是经过Linux社区千锤百炼的东西, 一个应用折腾一个月都玩不够. 类型覆盖网络, 音频, 视频, 程序开发, Linux系统管理等. 当然, 如果是专业比较偏的东西OpenWrt的软件源里还是不够完善, 比如笔者团队用到的OpenCV的东西, 源里就没有, 就靠自己交叉编译了.

4. Luci WEB界面系统

除CLI命令行终端界面外, 不同于桌面级Linux使用屏幕GUI作为交互界面, OpenWrt使用WEB界面交互. 而不同于传统路由器web管理界面的是, luci是用户可订制的, 安装了支持luci的软件后, WEB界面系统就中出现了新的模块, 而opkg本身也web化了.这个特征让用户感觉很像手机的app store.

5.积极, 完整的社区

OpenWrt与Arch Linux, Debian, FFmpeg, MinGW, PostgreSQL等开源领域重要的软件一起, 是Software in the Public Interest, Inc.资助和保护的项目, OpenWrt社区在美国, 欧洲, 中国, 俄罗斯有大量的追随者, 有不计其数的分支和代码贡献者. 社区活跃度非常高.

这几个特征加起来, 赋予OpenWrt比肩桌面级Linux和现代移动操作系统(Android)的用户体验, 完全回避了传统嵌入式Linux的磨叽和枯燥, 使一个小小的路由器真正成为完整的, 现代的, 开放的计算系统, 降低了入门门槛, 产生了大量非嵌入式专业的爱好者群体. OpenWrt框架的奠定者们和广泛的代码贡献者们, 在桌面级和现代操作系统的理念下, 也使OpenWrt成为嵌入式Linux领域个性十足而广受追捧的佼佼者.

OpenWrt社区的组织者Gregers Petersen在一次采访中提到, 除了传统的路由器用途, 在智能家居主控设备, 机器人, 飞行器, 工业控制设备, voIP设备等很多领域, 都有爱好者和商业项目在使用OpenWrt, 甚至有爱好者已经完整移植了Android系统并且真正打通了电话. 而嫁接到OpenWrt上的Arduino Yun, 使大家意识到Linux与物理世界交互一种非常简单的可能性, 这赋予了OpenWrt更大的想象空间.

在新型智能设备和机器人的热潮中, OpenWrt的价值也越来越得到人们的重视. 相比Android系统, OpenWrt被认为是更加适应智能设备和机器人的平台. Android的整体设计构架全部着眼于重度依赖屏幕与人进行交互, 导致从硬件到软件的设计上都严重依赖于图形界面的展示, 有过多的GPU硬件加速和软件上的图形, 3D库, 而智能设备和机器人并不强调屏幕, 相对更多地依赖于机械, 网络与人进行交互, Android上的这些图形图像特征反倒成为影响功耗, 系统尺寸, 稳定性的负面因素. 再加之相对于Android的Java虚拟机对效率的严重损耗, OpenWrt直接的原生二进制代码得到了更高的计算效率.

在这个重新强调人工智能, 机器学习, 重新重视物理机械交互的新兴智能机器人的时代, 我们有理由相信因为其纯正的Linux味道, 小型化, 亲近物理交互的特征, 身为Linux社区与物理交互的最佳桥梁, OpenWrt会迎来新一轮的发展.

Refs

OpenWrt开发文档

优秀文档中文项目-欢迎你的加入

wiki

wiki导航

编译

开发

Refs

移植OpenWrt到pcDuino

OpenWrt

OpenWrt是一个高度模块化, 高度自动化的嵌入式Linux系统, 拥有强大的网络组件, 常常被用于工控设备, 电话, 小型机器人, 智能家居, 路由器以及VOIP设备中. OpenWrt支持各种处理器架构,无论是对ARM,X86,PowerPC或者MIPS都有很好的支持. 其多达3000多种软件包, 囊括从工具链(toolchain), 到内核(linux kernel), 到软件包(packages), 再到根文件系统(rootfs)整个体系, 使得用户只需简单的一个make命令即可方便快速地定制一个具有特定功能的嵌入式系统来制作固件. 其模块化设计也可以方便的移植各类功能到OpenWrt下, 加快开发速度.

对于开发人员, OpenWrt是使用框架来构建应用程序, 而无需建立一个完整的固件来支持. 对于用户来说, 这意味着其拥有完全定制的能力, 可以用前所未有的方式使用该设备.

openwrt-logo

2014年12月19日小米路由器公测版正式发售, 也意味着OpenWrt进入国内主流科技企业的眼球. 然而OpenWrt到底是一款什么样的操作系统呢? 对于创客来讲, 怎么才能融入创客的设计, 下面就从零介绍如果在pcDuino上开发OpenWrt.

OpenWrt项目始于2004年1月. 最早的OpenWrt版本基于Linksys为遵守GPL而放出的, 为WRT54G所编写的代码, 以及uclibc项目的buildroot. 这个版本以OpenWrt “stable release”之名为人所知, 使用广泛. 仍有许多OpenWrt应用程序是基于这一版的, 例如Freifunk-Firmware和Sip@Home.

2005年初, 一些新的开发者进入了团队. 在封闭开发了数月之后, 团队决定发布OpenWrt的第一个experimental版本. 这个实验版本使用的build系统是基于buildroot2大改而成的, 而buildroot2来自于uclibc项目. OpenWrt使用官方版GNU/Linux内核代码, 只是额外添加了片上系统(SoC)的补丁和网络接口的驱动. 开发团队尝试重新实现GPL tarball中不同开发商的绝大多数专有代码. 其中有: 将新固件镜像文件直接写入闪存的自由工具(mtd), 配置无线局域网(wlcompat/wificonf), 通过proc文件系统对支持VLAN的switch(交换机)进行编程. 最初发布的OpenWrt的代号是”White Russian”, 来自于著名鸡尾酒的名称. 在OpenWrt发布0.9版的时候, White Russian的生命周期结束.

下一个版本的开发正在我们的SVN中进行. 下面一张图将很清晰的反映OpenWrt的版本史. 从图上可以看出最新的稳定版本代号为Attitude Adjustment, 早期的稳定版本为Backfire 和 Kamikaze, 开发版本一直都是trunk. 各个版本的官方下载地址为 https://dev.openwrt.org/wiki/GetSource

code_overview

下载编译OpenWrt

  1. 安装依赖包(deb系列)
sudo aptitude install -y libncurses5-dev zlib1g-dev gawk flex patch git-core g++ subversion
  1. OpenWrt的源代码管理默认用的是SVN, 当然你还可以用Git, 本教程中使用最新的trunk版本, 用SVN工具下载源码
svn co svn://svn.openwrt.org/openwrt/trunk/ openwrt-pcduino

还可以用Git下载

git clone git://git.openwrt.org/openwrt.git
git clone git://git.openwrt.org/packages.git
  1. 扩展软件包package feeds, feeds即为包含到你的OpenWrt环境中的额外软件包的软件列表索引(类似linux发行版中的软件源, 不过此处为软件包的源码). 目前常用的feeds有:
src-git packages git://git.openwrt.org/packages.git
src-svn xwrt http://x-wrt.googlecode.com/svn/trunk/package
src-git luci git://nbd.name/luci.git
src-git routing git://github.com/openwrt-routing/packages.git
src-git telephony http://feeds.openwrt.nanl.de/openwrt/telephony.git
src-svn phone svn://svn.openwrt.org/openwrt/feeds/phone
src-svn efl svn://svn.openwrt.org/openwrt/feeds/efl
src-svn xorg svn://svn.openwrt.org/openwrt/feeds/xorg
src-svn desktop svn://svn.openwrt.org/openwrt/feeds/desktop
src-svn xfce svn://svn.openwrt.org/openwrt/feeds/xfce
src-svn lxde svn://svn.openwrt.org/openwrt/feeds/lxde
src-link custom /usr/src/openwrt/custom-feed

一般情况, 你至少需要含packages feeds, 其他可根据需求下载, 安装feeds. * packages – 提供众多库, 工具等基本功能. 也是其他feed所依赖的软件源, 因此在安装其他feed前一定要先安装packages! * luci – OpenWrt默认的GUI(WEB管理界面). * xwrt – 另一种可替换LuCI的GUI * qpe – DreamBox维护的基于Qt的图形界面, 包含Qt2, Qt4, Qtopia, OPIE, SMPlayer等众多图形界面. * device – DreamBox维护与硬件密切相关的软件, 如uboot, qemu等. * dreambox_packages – DreamBox维护的国内常用网络工具, 如oh3c, njit8021xclient等. * desktop - OpenWrt用于桌面的一些软件包. * xfce - 基于Xorg的著名轻量级桌面环境. Xfce建基在GTK+2.x之上, 它使用Xfwm作为窗口管理器. * efl - 针对enlightenment. * phone -针对fso, paroli.

Trunk中默认的feeds下载有packages、xwrt、luci、routing、telephony。如果你需要其他的软件包,你只需要打开源码根目录下面的feeds.conf.default文去掉你需要的软件包前面的#号,本教程中使用默认的软件,确定了软件源之后,更新源:

./scripts/feeds update -a

安装下载好的包:

./scripts/feeds install -a
  1. OpenWrt源码目录结构, 执行上面命令之后你就可以得到全部的Openwrt源码.

目录结构:

  • tools和toolchain包含了一些通用命令, 用来生成固件, 编译器, 和C库.
  • build dir/host是一个临时目录, 用来储存不依赖于目标平台的工具.
  • build dir/toolchain-用来储存依赖于指定平台的编译链. 只是编译文件存放目录无需修改.
  • build dir/target-用来储存依赖于指定平台的软件包的编译文件, 其中包括linux内核, u-boot, packages, 只是编译文件存放目录无需修改.
  • staging_dir是编译目标的最终安装位置, 其中包括rootfs, package, toolchain.
  • package软件包的下载编译规则, 在OpenWrt固件中, 几乎所有东西都是.ipk, 这样就可以很方便的安装和卸载.
  • target目标系统指嵌入式设备, 针对不同的平台有不同的特性, 针对这些特性, “target/linux”目录下按照平台进行目录划分, 里面包括了针对标准内核的补丁, 特殊配置等.
  • bin编译完OpenWrt的二进制文件生成目录, 其中包括sdk, uImage, u-boot, dts, rootfs构建一个嵌入式系统完整的二进制文件.
  • config存放着整个系统的的配置文件.
  • docs里面不断包含了整个宿主机的文件源码的介绍, 里面还有Makefile为目标系统生成docs.
  • include里面包括了整个系统的编译需要的头文件, 但是是以Make进行连接的.
  • feeds扩展软件包索引目录.
  • scripts组织编译整个OpenWrt的规则.
  • tmp编译文件夹, 一般情况为空.
  • dl所有软件的下载目录, 包括u-boot, kernel.
  • logs如果编译出错, 可以在这里找到编译出错的log.
  1. 配置OpenWrt编译系统
make menuconfig

openwrt-buildroot

在官方最新的trunk分支中已经支持pcDuino这个target了.

具体配置如下: A. 配置目标系统(Target System)

Target System (Allwinner A1x/A20/A3x) —> 

B. 配置目标硬件(Target Profile)

Target Profile (pcDuino) —> 

C. 配置编译出来的image, 配置rootfs文件系统的格式这里选择ext4, rootfs文件系统的大小这里设置(48M).

Target Images  —> 
[ ] ramdisk  —> 
*** Root filesystem archives *** 
[ ] cpio.gz
[*] tar.gz 
*** Root filesystem images *** 
[*] ext4
[ ] jffs2
[ ] squashfs
[*] GZip images
*** Image Options ***
(48) Root filesystem partition size (in MB)
(6000) Maximum number of inodes in root filesystem
(0) Percentage of reserved blocks in root filesystem
[ ] Include kernel in root filesystem  —>
[ ] Include DTB in root filesystem

D. 选择编译交叉编译器, 还有开发SDK.

[*] Build the OpenWrt Image Builder
[*] Build the OpenWrt SDK

E. 配置无线网卡, V2/V3都是用的rtl8188cus无线网卡

Kernel modules  —>
Wireless Drivers  —>
-*- kmod-cfg80211…………………. cfg80211 – wireless configuration API
<*> kmod-lib80211……………………………… 802.11 Networking stack
{M} kmod-mac80211………………… Linux 802.11 Wireless Networking Stack
<M> kmod-rtl8192cu………………….. Realtek RTL8192CU/RTL8188CU support
{M} kmod-rtlwifi……………………………. Realtek common driver part 

F. LucI系统快速配置接口

LuCI  —>

1. Collections  —>
{*} luci
<M> luci-ssl……………………. Standard OpenWrt set with HTTPS

4. Themes  —> 
-*- luci-theme-base…………………………. Common base for all
-*- luci-theme-bootstrap……………………… Bootstrap Theme
<*> luci-theme-freifunk-bno……………….. Freifunk Berlin Nordost Theme
<*> luci-theme-freifunk-generic………………….. Freifunk Generic Theme
<*> luci-theme-openwrt……………………………………. OpenWrt.org

5. Translations  —>
<*> luci-i18n-chinese………………….. Chinese (by Chinese Translators)
-*- luci-i18n-english………………………………………… English
  1. 编译OpenWrt系统
make –j 8 V=s 

由于OpenWrt整个系统非常庞大, 编译很慢. “-j 8” 表示用8线程进行编译, “V=s”编译的时候显示编译信息. 如果你的电脑是4核建议你用8线程进行编译, 双核建议你使用4线程. 这里测试8线程编译需要一个小时才能编译完成.

构建pcDuino BSP

由于OpenWrt的u-boot用的是u-boot-2013的版本, 目前只支持SD卡启动, 而且内核用的是3.12.5版本. 另外我们的3.4.29的内核用的是全志fex, 而且3.12.5用的是linux官方的kernel使用的是dts设备树.这样的话我们就不能用之前的BSP方案, 我们要自己做一个从SD卡启动的系统.

pcDuino从SD卡启动顺序是A10—>u-boot–>uImage–>OpenWrt.

根据全志官网的说明, 这些软件都必须放在SD卡固定的地址. 那么首先要对A10进行分区. 根据全志芯片的说明, 需要对SD卡进行下表固定分区.

a10-flash

  1. 先格式化TF卡前面的1M空间, 这里是将TF卡通过读卡器插入到PC的虚拟机. 可以看出TF卡的设备是sdb.
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       195G   60G  126G  33% /
udev            989M  4.0K  989M   1% /dev
tmpfs           400M  940K  399M   1% /run
none            5.0M     0  5.0M   0% /run/lock
none            998M   76K  998M   1% /run/shm
/dev/sdb1       3.8G   12K  3.8G   1% /media/0005-559B

sudo dd if=/dev/zero of=/dev/sdb bs=1M count=1
  1. 写入u-boot-spl.bin和u-boot.bin. OpenWrt的生成的二进制文件都在openwrt/trunk/bin/sunxi目录下, 这里OpenWrt做了一些工作将u-boot-spl.bin和u-boot.bin合在了一起, 只需要把openwrt-sunxi-pcDuino-sunxi-with-spl.bin写到
cd  uboot-sunxi-pcDuino
pillar@monster:~/openwrt/trunk/bin/sunxi/uboot-sunxi-pcDuino$ ls 
openwrt-sunxi-pcDuino-sunxi-spl.bin       openwrt-sunxi-pcDuino-u-boot.bin
openwrt-sunxi-pcDuino-sunxi-with-spl.bin
sudo dd if=openwrt-sunxi-pcDuino-sunxi-with-spl.bin of=/dev/sdb bs=1024 seek=8

这时候把SD卡插到板子上, 重新上电就会看到下面打印信息.

U-Boot 2013.10-rc2 (Jan 15 2014 – 17:48:38) Allwinner Technology
CPU:   Allwinner A10 (SUN4I)
Board: pcDuino
I2C:   ready
DRAM:  1 GiB
MMC:   SUNXI SD/MMC: 0

*** Warning – bad CRC, using default environment
In:    serial
Out:   serial
Err:   serial
Net:   emac
Hit any key to stop autoboot:  0

sun4i#

从上面可以看到u-boot已经完全启动了, 上面的时间是编译的时间. 上面的信息还可以看到我们的环境变量没有设置, 它使用的是默认的环境变量. 前面介绍, 系统建立在分区表不同的地方, 但是我们现在SD卡还没有分区表, 我们需要先建立分区表再做环境变量.

  1. 建立分区表. 重新把SD卡插回到电脑的虚拟机里面, 使用fdisk创建分区表. 具体的分区见下操作, 步骤的说明请看#后面的注释.
pillar@monster:~/openwrt/trunk/bin/sunxi$ sudo fdisk /dev/sdb 
[sudo] password for pillar: 
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel with disk identifier 0x97bf3019.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won’t be recoverable.
Warning: invalid flag 0×0000 of partition table 4 will be corrected by w(rite) 
Command (m for help): m   #帮助 
Command action 
   a   toggle a bootable flag
   b   edit bsd disklabel
   c   toggle the dos compatibility flag
   d   delete a partition
   l   list known partition types
   m   print this menu
   n   add a new partition   #创建分区
   o   create a new empty DOS partition table
   p   print the partition table  #查看分区
   q   quit without saving changes
   s   create a new empty Sun disklabel
   t   change a partition’s system id   #改变分区类型
   u   change display/entry units
   v   verify the partition table
   w   write table to disk and exit
   x   extra functionality (experts only) 

Command (m for help): p             #查看分区
Disk /dev/sdb: 4027 MB, 4027580416 bytes
124 heads, 62 sectors/track, 1023 cylinders, total 7866368 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x97bf3019

Device Boot      Start         End      Blocks   Id  System

#没有分区 

Command (m for help): n      #创建分区 

Partition type: 
   p   primary (0 primary, 0 extended, 4 free)    #主分区 
   e   extended                                   #扩展分区 

Select (default p):                            #选择默认主分区
Using default response p
Partition number (1-4, default 1):                #分区号为1
Using default value 1
First sector (2048-7866367, default 2048):         #选择默认值
Using default value 2048
Last sector, +sectors or +size{K,M,G} (2048-7866367, default 7866367): 34815 #这个根据全志的手册来第一个分区必须这么大

Command (m for help): p                       #查看分区 

Disk /dev/sdb: 4027 MB, 4027580416 bytes
124 heads, 62 sectors/track, 1023 cylinders, total 7866368 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x97bf3019

   Device Boot      Start         End      Blocks   Id  System 
/dev/sdb1            2048       34815       16384   83  Linux  #创建的第一个分区

Command (m for help): n                    #再创建一个分区 
Partition type:
   p   primary (1 primary, 0 extended, 3 free) 
   e   extended 

Select (default p):                          #主分区
Using default response p 
Partition number (1-4, default 2):              #第二个主分区
Using default value 2
First sector (34816-7866367, default 34816):      #默认大小从34816开始
Using default value 34816
Last sector, +sectors or +size{K,M,G} (34816-7866367, default 7866367): #默认全部分到第二分区 

Using default value 7866367 

Command (m for help): p                       #再一次查看分区 

Disk /dev/sdb: 4027 MB, 4027580416 bytes
124 heads, 62 sectors/track, 1023 cylinders, total 7866368 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x97bf3019

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048       34815       16384   83  Linux
/dev/sdb2           34816     7866367     3915776   83  Linux

#可以看出创建了两个分区都为linux类型,但是u-boot只能识别第一个分区为FAT32分区 

Command (m for help): t            #修改分区类型
Partition number (1-4): 1            #选择修改哪个分区
Hex code (type L to list codes): L      #列出所有类型

 0  Empty           24  NEC DOS         81  Minix / old Lin bf  Solaris
 1  FAT12           27  Hidden NTFS Win 82  Linux swap / So c1  DRDOS/sec (FAT-
 2  XENIX root      39  Plan 9          83  Linux           c4  DRDOS/sec (FAT-
 3  XENIX usr       3c  PartitionMagic  84  OS/2 hidden C:  c6  DRDOS/sec (FAT-
 4  FAT16 <32M      40  Venix 80286     85  Linux extended  c7  Syrinx
 5  Extended        41  PPC PReP Boot   86  NTFS volume set da  Non-FS data
 6  FAT16           42  SFS             87  NTFS volume set db  CP/M / CTOS / .
 7  HPFS/NTFS/exFAT 4d  QNX4.x          88  Linux plaintext de  Dell Utility
 8  AIX             4e  QNX4.x 2nd part 8e  Linux LVM       df  BootIt
 9  AIX bootable    4f  QNX4.x 3rd part 93  Amoeba          e1  DOS access
 a  OS/2 Boot Manag 50  OnTrack DM      94  Amoeba BBT      e3  DOS R/O
 b  W95 FAT32       51  OnTrack DM6 Aux 9f  BSD/OS          e4  SpeedStor
 c  W95 FAT32 (LBA) 52  CP/M            a0  IBM Thinkpad hi eb  BeOS fs
 e  W95 FAT16 (LBA) 53  OnTrack DM6 Aux a5  FreeBSD         ee  GPT
 f  W95 Ext’d (LBA) 54  OnTrackDM6      a6  OpenBSD         ef  EFI (FAT-12/16/
10  OPUS            55  EZ-Drive        a7  NeXTSTEP        f0  Linux/PA-RISC b
11  Hidden FAT12    56  Golden Bow      a8  Darwin UFS      f1  SpeedStor
12  Compaq diagnost 5c  Priam Edisk     a9  NetBSD          f4  SpeedStor
14  Hidden FAT16 <3 61  SpeedStor       ab  Darwin boot     f2  DOS secondary
16  Hidden FAT16    63  GNU HURD or Sys af  HFS / HFS+      fb  VMware VMFS
17  Hidden HPFS/NTF 64  Novell Netware  b7  BSDI fs         fc  VMware VMKCORE
18  AST SmartSleep  65  Novell Netware  b8  BSDI swap       fd  Linux raid auto
1b  Hidden W95 FAT3 70  DiskSecure Mult bb  Boot Wizard hid fe  LANstep
1c  Hidden W95 FAT3 75  PC/IX           be  Solaris boot    ff  BBT
1e  Hidden W95 FAT1 80  Old Minix

Hex code (type L to list codes): c             #选择FAT32
Changed system type of partition 1 to c (W95 FAT32 (LBA))

Command (m for help): p                  #再一次查看分区 

Disk /dev/sdb: 4027 MB, 4027580416 bytes
124 heads, 62 sectors/track, 1023 cylinders, total 7866368 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x97bf3019

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1        2048       34815     16384    c  W95 FAT32 (LBA)#已经修改过了了
/dev/sdb2           34816     7866367     3915776   83  Linux

Command (m for help): w                                   #保存分区表
The partition table has been altered! 
Calling ioctl() to re-read partition table.
WARNING: If you have created or modified any DOS 6.x
partitions, please see the fdisk manual page for additional
information.
Syncing disks.
  1. 格式化分区. 刚才创建了分区, 但是没有格式化, 我们还是不能使用.
pillar@monster :~/openwrt/trunk/bin/sunxi$ ls /dev/sdb  #查看已经分好的分区
sdb   sdb1  sdb2
pillar@monster :~/openwrt/trunk/bin/sunxi$ mkf        #查看有哪些分区类型
mkfifo        mkfontscale   mkfs.bfs      mkfs.ext2     mkfs.ext4     mkfs.minix    mkfs.ntfs
mkfontdir     mkfs          mkfs.cramfs   mkfs.ext3     mkfs.ext4dev  mkfs.msdos    mkfs.vfat 

pillar@monster :~/openwrt/trunk/bin/sunxi$ sudo mkfs.vfat /dev/sdb1 #第一个分区格式化为fat分区
[sudo] password for pillar:
mkfs.vfat 3.0.12 (29 Oct 2011)

pillar@monster :~/openwrt/trunk/bin/sunxi$ sudo mkfs.ext4 /dev/sdb2 #第二个分区格式化为ext4分区,这里需要几分钟

mke2fs 1.42 (29-Nov-2011)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
244800 inodes, 978944 blocks
48947 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=1002438656
30 block groups
32768 blocks per group, 32768 fragments per group
8160 inodes per group
Superblock backups stored on blocks:
         32768, 98304, 163840, 229376, 294912, 819200, 884736

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done
  1. 挂载分区
pillar@monster :~/openwrt/trunk/bin/sunxi$ sudo mount /dev/sdb1 /media/1
pillar@monster :~/openwrt/trunk/bin/sunxi$ sudo mount /dev/sdb2 /media/2
pillar@monster :~/openwrt/trunk/bin/sunxi$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       195G   60G  126G  33% /
udev            989M  4.0K  989M   1% /dev
tmpfs           400M  944K  399M   1% /run
none            5.0M     0  5.0M   0% /run/lock
none            998M   76K  998M   1% /run/shm
/dev/sdb1        16M     0   16M   0% /media/1
/dev/sdb2       3.7G  7.5M  3.5G   1% /media/2
  1. 制作u-boot环境变量文件. 刚刚创建了分区, 这里只需要将环境变量文件, 还有uImage拷贝到第一分区让u-boot读取, 就可以引导系统了. 下面开始制作u-boot环境变量文件.
pillar@monster :/media/1$ vim boot.cmd
  1 setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait panic=10 ${extra}
  2 fatload mmc 0 0×46000000 uImage
  3 fatload mmc 0 0×49000000 sun4i-a10-pcduino.dtb
  4 fdt_high ffffffff
  5 bootm 0×46000000 – 0×49000000
pillar@monster :/media/1$mkimage -C none -A arm -T script -d boot.cmd boot.scr
  1. 将系统文件拷贝到第一和第二分区.
pillar@monster :/media/1$ cp ~/openwrt/trunk/bin/sunxi/sun4i-a10-pcduino.dtb .
pillar@monster :/media/1$ cp ~/openwrt/trunk/bin/sunxi/openwrt-sunxi-uImage uImage 
pillar@monster :/media/1$ ls           #第一分区文件
boot.scr  sun4i-a10-pcduino.dtb    uImage
pillar@monster :/media$ sudo dd if=~/openwrt/trunk/bin/sunxi/openwrt-sunxi-root.ext4 of=/dev/sdb2 bs=1M                   #拷贝第二分区文件

好了, 现在整个的从SD启动的BSP已经最好了.

  1. 发布并烧写系统. 现在把系统做好了, 你可以发布你制作的系统, 然后别人可以通过win32diskimager来把你的系统写入到他的SD卡, 他就可以和你一起玩OpenWrt了.
pillar@monster :/media$ sudo dd if=/dev/sdc of=OpenWrt.img  bs=4M 

现在把OpenWrt.img拷贝到windows上, 把你新的SD卡插到电脑开始用win32diskimager写入

win32-disk-imager

配置OpenWrt系统

  1. 让系统上网
vim /etc/config/network
config interface ‘net’
        option ifname ‘eth0′
        option proto ‘dhcp’
  1. 设置固定的mac地址

当系统的启动的时候发现mac地址老是在变,这就会出现一个问题, 有时候能获取到ip, 有时候获取不到ip. 这里可以做一个系统服务, 让系统开机保存mac地址, 然后再开机的时候恢复之前的mac地址.

1) 在/etc/init.d/mac里面编写如下脚本

#!/bin/sh /etc/rc.common
START=18
STOP=91
start() {
if [ -f /mac ]; then
dd if=/mac bs=1 count=17 of=/tmp/mac >/dev/null 2>&1
mac_addr=`cat /tmp/mac`
else
mac_file=/sys/class/net/eth0/address
dd if=$mac_file bs=1 of=/mac count=17 >/dev/null 2>&1
mac_addr=`cat /tmp/mac`
fi
ifconfig eth0 down
ifconfig eth0 hw ether $mac_addr
#if failed, save current mac address
if [ $? -ne 0 ]; then
mac_file=/sys/class/net/eth0/address
dd if=$mac_file bs=1 of=/mac count=17 >/dev/null 2>&1
fi
}

2) 指定运行的模式

/etc/rc.d/rc则根据其参数指定的运行模式(运行级别, 你在inittab文件中可以设置)来执行相应目录下的脚本. 凡是以Kxx开头的, 都以stop为参数来调用. 凡是以Sxx开头的, 都以start为参数来调用. 调用的顺序按xx 从小到大来执行. 例如, 假设缺省的运行模式是3, /etc/rc.d/rc就会按上述方式调用. 由于设定mac地址要在network之前. 所以要创建链接:

ln  -s   /etc/init.d/mac   /etc/rc.d/S18mac
  1. 开启wifi

openwrt启动之后输入:

root@OpenWrt :/# ifconfig
eth0      Link encap:Ethernet  HWaddr AE:DB:9A:D9:31:DE
inet addr:192.168.1.119  Bcast:192.168.1.255  Mask:255.255.255.0
inet6 addr: fe80::acdb:9aff:fed9:31de/64 Scope:Link
UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
RX packets:696 errors:0 dropped:0 overruns:0 frame:0
TX packets:640 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:86028 (84.0 KiB)  TX bytes:377264 (368.4 KiB)
Interrupt:17 Base address:0×4000 

lo        Link encap:Local Loopback
inet addr:127.0.0.1  Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING  MTU:65536  Metric:1
RX packets:16 errors:0 dropped:0 overruns:0 frame:0
TX packets:16 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1786 (1.7 KiB)  TX bytes:1786 (1.7 KiB)

wlan0     Link encap:Ethernet  HWaddr 00:7A:03:00:29:F4
inet6 addr: fe80::27a:3ff:fe00:29f4/64 Scope:Link
UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:7 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B)  TX bytes:864 (864.0 B)

确保ethX和wlanX都有. openwrt的root密码是没有设置, 你需要从serial debug进入系统设置root密码, 设置方法如下:

passwd  root

然后在同一个局域网内你的PC的浏览器上输入: ethX的ip, 这里是192.168.1.119.就会出现下面界面:

luci-login

输入你刚才设置的密码, 进入系统管理界面, 默认是进入状态标签, 这里你可以看到整个系统的运行的状态.

system-status

如果你对当前页面不太习惯, 而且在使用上语言上也有些困难, 你可以进入system标签, 在System Properties里面设置language and style如下图所示. 设置完之后save & apply, 重新刷新一下浏览器就可以使用你设置的语言和主题.

luci-theme

下面进入网络标签栏设置wifi节点, 这个部分是openwrt比较复杂的一个部分, 这个部分的设置直接决定着你的openwrt能不能使用.

luci-wifi

添加新接口, 选择静态ip, 新接口的名称, 你需要用英文自定义一个名字, 在包括一下接口里面选择无线网络. 设置完之后提交, 进入下一个页面继续设置.

luci-interface

基本设置设置完成之后, 进入防火墙设置, 这里wifi必须选择为lan口. 设置完成之后保存应用. 这个时候你电脑就可以连接使用openwrt这个路由器了.

pc-wifi

luci-wifi-net

点击修改后进入防火墙设置标签栏, 分配防火墙区域为wan, 设置完成之后保存&应用.

luci-firewall

制作内核补丁

  1. 清空恢复上一个全新的内核
make target/linux/{clean,prepare} V=s QUILT=1
  1. 到内核源码目录
cd build_dir/target-*/linux-*/linux-3.*
  1. 建立git代码仓库
git init
git add * -f
git commit -am "initial commit"
  1. 修改你的代码, 这里我给我的代码添加rtl8188cus驱动.
mkdir drivers/net/wireless/rtl8192cus

cp  /home/pillar/openwrt/openwrt-pcDuino/RTL8188C_8192C_USB_linux_v4.0.2_9000.20130911/driver/rtl8188C_8192C_usb_linux_v4.0.2_9000.20130911/*   drivers/net/wireless/rtl8192cus/ -rf

vim  drivers/net/wireless/Kconfig
284  source “drivers/net/wireless/rtl8192cus/Kconfig”

vim drivers/net/wireless/rtl8192cus/Kconfig
  1 config RTL8192CU_SW
  2 tristate “Realtek 8192C USB WiFi for SW”
  3 depends on USB
  4 select WIRELESS_EXT
  5 select WEXT_PRIV

vim  drivers/net/wireless/Makefile
28             obj-$(CONFIG_RTL8192CU_SW)  += rtl8192cus/

vim  drivers/net/wireless/rtl8192cus/Makefile
575  obj-$(CONFIG_RTL8192CU_SW) := $(MODULE_NAME).o 

579  export CONFIG_RTL8192CU_SW = m
  1. 建立git分支, 并制作补丁
git branch rtl8192
git checkout rtl8192
git add * -f
git commit -a -m  "add rtl8192cus for pcDuino"
git format-patch -M master  #会生成0002-add-rtl8192cus-for-pcDuino.patch 

cp  0001-add-rtl8192cus-for-pcDuino.patch patches/
cd ../../../../
make target/linux/update package/index V=s
cp  build_dir/target-arm_cortex-a8+vfpv3_uClibc-0.9.33.2_eabi/linux-sunxi/linux-3.12.5/patches/0001-add-rtl8192cus-for-pcDuino.patch target/linux/sunxi/patches-3.12/
  1. 检测是否生效, 执行完之后就是rtl8192分支的代码了.
make target/linux/{clean,prepare} V=s QUILT=1
  1. 配置内核应用选项
make kernel_menuconfig
         Device Drivers  —> 
                   [*] Network device support  —> 
                                     [*]   Wireless LAN  —> 
                                                          <*>   Realtek 8192C USB WiFi

其他的patch的制作方法请参考: http://wiki.openwrt.org/doc/devel/patches

建立App服务器

OpenWrt通过opkg来管理安装整个系统的软件. 目前有很多OpenWrt的软件源, 但是哪些都是针对于MIPS平台的, pcDuino使用的ARM平台, 我们必须自己搭建软件源. 查看了一下MIPS平台的服务器, 其实很简单的, 就是一个apache服务器, 而且OpenWrt编译完成之后, 在openwrt/trunk/bin/sunxi/packages下面已经生成了软件源. 我们只需要将他们联系起来就行了, 这里是在我的PC的虚拟机上搭建的.

# sudo apt-get install apache2

修改https的根目录

pillar@monster :~/openwrt$ vim /etc/apache2/sites-available/default
4     DocumentRoot /home/pillar/openwrt/trunk/bin/sunxi/

重启服务器使修改过的配置生效

pillar@monster :~/openwrt$ sudo /etc/init.d/apache2 restart

修改pcDuino上OpenWrt的源配置

root@OpenWrt :/# vim /etc/opkg.conf
src/gz barrier_breaker http://192.168.1.125/packages
dest root /
dest ram /tmp
lists_dir ext /var/opkg-lists
option overlay_root /overlay

上面的IP为我们电脑虚拟机的IP, 下面更新一下软件源.

# opkg update

应用程序开发

OpenWrt上面应用程序开发有两种方式, 一种是利用OpenWrt SDK, 一种是利用OpenWrt源码. 这里主要介绍利用OpenWrt源码, 进行开发应用程序, 制作成ipk软件可以安装.

  1. 进入package目录, 创建软件目录
#cd   /home/pillar/openwrt/trunk/package
#mkdir example1
  1. 进入example1目录, 创建Makefile文件和代码路径
#cd example1
#touch Makefile
#mkdir  src

该Makefile具体内容如下:

#User mode tool example
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=example1
PKG_RELEASE:=1
PKG_BUILD_DIR := $(KERNEL_BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk

define Package/example1
 SECTION:=utils
 CATEGORY:=Base system
 TITLE:=Build for example1 commands
endef

define Package/example1/description
 This package contains an utility useful to use example1 commands.
endef

define Build/Prepare
  mkdir -p $(PKG_BUILD_DIR)
  $(CP) ./src/* $(PKG_BUILD_DIR)/
endef 

target=$(firstword $(subst -, ,$(BOARD)))
MAKE_FLAGS += TARGET="$(target)"
TARGET_CFLAGS += -Dtarget_$(target)=1 -Wall

define Build/example1/compile
  $(MAKE) -C “$(LINUX_DIR)” \
   CROSS_COMPILE=”$(TARGET_CROSS)” \
   ARCH=”$(LINUX_KARCH)” \
   SUBDIRS=”$(PKG_BUILD_DIR)” \
   EXTRA_CFLAGS=”$(BUILDFLAGS)”
endef 

define Package/example1/install
  $(INSTALL_DIR) $(1)/sbin
  $(INSTALL_BIN) $(PKG_BUILD_DIR)/example1 $(1)/sbin/
endef 

$(eval $(call BuildPackage,example1))
  1. 进入src目录, 创建相关源文件
cd src 
touch example1.c Makefile 

example1.c 具体内容如下:

#include <stdio.h>
int main(void)
{
  printf(“Hello, world\n”);
  return 0;
}

Makefile文件具体内容如下:

.NOTPARALLEL: 
#OCTEON_ROOT=$(PWD)/src/ 
CC=~/openwrt/main/staging_dir/toolchain-mips64_gcc-4.4.1_eglibc-2.10.1/usr/bin/mips64-openwrt-linux-gnu-gcc
CFLAGS=-mips64r2 -mabi=64 -march=octeon -mtune=octeon
LFLAGS=
.PHONY: all
all: example1
example1:example1.c
  ${CC} ${CFLAGS} ${LFLAGS} -W -g -Wall -Wno-unused-parameter -DUSE_RUNTIME_MODEL_CHECKS=1 \
    -o $@ example1.c
  1. 回到主路径/home/pillar/openwrt/trunk/, 编译选项配置保存并编译.
make menuconfig
  Base system —>
   example1

选项设置为M, 保存退出. 然后编译该模块:

make package/example1/compile
  1. 更新package
make package/ example1/install
make package/index

内核驱动开发

OpenWrt开发内核驱动有多种方式, 前面讲到的制作内核补丁也是一种开发方法. 这里介绍直接在OpenWrt系统上开发内核驱动, 把内核驱动做成ipk软件包的形式.

  1. 建立工作目录
cd  openwrt/trunk/package
mkdir example
  1. 进入example目录, 创建Makefile文件和代码路径
cd example 
mkdir src
vim Makefile
# Kernel module example
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=example
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk

define KernelPackage/example
  SUBMENU:=Other modules
  DEPENDS:=@TARGET_octeon
  TITLE:=Support Module for example
  AUTOLOAD:=$(call AutoLoad,81,example)
  FILES:=$(PKG_BUILD_DIR)/example/example.$(LINUX_KMOD_SUFFIX)
endef

define Build/Prepare
  mkdir -p $(PKG_BUILD_DIR)
  $(CP) -R ./src/* $(PKG_BUILD_DIR)/
endef 

define Build/Compile
  $(MAKE) -C “$(LINUX_DIR)” \
    CROSS_COMPILE=”$(TARGET_CROSS)” \
    ARCH=”$(LINUX_KARCH)” \
    SUBDIRS=”$(PKG_BUILD_DIR)/example” \
    EXTRA_CFLAGS=”-g $(BUILDFLAGS)” \
    modules
endef 

$(eval $(call KernelPackage,example))
  1. 进入src目录, 创建代码路径和相关源文件
cd src
mkdir example
cd example
vim example.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

/* hello_init —- 初始化函数, 当模块装载时被调用, 如果成功装载返回0, 否则返回非0值 */ 

static int __init hello_init(void)
{
   printk("I bear a charmed life.\n");
   return 0;
} 

/ * hello_exit —- 退出函数, 当模块卸载时被调用 */
static void __exit hello_exit(void) 
{
   printk("Out, out, brief candle\n");
} 

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pillar_zuo");
vim Kconfig

config EXAMPLE
  tristate "Just a example"
  default n
  help
   This is a example, for debugging kernel model.
   If unsure, say N.

vim Makefile

obj-m := example.o
  1. 回到OpenWrt源码根目录下
make menuconfig
  Kernel modules —>
    Other modules —>
      kmod-example

选项设置为M, 保存退出 然后编译该模块:

make package/example/compile
make package/index
  1. 在OpenWrt系统里面就可以用opkg下载使用了.

使用OpenWrt SDK

OpenWrt为了避免每次都重新编译系统, 引入了SDK机制. 我们在发布系统的时候也需要发布SDK, 具体的使用方法请下面例子.

  1. 解压SDK
pillar@monster :~/openwrt/trunk/bin/sunxi$ tar xvf OpenWrt-SDK-sunxi-for-linux-x86_64-gcc-4.6-linaro_uClibc-0.9.33.2.tar.bz2
cd  OpenWrt-SDK-sunxi-for-linux-x86_64-gcc-4.6-linaro_uClibc-0.9.33.2
  1. 建立软件工作目录
cd package
mkdir helloworld
vim Makefile    #这个Makefile可以作为模板
##############################################
# OpenWrt Makefile for helloworld program
#
#
# Most of the variables used here are defined in
# the include directives below. We just need to
# specify a basic description of the package,
# where to build our program, where to find
# the source files, and where to install the
# compiled program on the router.
#
# Be very careful of spacing in this file.
# Indents should be tabs, not spaces, and
# there should be no trailing whitespace in
# lines that are not commented.
#
############################################## 

include $(TOPDIR)/rules.mk 

# Name and release number of this package 
PKG_NAME:=helloworld 
PKG_RELEASE:=1 

# This specifies the directory where we’re going to build the program.
# The root build directory, $(BUILD_DIR), is by default the build_mipsel
# directory in your OpenWrt SDK directory

PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk

# Specify package information for this program.
# The variables defined here should be self explanatory.
# If you are running Kamikaze, delete the DESCRIPTION
# variable below and uncomment the Kamikaze define
# directive for the description below

define Package/helloworld
    SECTION:=utils
    CATEGORY:=Utilities
    TITLE:=Helloworld — prints a snarky message
endef 

# Uncomment portion below for Kamikaze and delete DESCRIPTION variable above
define Package/helloworld/description
        If you can’t figure out what this program does, you’re probably
        brain-dead and need immediate medical attention.
endef 

# Specify what needs to be done to prepare for building the package.
# In our case, we need to copy the source files to the build directory.
# This is NOT the default.  The default uses the PKG_SOURCE_URL and the
# PKG_SOURCE which is not defined here to download the source from the web.
# In order to just build a simple program that we have just written, it is
# much easier to do it this way. 

define Build/Prepare
    mkdir -p $(PKG_BUILD_DIR)
    $(CP) ./src/* $(PKG_BUILD_DIR)/
endef 

# We do not need to define Build/Configure or Build/Compile directives
# The defaults are appropriate for compiling a simple program such as this one
# Specify where and how to install the program. Since we only have one file,
# the helloworld executable, install it by copying it to the /bin directory on
# the router. The $(1) variable represents the root directory on the router running
# OpenWrt. The $(INSTALL_DIR) variable contains a command to prepare the install
# directory if it does not already exist.  Likewise $(INSTALL_BIN) contains the
# command to copy the binary file from its current location (in our case the build
# directory) to the install directory. 

define Package/helloworld/install
    $(INSTALL_DIR) $(1)/bin
    $(INSTALL_BIN) $(PKG_BUILD_DIR)/helloworld $(1)/bin/
endef 

# This line executes the necessary commands to compile our program.
# The above define directives specify all the information needed, but this
# line calls BuildPackage which in turn actually uses this information to
# build a package.

$(eval $(call BuildPackage,helloworld))
mkdir src
cd src
  1. 编写自己的软件, 这里以helloworld为例.
vim helloworld.c

#include<stdio.h>
int main(void)
{
    printf(“Hell! O’ world, why won’t my code compile?\n\n”); 
    return 0;
}

vim Makefile

# build helloworld executable when user executes "make"
helloworld: helloworld.o 
    $(CC) $(LDFLAGS) helloworld.o -o helloworld

helloworld.o: helloworld.c 
    $(CC) $(CFLAGS) -c helloworld.c 

# remove object files and executable when user executes "make clean"
clean:
    rm *.o helloworld
  1. 编译软件, 回到SDK根目录下.
cd ../../
make V=s
pillar@monster :~/openwrt/trunk/bin/sunxi/OpenWrt-SDK-sunxi-for-linux-x86_64-gcc-4.6-linaro_uClibc-0.9.33.2$ ls bin/sunxi/packages/
helloworld_1_sunxi.ipk  Packages  Packages.gz
  1. 修改软件源根目录. 如果你不想每次都拷贝, 你可以把软件源的根目录下设置在
OpenWrt-SDK-sunxi-for-linux-x86_64-gcc-4.6-linaro_uClibc-0.9.33.2/bin/sunxi/packages 

然后你可以在OpenWrt系统里面下载安装.

Refs

Protothreads

Specs

  • 没有专用的机器代码, 纯c实现.
  • 不使用容易犯错的跳转指令.
  • 占用极少内存.
  • 在不在操作系统里用都可以提供blocking event-handlers(可阻塞的事件句柄??).
  • 提供给事件触发系统(event-driven)线性代码执行(linear code execution).
  • 提供顺序的控制流程(sequential flow of control)不需要使用复杂的状态机(state machine)或者完全的多线程(full multi-threading).
  • Protothreads 是无优先级的, 因此, 一个上下文切换(context switch)只会发生在阻塞操作(blocking operations)上.
  • Protothreads function as stackless, lightweight threads providing a blocking context cheaply using minimal memory per protothread (on the order of single bytes).
  • Protothreads 是无栈的, 表示需要全局变量来保持变量用来跨上下文切换(across context switches).
  • Protothread 的概念是被Adam Dunkels和Oliver Schmidt开发的.

Adam Dunkels

  • 看了一下他的wiki页面, 原来这个人还是个牛人, 在嵌入式领域写了不少东西.
  • 博士, 瑞典的企业家和程序员, Thingsquare的创始人.
  • IPSO Alliance的创始人, 推广对于小的设备(嵌入式和无线传感器)的IP网络通信. alliance’s white paper的作者.
  • 他的工作主要是关注网络技术和小的嵌入式设备和无线传感器的分布式通信.
  • 作品有: uIP(micro-IP), lwIP, Protothreads, Contilki, uVNC, MiniWeb, phpstack, uBASIC.
  • 书籍: .

Example

#include "pt.h"
 
struct pt pt;
struct timer timer;
 
PT_THREAD(example(struct pt *pt))
{
  PT_BEGIN(pt);
 
  while(1) {
    if(initiate_io()) {
      timer_start(&timer);
      PT_WAIT_UNTIL(pt,
         io_completed() ||
         timer_expired(&timer));
      read_data();
    }
  }
  PT_END(pt);
}

Refs

Learning Perl

Books

  • 小骆驼/大骆驼, 在知乎上搜了一下,还有草泥马什么的, 呵呵, 好多搞生物信息学的用Perl.
  • Modern Perl: 我选择了这本, 原因是github上有该书地址, 还有因为如 Why “Modern Perl” 中所说的, 以前对于当时Perl的好的编程方法现在并不适用了.

Tools

  • perlbrew: 方便的perl版本管理工具, 把多个perl版本下载到home目录, 然后可以随时切换,也可以关闭使用系统的perl.

Refs

在Mac OS X下配置OpenWrt Buildroot环境

与linux相比会遇到的2个问题

  1. OpenWrt需要一个case-sensitive filesystem, 而Mac OS X默认提供的文件系统是case-insensitive.
  2. Mac OS X下缺少大量的开发工具包, 在普通linux下都有.
  3. 使用一个disk image(避免再次分区硬盘), 和Homebrew.

steps

  1. Disk Image Creation hdiutil create -size 20g -fs “Case-sensitive HFS+” -volname OpenWrt OpenWrt.dmg hdiutil attach OpenWrt.dmg 这个命令会在当前目录创建一个20GB image, 并且attach他名字为”OpenWrt”, 执行后你会在Finder中看到OpenWrt volume, 是空的. cd /Volumes/OpenWrt
  2. Packages installation 有两种类型的packages:
  3. XCode framework: Apple development SDK . 包含了core compilers和libraries.
  4. Homebrew framework: a package manager用来下载开源components到你的系统. brew install coreutils e2fsprogs ossp-uuid asciidoc binutils bzip2 fastjar flex getopt gtk2 intltool jikes hs-zlib openssl p5-extutils-makemaker python26 subversion rsync ruby sdcc unzip gettext libxslt bison gawk autoconf wget gmake ncurses findutils
  5. missing: bzip2 getopt(gnu-getopt代替, 将mac os x自带的/usr/bin/getopt 重命名备份,把gnu-getopt链接到/usr/local/bin/getopt) gtk2 jikes zlib p5-extutils-makemaker tar(gnu-tar代替) python26(python代替) rsync unzip gmake ncurses

Easy Build

不需要产生或下载不必要的packages.http://wiki.openwrt.org/doc/howto/easy.build

Refs

minicom on mac

USB转串口驱动

  • 我使用的芯片是FTDI的根据FTDI官网的说明,需要下载VCP(Virtual COM Port)驱动.下载了64位的2.2.18版本,ps这东西版本也挺旧的了,是2012/08/08发布的.
  • 另外一种适配器基于Silicon Labs的CP2012芯片, Windows, Linux和 Mac下都有驱动http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx
  • ps: 另外一种常见得USB转串口驱动(Prolific PL2303): 在Prolific官网上面也有:(http://www.prolific.com.tw/US/ShowProduct.aspx?pcid=41&showlevel=0041-0041).
  • 参考文献中还有其他的驱动,有需要的可以到参考文献中去查.
  • 安装前先删除已有的USB转串口驱动(我跳过了)
  • 安装好之后,打开一个Terminal, 输入 ls /dev/cu.*, 找到包含有usbserial或类似的东西.我的结果:
    $ ls /dev/cu.*                                                             
    /dev/cu.Bluetooth-Incoming-Port /dev/cu.usbserial-AD02COJ7
    /dev/cu.Bluetooth-Modem
  • 注意:一个serial device会在/dev下面出现2次, 一个是tty.,用来 calling into UNIX systems, 一个是cu., (Call-Up)用来从他们中 calling out(如: modems).我们想要从我们的Mac中call-out所以我们选用/dev/cu.*.
  • TTY device与CU device的技术区别是: /dev/tty.*会wait(或者listen)for DCD(data-carrier-detect),如:某人在response之前calling in. /dev/cu.*不会assert DCD, 所以他们将总是立刻connect(respond or succeed).

Terminal emulation

  • GUI apps: Zterm(收费, 支持VT100 emulation), goSerial(不支持 VT100), SecureCRT for Mac(收费)
  • Terminal apps: screen, minicom(recommend,and I used)
  • built-in: screen(screen /dev/cu.usbserial-AD02COJ7 115200, 退出: type CTRL-A, then CTRL-.)

Minicom

  • 支持VT100 emulation(which means it sorta kinda works with Meridian Mail (Function keys on a MacBook: fn + f-key))
  • 先运行minicom -s来配置, 修改device name和流控信息, 然后 Save setup as dfl(default) and Exit.
  • 每次运行minicom前都记得插入usb转串口.
  • 在minicom中, 命令都是 Ctrl-A 修改port settings: Ctrl-A P Command Summary: Ctrl-A Z Quit Minicom: Ctrl-A X

screen

Mac自带的命令行工具, 可以用来连接USB虚拟串口, 使用方法:

screen /dev/cu.SLAB_USBtoUART 115200

这里的/dev/cu.SLAB_USBtoUART是虚拟串口的设备文件节点, 确保装好了适配器的驱动, 插入适配器应该就会找到它们了. 电脑通过/dev/cu.设备文件来连接其他串口设备, 通过/dev/tty. 接受来自其他设备的连接.

Refs

Keep Gitting My CV With Markdown

  • 我还是决定一点一点的写吧, 自己的拖延症太厉害了.
  • Github是个好东西, 现在好玩的东西也越来越多. 其实一个程序员的Github账号就是一份很好的简历.(像我们目前的公司不能联网, 我就不多说什么了, 好公司都不会这样)
  • Github的markup是个优秀的渲染引擎, 支持markdown等标记语言. 而且, Github还带了简洁大气的css渲染. 也就是说, 直接把简历写成.rst或者.md放在Github上吧!

My CV online version

http://cv.akagi201.org

招聘网站

TODO

  1. pdf version
  2. latex modern cv version
  3. 简历: 海投, v2ex, 内推, linkedin

Refs

Fitbit Flex

  • 排斥英文的小伙伴们可以不用看了, 各种数据, 各种软件全是英文的.

    clients

  • pc: http://www.fitbit.com/setup 下载安装 FitbitConnect, 貌似只有2个功能: 同步和升级固件.第一次使用会搜索设备, 然后搜到之后, 双击一下手环表示确认, 我勒个去, 太有科技感了.看配置信息(dashboard)会跳转到网页.

  • Android: 只能看数据, 同步目前需要蓝牙4.0, wifi现在还不支持, 看fitbit的开发博客貌似正在beta阶段.

update firmware

  • 通过pc端可以升级, 刚入手时候是50, 升级后变成64了. 具体多了哪些功能还不清楚, 总之越新越好. 升级过程有点慢, 手都不敢动, 很怕升级失败的说.

Specs

  • 5个白点LED灯,一个蓝牙芯片,一个三轴加速度传感器,一个振动马达,还有一个主控芯片,微小的电池续航能力竟能达到5-7天。还有一块NFC标签, 用于支持NFC的手机同步的.
  • 五个LED灯是跟天线套在一个塑料壳上,有一层黑色胶布把它、电池和整块电路板绑在一起,合得比较紧密.
  • 蓝牙芯片使用的是Nordic的NRF8001.
  • 接下来有两片不明的芯片写有“NXE”和“NAI”字样,其中NAI芯片上还带有疑似二维码的信息.
  • 主控芯片用的是意法半导体的STM32L。电池容量未标清,目测可能是20mAh左右.

关于卡路里消耗的精度问题

  • 由于fitbit有庞大的数据库源,只要你输入了精确的身高+体重,fitbit就能基本了解你的步长和单步消耗卡路里的平均值,之后这些数据都会很准。
  • 所以初次使用,建议录入自己的精确身高和体重,以后记得更新体重的变化,这样会让数据更准。

睡眠跟踪

  • 睡前 拍打4-5下触发睡眠跟踪模式,早上醒来再同样操作即可。 在刚躺下的时候记得猛拍它,进入睡眠模式,这样早晨sync一下就能以图表的形式看到昨晚睡眠情况,中途醒了几次,深度睡眠浅度睡眠分别占了多少。我觉得一天几个小时就差不多了,再多真是挥霍生命。

充电

  • 大概半小时就能冲到8成以上,但是足足充满需要两三小时的,充满电五颗LED会同时闪烁.

操作

  • 没有任何可见的按钮,平时的操作是靠快速点击:啪啪两下,显示今日目标完成情况,5颗LED各代表20%完成度。。如果猛烈的啪啪啪啪啪点了好多下,就会进入睡眠模式,最左和最右两颗LED亮,再一次啪啪啪啪啪就会切回普通计步模式。而且LED绝大部分时候也是不会亮的,据说运动量达到了设定的目标会有震动和”happy dancing”

Refs

Compile Debian Kernel

Debian Logo

Changes to the pristine kernel source(原始的kernel source, www.kernel.org)

  • pristine kernel source == upstream kernel source
  • 由于 licensing restrictions, unclear license information, failure to comply with the Debian Free Software Guidelines (DFSG), 部分的kernel被移除为了发布Debian archive的 main section(the source in the main section of the Debian archive). 这种搬移工具产生了 linux_version.orig.tar.xz tarball, 作为原始的上流源码(original upstream source). version是实际的upstream version.
  • 在2.6.31-1以后的版本中, 所有的已知的没有源码的firmware已经被从Debian package中移除了, 但是他们大多被包含在 firmware-nonfree package中.
  • Reference:
  • Handling source-less firmware in the Linux kernel
  • position statement
  • KernelFirmwareLicensing

Debian kernel patches

  • Debian kernel的源码: linux_version.orig.tar.xz(去掉license不合格的firmware) + Debian patches
  • Debian patches: 必要的修复严重bug fixes 和 安全漏洞(security holes)
  • Debian version of the kernel packages: version-revision(version: upstream kernel version, revision: patchlevel)
  • 接受patch的原则: fix a bug or add hardware support.
  • patches introducing optional features 推荐提交到 upstream maintainer.

Refs

  1. 一直保持最新的文档 Debian Linux Kernel Handbook

ak dotfiles

整理自己的dotfiles

  • 很早就有这个想法了, 可是自己太懒惰了, 另外, 公司的事情属实让人很蛋疼, 一直搁浅到现在. 为了弥补我丢失的时光, 要求自己以后每天最早10点下班. 晚上用来整理自己的文档和笔记.
  • 本来想法很粗糙, 后来想到通过网上的大家来学习, 发现了比自己整理自己的糟糕配置更有效的方法. GitHub真是个好东西. 我觉得GitHub是我们这代与上一代Programmer的明显差别的地方, 一定要好好利用.

dotfiles on GitHub

idgatt

  • idgatt: It’s Dangerous to Go Alone! Take This! 的首字母, 不得不对老外的思维差异感到震惊, 呵呵, 个人觉得很丑, 有空熟悉了github命令和规则后, 改下repo的名字吧: ak_dotfiles.
  • 简单的说这个就是一个自动化脚本, 帮助你管理oh my zsh 和 spf13-vim(呵呵, 刚巧我跟这位author同样的兴趣) idgatt

oh-my-zsh

  • zsh: 已经是MAC OS X的默认shell了, 比bash强大n倍, 兼容bash, linux默认bash坑了多少人啊(包括我). z代表最终的意思, 可见其完美程度. 我现在用的还不多, 但交互操作显示和自动提示功能明显很强大. 慢慢熟悉. oh-my-zsh

spf13-vim

  • spf13-vim: vim谁都知道, 那配置起来是相当的麻烦, 要至少花个半年时间吧, 这个配置有点缺点的, 一个是名字不太好看, 另外就是配置文件到处都是, 有点乱. spf13-vim

TODO

  • 软件列表怎么用github同步呢?
  • windows开发环境怎么记录呢?类似coolapk的应用集和豆瓣的豆列这种就好了.
  • windows下的Portableapps, 这个是必须的.可惜公司dropbox没有能穿透啊, 想把portableapps放到dropbox里面

Refs