
2009年4月28日
方法一:
注:
参考《
phoneme feature移植》一贴
1、编译脚本(MakefileFB):
export ME_ROOT=${HOME}/work/svn/phoneME
export COMPONENTS_DIR=$(ME_ROOT)
export Scripts=`pwd`
export Output=$(ME_ROOT)/outputfb
export Log=$(ME_ROOT)/log.txt
export JDK_DIR=/usr/local/jdk1.6.0_13
export GNU_TOOLS_DIR=/usr/crosstool/gcc-3.4.5-glibc-2.3.6/arm-linux/arm-linux
export DIRECTFB_INSTALL_DIR=/usr/local/directfb
export USE_DEBUG_ROMGEN=true
export USE_DEBUG_ROMGEN_SYMBOLS=true
export USE_JSR_293 = false
all : dump pcsl1s pcsl2s cldc midp
dump :
@echo "---------start build phoneME feature--------------------"
pcsl1s ::
@echo "---------build pcsl1s-----------------------------------"
make -C $(ME_ROOT)/pcsl \
PCSL_PLATFORM=linux_i386_gcc \
PCSL_OUTPUT_DIR=$(Output)/pcsl \
NETWORK_MODULE=bsd/generic \
TOOLS_DIR=$(ME_ROOT)/tools \
TOOLS_OUTPUT_DIR=$(Output)/tools
pcsl2s ::
@echo "---------build pcsl2s-----------------------------------"
make -C $(ME_ROOT)/pcsl \
PCSL_PLATFORM=linux_arm_gcc \
PCSL_OUTPUT_DIR=$(Output)/pcsl \
NETWORK_MODULE=bsd/generic \
TOOLS_DIR=$(ME_ROOT)/tools \
TOOLS_OUTPUT_DIR=$(Output)/tools \
cldc ::
@echo "---------build cldc-----------------------------------"
make -C $(ME_ROOT)/cldc/build/linux_arm \
JDK_DIR=$(JDK_DIR) \
ENABLE_PCSL=true \
PCSL_OUTPUT_DIR=$(Output)/pcsl \
JVMWorkSpace=$(ME_ROOT)/cldc \
JVMBuildSpace=$(Output)/cldc \
TOOLS_DIR=$(ME_ROOT)/tools \
TOOLS_OUTPUT_DIR=$(Output)/tools
midp ::
@echo "---------midp----------------------------------------"
make -C $(ME_ROOT)/midp/build/linux_fb_gcc \
GNU_TOOLS_DIR=$(GNU_TOOLS_DIR) \
JDK_DIR=$(JDK_DIR) \
PCSL_OUTPUT_DIR=$(Output)/pcsl \
CLDC_DIST_DIR=$(Output)/cldc/linux_arm/dist \
TOOLS_DIR=$(ME_ROOT)/tools \
TOOLS_OUTPUT_DIR=$(Output)/tools \
MIDP_OUTPUT_DIR=$(Output)/midp \
USE_DEBUG=true \
CPU=arm
clean: jclean pclean cclean mclean
jclean :
rm -rdf $(Output)/javacall
pclean :
rm -rdf $(Output)/pcsl1s
rm -rdf $(Output)/pcsl2s
cclean :
rm -rdf $(Output)/cldc
mclean :
rm -rdf $(Output)/midp
2、使用方法:
make clean -f MakefileFB
make -f MakefileFB
方法二:
1、编译脚本(build-phoneme):
#!/bin/bash
#==========================================================
# Figure out which component to build for which platform
#==========================================================
COMPONENT="none"
TARGET_PLATFORM="i386"
#CROSS_COMPILE=arm-none-linux-gnueabi-
if [ $# -gt 0 ]
then
COMPONENT=$1
fi
if [ $# -eq 2 ]
then
TARGET_PLATFORM=$2
fi
#==========================================================
# Setup some environment variables and create dirs.
#==========================================================
export MEHOME=${HOME}/work/svn/phoneME
export LOG=$MEHOME/log.txt
export BUILD_OUTPUT_DIR=$MEHOME/build-$TARGET_PLATFORM
export JDK_DIR=${HOME}/work/java/jdk1.6.0_13
#export GNU_TOOLS_DIR=${HOME}/work/toolchain/CodeSourcery/Sourcery_G++_Lite/arm-none-linux-gnueabi
if [ "$TARGET_PLATFORM" = "arm" ]
then
echo
echo "Building for ARM"
echo
export PCSL_PLATFORM=linux_arm_gcc
export GNU_TOOLS_DIR=${HOME}/work/toolchain/CodeSourcery/Sourcery_G++_Lite/arm-none-linux-gnueabi
CROSS_COMPILE=arm-none-linux-gnueabi-
else
echo
echo "Building for i386"
echo
export PCSL_PLATFORM=linux_i386_gcc
export GNU_TOOLS_DIR=/usr
CROSS_COMPILE=
fi
if ! [ -d $BUILD_OUTPUT_DIR ]
then
mkdir $BUILD_OUTPUT_DIR
fi
#==========================================================
# Build PCSL
#==========================================================
if [ "$COMPONENT" = "pcsl" ]
then
export PCSL_OUTPUT_DIR=$BUILD_OUTPUT_DIR/pcsl
cd $MEHOME/pcsl
if [ "$TARGET_PLATFORM" = "i386" ]
then
make NETWORK_MODULE=bsd/generic
else
make NETWORK_MODULE=bsd/generic \
CC='$(CROSS_COMPILE)gcc' \
CPP='$(CROSS_COMPILE)g++' \
AR='$(CROSS_COMPILE)ar cqs' \
LD='$(CROSS_COMPILE)ld'
fi
fi
#==========================================================
# Build PCSL documentation
#==========================================================
if [ "$COMPONENT" = "pcsl-doc" ]
then
cd $MEHOME/pcsl &&\
make doc
fi
#==========================================================
# Build CLDC
#==========================================================
if [ "$COMPONENT" = "cldc" ]
then
export JVMWorkSpace=$MEHOME/cldc
export JVMBuildSpace=$BUILD_OUTPUT_DIR/cldc
if [ "$TARGET_PLATFORM" = "i386" ]
then
cd $JVMWorkSpace/build/linux_i386
make ENABLE_PCSL=true \
ENABLE_ARM_V7=true \
PCSL_OUTPUT_DIR=$BUILD_OUTPUT_DIR/pcsl \
ENABLE_ISOLATES=true
else
cd $JVMWorkSpace/build/linux_arm
make ENABLE_PCSL=true \
PCSL_OUTPUT_DIR=$BUILD_OUTPUT_DIR/pcsl \
CC='$(CROSS_COMPILE)gcc' \
CPP='$(CROSS_COMPILE)g++' \
AR='$(CROSS_COMPILE)ar cqs' \
LD='$(CROSS_COMPILE)ld'
fi
fi
#==========================================================
# Build CLDC documentation
#==========================================================
if [ "$COMPONENT" = "cldc-doc" ]
then
export JVMWorkSpace=$MEHOME/cldc
cd $JVMWorkSpace/build/linux_i386
make docs_html
fi
#==========================================================
# Build MIDP
#==========================================================
if [ "$COMPONENT" = "midp" ]
then
export MIDP_OUTPUT_DIR=$BUILD_OUTPUT_DIR/midp
if [ "$TARGET_PLATFORM" = "i386" ]
then
cd $MEHOME/midp/build/linux_fb_gcc
make PCSL_OUTPUT_DIR=$BUILD_OUTPUT_DIR/pcsl \
CLDC_DIST_DIR=$BUILD_OUTPUT_DIR/cldc/linux_i386/dist \
TOOLS_DIR=$MEHOME/tools \
TARGET_CPU=i386 \
USE_MULTIPLE_ISOLATES=true
else
make PCSL_OUTPUT_DIR=$BUILD_OUTPUT_DIR/pcsl CLDC_DIST_DIR=$BUILD_OUTPUT_DIR/cldc/linux_arm/dist TOOLS_DIR=$MEHOME/tools TARGET_CPU=arm USE_MULTIPLE_ISOLATES=true CC='$(CROSS_COMPILE)gcc' CPP='$(CROSS_COMPILE)g++' AR='$(CROSS_COMPILE)ar cqs' LD='$(CROSS_COMPILE)ld'
fi
fi
echo
echo "======================================"
echo "FINISHED"
echo
2、使用方法:
./build-phoneme pcsl arm
./build-phoneme cldc arm
posted @
2009-04-28 16:42 lfc 阅读(190) |
评论 (0) |
编辑 收藏

2009年4月25日
1、qtopia-core-opensource-src-4.3.5:
./configure -embedded arm -xplatform qws/linux-arm-g++ -depths 4,8,16,32 -no-qt3support -prefix /home/luofc/work/qt/target -fast -qt-sql-sqlite -no-libtiff -no-libmng -qt-libjpeg -qt-zlib -qt-libpng -qt-freetype -optimized-qmake -no-nis -no-separate-debug-info -no-qvfb -qt-gfx-linuxfb -no-gfx-qvfb -qt-kbd-usb -no-kbd-qvfb -no-mouse-qvfb -no-mouse-linuxtp -qt-mouse-pc -qt-mouse-tslib -I/home/luofc/work/qt/tslib/include -L/home/luofc/work/qt/tslib/lib
2、qtopia-opensource-4.2.4
../source/qtopia-opensource-4.2.4/configure -debug -image /opt/qtopia -prefix /opt/qtopia -xplatform linux-arm-g++ -arch arm -no-qvfb -displaysize 480x272 -no-modem -no-bluetooth -no-infrared -extra-qtopiacore-config "-debug -xplatform qws/linux-arm-g++ -embedded arm -depths 4,8,16,32 -no-qt3support -fast -qt-sql-sqlite -no-libmng -qt-libjpeg -qt-zlib -qt-libpng -qt-freetype -no-nis -no-separate-debug-info -no-qvfb -qt-gfx-linuxfb -no-gfx-qvfb -qt-kbd-usb -no-kbd-qvfb -no-mouse-qvfb -no-mouse-linuxtp -qt-mouse-pc -qt-mouse-tslib -I/home/lfc/work/qt/tslib/include -L/home/lfc/work/qt/tslib/lib" 2>configure_error.log
后注:
使用arm-none-linux-gnueabi的编译器,qtopia-core-opensource编译后使用正常,qtopia-opensource编译成release的话,部分程序(包括qpe)运行的时候提示Segmentation fault错误,如果编译成debug的话,运行还算正常,可是要占用226M的空间,真是无语~
posted @
2009-04-25 11:44 lfc 阅读(222) |
评论 (2) |
编辑 收藏

2009年4月23日
ARM平台上蓝牙协议栈Bluez的移植使用和配置
作者:刘旭晖 Raymond转载请注明出处
Email:colorant@163.com
BLOG:http://blog.csdn.net/colorant/
主页:http://rgbbones.googlepages.com/
Bluez作为当前最成熟的开源蓝牙协议栈,在Linux的各大发行版中已经得到了广泛的应用。在桌面环境下,使用Bluez应该已经没有太大的
问题,本文的主要目的是介绍在嵌入式平台上,搭建和配置Bluez的各个Profile运行所需做的工作,讨论可能遇到的问题,介绍一些工具的使用和工作
原理。因为本人的能力和测试时间有限,可能下文中有些理解、分析不一定准确,欢迎联系指正。
1 相关说明
1.1 网站资源
Bluez的官方网址:http://www.bluez.org/ 这里提供最新的源码下载,最近服务器崩溃了一次,有些东西没了。。。。
Bluez的Wiki:http://wiki.bluez.org/wiki/ 这里提供Bluez相关的Howto等文档资源
相关邮件列表:
https://lists.sourceforge.net/lists/listinfo/bluez-users 关于如何使用和配置Bluez,多数是在讨论PC环境下的问题。。。。
https://lists.sourceforge.net/lists/listinfo/bluez-devel Bluez开发者活动的地方,有什么Bug之类的怀疑,还有编程接口之类的问题,就发到这里吧。
1.2 工作环境
个人感觉,使用Bluez最大的问题就是文档的欠缺,除了Wiki上的有限资料以外,很难找到其它有用的文档。
由于Bluez的代码实现更新变化得很快,网上的许多文档介绍的都是早期版本的使用,再有的文章多数是基于成熟的Linux发行版,来讨论蓝牙设备的配置和使用,对于嵌入系统开发,自己编译,搭建和配置相关环境的文章很少。此外和具体蓝牙芯片相关的资料也很难找到。
这里我不打算也没有能力写一个完整的指南,只能基于前段时间在自己的板子上所做的工作,总结一下相关的步骤和所遇到的各类问题以及这期间所掌握的各种相关知识。希望能给有类似开发需求的朋友一些有益的帮助,下面是这篇文章所基于的工作环境:
硬件平台:基于ARM的嵌入式板子
蓝牙芯片:CSR BC4 ROM 版本芯片,不带eeprom
软件环境:Linux 2.6.21 ,自制文件系统
Bluez版本:bluez-libs 3.22 bluez-utils 3.22
2 编译
2.1 内核
相信多数人使用的都是2.6的内核了,在2.6的内核中要支持Bluez,只要你的内核版本不是太旧,无需打Patch,直接配置好就OK了,内
核里面的代码相对比较稳定了。当然,Bluez对一些Bluetooth协议栈新特性的支持,还是需要更新kernel代码的。你应该确认你使用的
kernel版本是否以及包含了对应的支持。
内核的配置,基本上把 networking下 --- Bluetooth subsystem support 里的以下几项全部选上即可:
L2CAP protocol support
SCO links support
RFCOMM protocol support
RFCOMM TTY support
BNEP protocol support
HIDP protocol support
此外,在Bluetooth device drivers里选上你所需要支持的Bluetooth设备。我使用的CSR的chip是我们直接build在板子上,通过串口和cpu通讯的,芯片默认使用BCSP作为通讯协议,所以我选择了:
HCI UART driver
BCSP protocol support
如果你是通过usb接口使用蓝牙适配器,需要选择
HCI USB driver
2.2 Bluez Lib / Utils
Bluez Lib的编译比较简单,而Bluez-Utils所依赖的库就比较多了,大体包括 dbus alsa hal gstreamer openobex xml等等,仔细观察./configure 的输出,将所需要的包先安装或者build好。
值得注意的一点是:
如果你需要打开所有的功能模块的支持,需要在 ./configure 参数中添加 --enable-all --enable-audio
--enable-input --enable-network –enable-serial 等,在3.22版本中 --enable-all
居然不包括
audio等相关模块的service的编译,不知道是否是因为还保留了daemon和service等不同方案的缘故。不过,这至少与他的
configure --help 对于 --enable-all 的描述是不符合的。
3 蓝牙硬件初始化及基础服务启动
如果在PC环境下,使用Ubuntu,调用 /etc/init.d/bluetooth start 应该就能完成这一步的工作了。下面叙述一下在我的嵌入式环境下,如何手动完成这一步骤。
3.1 何谓硬件初始化
硬件初始化,指的是配置蓝牙芯片,将其置于一个能够正常通讯的状态。
对于CSR的芯片来说,就是通过设置PSKEY,设置其晶振频率,UART波特率等等一些关键参数。
如果使用的是USB形式的适配器,因为其EEPROM存储了相关的默认参数,这一步很可能不需要做,而我使用的是不带EEPROM的ROM版本芯片,如何
正确完成初始化工作着实让我折腾了一阵。
对于其它芯片,没有太多研究,不过,据我有限的了解,TI的芯片在hciattach时也需要完成一些额外的初始化工作,其它如ST的芯片则可能需要下载firmware。
3.2 硬件初始化步骤
通常蓝牙芯片的初始化和协议绑定可以通过 hciattach 来完成(通过配置bluez的启动脚本,可以不需要使用hciattach,标准发行版应该都是不用hciattach,如何配置,还没有研究 。。。 8 )
Hciattach 需要的参数主要包括 TTY节点,设备类型,波特率等。多数类型的设备的初始化工作,在选择了正确的设备类型参数后,都由hciattach在init_uart函数中调用具体的初始化函数所完成。
很遗憾的是,因为要重新设置晶振频率和波特率,并同步BCSP协议,这种方式好像处理不了我所使用的芯片(不排除我没有找到正确的解决方案的可能
性),我最终的解决办法是在hciattach之前,使用Bluez-utils里的BCCMD工具先完成这些PSKEY的设置工作。
具体命令是:
bccmd -t bcsp -d /dev/ttyS1 psload -r csr.psr
在这时,由于HCI接口还没有启动,所以只能使用BCSP协议来进行通讯,我的设备是暴露在ttyS1下,你的可能不一样,-r参数指明在psload完成 PSKEY的批量加载操作之后,对芯片进行Warmreset,否则这些参数的修改不会起作用。
Csr.psr的内容取决与你的芯片,我的大致如下:
// PSKEY_ANA_FREQ
&01fe = 9C40 // 相当于40M的晶振
// PSKEY_UART_BAUD_RATE
&01be = 0EBF // 921600的波特率
// PSKEY_UART_SEQ_WINSIZE
&0407 = 0006
// BDADDR
&0001 = 1122 3344 5566 7788
。。。
这里有个问题,你会发现,通过bccmd -t bcsp psset
命令理论上应该是可以单步设置每一个PSKEY的,但是从我实践看来,单步的操作在两次对bccmd的调用过程中,上一次对PSKEY的修改,都会在下一
次调用之前被复位,从代码上看估计和BCSP协议的同步过程有关。
3.2.1 关于PSKEY的获取
如何获得正确的完整的PSKEY参数,大概会有几个途径:
通过CSR的网站下载boot_strap包,这是CSR自己的BCHS协议栈所使用的初始化代码,在里面找到你所需要的pskey值。
下载CSR的bluesuite工具,里面包含了一个叫pstool的工具,可以用它来读写CSR的Casira开发板或其它BT设备的PSKEY设置,试验并找出你能用的参数。
找CSR或模组厂商支持 8 )
不过,基本上来说,如果只是要让芯片通过串口能够和Bluez协议栈正常通讯上,只需要设置PSKEY_ANA_FREQ 和 PSKEY_UART_BAUD_RATE 这两个PSKEY就可以了。
3.3 Daemon进程的启动
早先的版本里,Bluez的Daemon很多,但是最近的版本,很多daemon都转为service的形式来做了,3.22 里面包括了以下这几个Service,其它profile貌似还保留着daemon的形式。
bluetoothd-service-serial
bluetoothd-service-network
bluetoothd-service-audio
bluetoothd-service-input
这几个Service的启动依赖于hcid的启动以及相关的配置文件
主要配置文件位于:/etc/bluetooth/
此外,通常还需要启动SDP来提供服务查询,另外,Bluez本身还依赖于Dbus daemon的运行。
所以,整体上来说,我的手动启动Bluez的全过程如下:(其中内核代码是以模块形式编译的)
insmod bluetooth.ko
insmod hci_uart.ko
insmod l2cap.ko
insmod rfcomm.ko
insmod sco.ko
insmod hidp.ko
/etc/rc2.d/S20dbus start
bccmd -t bcsp -d /dev/ttyS1 psload -r csr.psr
hciattach -s 921600 /dev/ttyS1 bcsp 921600
hciconfig hci0 up
sdpd
hcid –d
4 Paring配对
4.1 Passkey_agent
在正常使用一个蓝牙设备前,通常都需要对该设备进行配对绑定的操作。
Bluez的配对机制貌似也修改了几次,2.x版本中通过pin_helper来处理pin
code的应答,3.22版本里使用的配对机制,其API是基于Dbus来实现的,需要向dbus注册一个agent,PC的发行版通常都会有一些基于各
种图形库的passkey_agent,对于嵌入式系统,这部分代码可以想象,应该是要按照相应的API自己实现一个,为了测试,我直接使用了
bluez-utils/daemon 目录下的passkey-agent
这是一个命令行下的可以使用预先设定的pin code进行配对的程序
为了使用它,我的文件系统里 /etc/Bluetooth/hcid.conf 中 option一节类似如下 :
# HCId options
options {
# Automatically initialize new devices
autoinit yes;
# Security Manager mode
# none - Security manager disabled
# auto - Use local PIN for incoming connections
# user - Always ask user for a PIN
#
security user;
# Pairing mode
pairing multi;
# Do the same as "hciconfig hci0 down" when SetMode("off")
# is called.
offmode devdown;
# Default PIN code for incoming connections
passkey "1234";
}
4.2 关于自动配对和请求的发起
配对的发起,这里主要是从请求的发起者是谁的角度来说。
通常可能不需要关心配对请求是由本地还是由远端发起的,使用passkey_agent都能够正确处理。
不过如果在hcid.conf中将 Security Manager mode 设置为
auto,则Bluez会将passkey后面的字符串作为默认的Pin
code,自动答复远端发起的配对请求。这是在没有使用passkey_agent的情况下的一种配对方式。
在这种情况下,Bluez可以处理远端的配对请求,但是对于本地发起的配对请求,将无法正确处理,我没有仔细的分析原因,或许是代码特意设计成这
种工作方式。所以在无法明确知道谁将会主动先发起配对请求的情况下,使用Atuo模式,可能就会出现有些时候设备能绑定有些时候不能绑定的现象。
通常如果是由本地设备搜索发现的新设备,配对绑定的操作应该也是由本地发起。
另外可以观察到,对远端一个非PC类的蓝牙设备,如蓝牙耳机,如果上次绑定过,在耳机启动时会主动发起连接请求,如果本地的link key丢失了,也就会再走一次绑定的流程,这种情况下配对请求就是由远端设备发起的。
5 A2DP
A2DP蓝牙立体声应该是蓝牙最常见的Profile之一。
2.x版本的Bluez,对A2DP的支持是通过BTSCO来实现的,3.22的版本通过bluetoothd-service-audio来支持。
对Bluez A2DP profile的支持,还依赖于Alsa或Gstreamer。
5.1 配置
测试A2DP的时候,我使用的是aplay,同时在相关的配置文件里面写死了蓝牙耳机的地址
主要的配置文件包括:
/etc/asound.conf :
pcm.bluetooth{
type bluetooth
device 00:02:5B:00:C1:A0
profile "hifi"
}
/etc/bluetooth/audio.conf :
[General]
# disable=Sink
SCORouting=PCM
[Headset]
DisableHFP=true
[A2DP]
SourceCount=2
配置好这些以后,使用 aplay -D bluetooth sample.wav 进行测试。
值得注意的是,使用Aplay打开蓝牙设备进行播放,需要有如下两个Alsa的plugin:
/usr/lib/alsa-lib/libasound_module_pcm_bluetooth.so
/usr/lib/alsa-lib/libasound_module_ctl_bluetooth.so
这两个so文件可以在bluez-utils 里面找到。需要注意他们和libasound 的版本匹配。
5.2 问题
在测试中发现,如果连接的耳机是由PC上的蓝牙适配器提供的AV耳机服务,那么配对可以完成,但是连接会失败,真正的耳机则没有这个问题,不知道
是否是因为以上的方法还存在缺陷?尝试使用由DBUS发起命令的形式来连接PC的AV耳机服务,也是一样的问题。是否连接PC模拟的AV耳机服务,首先要
切换设备的role?
没有测试Ctl接口
如何动态选择不同的耳机,而不是写死在脚本里?(这个想来估计是要自己基于Alsa的API编程处理,aplay无法直接完成测试)
播放大文件会出现under run错误,需要测试是由与波特率设置不够高造成,还是SBC编码效率不够,还是这个版本里存在的bug。
6 DUN的使用
Dun profile运行于rfcomm之上,主要是通过蓝牙接口暴露一个Modem的接口,用于提供拨号上网服务。
在这里所讨论的不是提供拨号上网服务本身,而是使用外部设备所提供的这个服务,进行网络连接。
6.1 系统配置
通常为了使用DUN,或者任何一个其它类型的Modem,我们会通过PPP协议来拨号和建立网络连接。
首先需要内核的支持,可以简单的把 device drivers -> Network device support 下面的PPP相关的内容全部选上。
其次,要编译应用层的PPP包,我的测试是基于ppp-2.4.4
主要的两个ppp配置文件:
/etc/ppp/peers/gprs:
/dev/rfcomm0
115200
defaultroute
usepeerdns
nodetach
noauth
local
debug
connect "/usr/sbin/chat -v -f /etc/ppp/chat-gprs"
/etc/ppp/chat-gprs:
TIMEOUT 10
ABORT 'BUSY'
ABORT 'NO ANSWER'
ABORT 'ERROR'
"" 'ATZ'
SAY 'Init....\n'
OK 'AT+CGDCONT=1,"IP","CMWAP"'
ABORT 'NO CARRIER'
SAY 'Dialing....\n'
OK 'ATD*99***1#'
CONNECT ''
前面一个配置文件基本就是那样了,后面一个,这两句要根据你的SIM卡的实际情况来处理:
OK 'AT+CGDCONT=1,"IP","CMWAP"'
OK 'ATD*99***1#'
这里我设置的是使用中国移动的CMWAP。
此外,使用CMWAP还需要设置你的浏览器的代理服务器:10.0.0.172 端口9201
6.2 连接步骤
首先是要查找提供dun服务的设备,将服务提供在哪个channel通道上。这可以通过sdptool来查看,在我的设备上查询的结果是在channel 1上:
~ # ./sdptool browse 00:08:C6:77:A0:6C
Browsing 00:08:C6:77:A0:6C ...
Service Name: Dial-upnetworking
Service RecHandle: 0x10000
Service Class ID List:
"Dialup Networking" (0x1103)
"Generic Networking" (0x1201)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 1
Profile Descriptor List:
"Dialup Networking" (0x1103)
Version: 0x0100
其次,如果rfcomm所需设备节点不存在,将其创建:
mknod -m 666 /dev/rfcomm0 c 216 0
然后就是拨号了,如果该设备之前没有绑定,这过程中会自动发起绑定操作:
pppd debug dump call gprs &
完成以后就可以看到 ppp0这样一个网络接口了。
~ # ifconfig
ppp0 Link encap:Point-to-Point Protocol
inet addr:10.154.76.82 P-t-P:192.200.1.21 Mask:255.255.255.255
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1500 Metric:1
RX packets:4 errors:0 dropped:0 overruns:0 frame:0
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:3
RX bytes:64 (64.0 B) TX bytes:101 (101.0 B)
7 Bluez相关的各种tools的使用
在这一段折腾Bluez的时间里,越来越发现Bluez相关的许多工具做得还是挺好用的,主要在Bluez-utils/tools 目录下。只是有一点让我很遗憾,除了man以外很难找到更多的帮助文档,而man文档本身对一些功能的描述也不是很详细。
其中有些选项,如果你不了解蓝牙协议栈,或者没有查阅过相关蓝牙芯片的一些文档,很难搞明白是什么意思,甚至有些选项的具体参数值的设定,如果不读源码你都无从得知有哪些备选值。。。。
能力有限,下面所写的只是我所用过的有限的几个工具的一些使用经验,希望能有所帮助。
7.1 Bccmd
Bccmd是用来和CSR的芯片进行BCCMD(Bluecore command protocol)通讯的一个工具。BCCMD并非蓝牙协议栈的标准,而是CSR芯片的专属协议
Bccmd的调用格式为:bccmd [-t <transport>] [-d <device>] <command> [<args>]
Tansport类型包括 HCI USB BCSP H4等,常用的估计就是HCI和BCSP两种。需要注意一下他们的使用场合:
HCI是一个抽象的标准的蓝牙通讯接口,在基于HCI协议调用BCCMD时,需要在Bluez已经建立好hci接口的基础上使用。
BCSP(Bluecore Serial
Protocol)是CSR自己制定的传输层协议,主要目的是用来加强在没有使用CTS、RTS进行流量控制的情况下进行可靠的数据传输的能力。其概念是
相对H3 , H4而言,( 具体分析,请参考下面杂项一章中相应的小节 )
BCCMD的主要用途就是用来读写pskey,这里以 psset 这个command来介绍一下格式:
Psset 格式如下: psset [-r] [-s <stores>] <key> <value>
其它都好理解,关键是-s参数之后跟的store具体的含义。这个参数可以是数值也可以是字符串
查询CSR的BCCMD相关的文档,可以找到具体的含义如下:
0x0000 Default
0x0008 psram
0x0001 psi
0x0002 psf
0x0004 psrom
0x0003 psi then psf
0x0007 psi, psf then psrom
0x0009 psram then psi
0x000b psram, psi then psf
0x000f psram, psi, psf then psrom
CSR的蓝牙芯片中,PSKEY可能存储在 rom flash eeprom
ram等介质里,这里的数值指明了psset/get命令操作PSKEY时所针对的存储介质及其优先顺序,通常我们会用 –s 0x0 或 –s
“default” 来使用该命令,0x0的含义与0xf一样。
值得注意的是,哪个参数是有效的,还取决于哪一类的存储介质实际存在于蓝牙芯片中,此外,只读类的介质对写操作类的命令也是无效的。
基本上来说,所修改的都是位于psram中的pskey,此外,pskey修改以后要起作用,还要一并使用 –r参数,或直接用warmreset命令将蓝牙芯片进行warm reset。
7.2 Hciattach
Hciattach主要用来初始化蓝牙设备,它的命令格式如下:
hciattach [-n] [-p] [-b] [-t timeout] [-s initial_speed] <tty> <type | id> [speed] [flow|noflow] [bdaddr]
其中最重要的参数就是 type和speed,type决定了要初始化的设备的型号,可以使用 hciattach –l 来列出所支持的设备型号。
并不是所有的参数对所有的设备都是适用的,有些设备会忽略一些参数设置,例如:查看hciattach的代码就可以看到,多数设备都忽略bdaddr参数。
Hciattach命令内部的工作步骤是:首先打开制定的tty设备,然后做一些通用的设置,如flow等,然后设置波特率为
initial_speed,然后根据type调用各自的初始化代码,最后将波特率重新设置为speed。所以调用hciattach时,要根据你的实际
情况,设置好initial_speed和speed。
对于type BCSP来说,它的初始化代码只做了一件事,就是完成BCSP协议的同步操作,它并不对蓝牙芯片做任何的pskey的设置。同步操作的具体流程和规范可以参考CSR的相关文档: BCSP Link Establishment Protocol
7.3 其它
下面几个,使用了,但是没有太多研究
7.3.1 Hcidump
Hcidump不在bluez-utils包里,而是在单独的hcidump包里。主要用来分析捕获和分析HCI数据包,如果使用bluez过程
中出了什么问题,用hcidump往往可以发现一些出错的线索,原因。 参数很多,基本上hcidump –X –V
就可以帮你获得详细的经过格式解析的数据包。
7.3.2 Hcitool
主要用hcitool来scan远端的设备,显示设备地址,名称等。
例如:Hcitool scan, hcitool inq
7.3.3 Sdptool
主要用来浏览远端设备SDP服务,或者管理本地的SDPD维护的数据库。
常用的应该就是查找远端设备的服务了
例如:
sdptool browse 00:02:72:B0:00:26 浏览地址为00:02:72:B0:00:26的设备所提供的服务
sdptool search 0x1112 00:02:72:B0:00:26 查找地址为00:02:72:B0:00:26的设备上的Headset Audio Gateway服务。
./sdptool search 0x1112 00:02:72:B0:00:26
Class 0x1112
Inquiring ...
Searching for 0x1112 on 00:02:72:B0:00:26 ...
Service Name: Headset Audio Gateway
Service RecHandle: 0x1001d
Service Class ID List:
"Headset Audio Gateway" (0x1112)
"Generic Audio" (0x1203)
。。。
7.3.4 Hciconfig
这个就不用多说了,格式上很类似于ifconfig,用来设置HCI设备的参数
例如
hciconfig hci0 up 启动hci0接口
hciconfig hci0 iscan 使能位于hci0接口的蓝牙芯片的inquery scan模式(使得设备能被其它蓝牙设备发现)
8 杂项
8.1 使用Dbus-send进行测试
由于Bluez使用dbus进行进程间通讯,所以我们可以使用dbus-send命令直接发送命令进行一些查询,试验的工作。
Bluez每个Daemon或service所支持的Dbus接口API描述文本,可以在各自的目录下找到,例如Audio的API写在 audio/audio-api.txt中。
以Audio为例,可以参考
http://wiki.bluez.org/wiki/HOWTO/AudioDevices 中的描述
8.2 HCI、H4、USB、BCSP 之间的关系
个人理解,严格的说HCI和其它几种protocol并不是可以对比的同一层次的东西。
HCI protocol
并不考虑在实际传输载体以及其中的纠错等问题,只是一个抽象的传输层或叫做接口。USB,H3,H4等才是具体的transport
layer(此外还有SD Transport layer)。HCI数据包需要附着在这些具体的Transport Layer的协议包中。
以BCSP为例,4种类型的HCI数据包各自使用了一个BCSP通道,做为这些通道的payload封装在BCSP的协议包里,需要通过TTY
的lldsic层走一次,并由hci_uart模块做相应的封装工作。而BCSP还通过其它通道支持其它的一些自定的Protocol。BCSP作为一个
具体的传输层协议,还支持了校验,同步等功能。
H4机制类似,SD和USB transport好像区别比较大一点。具体可以参考 Bluetooth Specification Volume 4.
8.3 BCSP数据包结构
HCI数据包的结构,在bluetooth的spec里面有详细定义,不过,CSR自己的BCSP,BCCMD等一系列协议,又添加了一堆的东
西,其中,HCI数据包是作为BCSP的payload,而BCCMD又是作为HCI的payload,所以测试过程中,发觉要分析清楚bluez通过
kernel最后到底往蓝牙芯片的串口发送了什么数据,特别是想要自己手工构建一串数据,着实要看上一堆spec,拼凑起来才能完成。
要具体学习分析一串命令,最好的办法,我能想到的就是修改bccmd的代码,将它传给串口的每一个字符串都打印出来,这样对照这spec看,事半功倍。
例如下面这条,是使用我修改后的bccmd指令。所做的操作是读取串口波特率的pskey:
./bccmd.dbg -t bcsp -d /dev/ttyS1 psget -s 0x0 0x01be
cmd : 00 fc 13 c2 00 00 09 00 01 00 03 70 00 00 be 01 01 00 00 00 00 00
c0 d1 65 01 c8 00 fc 13 c2 00 00 09 00 01 00 03 70 00 00 be 01 01 00 00 00 00 69 a6 c0
在这里 HCI的数据包是第一行,具体解释一下:
头4个字节是HCI Head,其中
00 fc :整体代表这是一个制造商自定义的命令。
13 :HCI命令长度为0x13。
C2 :包的内容是唯一的一个BCCMD数据包。
后面是BCCMD的Head
00 00 :这是一个GetReq命令
09 00 :BCCMD的命令9个word长度,及18字节
01 00 :seqno, 包的顺序标记 包1
03 70 :varid 7003, 表示这是对PSKEY的操作
00 00 :状态标志
再下来是BCCMD的payload
be 01 :0x01be 波特率PSKEY的index
01 00 :该PSKEY的长度为1
00 00 :strore 为 00
00 00 :该PSKEY的值,这里是发送读命令,所以填0
第二行的数据是将HCI包封装在了BCSP数据包里:
前面部分:c0 d1 65 01 c8 :
C0 :是BCSP数据包的分割符
D1 :类型为可靠链接数据流,有CRC校验
65 01 :channel 5 ( HCI CMD ), 长度为0x16
C8 :包头的校验
后面部分:69 a6 c0:
69 a6 :整个BCSP包的CRC校验
C0 :分隔符
其它命令类似的分析可得。
如果只是希望看到HCI命令本身,也可以用hcidump来看。这是上面的读pskey操作通过HCI接口操作的dump:
< HCI Command: Vendor (0x3f|0x0000) plen 19
BCCMD: Get req: len 9 seqno 1 varid 0x7003 status 0
PSKEY: key 0x01be len 1 stores 0x0000
UART_BAUDRATE: value 0 (0x0000)
8.4 Hid / Serial / HF / OBEX
这几个比较常用的profile,还没测试哪。。。。。。谁给我买个蓝牙鼠标玩玩?!
8.5 总的遗留问题
整体上,PC上实现的自动识别,自动启动服务的一整套脚本,还没有仔细研究。
posted @
2009-04-23 11:57 lfc 阅读(394) |
评论 (0) |
编辑 收藏

2009年3月15日
alsa-lib:
./configure --host=arm-linux --prefix=/usr/local/arm/3.4.5/arm-linux --datadir=/usr/share
make
make install
alsa-utils:
./configure --host=arm-linux --prefix=/home/lfc/work/alsa/target
make
make install
posted @
2009-03-15 10:11 lfc 阅读(208) |
评论 (0) |
编辑 收藏

2009年3月11日
第一、什么是ubifs?由IBM、nokia工程师Thomas Gleixner,Artem Bityutskiy等人于2006年发起,致力于开发性能卓越、扩展性高的FLASH专用文件系统,以解决当前嵌入式环境下以FLASH作为MTD设备使用时的技术瓶颈。
关键字:
UBI:一种类似于LVM的逻辑卷管理层。主要实现损益均衡,逻辑擦除块、卷管理,坏块管理等。
UBIFS:基于UBI的FLASH日志文件系统。
有关ubifs的详细介绍,请参考:
http://www.linux-mtd.infradead.org/doc/ubi.html
http://www.linux-mtd.infradead.org/doc/ubifs.html
第二、为何使用ubifs?第三、如何得到ubifs?2.6.22以后,ubifs活跃于git管理工程中:
git://git.infradead.org/ubi-2.6.git
2.6.27以后,ubifs被整合进内核树中,用户只需下载最新内核即可获取ubifs支持。
第四、如何使用ubifs?软件环境:
linux-2.6.28
arm-linux-gcc 3.4.5
硬件环境:
s3c2410
k9f1208
一、准备1、内核
配置的时候选上
1)Device Drivers --->Memory Technology Device (MTD) support --->UBI - Unsorted block images --->Enable UBI
2)File systems --->Miscellaneous filesystems --->UBIFS file system support
2、mtd-utils工具(flash_eraseall、ubiattach、ubimkvol)准备
1)下载(mtd-utils、zlib、lzo)源码
wget http://debian.mirror.inra.fr/debian/pool/main/m/mtd-utils/mtd-utils_20080508.orig.tar.gz
wget http://www.zlib.net/zlib-1.2.3.tar.gz
wget http://www.oberhumer.com/opensource/lzo/download/lzo-2.03.tar.gz
2)编译安装zlib
tar xzvf zlib-1.2.3.tar.gz
cd zlib-1.2.3
CC=arm-linux-gcc ./configure --shared --prefix=/usr/local/arm/3.4.5/arm-linux
make
make install
cd ..
3)编译安装lzo
tar xzvf lzo-2.03.tar.gz
cd lzo-2.03
CC=arm-linux-gcc ./configure --host=arm-linux --prefix=/usr/local/arm/3.4.5/arm-linux
make
make install
cd ..
4)编译mtd-utils
tar xzvf mtd-utils_20080508.orig.tar.gz
cd mtd-utils-20080508
修改Makefile文件:
#CROSS=arm-linux-
修改为 CROSS=arm-linux-
BUILDDIR := $(CROSS:-=)
修改为 BUILDDIR := .
修改ubi-utils/Makefile文件:
添加 CROSS=arm-linux-
修改 ubi-utils/new-utils/Makefile文件:
添加 CROSS=arm-linux-
make WITHOUT_XATTR=1
ubi-utils子目录下生成我们需要的ubiattach、ubimkvol等文件(请确保是交叉编译所得)
3、mtd-utils工具(mkfs.ubifs、ubinize)准备
git-clone git://git.infradead.org/mtd-utils.git
cd mtd-utils/
make
mkfs.ubifs子目录下生成我们需要的mkfs.ubifs工具
ubi-utils/new-utils子目录下生成我们需要的ubinize工具
二、使用1、使用ramfs或nfs启动系统,执行以下命令挂载ubifs:
1)flash_eraseall /dev/mtd4
2)ubiattach /dev/ubi_ctrl -m 4
3)ubimkvol /dev/ubi0 -N rootfs -s 50MiB
4)mount -t ubifs ubi0_0 /mnt或mount -t ubifs ubi0:rootfs /mnt
2、如果你想使用ubifs为rootfs,把文件系统内容解压到ubifs挂载目录下,并修改内核启动参数为:
console=ttySAC0 ubi.mtd=4 root=ubi0:rootfs rootfstype=ubifs
3、如果你想直接在bootloader下烧写ubifs映像,使用以下命令制作ubi烧写映像:
mkfs.ubifs -r rootfs -m 512 -e 15872 -c 3303 -o ubifs.img
ubinize -o ubi.img -m 512 -p 16KiB -s 256 ubinize.cfg
其中:
1)以上命令的参数可从ubifs挂载信息中提取:
UBI: attaching mtd4 to ubi0
UBI: physical eraseblock size: 16384 bytes (16 KiB)
UBI: logical eraseblock size: 15872 bytes
UBI: smallest flash I/O unit: 512
UBI: sub-page size: 256
UBI: VID header offset: 256 (aligned 256)
UBI: data offset: 512
UBI: attached mtd4 to ubi0
2)配置文件ubinize.cfg的内容为:
[ubifs]
mode=ubi
image=ubifs.img
vol_id=0
vol_size=50MiB
vol_type=dynamic
vol_name=rootfs
vol_flags=autoresize
注:
其他nand flash下ubifs的使用方法类似(参数不同)。
posted @
2009-03-11 01:00 lfc 阅读(620) |
评论 (2) |
编辑 收藏

2008年10月7日
摘要: 第十四章 Linux终端设备驱动
本章导读
在Linux系统中,终端设备非常重要,没有终端设备,系统将无法向用户反馈信息,Linux中包含控制台、串口和伪终端3类终端设备。 14.1节阐述了终端设备的概念及分类,14.2节给出了Linux终端设备驱动的框架结构,重点描述tty_driver结构体及其成员。14.3~14.5节在14.2节的基础上,分别给出了Linux终端设备驱动模块...
阅读全文
posted @
2008-10-07 11:50 lfc 阅读(545) |
评论 (0) |
编辑 收藏

2008年10月5日
了解了i2c总线的主要结构成员及适配器、设备驱动的注册后,现在我们从上而下的来研究一下i2c总线的使用(仍然以i2c-dev.c为例):
1、这是面向用户的虚拟字符设备所提供的所有i2c总线操作接口函数:
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.ioctl = i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
1)drivers/i2c/i2c-dev.c
static int i2cdev_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct i2c_client *client;
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
i2c_dev = i2c_dev_get_by_minor(minor); //通过设备文件的从设备号查找对应的i2c_dev
if (!i2c_dev)
return -ENODEV;
adap = i2c_get_adapter(i2c_dev->adap->nr); //查找对于的adap
if (!adap)
return -ENODEV;
client = kzalloc(sizeof(*client), GFP_KERNEL); //i2c从设备描述结构体
if (!client) {
i2c_put_adapter(adap);
return -ENOMEM;
}
snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
client->driver = &i2cdev_driver;
/* registered with adapter, passed as client to user */
client->adapter = adap;
file->private_data = client;
return 0;
}
注:
i2cdev_open的主要作用是构建并初始化用于描述i2c从设备的结构体struct i2c_client。
2)drivers/i2c/i2c-dev.c
i2cdev_read
-> i2c_master_recv
-> i2c_transfer
-> adap->algo->master_xfer(s3c24xx_i2c_xfer)
3)drivers/i2c/i2c-dev.c
i2cdev_write
-> i2c_master_send
-> i2c_transfer
-> adap->algo->master_xfer(s3c24xx_i2c_xfer)
7)drivers/i2c/busses/i2c-s3c2410.c
s3c24xx_i2c_xfer
-> s3c24xx_i2c_doxfer(wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5)) <----|
-> s3c24xx_i2c_irq |
-> i2s_s3c_irq_nextbyte |
-> s3c24xx_i2c_stop |
-> s3c24xx_i2c_master_complete(wake_up(&i2c->wait))------------------|
posted @
2008-10-05 23:44 lfc 阅读(332) |
评论 (0) |
编辑 收藏
1、总线适配器注册:
1)drivers/i2c/i2c-core.c
int i2c_add_adapter(struct i2c_adapter *adapter)
{
int id, res = 0;
retry:
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
return -ENOMEM;
mutex_lock(&core_lists);
/* "above" here means "above or equal to", sigh */
res = idr_get_new_above(&i2c_adapter_idr, adapter,
__i2c_first_dynamic_bus_num, &id);
mutex_unlock(&core_lists);
if (res < 0) {
if (res == -EAGAIN)
goto retry;
return res;
}
adapter->nr = id; //使用动态的总线号来标识总线适配器。
return i2c_register_adapter(adapter);
}
EXPORT_SYMBOL(i2c_add_adapter);
2)drivers/i2c/i2c-core.c
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0;
struct list_head *item;
struct i2c_driver *driver;
mutex_init(&adap->bus_lock); //初始化总线访问控制变量(总线上数据传输时使用)
mutex_init(&adap->clist_lock); //初始化客户端访问控制变量(操作客户端结构时使用)
INIT_LIST_HEAD(&adap->clients); //初始化客户端链表头
mutex_lock(&core_lists);
list_add_tail(&adap->list, &adapters); //添加到总线适配器链表中
/* Add the adapter to the driver core.
* If the parent pointer is not set up,
* we add this adapter to the host bus.
*/
if (adap->dev.parent == NULL) {
adap->dev.parent = &platform_bus;
pr_debug("I2C adapter driver [%s] forgot to specify "
"physical device\n", adap->name);
}
sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
adap->dev.release = &i2c_adapter_dev_release;
adap->dev.class = &i2c_adapter_class;
res = device_register(&adap->dev); //注册设备
if (res)
goto out_list;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
/* create pre-declared device nodes for new-style drivers */
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
/* let legacy drivers scan this bus for matching devices */
list_for_each(item,&drivers) { //搜索总线上的所有设备驱动,通过调用其attach_adapter接口函数,查找匹配的设备。
driver = list_entry(item, struct i2c_driver, list);
if (driver->attach_adapter)
/* We ignore the return code; if it fails, too bad */
driver->attach_adapter(adap);
}
out_unlock:
mutex_unlock(&core_lists);
return res;
out_list:
list_del(&adap->list);
idr_remove(&i2c_adapter_idr, adap->nr);
goto out_unlock;
}
2、设备驱动注册(以i2c-dev.c为例):
1)include/linux/i2c.h
static inline int i2c_add_driver(struct i2c_driver *driver)
{
return i2c_register_driver(THIS_MODULE, driver);
}
2)drivers/i2c/i2c-core.c
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
/* new style driver methods can't mix with legacy ones */
if (is_newstyle_driver(driver)) {
if (driver->attach_adapter || driver->detach_adapter
|| driver->detach_client) {
printk(KERN_WARNING
"i2c-core: driver [%s] is confused\n",
driver->driver.name);
return -EINVAL;
}
}
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
/* for new style drivers, when registration returns the driver core
* will have called probe() for all matching-but-unbound devices.
*/
res = driver_register(&driver->driver); //注册驱动
if (res)
return res;
mutex_lock(&core_lists);
list_add_tail(&driver->list,&drivers); //添加到设备驱动链表中
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
/* legacy drivers scan i2c busses directly */
if (driver->attach_adapter) {
struct i2c_adapter *adapter;
list_for_each_entry(adapter, &adapters, list) { //让设备驱动搜索匹配的适配器(通过调用其attach_adapter接口)
driver->attach_adapter(adapter);
}
}
mutex_unlock(&core_lists);
return 0;
}
EXPORT_SYMBOL(i2c_register_driver);
3)drivers/i2c/i2c-dev.c
static int i2cdev_attach_adapter(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
int res;
i2c_dev = get_free_i2c_dev(adap); //创建并初始化i2c_dev结构
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
/* register this i2c device with the driver core */
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev, //注意,这里使用的次设备号为adap->nr,便于以后获取adap结构。
MKDEV(I2C_MAJOR, adap->nr),
"i2c-%d", adap->nr);
if (IS_ERR(i2c_dev->dev)) {
res = PTR_ERR(i2c_dev->dev);
goto error;
}
res = device_create_file(i2c_dev->dev, &dev_attr_name); //创建设备文件
if (res)
goto error_destroy;
pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
adap->name, adap->nr);
return 0;
error_destroy:
device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
error:
return_i2c_dev(i2c_dev);
return res;
}
总结:
一个适配器对应一个i2c控制器。
posted @
2008-10-05 23:43 lfc 阅读(423) |
评论 (0) |
编辑 收藏

2008年9月15日
1、总线配置结构体:
struct s3c2410_platform_i2c {
unsigned int flags;
unsigned int slave_addr; /* slave address for controller */
unsigned long bus_freq; /* standard bus frequency */
unsigned long max_freq; /* max frequency for the bus */
unsigned long min_freq; /* min frequency for the bus */
unsigned int sda_delay; /* pclks (s3c2440 only) */
};
2、总线描述结构体:
struct s3c24xx_i2c {
spinlock_t lock; //自选锁(防止总线资源被并发访问)
wait_queue_head_t wait; //等待队列(当有数据需要收/发时启动总线,然后守候在等待队列,直到数据收/发结束后被唤醒返回)
struct i2c_msg *msg; //i2c信息指针
unsigned int msg_num; //需要传输的i2c msg数
unsigned int msg_idx; //成功传输的i2c msg数
unsigned int msg_ptr; //当前i2c msg内指针
unsigned int tx_setup; //延时值(保证总线启动时数据已经传输到总线上)
enum s3c24xx_i2c_state state; //i2c总线状态
void __iomem *regs;:
struct clk *clk;
struct device *dev;
struct resource *irq;
struct resource *ioarea;
struct i2c_adapter adap; //总线适配器(个人觉得它更像设备驱动中的设备而非驱动)
};
3、总线适配器:
struct i2c_adapter {
struct module *owner;
unsigned int id;
unsigned int class;
const struct i2c_algorithm *algo; //i2c总线访问算法
void *algo_data; //用来保存struct s3c24xx_i2c结构指针
/* --- administration stuff. */
int (*client_register)(struct i2c_client *);
int (*client_unregister)(struct i2c_client *);
/* data fields that are valid for all devices */
u8 level; //nesting level for lockdep
struct mutex bus_lock;
struct mutex clist_lock;
int timeout;
int retries;
struct device dev; //the adapter device
int nr;
struct list_head clients;
struct list_head list;
char name[48];
struct completion dev_released;
};
4、总线访问算法:
struct i2c_algorithm {
/* If an adapter algorithm can't do I2C-level access, set master_xfer
to NULL. If an adapter algorithm can do SMBus access, set
smbus_xfer. If set to NULL, the SMBus protocol is simulated
using common I2C messages */
/* master_xfer should return the number of messages successfully
processed, or a negative value on error */
int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs, //i2c msg发送函数
int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data);
/* --- ioctl like call to set div. parameters. */
int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long);
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *); //标志i2c适配器所支持的功能
};
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_TEN 0x10 /* we have a ten bit chip address */
#define I2C_M_RD 0x01
#define I2C_M_NOSTART 0x4000
#define I2C_M_REV_DIR_ADDR 0x2000
#define I2C_M_IGNORE_NAK 0x1000
#define I2C_M_NO_RD_ACK 0x0800
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
这里有我画的简单i2c重要结构体分析图:
http://www.cnitblog.com/Files/luofuchong/i2c%E9%87%8D%E8%A6%81%E7%BB%93%E6%9E%84%E4%BD%93.rar
posted @
2008-09-15 23:38 lfc 阅读(294) |
评论 (0) |
编辑 收藏

2008年8月23日
最近在看Linux 2.6.21内核的I2C驱动,也在网上查了一下资料,有错也有对,有些心得,记录下来吧。里面认识或许多有不当之处,还恳请指正。
1. I2C 协议
1.1 I2C总线工作原理
I2C总线是由数据线SDA和时钟SCL构成的串行总线,各种被控制器件均并联在这条总线上,每个器件都有一个唯一的地址识别,可以作为总线上的一个发送器件或接收器件(具体由器件的功能决定)
1.2 I2C总线的几种信号状态
1. 空闲状态:SDA和SCL都为高电平。
2. 开始条件(S):SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
3. 结束条件(P):SCL为低电平时,SDA由低电平向高电平跳变,结束传送数据。
4. 数据有效:在SCL的高电平期间, SDA保持稳定,数据有效。SDA的改变只能发生在SCL的底电平期间。
5. ACK信号: 数据传输的过程中,接收器件每接收一个字节数据要产生一个ACK信号,向发送器件发出特定的低电平脉冲,表示已经收到数据。
1.3 I2C总线基本操作
I2C总线必须由主器件(通常为微控制器)控制,主器件产生串行时钟(SCL),同时控制总线的传输方向,并产生开始和停止条件。
数据传输中,首先主器件产生开始条件,随后是器件的控制字节(前七位是从器件的地址,最后一位为读写位 )。接下来是读写操作的数据,以及 ACK响应信号。数据传输结束时,主器件产生停止条件
2. Linux I2C 结构分析
2.1 层次分析
1. I2C Core
I2C Core用于维护Linux的I2C核心部分,其中维护了两个静态的List,分别记录系统中的I2C driver结构和I2C adapter结构。
static LIST_HEAD(adapters);
static LIST_HEAD(drivers);
I2C core提供接口函数,允许一个I2C adatper,I2C driver和I2C client初始化时在I2C core中进行注册,以及退出时进行注销。具体可以参见i2c_core.c代码。
同时还提供了I2C总线读写访问的一般接口(具体的实现在与I2C控制器相关的I2C adapter中实现),主要应用在I2C设备驱动中。
常用的主要是
i2c_master_send()
i2c_master_recv()
i2c_transfer()
2. I2C bus driver
总线驱动的职责,是为系统中每个I2C总线增加相应的读写方法。但是总线驱动本身并不会进行任何的通讯,它只是存在在那里,等待设备驱动调用其函数。
在系统开机时,首先装载的是I2C总线驱动。一个总线驱动用于支持一条特定的I2C总线的读写。一个总线驱动通常需要两个模块,一个struct i2c_adapter和一个struct i2c_algorithm来描述:
在
buses目录下的i2c-pxa.c中实现了PXA的I2C总线适配器,I2C adapter 构造一个对I2C
core层接口的数据结构,并通过接口函数向I2C core注册一个控制器。I2C
adapter主要实现对I2C总线访问的算法,iic_xfer() 函数就是I2C adapter底层对I2C总线读写方法的实现。同时I2C
adpter 中还实现了对I2C控制器中断的处理函数。
1) i2c-pxa.c定义了i2c_algorithm,并且实现了master的发送函数i2c_pxa_xfer(),以及设备查询总线的模式的函数i2c_pxa_functionality()
static const struct i2c_algorithm i2c_pxa_algorithm = { .master_xfer = i2c_pxa_xfer, .functionality = i2c_pxa_functionality, }; |
2) i2c-pxa.c中,实现了i2c_adapter,主要是在定义pxa-i2c时进行初始化,并且i2c_pxa_probe()中进行填充parent指针,并且调用
ret = i2c_add_adapter(&i2c->adap);
进行添加。
static struct pxa_i2c i2c_pxa = { .lock = SPIN_LOCK_UNLOCKED, .adap = { .owner = THIS_MODULE, .algo = &i2c_pxa_algorithm, .name = "pxa2xx-i2c.0", .retries = 5, }, }; |
总的来说,在i2c-pxa中,使用platform驱动模型,完成了i2c的总线两种模块struct i2c_adapter和struct i2c_algorithm
3. I2C device driver
I2C只有总线驱动是不够的,必须有设备才能工作。这就是I2C device driver的必要性。I2C的device是有两个模块来描述的,struct i2c_driver和struct i2c_client。
在介绍chips目录下的device driver前有必要介绍一下i2c-dev.c文件。
i2c-dev.c中提供了一个通用的I2C设备的驱动程序,实现了字符类
型设备的访问接口,对设备的具体访问是通过I2C adapter来实现的。构造一个对I2C core层接口的数据结构,通过接口函数向 I2C
Core注册一个I2C设备驱动。同时构造一个对用户层接口的数据结构,并通过接口函数向内核注册为一个主设备号为89的字符类型设备。
|
static struct i2c_driver i2cdev_driver = { .driver = { .name = "dev_driver", }, .id = I2C_DRIVERID_I2CDEV, .attach_adapter = i2cdev_attach_adapter, .detach_adapter = i2cdev_detach_adapter, .detach_client = i2cdev_detach_client, };
struct i2c_dev { struct list_head list; struct i2c_adapter *adap; struct device *dev; };
|
该
文件提供了用户层对I2C设备的访问,包括open,read,write,ioctl,release等常规文件操作,我们可以通过open函数打开
I2C的设备文件,通过ioctl函数设定要访问从设备的地址,然后就可以通过 read和write函数完成对I2C设备的读写操作。
static const struct file_operations i2cdev_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = i2cdev_read, .write = i2cdev_write, .ioctl = i2cdev_ioctl, .open = i2cdev_open, .release = i2cdev_release, }; |
注:通过I2C
driver提供的通用方法可以访问任何一个I2C的设备,但是其中实现的read,write及ioctl等功能完全是基于一般设备的实现,所有的操作
数据都是基于字节流,没有明确的格式和意义。为了更方便和有效地使用I2C设备,我们可以为一个具体的I2C设备开发特定的I2C设备驱动程序,在驱动中
完成对特定的数据格式的解释以及实现一些专用的功能。
在chips目录下包含着各种device 的driver,完成各种从设备的注册。作为一般的I2C设备,使用i2c-dev.c里的操作足够完成操作了。
当然如果不能完成,则需要独立完成该驱动,这就是chips目录下的代码。
因为i2c-dev.c已经实现了I2C设备的文件操作接口,所以只要实现struct
i2c_driver就可以了。对于某些特殊的操作,可以使用command接口进行控制。 当然,对于i2接口的fm芯片,则将struct
i2c_driver放在i2c的chips目录下,而将另外fm操作相关的代码放在了/media/radio目录下了。在这个目录下需要完成读写接口
的完成,这个大部分使用V4L2架构。
继续分析中……
posted @
2008-08-23 11:01 lfc 阅读(901) |
评论 (1) |
编辑 收藏