I'm back
把信送给加西亚
(作者置顶)
把信送给加西亚
内容简介
本书讲述的是美西战争时期一个送信的传奇故事,它从一个英雄的事迹揭示出企业成功和个人发展双赢的真谛,它以其上百年的广泛流传和超过8亿册的全球销量证明了一个经久不衰的成功模式,一种用忠诚、自信、责任心和主动性就可以创造奇迹的工作方法。罗文--那个把信送给加西亚的人,已成为无数的政府、军队、企业及其他各种社会组织机构梦寐以求的标志化人才,正如本书作者所言:文明,就是孜孜不倦地寻找这种人才的一段漫长过程。本书可以被看作为最经典和最优秀的商业论著,也可以被当作最有价值的员工培训指导用书,有助于我们树立良好的职业形象。
序
在现实社会中,个性解放,自我实现与忠诚、敬业、主动性,它们的关系究竟是否对立?事实却告诉我们,它们竟然并不对立,而是相辅相成,缺一不可。[美]阿尔伯特·哈伯德的《把信送给加西亚》一书,反映的是一个叫罗文的人以其绝对忠诚、责任感和创造奇迹的主动性,完成了一件“不可能的任务”的传奇故事。他的事迹100多年来在全世界广为流传。该书也成为有史以来世界最畅销图书第6名。
据说,这本薄薄的小书——《把信送给加西亚》现在还放在布什办公室的一张桌子上。布什在本书写下了这样一句话:“你是一个送信者!”他解释说:“我把它送给那些所有在本届政府成立之时共同奋斗的人们……我一直在寻找那些能把信送给加西亚的人,让他们成为我们当中的一员。”。
该书的故事发生于1898年,而《把信送给加西亚》写于1899年。但是故事和该书所表述的精神却已经成为了一代又一代领导者的信念。故事中的英雄,就是安德鲁·罗文,美国陆军的一位年轻的中尉,一个送信人。当时美西战争爆发,美国总统麦金莱急需一名称职的特使去完成一项极其重要的任务,军事情报局向总统推荐了安德鲁·罗文。罗文接受任务后,没有问:“加西亚在什么地方?”“到哪里能找到加西亚?”就立即出发,没有任何人跟随前往。直到他潜入古巴岛,古巴的起义军才给他派了几名当地的向导,几经冒险,或者用他自己谦虚幽默的话来说,仅仅受到了几名敌人的包围,然后设法逃了出来,他终于把信送给了加西亚将军,一个在战争中发挥着关键性作用的人。该书告诉人们:我们绝大多数人都必须在社会组织中奠基事业生涯,只要你还是公司企业的一员,就应当抛开任何借口,投入自己的忠诚和责任,一荣俱荣,一损俱损!当你把身心彻底融入公司,尽职尽责,处处为公司着想,对投资人承担风险的勇气极以钦佩,理解企业主的压力,那么,任何一个老板都会视你为公司的支柱。忠诚带来信任,你将被委以重任,获得梦寐以求的广阔舞台。
该书传达的理念,影响力之大是不可想像的。正如作者阿尔伯特·哈伯德所说:“文明,就是充满渴望地寻找这种人才的一个漫长的过程”。因为公司企业要想获得成功,其员工的主动性、责任感和忠诚都是至关重要的,那“送信的人”是他们梦寐以求的栋梁之才。该书更是一本关于成功的励志佳品,它激励着人们:不被困难吓倒,满怀信心坚持完成所托付的任务。揭示了成功的一种模式。
一、 致加西亚的信
在一切有关古巴的事件中,有一个人最让我忘不了。
美西战争爆发后,美国必须立即跟西班牙的反抗军首领加西亚取得联系、加西亚在古巴丛林的山里——没有人知道确切的地点,所以无法带信给他。然而,美国总统必须尽快地获得他的合作。 怎么办呢? 有人对总统说;“有一个名叫罗文的人,有办法找到加西亚,也只有他才找得到。” 他们把罗文找来,交给他一封写给加西亚的信。关于那个名叫罗文的人,如何拿了信,把它装进一个油纸袋里,封好,吊在胸口,3个星期之后,徒步走过一个危机四伏的国家,把那封信交给加西亚——这些细节都不是我想说明的。我要强调的重点是:美国总统把一封写给加西亚的信交给罗文,而罗文接过信之后,并没有问:“他在什么地方?” 像他这种人,我们应该为他塑造不朽的雕像,放在每一所大学里。年轻人所需要的不只是学习书本上的知识,也不只是聆听他人种种的指导,而是更需要一种敬业精神,对上级的托付,立即采取行动,全心全意去完成任务——“把信送给加西亚”。
加西亚将军已不在人间,但现在还有其他的加西亚。凡是需要众多人手的企业经营者,有时候都会因一般人无法或不愿专心去做一件事而大吃一惊。懒懒散散、漠不关心、马马虎虎的做事态度,似乎已经变成常态;除非苦口婆心、威逼利诱地叫属下帮忙,或者,除非奇迹出现,上帝派一名助手给他,否则,没有人能把事情办成。
不信的话我们来做个试验:你此刻坐在办公室里——周围有6名职员。把其中一名叫来,对他说:“请帮我查一查百科全书,把某某的生平做成一篇摘录。” 那个职员会静静地说:“好的,先生。”然后就去执行吗? 我敢说他绝不会,反而会满脸狐疑地提出一个或数个问题: 他是谁呀? 他过世了吗? 哪套百科全书? 百科全书放在哪儿? 这是我的工作吗? 为什么不叫查理去做呢? 急不急? 你为什么要查他?
我敢以十比一的赌注跟你打赌,在你回答了他所提出的问题,解释了怎么样去查那个资料,以及你为什么要查的理由之后,那个职员会走开,去找另外一个职员帮助他查某某的资料,然后,会再回来对你说,根本查不到这个人。真的。如果你是聪明人,你就不会对你的“助理”解释,某某编在什么类,而不是什么类,你会满面笑容地说:“算啦。”然后自己去查、这种被动的行为,这种道德的愚行,这种心灵的脆弱,这种姑息的作风,有可能把这个社会带到三个和尚没水喝的危险境界。如果人们都不能为了自己而自动自发,你又怎能期待他们为别人采取行动呢?
你登广告征求一名速记员,应征者中,十之八九不会拼也不会写,他们甚至不认为这些是必要条件。这种人能把信带给加西亚吗? 在一家大公司里,总经理对我说:“你看那职员。” “我看到了,他怎样?” “他是个不错的会计,不过如果我派他到城里去办个小差事,他可能把任务完成,但也可能就在途中走进一家酒吧,而当他到了闹市区,可能根本忘了他的差事。” 这种人你能派他送信给加西亚吗?
近来我们听到了许多人,为“那些为了廉价工资工作而又无出头之日的工人”以及“那些为求温饱而工作的无家可归的人士”表示同情,同时把那些雇主骂得体无完肤。 但从没有人提到,有些老板一直到年老,都无法使那些不求上进的懒虫做点正经的工作,也没有人提到,有些老板长久而耐心地想感动那些当他一转身就投机取巧的员工。
在每个商店和工厂,都有一个持续的整顿过程。公司负责人经常送走那些显然无法对公司有所贡献的员工,同时也吸引新的进来。不论业务怎么忙碌,这种整顿一直在进行着。只有当公司不景气,就业机会不多,整顿才会出现较佳的成绩——那些不能胜任、没有才能的人,都被摈弃在就业的大门之外,只有最能干的人,才会被留下来。为了自已的利益,使得每个老板只保留那些最佳的职员——那些能把信送给加西亚的人。
我认识一个极为聪明的人,他没有自已创业的能力,而对别人来说也没有一丝一毫的价值,因为他老是疯狂地怀疑他的雇主在压榨他,或存心压迫他。他无法下命令,也不敢接受命令。如果你要他送封信给加西亚,他极可能回答:“你自己去吧。”
当然,我知道像这种道德不健全的人,并不会比一个四肢不健全的人更值得同情;但是,我们也应该同情那些努力去经营一个大企业的人,他们不会因为下班的铃声而放下工作。他们因为努力去使那些漠不关心、偷懒被动、没有良心的员工不太离谱而日增白发。如果没有这份努力和心血,那些员工将挨饿和无家可归。
我是否说得太严重了?不过,当整个世界变成贫民窟,我要为成功者说几句同情的话——在成功机会极小之时,他们导引别人的力量,终于获得了成功;但他们从成功中所得到的是一片空虚,除了食物外,就是一片空无。
我曾为了三餐而替人工作,也曾当过老板,我知道这两方面的种种甘苦。贫穷是不好的,贫苦是不值得推介的,但并非所有的老板都是贪婪者、专横者,就像并非所有的人都是善良者一样。
我钦佩的是那些不论老板是否在办公室都会努力工作的人,我也敬佩那些能够把信交给加西亚的人。静静地把信拿去,不会提出任何愚笨问题,也不会随手把信丢进水沟里,而是不顾一切地把信送到。这种人永远不会被解雇,也永远不必为了要求加薪而罢工。文明,就是为了焦心地寻找这种人才的一段长远过程。这种人不论要求任何事物都会获得。他在每个城市、村庄、乡镇,以及每个办公室、商店、工厂,都会受到欢迎。 世界上极需这种人才,这种能够把信送给加西亚的人。
二、要做就要做到最好
如果林肯的所有信件和演讲都被销毁了,只要一封给胡克(美国内站时期联邦军将领,参加过所有东部的重要战役,败于南方联盟的李将军,但是在卢考特山战役中获得决定性胜利----译者注)的信保存下来,那么我们就可以窥测”劈木人”(林肯的绰号----译者注)的内心世界。
在这封信中,我们可以看到林肯的自制精神;我们还可以看到人控制他人的能力。从这封信里可以看到坦率、善良、机智、老练;联明的外交和无限的耐心。
胡克严厉而有失公允地批评了他的总司令-----林肯,还羞辱了他的顶头上司----伯思赛德(美国内战时联邦军将领,作战无数,以连 胡子而著名,为人纷纷效仿,辞去军职后曾任罗州州长----译者注)。但是林肯将这些挥之而去,他相信胡克具有才能,于是让他取代了伯恩赛德。换句话说,一个被误解的人提升了一个误解他的人,使之超过了与自己有着深厚友谊的人人。
但是为了大局,所有个人的考虑都弃之不顾了。当然,被提升的人应该掌握趔,这是必要的,而且林肯平心静气地通知他这件事,当然,这种方式还避免了胡克夸大其辞的攻击。也许我们最好还是将这封信全文抄录如下:
胡克少将:
我委任你为波托马克军司令。当然,我这样做是有着充分的理由的,但是你最好也应该知道,在有些事情上我对你并不太满意。我相信你是一名勇敢善战的军人,对于这样的人,我当然喜欢了。我还相信你不会将政治和你的职业相混淆。在你的职业方面,你是正确的。你很自信。这即使不是一个不可缺少的品质,至少也是一个珍贵的品质。你有着雄心壮志。在合理范围内,这是好事而不是坏事。但是,我认为,在伯恩赛德将军指挥期间,你保守着自己的雄心,并且极力阻挠他。你这样做,无论是对国家,还是对一个最优秀可敬的将领,你都犯了一个巨大的错误。
你最近说,无论是军队还是政府都需要一个独裁者。我相信这种传言是真的。当然,我授权于你,并不是因为这件事,而是尽管有这件事。只有那些建功立业的将军才能建立独裁。现在,我要求你的是军事上的成功,我将为此而面临独裁的危险。政府将全力支持你,对于所有的将军,它都一视同仁,也不会另眼看待你。你对司令横加指责,心存疑虑,你助长的这种风气将会毁了你,就是拿破仑再世,也无能为力。现在,戒骄戒躁,勇往直前,去争取胜利。
你真诚的朋友亚伯拉罕.林肯
这封信中有一点特别值得我们重视,它暗示了一种在组织中潜滋暗长的恶习,那就是,对于位居我们之上的人加以冷嘲热讽、吹毛求疵、批评抱怨的习惯。
任何一个人,想当个人物,想做点事,肯定会受到批评、侮辱和误解。这是必经的磨难,每一位伟人都懂得这一点,他们还懂得,伟大是无从证明的。最后的证明在于能够含垢忍辱,无怨无悔。林肯没有忌恨胡克对他的批评,他知道,每一个生命都有其存在的理由,不过我们还是看他是如何提醒胡克这样一个事实吧:胡克因自己的做法而自食其果!”如果任其蔓延,别说是你,就是拿破仑再世,也无能为力。”胡克将搬起石头砸自己的脚。
不久前,我遇到一个耶鲁大学的学生。他要回家度假。我肯定,他不能代表真正的耶鲁精神,因为他对学校的规章制度,牢骚满腹,怨声载道。哈德里校长也成为他批评的对象,他向我摆了一大堆理由、事实、材料、还附带着时间、地点,说得有鼻子有眼的。
很快,我就看到麻烦出现了,不是耶鲁有麻烦,而是那个学生。他对一些微不足道的事耿耿于怀,他终于做得太离谱了,失去了在耶鲁大学学习的资格。我想耶鲁并不是完善无缺的,而哈德里校长和其他的耶鲁人都愿意承认这一点;但是,耶鲁有着某些优势,而且它也依赖这些学生,不管他们是否利用了这些优势。
如果你是一所大学的学生,那就抓住那些现有的好处。你给予他容忍,你就得到益处。你对它的制度给予同情和忠诚,你就会得到报偿。为它骄傲吧。站在老师们一边,他们就会尽力而为。如果一个地方不好,那么你平时就尽力而为,给别人树立榜样,让它变得好起来。做自己份内之事。
如果问题出在公司一方,老板性乖戾,那么你最好就去找他,诚恳地、平静地、温和地告诉他:他是一个性情乖戾的人;向他说明,他的政策是荒谬的。然后,让他知道改进的方式,你还可以把这些问题揽过来,悄悄地清除它们。
或者要做,或者不做,二者必居其一;要么全身退出,要么全力以赴。你只能做一种选择。如果你为一个人工作,那就以上帝的名义去为他工作。如果他的报酬足以让你白挣饭吃,那就尽心为他工作,为他着想,支持他,支持他所代表的机构。我想,如果我为一个人工作,我就为他工作。我不能对他三心二意,不能阳奉阴违。我不是全心全意,就是干脆不干。严格说来,一丁点的忠诚抵得上一大堆的智慧。
如果你非要辱骂、诅咒和没完没了地贬损不可,那么你为什么不辞职呢?当你身在局外时,你可以心情发泄。但是,我请求你,身在其中时,不要诅咒它。当你贬损它进,你身置其中,那么你也是在贬损自己。
不仅如此,你还是在松懈把自己与这个机构联系纽带。树大招风,当有一天你被连根拔起,无所依附时,你甚至还不知道是怎么一回事。那封解雇信上只会说:”合同到期了,很抱歉,我们没有足够的职位”,等等。
你到处都可以看到那些失业的人。跟他们聊一聊,你就会发现,他们牢骚满腹、怨天尤人、愤愤不平。那是他们性格上的缺陷给他们造成的麻烦。他们自毁前程、自食其果。他们总是显得格格不入,无所作为。所有雇主都在寻找能够助他一臂之力的人,他却在冷眼旁观。对于那些无所作为的人,碍手碍脚的人,让其趁早离开,这是商业上的规矩;基于自然的法则,奖赏只能属于那些得力的人。为了能得到提携,你必须具有同情之心。
只要你叽叽喳喳、说三道四、指桑骂槐、阳奉阴违,说老板是一个性情乖戾的人,他的事业就要完蛋了,那么你对他毫无帮助。你没必要以不满来威胁他,没必要将忌恨升级为冲突,但是你正在步入险境,将很快被淘汰出局。
当你告诉别人说你的老板是一个性情乖戾的人,那么你就暴露了你就是这样一个人;当你告诉别人说机构政策”不可救药”,那么显然你也是这样。
尽管有着缺点,但在胡克还是得到了晋升。但很有可能是,你的雇主没有林肯那样宽厚。但即使林肯也不能永远保护胡克,胡克作战不利,林肯也就不得不另请高明。终有一天,胡克为一个高明的人所取代。这个从不批评任何人,从不抱怨任何人;这个高明的人控制着自己的情感,恰到好处;他做着自己的份内之事,以忠诚、信任和义无反顾的献身精神,做着常人不可能做的事。
让我们做自己的份内之事吧,尽全力而为之。
三、不要被动服,从而要主动开拓
就像许多人一样,我在十几岁时和大学期间做过许多有趣的工作。我修理过自行车(我被解雇了),我挨家挨户卖过词典。有一年,我整整一个夏天都在为一个选美比赛收集那些订出去而未收上款来的票,那是一些中年人在甜言蜜语的竞争者劝说下订下的,但他们根本无意去观看。我还做过数学课的家教、书店收银员、商店出纳员和夏令营童子军顾问。为了读完大学,还打扫过院子,整理守房间和船舱。这些工作大部分都低俗不堪,我一度认为它们都是下贱而收入不高的工作。
后来,我知道自己错了。这些工作潜移默化地给予我珍贵的教诲和机会,不管在什么样的工作中,也不管在哪个档次上,我都学到了不少东西。
就拿我在商店的工作来说吧。我觉得我是一个好雇员,我做了我应该做的事,主要就是记录顾客的购物款。
然而,有一天,当我正和一个同事闲聊时,地区经理走进门来。他环顾四周,然后示意我跟他走。他一句话也不说,开始整理已经订出的商品。然后,他走到食物区,清理柜台,把购物车清空。
我惊奇地看着这一切,逐渐醒悟过来:他也想让我做这些事!我为之所以惊诧万分,不是因为这是一些新任务,而是因为这意味着我要一直做下去。可是,从前没人告诉我要做这些事啊!即使是现在,谁也没有说过。
那时,我学到了终生受益的经验。它不仅使我成为一名更优秀的雇员,还让我从每一项工作中学到了更多的东西。这个教益就是我要对自己的工作负责,我要更上一层楼,对自己的行为切实负起责。总之:不仅仅是别人告诉我要去做的事。
一旦获得这个教益,我以前认为低谷的工作开始有意思起来。我越是专注我的工作,我学到的东西和完成的东西也就越多。我离开那家商店去上大学,但是这种经验对我人生和事业的影响是深远的。我从一个旁观者变成了一个认真负责的人。学业变得有趣了,兼职和实习成为探索未来发展的机会。
当我成为经理或者执行官时,我总是努力发现需要做的事。事实上,在各种各样的工作中,我都可以发现超过他人的机会-----不仅让我的雇主与众不同,也让我自己出人头地。
我的结论就是,每一位雇员在每一项工作中都要倾听和相信这一点:你可以使自己的生活好转起来,就从今天开始,就从你现在的工作做起,而不必等到遥远未来的某一天你找天理想的工作再去行动。
四、哈伯德商业信条
我相信我自己
我相信我的产品
我相信我的公司
我相信我的同事和伙伴
我相信产品的生产者、设计者、制造者、销售者以及世界上所有工作的人们
我相信真理的价值
我相信好的性情和好的身体
我相信 成功的必要条件并不是赚钱,而是创造价值,创造了价值,成功就自然而来
我相信阳光、新鲜空气、蔬菜、水果以及世界上一切美好的东西
我相信世界上最美好的词就是“自信”
我相信我每做一笔生意我就多了一个朋友
我相信我和别人分开后,我们都会渴望再次相聚,并且相聚时大家都很愉快
我相信工作的双手、思考的大脑和友爱的心灵
行走社会的100条忠告
(作者置顶)
行走社会的100条忠告
1.所有的困苦都是有用意的-这是老天爷在磨练你,为了把重任交给你
2.毛遂自荐,好处多多-让别人看到你,知道你的存在,知道你的能力
3.千万别入错行-人情有牵绊,恩怨的纠葛,转行可不是那幺容易的呀
4.别轻易转行-转行的风险很林,若无大决心,大魄力,最好不要轻率为之
5.不只为了糊口,还要有抱负-你要想:在这个行业中,我要成为什幺样的人
6.不要独享荣耀-独享荣耀,有天就会独吞苦果
7.用耐心把冷板凳坐热-冷板凳都坐过了,还有什幺好怕的呢
8.留一点空间给你的主管-当主管也需要安全感
9.摸鱼 小心摸到螃蟹!-摸鱼会成为你在社会上行走的负载
10.不要有[怀才不遇]的想法-怀才不遇多半是自己造成的
11.天下没有[好赚]的钱-先从小钱开始赚吧
12.当你遇到魔鬼型的主管-接受他的磨练吧
13.勿当众辱骂你的主管-那会让你无路可走,结果只有走路一途
14.向不同的行业的人吸收新知识-记住要用请教的态度
15.犯错,就诚实的认错-狡辩,诿过只会害了你自己
16.用[吃亏就是占便宜]的心态做事做人-它可累积你的工作经验,充实你的作事的能力,扩张
你的人际网络
17.以失败为师-与其在失败中记取教训,不如从别人有失败中检取教训
18.勿在失意者面前谈论你的得意-这会对你的人际关系造成伤害
19.别轻视[守时]这件事-守时是对别人的一种尊重
20.精诚所至,金石为开-你的真心成意会在对方的感动中激起你的同情和不忍
21.朋友要分[等级]-这是为了保护自己免受伤害
22.用[时间]来看人-时间是让对方不自觉的检验师
23.用[打听]来看人-把获得的信息汇集起来,就可以了解这个人
24.用[投其所好]来看人-他会不知不觉地把假面具掀掉
25.建立一个[朋友档案]-以免人到用时方恨少
26.扩大交友的圈子-主动出击,勿等别人上门来
27.保持交朋友的弹性-敌人也可以变朋友
28.交会[修理]你的朋友-这种朋友正是你人生的导师
29.好朋友要[保持距离]-这样的友谊才能长久
30.小心突然升高热度有友情-不推不迎,冷眼以观,礼上往来
31.以[勤]补能力不足-勤将使你自然发亮,为人注意
32.把一天变成四十八小时-让每一分每一秒都发挥最高的效益
33.为自己拼几枚[勋章]-建立地位,声望,让别人尊敬你,礼遇你
34.碰到困难,决不轻言退却-要把困难当成对自己的试练
35.作老二,不要作老大-老大没当好,会变成老三老四
36.勿在工作上被人看轻-别混日子,别看轻职份,别混水摸鱼
37.钱追人,人追健康-有了健康,还怕赚不到钱
38.勿吝于提携后进-他们会成支持你的力量
39.中规中矩,赢得尊重-赢得尊重,也赢得信赖
40.适度的[自抬身价]-在就业市场里,人也是一种商品
41.用[物质利]换取[人情利]-物质利是一时的,人情利是长远的
42.把敬业变成习惯-短期来看是为了雇主,长期来看是为了自己
43.寻找一位[衣食父母]-他可省下你的摸索的碰撞
44.运用[累积法]壮大资产-不求快,不求多,不中断
45.找一位对手来跟-跟住他,并且超越他
46.全力以赴,迎接工作上的种种[测试]-不要把突来的状况当成是整你
47.忍一时,争千秋-形势比人强时,就要忍
48.套用别人的成功模式-别人的成功模式是一种指引,让你有方向可循
49.把自己当成老板或主管-在工作中见习,培养当老板或主管的格局和能力
50.勿满足于眼前的小小成就-你要问自己,我这辈子只有这样吗
51.尽快成为你那一行的专家-只要下功夫,很快就办得到
52.与其你死我活,不如你活我也活-这就是双赢,是良性竞争
53.以[播种]的心情来经营人际关系-越早播种越好
54.也以[播种]的心情来经营事业-不播种,就不会有收获
55.跌倒了,[一定]要爬起来-不爬起来,别人会看不起你,你也会失去机会
56.不要为失败找借口-应该面对失败,找出失败的原因来
57.改变环境,或是改变自己-与其改变环境,不如改善自己
58.拉长战线,避免主力对决-人生没有几场主力战好打
59.不要打没有把握的仗-人生是经不起浪费的
60.把反省自己当成每日的功课-因为你不是完美的,会说错话,也会做错事
61.碰到低潮,自己鼓励自己-千万别乞求,依靠别人来鼓励你
62.借用别人的智能来作事-你要了解,人的智能是有限的
63.时时要有危机意识-别以为你命好运也好
64.主角配伯都能演,台上台下都自在-这是面对现实人生,能屈能伸的弹性
65.做事切勿[率性而为]-率性而为只会害了你自己
66.事越烦,越要耐烦-天底下没有不烦的事
67.控制自己的情绪-别让别人以为你还没长大
68.做乌龟,不要做兔子-有兔子资质的人最容易骄傲
69.先做小事,先赚小钱-这是为了累积坐大事赚大钱的条件
70.别让过去的失败绑住你的手脚-环境会变,人会成长,再度出击,可能就成功了
71.像蟑螂一样地活着-人如果有蟑螂韧性,还有什幺日子不能过呢
72.稳扎稳打,可攻可守-没抓在手里的成功都是不算的
73.靠实力,不要靠派系-派系不是永远的,实力才是你一生最好的依靠
74.自己发光,不要等别人不磨光-每个人都为自己,谁有空,有心好好去了解你呢
75.逢山开路,遇水架桥-过了桥,上了山,常会有一片新的天地
76.永远跑在部属的前面-权力领导,情义领导都不如才能领导
77.[新官上任三把火]有必要-告诉别人:我来了
78.巧用[致命的吸引力]-男人温柔,女人撒娇
79.正视你的弱点,躲避别人的陷井-光明正大,脚注踏实地,不痴心妄想,便可避免踏入陷阱
80.只要五毛钱,不要一块钱-拿一块钱的机会只的一次
81.放下身段,路越走越宽-身段只会把你绑起来
82.话别说得太满-好容纳一些意外,以免下不了台
83.了解中国人的处世风格-重视人情,表里不一,形式主义
84.善用[见面三分情]的中国人心理-你尊重对方,对方也会尊重你
85.多赞美别人-不用花钱,又可使人快乐,何乐而不为
86.尊重别人的[领土范围]-别因为疏忽而引起不心要的麻烦
87.勿轻易吐露你的失意-以免被人认为软弱无能
88.人际关系是:有(舍)才有(得)-主动出击,先去满足对方的自我
89.别忽略(面子问题)-不给面子的行为最易引起是非
90.你要小心应对的几种人-并不是每个人都是好人
91.你态度要有所保留的几种人-完全信任,你便会受到伤害
92.妥善自理和(小人)的关系-不依附小人,也不得罪小人
93.最好不要挡人财路-与其挡人财路,不如自己另辟财路
94.以低姿态化解别人的嫉妒-嫉妒是一把烈火,会毁灭一个人
95.做人做事不心面面俱到-因为总有人会不满意你
96.认识并运用人性中的自私-不必在意别人的自私,应好好应对这些自私
97.顺着毛摸,他就听你的-脾气再大,城府再深,主观再强的人也吃不消这招
98.以积极的作为推动(否极泰来)-保持存在,发挥耐心,累稷能力
99.以戒慎的心情延缓(盛极而衰)的时间-很多失败都是在盛时种下的
100.时到,花自然便开-你只要努力就对了,什幺时候花才盛开那是老天爷的事
每天问自己十个问题
(作者置顶)
如果你想走出常规,放松心情,以积极的的心态开始新的一天,那就很有必要以自问的方式开始一天,这些问题会给我们带来力量和好心情.
1、我拥有什么?通常我们会为自己没有的东西而苦恼,却看不到自己拥有的,如健康---可以听、可以看、可以爱与被爱,每天拥有实物供我们享用等。正如那句口口相传的话所说的:“失去了才知道珍贵。”让我们走出哀怨,这样就可以让我们看到什么是我们拥有的。
2.我应该为什么感到自豪?为你已经取得的成绩而自豪。成绩不分大小,每一次成绩都意味着向前迈了一不。你可以为你刚刚战胜的一个挑战感到骄傲,可以为帮助了一个陌生人而感到幸福,可以为帮助了一个朋友露出微笑,也可以为结识了新朋友或读了一本新书而高兴。总之一切都值得你自豪。
3、我应对什么心存感激?每天都有很多事情让我们为之心存感激,同时也有很多人值得我们感激,因为他们在无形中教会了我们一些事情。生活的每一天对于我们来说都是一份珍贵的礼物。
4、我怎样才能充满活力?每天都要计划好做一些积极的事情,让自己充满活力。例如,可以给那些一直以来都很欣赏,却很久未联系的人打电话保持微笑,或者留出时间和孩子玩耍等。
5、我今天能解决什么问题?设法把那些原本想留到明天才解决的问题今天就解决掉,尽量在当天完成手边的工作,要敢于面对那些棘手的问题,并换一种角度看待它们。
6、我能抛下过去的包袱吗?“过去的包袱”就是指那些常年积累起来的伤心的经历和怨气。背着这些沉重的包袱有什么用呢?建议你对过去做一个总结,把值得借鉴的经验保存起来,然后永远地卸下重负。
7、我怎么换个角度看待问题?人往往都是别人的建议者,却不是自己的。很多时候根本问题就是我们看待问题的方式。很多人都经历过为一件事苦恼不堪,然后又觉得可笑的时候悲和喜只是我们看问题的角度不同而已。
8、我怎样过好今天?做些与往常不一样的事情。如我们走出常规,学会享受生活,那么生活就是丰富多采的。我们要敢于创造和创新。
9、今天我要拥抱谁?拥抱是我们的精神食粮。曾经有一位心理学家说过,要想健康,每天要至少拥抱八次。身体接触是人最为基础的要求,它甚至可以帮助我们开发大脑。
10、我现在就开始行动?不要认为这些都是“听起来不错”的建议,也不要认为生活是这样的。其实每天的生活都不是你想象中的那样。是让生活过的索然无味,还是积极向上,决定权就在自己的手中。努力幸福的生活,你又会失去什么呢?
Ubuntu挂载网络共享
android模拟器无法启动的两个问题
Linux操作系统PS命令详细 解析
ubuntu evolution邮件自动提示
使用蓝牙传送文件的问题
dbus 例程
dbus-send和dbus-monitor
dbus提供了两个小工具:dbus-send和dbus-monitor。我们可以用dbus-send发送消息。用dbus-monitor监视总线上流动的消息。 让我们通过dbus-send发送消息来调用前面的Add方法,这时dbus-send充当了应用程序B。用dbus-monitor观察调用过程中的消息。
启动example-service:
$ ./example-service
在另一个控制台启动dbus-monitor:
$ dbus-monitor
dbus-monitor默认监视会话总线。执行:
$ dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test /TestObj org.fmddlmyy.Test.Basic.Add int32:100 int32:999
输出为:
method return sender=:1.21 -> dest=:1.22 reply_serial=2 int32 1099
dbus-monitor的相关输出包括:
signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged string ":1.22" string "" string ":1.22" method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=Hello method call sender=:1.22 -> dest=org.fmddlmyy.Test path=/TestObj; interface=org.fmddlmyy.Test.Basic; member=Add int32 100 int32 999 method return sender=:1.21 -> dest=:1.22 reply_serial=2 int32 1099 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged string ":1.22" string ":1.22" string ""
:1.22就是dbus-send在本次调用中与会话总线所建立连接的唯一名。:1.21是连接“org.fmddlmyy.Test”的唯一名。 在以上输出中我们可以看到:1.22向“org.fmddlmyy.Test”发送method_call消息,调用Add方法。 :1.21通过method_return消息将调用结果发回:1.22。其它输出信息会在以后说明。
dbus-send的详细用法可以参阅手册。调用远程方法的一般形式是:
$ dbus-send [--system | --session] --type=method_call --print-reply --dest=连接名 对象路径 接口名.方法名 参数类型:参数值 参数类型:参数值
dbus-send支持的参数类型包括:string, int32, uint32, double, byte, boolean。
消息总线是一个特殊的应用,它可以在与它连接的应用之间传递消息。 可以把消息总线看作一台路由器。正是通过消息总线,D-Bus才在一对一的通信协议基础上实现了多对一和一对多的通信。
消息总线虽然有特殊的转发功能,但消息总线也还是一个应用。 其它应用与消息总线的通信也是通过1.1节的基本消息类型完成的。作为一个应用,消息总线也提供了自己的接口,包括方法和信号。
我们可以通过向连接“org.freedesktop.DBus ”上对象“/”发送消息来调用消息总线提供的方法。 事实上,应用程序正是通过这些方法连接到消息总线上的其它应用,完成请求公共名等工作的。
消息总线对象支持第一讲中提到的标准接口"org.freedesktop.DBus.Introspectable", 我们可以调用org.freedesktop.DBus.Introspectable.Introspect方法查看消息总线对象支持的接口。例如:
$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.Introspectable.Introspect
输出为:
method return sender=org.freedesktop.DBus -> dest=:1.20 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" direction="out" type="s"/>
</method>
</interface>
<interface name="org.freedesktop.DBus">
<method name="Hello">
<arg direction="out" type="s"/>
</method>
<method name="RequestName">
<arg direction="in" type="s"/>
<arg direction="in" type="u"/>
<arg direction="out" type="u"/>
</method>
<method name="ReleaseName">
<arg direction="in" type="s"/>
<arg direction="out" type="u"/>
</method>
<method name="StartServiceByName">
<arg direction="in" type="s"/>
<arg direction="in" type="u"/>
<arg direction="out" type="u"/>
</method>
<method name="NameHasOwner">
<arg direction="in" type="s"/>
<arg direction="out" type="b"/>
</method>
<method name="ListNames">
<arg direction="out" type="as"/>
</method>
<method name="ListActivatableNames">
<arg direction="out" type="as"/>
</method>
<method name="AddMatch">
<arg direction="in" type="s"/>
</method>
<method name="RemoveMatch">
<arg direction="in" type="s"/>
</method>
<method name="GetNameOwner">
<arg direction="in" type="s"/>
<arg direction="out" type="s"/>
</method>
<method name="ListQueuedOwners">
<arg direction="in" type="s"/>
<arg direction="out" type="as"/>
</method>
<method name="GetConnectionUnixUser">
<arg direction="in" type="s"/>
<arg direction="out" type="u"/>
</method>
<method name="GetConnectionUnixProcessID">
<arg direction="in" type="s"/>
<arg direction="out" type="u"/>
</method>
<method name="GetConnectionSELinuxSecurityContext">
<arg direction="in" type="s"/>
<arg direction="out" type="ay"/>
</method>
<method name="ReloadConfig">
</method>
<method name="GetId">
<arg direction="out" type="s"/>
</method>
<signal name="NameOwnerChanged">
<arg type="s"/>
<arg type="s"/>
<arg type="s"/>
</signal>
<signal name="NameLost">
<arg type="s"/>
</signal>
<signal name="NameAcquired">
<arg type="s"/>
</signal>
</interface>
</node>
"
从输出可以看到会话总线对象支持标准接口“org.freedesktop.DBus.Introspectable”和接口“org.freedesktop.DBus”。 接口“org.freedesktop.DBus”有16个方法和3个信号。下表列出了“org.freedesktop.DBus”的12个方法的简要说明:
| org.freedesktop.DBus.RequestName (in STRING name, in UINT32 flags, out UINT32 reply) | 请求公众名。其中flag定义如下: DBUS_NAME_FLAG_ALLOW_REPLACEMENT 1 DBUS_NAME_FLAG_REPLACE_EXISTING 2 DBUS_NAME_FLAG_DO_NOT_QUEUE 4 返回值reply定义如下: DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1 DBUS_REQUEST_NAME_REPLY_IN_QUEUE 2 DBUS_REQUEST_NAME_REPLY_EXISTS 3 DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4 |
| org.freedesktop.DBus.ReleaseName (in STRING name, out UINT32 reply) | 释放公众名。返回值reply定义如下: DBUS_RELEASE_NAME_REPLY_RELEASED 1 DBUS_RELEASE_NAME_REPLY_NON_EXISTENT 2 DBUS_RELEASE_NAME_REPLY_NOT_OWNER 3 |
| org.freedesktop.DBus.Hello (out STRING unique_name) | 一个应用在通过消息总线向其它应用发消息前必须先调用Hello获取自己这个连接的唯一名。返回值就是连接的唯一名。dbus没有定义专门的切断连接命令,关闭socket就是切断连接。 在1.2节的dbus-monitor输出中可以看到dbus-send调用消息总线的Hello方法。 |
| org.freedesktop.DBus.ListNames (out ARRAY of STRING bus_names) | 返回消息总线上已连接的所有连接名,包括所有公共名和唯一名。例如连接“org.fmddlmyy.Test”同时有公共名“org.fmddlmyy.Test”和唯一名“:1.21”, 这两个名称都会被返回。 |
| org.freedesktop.DBus.ListActivatableNames (out ARRAY of STRING bus_names) | 返回所有可以启动的服务名。dbus支持按需启动服务,即根据应用程序的请求启动服务。 |
| org.freedesktop.DBus.NameHasOwner (in STRING name, out BOOLEAN has_owner) | 检查是否有连接拥有指定名称。 |
| org.freedesktop.DBus.StartServiceByName (in STRING name, in UINT32 flags, out UINT32 ret_val) | 按名称启动服务。参数flags暂未使用。返回值ret_val定义如下: 1 服务被成功启动 2 已经有连接拥有要启动的服务名 |
| org.freedesktop.DBus.GetNameOwner (in STRING name, out STRING unique_connection_name) | 返回拥有指定公众名的连接的唯一名。 |
| org.freedesktop.DBus.GetConnectionUnixUser (in STRING connection_name, out UINT32 unix_user_id) | 返回指定连接对应的服务器进程的Unix用户id。 |
| org.freedesktop.DBus.AddMatch (in STRING rule) | 为当前连接增加匹配规则。 |
| org.freedesktop.DBus.RemoveMatch (in STRING rule) | 为当前连接去掉指定匹配规则。 |
| org.freedesktop.DBus.GetId (out STRING id) | 返回消息总线的ID。这个ID在消息总线的生命期内是唯一的。 |
接口“org.freedesktop.DBus”的3个信号是:
| org.freedesktop.DBus.NameOwnerChanged (STRING name, STRING old_owner, STRING new_owner) | 指定名称的拥有者发生了变化。 |
| org.freedesktop.DBus.NameLost (STRING name) | 通知应用失去了指定名称的拥有权。 |
| org.freedesktop.DBus.NameAcquired (STRING name) | 通知应用获得了指定名称的拥有权。 |
让我们来试试消息总线提供的方法。
用dbus-send调用:
$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames
输出为:
method return sender=org.freedesktop.DBus -> dest=:1.23 reply_serial=2
array [
string "org.freedesktop.DBus"
string "org.freedesktop.Notifications"
string "org.freedesktop.Tracker"
string "org.freedesktop.PowerManagement"
string ":1.7"
string ":1.8"
string "org.gnome.ScreenSaver"
string ":1.9"
string ":1.10"
string ":1.22"
string ":1.11"
string "org.gnome.GnomeVFS.Daemon"
string ":1.23"
string ":1.12"
string ":1.13"
string ":1.0"
string ":1.14"
string ":1.1"
string ":1.15"
string ":1.2"
string ":1.16"
string ":1.3"
string "org.gnome.GkbdConfigRegistry"
string ":1.4"
string "org.fmddlmyy.Test"
string ":1.5"
string "org.gnome.SettingsDaemon"
string ":1.6"
]
这是会话总线当前已连接的连接名。在d-feet窗口的左侧窗口显示的就是ListNames返回的连接名。 聪明的读者也许已经想到使用消息总线的“org.freedesktop.DBus.ListNames”方法和各连接的“org.freedesktop.DBus.Introspectable.Introspect”, 我们就可以像d-feet一样查看总线上所有连接的所有对象的所有接口的所有方法和信号。
你的想法很好。但有一个问题,我们必须对连接中的对象调用“org.freedesktop.DBus.Introspectable.Introspect”方法。 ListNames只列出了连接名,我们怎么获取连接中的对象路径呢?
答案很简单,如果我们不知道对象路径就从根目录开始吧。连接中的对象是按照树型结构组织的。我们遍历连接的对象树就可以找到所有的对象。 调用对象的“org.freedesktop.DBus.Introspectable.Introspect”方法就可以查看对象的所有接口的所有方法和信号。 例如:假设我们不知道连接"org.fmddlmyy.Test"里有什么对象,我们可以对根对象"/"执行:
$ dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test / org.freedesktop.DBus.Introspectable.Introspect
输出为:
method return sender=:1.22 -> dest=:1.25 reply_serial=2 string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node> <node name="TestObj"/> </node> "
"org.fmddlmyy.Test"的对象树的根节点只有一个子节点"TestObj",再查看"/TestObj":
$ dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test /TestObj org.freedesktop.DBus.Introspectable.Introspect
输出为:
method return sender=:1.22 -> dest=:1.26 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" direction="out" type="s"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" direction="in" type="s"/>
<arg name="propname" direction="in" type="s"/>
<arg name="value" direction="out" type="v"/>
</method>
<method name="Set">
<arg name="interface" direction="in" type="s"/>
<arg name="propname" direction="in" type="s"/>
<arg name="value" direction="in" type="v"/>
</method>
<method name="GetAll">
<arg name="interface" direction="in" type="s"/>
<arg name="props" direction="out" type="a{sv}"/>
</method>
</interface>
<interface name="org.fmddlmyy.Test.Basic">
<method name="Add">
<arg name="arg0" type="i" direction="in"/>
<arg name="arg1" type="i" direction="in"/>
<arg name="ret" type="i" direction="out"/>
</method>
</interface>
</node>
"
作为一个练习,让我们来查看系统总线的上的bluez接口。执行:
$ dbus-send --system --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames
输出为:
method return sender=org.freedesktop.DBus -> dest=:1.30 reply_serial=2
array [
string "org.freedesktop.DBus"
string ":1.7"
string ":1.8"
string ":1.9"
string "org.freedesktop.SystemToolsBackends"
string ":1.30"
string "org.freedesktop.NetworkManagerInfo"
string ":1.20"
string "org.freedesktop.Avahi"
string ":1.21"
string "org.bluez"
string ":1.22"
string "org.freedesktop.NetworkManager"
string "org.freedesktop.ConsoleKit"
string ":1.23"
string "com.redhat.dhcp"
string ":1.13"
string ":1.0"
string ":1.14"
string ":1.1"
string ":1.15"
string ":1.2"
string "org.freedesktop.Hal"
string "com.redhat.NewPrinterNotification"
string ":1.16"
string ":1.3"
string ":1.17"
string ":1.4"
string ":1.18"
string ":1.5"
string ":1.19"
string ":1.6"
]
我们看到连接"org.bluez"。查看它的根对象:
$ dbus-send --system --type=method_call --print-reply --dest=org.bluez / org.freedesktop.DBus.Introspectable.Introspect
输出为:
method return sender=:1.7 -> dest=:1.31 reply_serial=2 string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node> <node name="org"/> </node> "
接着查对象"/org":
$ dbus-send --system --type=method_call --print-reply --dest=org.bluez /org org.freedesktop.DBus.Introspectable.Introspect
输出为:
method return sender=:1.7 -> dest=:1.32 reply_serial=2 string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node> <node name="bluez"/> </node> "
接着查对象"/org/bluez":
$ dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez org.freedesktop.DBus.Introspectable.Introspect
输出为:
method return sender=:1.7 -> dest=:1.33 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/org/bluez">
<interface name="org.bluez.Manager">
<method name="InterfaceVersion">
<arg type="u" direction="out"/>
</method>
<method name="DefaultAdapter">
<arg type="s" direction="out"/>
</method>
<method name="FindAdapter">
<arg type="s" direction="in"/>
<arg type="s" direction="out"/>
</method>
<method name="ListAdapters">
<arg type="as" direction="out"/>
</method>
<method name="FindService">
<arg type="s" direction="in"/>
<arg type="s" direction="out"/>
</method>
<method name="ListServices">
<arg type="as" direction="out"/>
</method>
<method name="ActivateService">
<arg type="s" direction="in"/>
<arg type="s" direction="out"/>
</method>
<signal name="AdapterAdded">
<arg type="s"/>
</signal>
<signal name="AdapterRemoved">
<arg type="s"/>
</signal>
<signal name="DefaultAdapterChanged">
<arg type="s"/>
</signal>
<signal name="ServiceAdded">
<arg type="s"/>
</signal>
<signal name="ServiceRemoved">
<arg type="s"/>
</signal>
</interface>
<interface name="org.bluez.Database">
<method name="AddServiceRecord">
<arg type="ay" direction="in"/>
<arg type="u" direction="out"/>
</method>
<method name="AddServiceRecordFromXML">
<arg type="s" direction="in"/>
<arg type="u" direction="out"/>
</method>
<method name="UpdateServiceRecord">
<arg type="u" direction="in"/>
<arg type="ay" direction="in"/>
</method>
<method name="UpdateServiceRecordFromXML">
<arg type="u" direction="in"/>
<arg type="s" direction="in"/>
</method>
<method name="RemoveServiceRecord">
<arg type="u" direction="in"/>
</method>
<method name="RegisterService">
<arg type="s" direction="in"/>
<arg type="s" direction="in"/>
<arg type="s" direction="in"/>
</method>
<method name="UnregisterService">
<arg type="s" direction="in"/>
</method>
<method name="RequestAuthorization">
<arg type="s" direction="in"/>
<arg type="s" direction="in"/>
</method>
<method name="CancelAuthorizationRequest">
<arg type="s" direction="in"/>
<arg type="s" direction="in"/>
</method>
</interface>
<interface name="org.bluez.Security">
<method name="RegisterDefaultPasskeyAgent">
<arg type="s" direction="in"/>
</method>
<method name="UnregisterDefaultPasskeyAgent">
<arg type="s" direction="in"/>
</method>
<method name="RegisterPasskeyAgent">
<arg type="s" direction="in"/>
<arg type="s" direction="in"/>
</method>
<method name="UnregisterPasskeyAgent">
<arg type="s" direction="in"/>
<arg type="s" direction="in"/>
</method>
<method name="RegisterDefaultAuthorizationAgent">
<arg type="s" direction="in"/>
</method>
<method name="UnregisterDefaultAuthorizationAgent">
<arg type="s" direction="in"/>
</method>
</interface>
<node name="service_audio"/>
<node name="service_input"/>
<node name="service_network"/>
<node name="service_serial"/>
</node>
"
我们看到了对象"/org/bluez"的所有接口。对象"/org/bluez"还有子节点"service_audio"、"service_input"、"service_network"和"service_serial"。 必要时我们可以接着查下去。d-feet的基本逻辑就是这样。 后面我们会自己实现一个dteeth。dteeth是命令行程序,可以遍历指定连接的对象树,列出所有对象的所有接口的方法和信号。
ubuntu10.04下pdf中文不显示或乱码问题
对于BlueZ的接口调用分析
gcc Makefile 入门(转载)
基于bluez程序编译时注意事项
在ubuntu中使用蓝牙
一,蓝牙存储设备
蓝牙适配器: CSR蓝牙, USB接口
1,加载蓝牙适配器
插入USB口之后,可以使用lsusb命令,看到:
Bus 004 Device 003: ID 1131:1001 Integrated System Solution Corp.
这是,运行hciconfig可以看到:
hci0: Type: USB
BD Address: 00:11:67:02:1A:03 ACL MTU: 678:8 SCO MTU: 48:5
DOWN
RX bytes:142289 acl:884 sco:0 events:823 errors:0
TX bytes:13302 acl:494 sco:0 commands:151 errors:0
运行:
hciconfig hci0 up
可以激活借口(这一步不做,hcitool scan无法运行)
3,配置bluez
默认的配置文件放在/etc/bluetooth目录
hcid.conf和pin不需要修改,安全方式已经被设置成auto,这是为了方便连接,用户可以自己修改,推荐不做修改,以防止有些设备连接不上。
4,重启bluetooth服务。
/etc/rc.d/init.d/bluetooth stop
/etc/rc.d/init.d/bluetooth start
5,扫描设备
运行hcitool scan可以得到:
Scanning ...
00:01:EC:0C:D2:00 Tree's T628
6,修改配置文件:
修改/etc/bluetooth/rfcomm.conf
将里面的:
device 11:22:33:44:55:66;
修改成hcitool scan的结果,也就是:
device 00:01:EC:0C:D2:00
保存退出。
7,创建设备:
运行
rfcomm_create_dev。
8,添加通道:
sdptool add --channel=1 DID SP DUN LAN FAX OPUSH FTP HS HF SAP NAP GN PANU HID CIP CTP A2SRC A2SNK SYNCML NOKID PCSUITE SR1
后面的参数不一定被支持,但是以防有些服务没有被打开,所以,干脆全部打开了。
rfcomm bind /dev/rfcomm0 电话的地址 通道(在rfcomm.conf中配置)
这里的命令应该写成:
rfcomm bind /dev/rfcomm0 00:01:EC:0C:D2:00 1
9,连接手机:
hcitool cc 电话的地址
也就是
hcitool cc 00:01:EC:0C:D2:00
这时候,可以看到手机上的蓝牙连接呈联通状态。
10,从计算机发送文件:
bluetooth-sendto
XP下硬盘安装ubuntu 9.04
Win PE + Ubuntu 9.10 双启动U盘的制作
Linux下Bluez的编程实现(4)
Welcome to the Open OBEX
project. The overall goal of this project is to make an open source
implementation of the Object Exchange (OBEX) protocol. OBEX is a session
protocol and can best be described as a binary HTTP protocol. OBEX is optimised
for ad-hoc wireless links and can be used to exchange all kind of objects like
files, pictures, calendar entries (vCal) and business cards (vCard).
OBEX was specified by the
IrDA™ (Infrared Data
Association), and although the protocol is very good for Infrared connections,
it is not limited to it. In fact OBEX does not specify the top or bottom API
making it very flexible and can run over most transports like TCP/IP and
Bluetooth. Therefore OBEX is also called IrOBEX when used over the Infrared
medium. There are some transport modules for serial links (cable OBEX) too.
Today, OBEX is builtin in
many devices e.g. PDA's like the Palm Pilot, and mobile phones
like the Sony
Ericsson
R320, R520, T68, T610, T630, K700 and many later phones, Siemens S25, S35, S45, S55, S65 Nokia NM207 and Nokia 9110
Communicator. The HP Scanner? CapShare 920 can also
talk OBEX in addition to JetSend. Microsoft Windows2000 has also
builtin OBEX support.
Link types currently supported by OpenOBEX are:
INFRARED DATA ASSOCIATION ® (IrDA) http://www.irda.org
USB-IF, Inc. http://www.usb.org
Bluetooth(TM) http://www.bluetooth.com
Support for TCP/IP links, file descriptors and custom transports
is also provided.
OBEX全称为Object Exchange,中文对象交换,所以称之为对象交换协议。它在此软件当中有着核心地位,文件传输和IrMC同步都会使用到它。OBEX协议构建在IrDA架构的上层. OBEX协议通过简单的使用“PUT”和“GET”命令实现在不同的设备、不同的平台之间方便、高效的交换信息。支持的设备广泛,例如PC,PDA,电话,摄像头,自动答录机,计算器,数据采集器,手表等等。OBEX协议定义了一种柔性的概念——objects。也即是对象。这些对象可以包括文件,诊断信息,电子商务卡片,银行的存款等等。Objects在这里没有高级的技术含义,而是视你的应用而定。OBEX协议小到可作“命令和控制”功能,例如对电视机,录像机等的操作。大道可以做很复杂的操作,例如数据库的事务处理和同步。
OBEX能够具有以下几个特点:
1、 友好的应用——可实现快速开发。
2、 紧缩——可用在资源有限的小型设备上。
3、 跨平台
4、 柔性的数据支持。
5、 方便的作为其他Internet传输协议的上层协议。
6、 可扩展性——提供了对未来需求的扩充支持而不影响以存在的实现。例如可扩展安全,数据压缩等。
7、 可测试可调试。
如下表:基于OBEX和BlueZ的数据传输的步骤
|
步骤 |
函数 |
意义 |
|
1 |
OBEX_Init() |
用于初始化一个obex instance handle; arg1:OBEX_TRANS_BLUETOOTH用于声明传输协议为bluetooth; arg2:callback function; arg3:flag=OBEX_FL_KEEPSERVER,接收到请求后,服务器可以继续接收其他客户端的请求; |
|
2 |
OBEX_SetUserData() |
设置用户自己的变量,该函数的使用完全取决于用户自己; |
|
3 |
BtOBEX_ServerRegister() |
一个专用于蓝牙协议的服务端函数,用于监听客户端发送的请求。该函数内部创建了一个 socket(调用socket(AF_BLUETOOTH,SOCK_STREAM,BTPROTO_RFCOMM)),bind该socket到本地蓝牙地址,将该socket转化为监听sockt,之后该socket才能够监听端口上来自客户端的连接请求; |
|
4 |
OBEX_HandleInput() |
函数用于读取并处理接收到的数据,如果没有数据到达,该函数将会阻塞;该函数内部调用了selet()函数,向系统登记了参数handle的客户端sockt与服务端sockt,让系 统监听socket上的事件,如果是服务端socket上有数据到达,则调用accept()函数为客户端创建一个新的sockt,如果OBEX_Init()的flag不是设置为OBEX_FL_KEEPSERVER,则关掉服务端socket,禁止其他客户端的连接请求; |
|
5 |
CALLBACK : (OBEX_EV_ACCEPTHINT) |
|
|
6 |
OBEX_ServerAccept() |
该函数返回上述客户端的socket。 函数内部重新创建了一个obex instance handle,并将上述服务器handle的参数复制到该handle,获得服务器handle的fd(accept为客户端创建的socket)后,清除服务器本身的fd; 该函数同时也为新创建的obex instance handle设置callback function及Userdata;至此,已经为客户端创建了一个与服务端完全独立的obex instance handle,此后该服务端的操作都由该handle标识,而服务器的socket则继续监听其他客户端的连接请求。 |
|
7 |
CALLBACK : OBEX_EV_REQDONE |
OBEX_CMD_CONNECT |
|
8 |
CALLBACK : OBEX_EV_REQHINT |
一个请求即将到来。 调用OBEX_ObjectSetRsp(object,OBEX_RSP_CONTINUE,OBEX_RSP_SUCCESS)设置响应操作码 |
|
9 |
CALLBACK : OBEX_EV_REQCHECK |
第一个接收到的请求包已经被解析 |
|
10 |
CALLBACK : OBEX_EV_PROGRESS |
收到n个此事件,说明客户端正在传输文件内容 |
|
11 |
CALLBACK : OBEX_EV_REQ |
OBEX_CMD_PUT OBEX_ObjectSetRsp(object,OBEX_RSP_CONTINUE,OBEX_RSP_SUCCESS)设置响应操作码;此时,客户端文件传输完毕,需进行处理: OBEX_ObjectGetNextHeader()分别取得文件的名称与内容; |
|
12 |
CALLBACK : OBEX_EV_REQDONE |
OBEX_CMD_PUT |
|
13 |
CALLBACK : OBEX_EV_REQHINT |
一个请求即将到来 |
|
14 |
CALLBACK : OBEX_EV_REQ |
OBEX_CMD_DISCONNECT OBEX_ObjectSetRsp(object,OBEX_RSP_SUCCESS,OBEX_RSP_SUCCESS)设置响应操作码 |
|
15 |
CALLBACK : OBEX_EV_REQDONE |
OBEX_CMD_DISCONNECT; OBEX_TransportDisconnect(handle)断开连接; 注意:该函数只是将socket关闭,并没有释放handle所占用的资源,所以在应用时需要释放handle占用的资源; |
|
16 |
OBEX_Cleanup() |
关掉obex handle并释放该handle占用的资源。 (该函数同样关闭了客户端及服务端socket) |
如下表:OpenOBEX的代码说明:
|
文件编号 |
文件名 |
作用 |
|
1 |
obex_connect.c/.h |
处理CONNECT PDU,打包和解包 |
|
2 |
obex_header.c/.h |
PDU处理的公共函数及数据类型定义 |
|
3 |
obex_object.c/.h |
对象处理函数,客户端和服务器公用代码 |
|
4 |
obex_server.c/.h |
服务器端处理代码 |
|
5 |
obex_client.c/.h |
客户端处理代码 |
|
6 |
obex_transport.c/.h |
传输接口的抽象 |
|
7 |
irobex.c/.h |
基于红外的传输方式 |
|
8 |
usbobex.c/.h |
基于USB的传输方式 |
|
9 |
inobex.c/.h |
基于TCP/IP的传输方式 |
|
10 |
btobex.c/.h |
基于蓝牙的传输方式 |
The main goal of this
project is to make mobile devices that feature the OBEX protocol and that
adhere to the OBEX FTP standard accessible using an open source implementation.
ObexFTP is a library
bundling everything needed for OBEX transfers and exposing it via a simple
interface. Quite a number of language bindings are provided using SWIG or other
means. There is a sample command line client "obexftp" and a server
"obexftpd" included. Besides FTP the ObexFTP library provides access
to the PUSH, GOEP and SYNCH services. It runs on Linux, FreeBSD, NetBSD and
Win32.
参考代码
D-bus官网,http://dbus.freedesktop.org/doc/dbus-tutorial.html
D-bus中文介绍,http://blog.sina.com.cn/s/blog_5412ede60100eml7.html
Bluetooth官网,www.bluetooth.com
Openobex官网,http://dev.zuckschwerdt.org/openobex/wiki/ObexIntroduction
Openobex
API,http://dev.zuckschwerdt.org/openobex/doxygen/
Linux下Bluez的编程实现(3)
物理信道(physical channel)是蓝牙系统的最底层结构,通过伪随机跳频序列、发送时槽定时、接入码及帧头编码来表征。蓝牙针对不同应用定义了一系列物理信道,包括用于匹克网内设通信的匹克网物理信道、用于寻呼设备的寻呼扫描物理信道和用于查找设备的查找扫描物理信道。两台设备必须采用相同的物理信道才能进行通信。
主从设备建立连接的过程就是建立相同匹克网信道的过程,该过程确保主从设备以同样的定时和次序进行载波频率的跳变,进行数据传输,同时可以根据匹克网接入码和帧头编码进行数据过滤和解析,避免和其他设备在同一个频段上的相撞。
寻呼扫描物理信道(page scan physical channel)用于主设备寻呼从设备,是设备建立连接的必经阶段。寻呼扫描跳频序列和寻呼请求帧的设备接入码(DAC)是由从设备物理地址运算,处于可被连接模式的从设备以固定的周期(由page scan
interval决定)在一个固定的时间窗(由page scan window决定)内以某个跳频频率监听主设备的寻呼请求。
为了方便用户的使用,大多蓝牙设备都实现了自动连接功能,根据以上对蓝牙连接机制的分析,设计自动连接方案时必须考虑设备在不同的工作状态下采用不同的物理信道和跳频序列,而不能按照人为的逻辑随意设置,否则会给用户带来不便。
以车载免提装置为例,上电自动连接对驾驶员来说可以提高使用车载免提的自觉性,降低行车期间通话带来的风险。采用搜索方式判断设备是否在有效范围内,按照优先级从低到高连接,最后一次连接的手机为优先级最高的,然后按照配对列表的逆序而优先级依次降低。链路丢失后的自动连接只针对最后一部手机,这样可以实现服务的连续性。
蓝牙核心规范要求时钟频率精度为±20ppm,如果载波频率不稳定,则会发生“过零点”错误(zero-cross)。载波频率由本地时钟(晶振)做为PLL的参考时钟倍频产生。一般说来,蓝牙设备的时钟设计便是指晶振电路的设计及微调。决定晶振工作精度的两个重要参数是制造公差和温度稳定度,如果选择有源晶振作为本地时钟,需要满足:制造公差+温度稳定度≤±20ppm,有源晶振内部集成晶体和相应的振荡电路,匹配精确,频率稳定性高,而且抗干扰性能好,缺点是成本较高。
图1 震荡电路设计
本文自行设计的振荡电路如图1所示。
选择温度稳定性高、制造公差低的高精度晶体,通过振荡电路设计实现谐振频率的精确调整,这是由晶体负载电容的匹配及可调实现的。负载电容是指CRY_IN和CRY_OUT两端的电容值,在晶体的CRY_IN引脚上并联一个可调电容,调整该电容便可以对谐振频率进行精密微调。晶体负载电容计算公式如下:
Cload=Cint+(Cin+Ctrim)×Cout/(Cin+Ctrim+Cout)
Cint包括IC内部电容(一般为固定值)以及PCB杂散电容(3pF~5pF)。
为了保证链路级的安全,蓝牙通信要求设备在连接建立前进行“双向认证”。认证成功的前提是设备双方存储了相同的链路密钥Kab,配对是产生初始密钥Kint的阶段,Kint由PIN码、从设备蓝牙地址和主设备发给从设备的一个随机数由一套固定的算法计算出来,只要PIN码一致,主从设备生成的的Kint也是一致的。链路密钥的输入是主从设备的蓝牙地址和主从设备各一随机数,只要主从设备能互换随机数,便能得到一致的Kab。主设备将随机数RandA与Kint异或的结果发给从设备,从设备只将该结果与Kint异或便得到RandA,即
![]()
配对列表的管理包括添加、替代及删除,添加配对设备是在非易失性存储中存储该设备的蓝牙地址及Kab。删除配对设备需要谨慎处理,如果存在连接,需要先断开连接然后删除,因为如果删掉该设备而有一种应用的连接没有断开,会存在临时密钥用于当前应用,这时不经配对也可以连接上其他应用,违反了蓝牙安全性要求
3.1.5、蓝牙文件传输模式文件传输的目的是使两个终端之间的数据交换成为可能,传输时使用的协议如图3.1所示,可传送的文件有doc、jpg、ppt、xls、wav等文件,还包括远端文件夹浏览功能。传输文件的设备可归结成C/S结构。客户可从服务器下载文件,或向服务器上传文件。服务器是一种使用对象交换协议(OBEX)文件夹列表格式的远端蓝牙设备,其支持目标交换服务、文件夹浏览功能,还允许客户修改、创建文件或文件夹。
Host Controller Interface(HCI) 就是用来沟通Host和Module。Host通常就是PC, Module则是以各种物理连接形式(USB,serial,pc-card等)连接到PC上的bluetooth Dongle。HCI则比较特殊,它一部分在软件中实现,用来给上层协议和程序提供访问接口(Bluez中,hci.c hci_usb.c,hci_sock.c等).另一部分也是在Firmware中实现,用来将软件部分的指令等用底层协议明白的方式传递给底层。
居于PC的上层程序与协议和居于Modules的下层协议之间通过HCI沟通,有4种不同形式的传输:Commands, Event, ACL Data, SCO/eSCO Data.。
HCI Command:HCI Command是Host向Modules发送命令的一种方式。
HCI Event:Modules向Host发送一些信息,使用HCI Event。
对本地dongle进行操作:
a. 得到Host上插入Dongle数目以及Dongle信息
实现步骤如下:
// 0. 分配一个空间给 hci_dev_list_req。这里面将放所有Dongle信息。
struct hci_dev_list_req *dl;
struct hci_dev_req *dr;
struct hci_dev_info di;
int i;
if (!(dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t)))) {
perror("Can't allocate memory");
exit(1);
}
dl->dev_num = HCI_MAX_DEV;
dr = dl->dev_req;
//1. 打开一个HCI socket.
if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
perror("Can't open HCI socket.");
exit(1);
}
// 2. 使用HCIGETDEVLIST,得到所有dongle的Device ID。存放在dl中。
if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) {
perror("Can't get device list");
exit(1);
}
// 3 使用HCIGETDEVINFO,得到对应Device ID的Dongle信息。
di.dev_id = (dr+i)->dev_id;
ioctl(ctl, HCIGETDEVINFO, (void *) &di);
这样就能得到所有Dongle信息。
struct hci_dev_info {
uint16_t dev_id; //dongle Device ID
char name[8]; //Dongle name
bdaddr_t bdaddr; //Dongle bdaddr
uint32_t flags; //Dongle Flags:如:UP,RUNING,Down等。
uint8_t type; //Dongle连接方式:如USB,PC Card,UART,RS232等。
uint8_t features[8];
uint32_t pkt_type;
uint32_t link_policy;
uint32_t link_mode;
uint16_t acl_mtu;
uint16_t acl_pkts;
uint16_t sco_mtu;
uint16_t sco_pkts;
struct hci_dev_stats stat; //此Dongle的数据信息,如发送多少个ACL Packet,正确多少,错误多少,等等。
};
b. 打开一个HCI Socket---int hci_open_dev(int dev_id)
这个function用来打开一个HCI Socket。它首先打开一个HCI protocol的Socket(房间),并将此Socket与device ID=参数dev_id的Dongle绑定起来。只有bind后,它才将Socket句柄与Dongle对应起来。
注意,所有的HCI Command发送之前,都需要使用 hci_open_dev打开并绑定。
c. 关闭一个HCI Socket
int hci_close_dev(int dd) //简单的关闭使用hci_open_dev打开的Socket。
d. 向HCI Socket(对应一个Dongle)发送 request
int hci_send_req(int dd, struct hci_request *r, int to)
BlueZ提供这个function非常有用,它可以实现一切Host向Modules发送Command的功能。
参数1:HCI Socket。
参数2:Command内容。
参数3:以milliseconds为单位的timeout.
下面详细解释此function和用法:
当应用程序需要向Dongle(对应为一个bind后的Socket)发送Command时,调用此function.
其中,参数一dd对应一个使用hci_open_dev()打开的Socket(Dongle)。
参数三to则为等待Dongle执行并回复命令结果的timeout.以毫秒为单位。
参数二hci_request * r 最为重要,首先看它的结构:
struct hci_request {
uint16_t ogf; //Opcode Group
uint16_t ocf; //Opcode Command
int event; //此Command产生的Event类型。
void *cparam; //Command 参数
int clen; //Command参数长度
void *rparam; //Response 参数
int rlen; //Response 参数长度
};
ogf,ocf不用多说,对应前面的图就明白这是Group Code和Command Code。这两项先确定下来,然后可以查HCI Spec。察看输入参数(cparam)以及输出参数(rparam)含义。至于他们的结构以及参数长度,则在~/include/net/bluetooth/hci.h中有定义。
例1:得到某个连接的Policy Setting.
HCI Spec以及~/include/net/bluetooth/hci.h中均可看到,OGF=OGF_LINK_POLICY(0x02). OCF=OCF_READ_LINK_POLICY(0x0C).
因为这个Command用来读取某个ACL连接的Policy Setting。所以输入参数即为此连接Handle.
返回参数则包含3部分,status(Command是否顺利执行), handle(连接Handle)。 policy(得到的policy值)
这就又引入了一个新问题,如何得到某个ACL连接的Handle。
可以使用ioctl HCIGETCONNINFO得到ACL 连接Handle。
ioctl(dd, HCIGETCONNINFO, (unsigned long) cr);
Connect_handle = htobs(cr->conn_info->handle);
所以完整的过程如下:
struct hci_request HCI_Request;
read_link_policy_cp Command_Param;
read_link_policy_rp Response_Param;
// 1.得到ACL Connect Handle
if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0)
{
return -1;
}
Connect_handle = htobs(cr->conn_info->handle);
memset(&HCI_Request, 0, sizeof(HCI_Request));
memset(&Command_Param, 0 , sizeof(Command_Param));
memset(&Response_Param, 0 , sizeof(Response_Param));
// 2.填写Command输入参数
Command_Param.handle = Connect_handle;
HCI_Request.ogf = OGF_LINK_POLICY; //Command组ID
HCI_Request.ocf = OCF_READ_LINK_POLICY; //Command ID
HCI_Request.cparam = &Command_Param;
HCI_Request.clen = READ_LINK_POLICY_CP_SIZE;
HCI_Request.rparam = &Response_Param;
HCI_Request.rlen = READ_LINK_POLICY_RP_SIZE;
if (hci_send_req(dd, &HCI_Request, to) < 0)
{
perror("\nhci_send_req()");
return -1;
}
//如果返回值状态不对
if (Response_Param.status) {
return -1;
}
//得到当前policy
*policy = Response_Param.policy;
e. 几个更基础的function
static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src) //bdaddr copy
static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2)//bdaddr 比较
f. 得到指定Dongle BDAddr
int hci_read_bd_addr(int dd, bdaddr_t *bdaddr, int to);
参数1:HCI Socket,使用hci_open_dev()打开的Socket(Dongle)。
参数2:输出参数,其中会放置bdaddr.
参数3:以milliseconds为单位的timeout.
g. 读写Dongle Name
int hci_read_local_name(int dd, int len, char *name, int to)
int hci_write_local_name(int dd, const char *name, int to)
参数1:HCI Socket,使用hci_open_dev()打开的Socket(Dongle)。
参数2:读取或设置Name。
参数3:以milliseconds为单位的timeout.
注意:这里的Name与IOCTL HCIGETDEVINFO 得到hci_dev_info中的name不同
h. 得到HCI Version
int hci_read_local_version(int dd, struct hci_version *ver, int to)
i. 得到已经UP的Dongle BDaddr
int hci_devba(int dev_id, bdaddr_t *bdaddr);
dev_id: Dongle Device ID.
bdaddr:输出参数,指定Dongle如果UP, 则放置其BDAddr
j. 得到BDADDR不等于参数bdaddr的Dongle Device ID
int hci_get_route(bdaddr_t *bdaddr)
查找Dongle,发现Dongle Bdaddr不等于参数bdaddr的第一个Dongle,则返回此Dongle Device ID。
所以,如果: int hci_get_route(NULL),则得到第一个可用的Dongle Device ID。
k. 将BDADDR转换为字符串
int ba2str(const bdaddr_t *ba, char *str)
对远程dongle进行操作:
1. inquiry 远程Bluetooth Device
int hci_inquiry(int dev_id, int len, int nrsp, const uint8_t *lap, inquiry_info **ii, long flags)
hci_inquiry()用来命令指定的Dongle去搜索周围所有bluetooth device.并将搜索到的Bluetooth Device bdaddr 传递回来。
参数1:dev_id:指定Dongle Device ID。如果此值小于0,则会使用第一个可用的Dongle。
参数2:len: 此次inquiry的时间长度(每增加1,则增加1.25秒时间)
参数3:nrsp:此次搜索最大搜索数量,如果给0。则此值会取255。
参数4:lap:BDADDR中LAP部分,Inquiry时这块值缺省为0X9E8B33.通常使用NULL。则自动设置。
参数5:ii:存放搜索到Bluetooth Device的地方。给一个存放inquiry_info指针的地址,它会自动分配空间。并把那个空间头地址放到其中。
参数6:flags:搜索flags.使用IREQ_CACHE_FLUSH,则会真正重新inquiry。否则可能会传回上次的结果。
返回值是这次Inquiry到的Bluetooth Device 数目。
注意:如果*ii不是自己分配的,而是让hci_inquiry()自己分配的,则需要调用bt_free()来帮它释放空间。
2. 得到指定BDAddr的reomte device Name
int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name, int to)
参数1:使用hci_open_dev()打开的Socket。
参数2:对方BDAddr.
参数3:name 长度。
参数4:(out)放置name的位置。
参数5:等待时间。
3. 读取连接的信号强度
int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to)
注意,所有对连接的操作,都会有一个参数,handle.这个参数是连接的Handle。 前面讲过如何得到连接Handle的。
Logical Link Control and Adaptation Protocol(L2CAP)逻辑连接控制和适配协议为上层协议提供面向连接和无连接的数据服务,并提供多协议功能和分割重组操作。L2CAP 充许上层协议和应用软件传输和接收最大长度为 64K 的 L2CAP 数据包。
L2CAP 基于通道(channel) 的概念。 通道 (Channel) 是位于基带 (baseband) 连接之上的逻辑连接。每个通道以多对一的方式绑定一个单一协议 (single protocol)。多个通道可以绑定同一个协议,但一个通道不可以绑定多个协议。 每个在通道里接收到的 L2CAP 数据包被传到相应的上层协议。 多个通道可共享同一个基带连接。也就是说,所有L2CAP数据均通过HCI传输到Remote Device。且上层协议的数据,大都也通过L2CAP来传送。L2CAP可以发送Command。例如连接,断连等等。
L2CAP编程非常重要,它和HCI基本就是Linux Bluetooth编程的基础了。几乎所有协议的连接,断连,读写都是用L2CAP连接来做的。
1.创建L2CAP Socket
socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
domain=PF_BLUETOOTH, type可以是多种类型。protocol=BTPROTO_L2CAP.
2.绑定:
// Bind to local address
memset(&addr, 0, sizeof(addr));
addr.l2_family = AF_BLUETOOTH;
bacpy(&addr.l2_bdaddr, &bdaddr); //bdaddr为本地Dongle BDAddr
if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("Can't bind socket");
goto error;
}
3.连接
memset(&addr, 0, sizeof(addr));
addr.l2_family = AF_BLUETOOTH;
bacpy(addr.l2_bdaddr, src);
addr.l2_psm = xxx;
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("Can't connect");
goto error;
}
注意:
struct sockaddr_l2 {
sa_family_t l2_family; //必须为 AF_BLUETOOTH
unsigned short l2_psm; //与前面PSM对应,这一项很重要
bdaddr_t l2_bdaddr; //Remote Device BDADDR
unsigned short l2_cid;
};
4. 发送数据到Remote Device:
send()或write()都可以。
5. 接收数据:
revc() 或read()
以下为实例:
注:在Bluetooth下,主动去连接的一端作为主机端。被动等别人连接的作为Client端。
背景知识1:Bluetooth设备的状态
之前HCI编程时,是用 ioctl(HCIGETDEVINFO)得到某个Device Info(hci_dev_info).其中flags当时解释的很简单。其实它存放着Bluetooth Device(例如:USB Bluetooth Dongle)的当前状态:
其中,UP,Down状态表示此Device是否启动起来。可以使用ioctl(HCIDEVUP)等修改这些状态。
另外:就是Inquiry Scan, PAGE Scan这些状态:Inquiry Scan状态表示设备可被inquiry. Page Scan状态表示设备可被连接。
参考:L2CAP编程,http://blog.csdn.net/baozhongchao/archive/2009/10/26/4728751.aspx
Service Discovery Protocol(SDP)提供一种能力,让应用程序有方法发现哪种服务可用以及这种服务的特性。服务发现协议(SDP或Bluetooth SDP)在蓝牙协议栈中对蓝牙环境中的应用程序有特殊的含意,发现哪个服务是可用的和确定这些可用服务的特征。SDP定义了bluetooth client发现可用bluetooth server服务和它们的特征的方法。这个协议定义了客户如何能够寻找基于特定属性的服务而不让客户知道可用服务的任何知识。SDP提供发现新服务的方法,在当客户登录到正在操作的蓝牙服务器的一个区域时是可用的时。
Service discovery机制提供client应用程序侦测server应用程序提供的服务的能力,并且能够得到服务的特性。服务的品质包含服务type或服务class。SDP也提供SDP server与SDP client之间的通讯。SDP server维护着一个服务条目(service record)列表.每个服务条目描述一个单独的服务属性。 SDP client可以通过发送SDP request来得到服务条目。如果一个client或者依附于client之上的应用程序决定使用某个service. 它创建一个单独的连接到service提供者。 SDP 只提供侦测Service的机制,但不提供如何利用这些Service的机制。这里其实是说:SDP只提供侦测Service的办法,但如何用,SDP不管。每个Bluetooth Device最多只能拥有一个SDP Server。如果一个Bluetooth Device只担任Client,那它不需要SDP Server。但一个Bluetooth Device可以同时担当SDP Server和SDP client.
Service Record(Service 条目):
一个service是一个实体为另一个实体提供信息,执行动作或控制资源。一个service可以由软件,硬件或软硬件结合提供。所有的Service信息都包含于一个Service Record内。一个Service Record 包含一个Service attribute(Service属性) list。在一个SDP Server内,每个Service Record拥有一个32-bit的唯一性数据。通常,这个唯一性只是在每个SDP Server内部。 如果SDP Server S1 和SDP Server S2拥有同样的一个Service Record。那他们在不同SDP Sever内的独特数值并不一定相同。SDP在SDP Server增加或减少Service Record时,并不会通知SDP client.
Service Attribute(Service 属性):
每个Service属性描述servcie的特性.一个Service Attribute由2部分:
Attribute ID + Attribute Value。
Attribute ID:16-bit无符号整数,用于区别一个Service Record内的其它属性。
Attribute Value:Attribute值。
Service Class:
每个Service 都是某个Service Class的实例. Service Class定义了Service Record中包含的Service 属性。属性ID,属性值都被定义好了。每个Service Class也有一个独特ID。这个Service Class标识符包含在属性值ServiceClassIDList属性中。并描绘为UUID。自从Service Record中的属性格式以及含义依赖于Service Class后,ServiceClassIDList属性变得非常重要。
Searching For Service:
Service Search transaction(事务?)允许client得到Service Record Handle。一旦SDP Client得到Service Record Handle,它就可以请求这个Record内具体属性的值。
如果某个属性值UUID,则可以通过查找UUID查到这个属性。
UUID: universally unique identifier.(唯一性标识符)
SDP协议栈使用request/response模式工作,每个传输过程包括一个request protocol data unit(PDU)和一个response PDU. SDP使用L2CAP连接传输数据。在发送Request PDU但未收到Response PDU之前,不能向同一个server再发送Request PDU。
PDU:protocol Data unit
PDU ID:用来识别PDU。
TransactionID:
用来识别Request PUD以及Response PUD。并用来对比某个Response PUD是否对应着Request PUD。
Linux下Bluez的编程实现(2)

The BlueZ D-Bus interfaces aim to provide
seamless Bluetooth technology integration into the desktop. A central Bluetooth
daemon "hcid"(planned to be renamed to bluetoothd) is responsible for
take care of all tasks that can’t or shouldn’t be handled inside the Linux
kernel. These jobs include PIN code and link key management for the
authentication and encryption, caching of device names and services and also
central control of the Bluetooth hardware. The interface exported allows to
abstract the internals of GNOME, KDE, Maemo, OpenMoko,
... applications from any technical details of the Bluetooth specification.
Even other application will get access to the Bluetooth technology without any
hassle.
Bluez和D-bus接口,提供了蓝牙技术和桌面系统的完美集成。蓝牙的中心守护进程hcid的职责就是处理那些不能被linux内核处理的任务,包括处理为鉴权和加密过程中需要的PIN码和密钥、缓存设备的名称和服务类型,同时也是蓝牙硬件的控制中心。
The BlueZ D-Bus services are exported through
the system message bus. Every D-Bus enabled desktop has a system message bus
instance running. This bus is used to broadcast system events, such as new
hardware devices, network connection status, and so forth. The session message
bus is not suitable for this architecture since the Bluetooth
hardware/connections are shared by all desktop sessions.
Bluez和D-bus服务通过系统消息总线提供。每个D - Bus使桌面有一个系统消息总线实例运行。这个bus是用来广播系统时间,如新的硬件设备、网络连接状态等等。会话消息总线对这种体系结构是不适合的,因为蓝牙硬件/连接被所有桌面会话共享。
The BlueZ D-Bus Architecture goal are:
·
Abstract Bluetooth HCI commands/events。
·
Provide an easy interface to setup Bluetooth adapter
and manage the services
Bluez和D-bus体系结构的目标:
l
抽象hci层命令和事件。
l
提供简单的接口来启动蓝牙适配器和管理蓝牙服务。
The hcid is the
main entity of the architecture. It implements methods to setup the Bluetooth adapters, retrieve remote device
properties, control the pairing procedure and control the services
registration/searches. The following figure shows a high level relationship
between the entities.
Hcid是该体系结构的主体。它实现启动蓝牙适配器的方法、获取远端设备属性、控制配对过程和控制服务的注册和搜索。下图显示了一个高层次的实体之间的关系。

什么是D-Bus?
D-BUS 是一种进程间通信的方式,从架构上来说,分为三层:
① 一个库,libdbus,允许2个进程间交换信息。
② 一个消息总线守护进程, 它使用libdbus库。其他进程都可以与它连接。它可以将消息从一个进程发给另外任意数量的其他进程。现在有一些基于特定应用框架的dbus库函数封装,例如libdbus-glib 和libdbus-qt,也有与一些语言绑定的形式,例如Python等。这些封装的API旨在令D-BUS编程更加简单,libdbus倾向于提供更低层次的调用。很多libdbus API只在绑定的组件中可用。
③ libdbus仅支持一对一的连接,就像原始 socket通讯方式一样。但它传递的不是以字节为单位的数据流,而是具有一定意义的消息包。消息的消息头部表示消息种类,消息体用来装载数据。 Libdbus也可以允许实现特定的传输通道,从而来完成比如像认证之类的应用细节(libdbus also
abstracts the exact transport used (sockets vs. whatever else), and handles
details such as authentication.)。
消息总线守护进程将D-bus上连接的所有程序构成一个轮形hub。Libdbus为中心,它和应用程序建立一对一的连接。每个应用程序通过通道发送消息到消息总线,然后总线进程将消息转发到其他连接到hub的应用程序。可以把消息总线理解为一个路由器。Dbus服务在一个操作系统中存在多个进程。第一个进程是一个全局进程,就如sendmail 或Apache 的系统守护进程一样。这个进程具有高度的安全限制,一般用于系统进程间的通讯。其他的dbus进程都是用户进程,针对于每个登录的用户建立。这些实例允许用户会话中的应用程序相互通信。Dbus全局进程和用户进程是相互独立的,他们并没有内在的依赖关系。
D-Bus应用
有很多种IPC或者网络通信系统,如:CORBA,DCE,DCOM,DCOP,XML-RPC,SOAP,MBUS,ICE等。Dbus的目的主要是下面两点:
1、在同一个桌面会话中,进行桌面应用程序之间的通讯。
2、桌面程序和内核或者守护进程之间通信。
D-Bus概念
对象路径(Native
Objects and Object Paths):D-Bus的底层接口,和libdbus相关,它提供一种叫对象路径(object
path),用于让高层接口绑定到各个对象中去,允许远端应用程序指向他们。Object path就像一个文件路径。
方法和信号(Methods and
Signals):每个对象都有一些成员,有两种成员:方法(methods)和信号(signals),在对象中,方法可以被调用。信号会被广播,感兴趣的对象可以处理这个信号,同时信号中也可以带有相关的数据。
接口(Interfaces):每个对象都有一个或者多个接口,一个接口就是多个方法和信号的集合。这个概念和Glib,
Qt或者Java中的是一致的。接口定义了对象实例的类型。dbus使用简单的命名空间字符串来表示接口,如org.freedesktop.Introspectable。可以说dbus接口相当于C++中的纯虚类。
代理(Proxies):使用代理对象就是让调用者感觉在直接使用远程对象一样。d-bus的底层接口完成了一些比较低级和繁琐的调用过程,比如必须先调用创建方法形成消息包,然后发送,然后等待接受和处理返回的消息。所以,高层的接口就可以使用代理对象提供的接口屏蔽这些细节。所以,当调用代理对象的方法时,代理内部会转换成dbus的方法调用,等待消息返回,对返回结果解包,返回给相应的方法。可以看看下面的例子,使用dbus底层接口编写的代码:
Message
message = new Message(”/remote/object/path”, “MethodName”, arg1, arg2);
Connection connection = getBusConnection();
connection.send(message);
Message reply = connection.waitForReply(message);
if (reply.isError()) {
}
else {
Object returnValue = reply.getReturnValue();
}
使用代理对象编写的代码:
Proxy
proxy = new Proxy(getBusConnection(), “/remote/object/path”);
Object returnValue = proxy.MethodName(arg1, arg2);
客户端代码减少很多。
总线名称(Bus
Names):当一个应用程序连接上bus daemon时,daemon会分配一个唯一的名字给它。以冒号(:)开始,这些名字在daemon的生命周期中是不会改变的,可以认为这些名字就是一个
IP地址。当这个名字映射到应用程序的连接上时,应用程序可以说拥有这个名字。同时应用可以声明额外的容易理解的名字,比如可以取一个名字
com.mycompany.TextEditor,可以认为这些名字就是一个域名。其他应用程序可以往这个名字发送消息,执行各种方法。
名字还有第二个重要的用途,可以用于跟踪应用程序的生命周期。当应用退出(或者崩溃)时,与bus的连接将被OS内核关掉,bus将会发送通知,告诉剩余的应用程序,该程序已经丢失了它的名字。名字还可以检测应用是否已经启动,这可以用来实现单实例启动程序。
地址(Addresses):使用d-bus的应用程序既可以是server也可以是client,server监听到来的连接,client连接到server,一旦连接建立,消息就可以流转。如果使用dbus
daemon,所有的应用程序都是client,daemon监听所有的连接,应用程序初始化连接到daemon。dbus地址指明server将要监听的地方,client将要连接的地方,例如,地址:unix:path=/tmp/abcdef表明
server将在/tmp/abcdef路径下监听unix域的socket,client也将连接到这个socket。一个地址也可以指明是TCP
/IP的socket,或者是其他的。
当使用bus
daemon时,libdbus会从环境变量中(DBUS_SESSION_BUS_ADDRESS)自动认识“会话daemon”的地址。如果是系统
daemon,它会检查指定的socket路径获得地址,也可以使用环境变量(DBUS_SESSION_BUS_ADDRESS)进行设定。当dbus中不使用daemon时,需要定义哪一个应用是server,哪一个应用是client,同时要指明server的地址,这不是很通常的做法。
D-bus工作原理
Calling a Method – Behind the Scenes
在dbus中调用一个方法包含了两条消息,进程A向进程B发送方法调用消息,进程B向进程A发送应答消息。所有的消息都由daemon进行分派,每个调用的消息都有一个不同的序列号,返回消息包含这个序列号,以方便调用者匹配调用消息与应答消息。调用消息包含一些参数,应答消息可能包含错误标识,或者包含方法的返回数据。
方法调用的一般流程:
1.使用不同语言绑定的dbus高层接口,都提供了一些代理对象,调用其他进程里面的远端对象就像是在本地进程中的调用一样。应用调用代理上的方法,代理将构造一个方法调用消息给远端的进程。
2.在DBUS的底层接口中,应用需要自己构造方法调用消息(method call message),而不能使用代理。
3.方法调用消息里面的内容有:目的进程的bus name,方法的名字,方法的参数,目的进程的对象路径,以及可选的接口名称。
4.方法调用消息是发送到bus daemon中的。
5.bus daemon查找目标的bus name,如果找到,就把这个方法发送到该进程中,否则,daemon会产生错误消息,作为应答消息给发送进程。
6.目标进程解开消息,在dbus底层接口中,会立即调用方法,然后发送方法的应答消息给daemon。在dbus高层接口中,会先检测对象路径,接口,方法名称,然后把它转换成对应的对象(如GObject,QT中的QObject等)的方法,然后再将应答结果转换成应答消息发给daemon。
7.bus daemon接受到应答消息,将把应答消息直接发给发出调用消息的进程。
8.应答消息中可以包容很多返回值,也可以标识一个错误发生,当使用绑定时,应答消息将转换为代理对象的返回值,或者进入异常。
bus daemon不对消息重新排序,如果发送了两条消息到同一个进程,他们将按照发送顺序接受到。接受进程并需要按照顺序发出应答消息,例如在多线程中处理这些消息,应答消息的发出是没有顺序的。消息都有一个序列号可以与应答消息进行配对。
Emitting a Signal – Behind the Scenes
在dbus中一个信号包含一条信号消息,一个进程发给多个进程。也就是说,信号是单向的广播。信号可以包含一些参数,但是作为广播,它是没有返回值的。
信号触发者是不了解信号接受者的,接受者向daemon注册感兴趣的信号,注册规则是”match rules”,记录触发者名字和信号名字。daemon只向注册了这个信号的进程发送信号。
信号的一般流程如下:
1.当使用dbus底层接口时,信号需要应用自己创建和发送到daemon,使用dbus高层接口时,可以使用相关对象进行发送,如Glib里面提供的信号触发机制。
2.信号包含的内容有:信号的接口名称,信号名称,发送进程的bus name,以及其他参数。
3.任何进程都可以依据”match rules”注册相关的信号,daemon有一张注册的列表。
4.daemon检测信号,决定哪些进程对这个信号感兴趣,然后把信号发送给这些进程
5.每个进程收到信号后,如果是使用了dbus高层接口,可以选择触发代理对象上的信号。如果是dbus底层接口,需要检查发送者名称和信号名称,然后决定怎么做。
pin_helper concept has been
removed starting with bluez-utils 3.X. and has been replaced with a feature
called passkey agents. An application that wants to handle passkey requests
must use the "hcid" security interface to register a passkey agent.
Currently, two types of passkey agents are supported: default and device
specific. A "specific" passkey agent handles all passkey requests for
a given remote device while a default handles all requests for which a specific
agent was not found. "specific" passkey agents are useful to address
pre-defined passkey values or environments where the user interaction is not
allowed/difficult.
When the CreateBonding method is
called the "hcid" daemon will verify if there is a link key stored in
the file system. If it is available an error is returned, and if not, a D-Bus
message is sent to the registered passkey agent asking for a passkey.
Each Passkey Agent is
represented by a D-Bus object path. The "hcid" distinguishes the
agents based on their unique bus names and their object paths.
Pin_help的理念在bluez-util 3.x时已经被移除,并被密钥代理所代替。任何想处理密钥请求的应用程序必须使用hcid安全接口来注册密钥代理。现在支持两种类型的密钥代理:默认和设备特定。一个"特定"的密钥代理处理所有远端的密钥请求,一个默认的密钥代理,处理特定的代理所没有发现的所有密钥请求。"特定"的密钥代理对处理那些和用户交互困难或者不允许用户交互的设备的预定义的密钥值或环境非常有用。
当CreateBonding方法被调用时,hcid守护进程将确认当前的文件系统中是否保存了链接密钥,若可以找到,就返回一个错误。若找不到,D-Bus消息将被发出来为密钥请求的设备,注册一个密钥代理。每个密钥代理被D-Bus对象路径所体现,hcid依据唯一的总线名称和对象路径来区分代理。
Architecture
·
Step 1: Represents the passkey agent registration
·
Step 2: Represents a client calling CreateBonding
·
Step 3: Represents the hcid asking for a passkey
valu
体系结构
l
第一步:表示密钥代理注册
l
第二步:表示客户调用CreateBonding
l
第三步:代表hcid请求密钥值。

Message Flow
In the following figure, the
"CreateBonding"
method call is hidden. The "PIN Request" HCI event is generated when
there is not a link available in the file system. In this case "Link Key
Request Negative Reply" command is sent triggering the "Pin
Request" event.
在下面得图表中,CreateBonding方法的调用被隐藏。当没有一个可用链接时,"PIN Request"HCI层事件被产生,在这种情况下,"Link Key
Request Negative Reply"命令被发送来回应"Pin Request" 事件。
·
Step 1: Represents the D-Bus message sent to
register the default/device specific passkey agent.
·
Step 2: Represents the HCI "PIN Request"
event sent by the Bluetooth Host Controller.
·
Step 3: Represents the D-Bus message sent to the
default/device speficic passkey agent requesting a passkey.
·
Step 4: Represents the "Auth Complete"
event where the status contains "LMP Response Timeout"(The remote
didn't type the passkey).
·
Step 5: Represents the "hcid" issuing a
"Cancel" to a previous Request call.
·
Step 6: Represents the D-Bus message sent to release
the passkey agent: basically sent when the hcid exits.
l
第一步:D-Bus发送消息来注册默认的或者特定的密钥代理。
l
第二步:蓝牙主机控制器发送HCI 层"PIN Request"事件。
l
第三步:D-Bus发送消息到默认的或者特定的密钥代理,请求密钥。
l
第四步:"Auth Complete"完成事件,如果这种状态包含"LMP
Response Timeout"。
l
第五步:hcid发出“Cancel”命令给D-Bus。
l
第六步:当hcid退出时,D - Bus的信息发送到释放密钥代理

Description¶
The "Adapter" interface provides methods
to setup the local adapter(s), search remote devices, pairing and search for
services.
This interface is considered stable and no change is
planned.
For each available adapter the hcid register an
adapter object instance provided in the path "/org/bluez/hci{0, 1, 2,
...}".
适配器接口提供,启动适配器、搜索设别、配对、和搜索服务的方法。这个接口被认为是稳定的,没有改变计划。对每个可用的适配器,hcid为其注册一个适配器对象实例,在路径
"/org/bluez/hci{0, 1, 2, ...}"里。
Description
Services that need ask the user to accept/reject an
operation such as accept OBEX objects or accept an incoming connection can use
the Security API to request
the userspace registered authorization agent responsible.
Development
warnings/recommendations
1
Just one Authorization request per time is allowed.
2
NotConnected error is returned if a connection is
not found between the devices is not found.
Trusted Devices
The BlueZ daemon keeps a list of trusted devices.
Trusted means that authorization is not required to accept incoming connections
or other operations that need the user response. Once a given device is added
to the list, the BlueZ daemon will reply authorized without call the
Authorization agent.
Authorization
Agent
Authorization agents are applications responsible
for address authorization requests. For more information check the BlueZ D-Bus
API and implementation references:
·
utils/daemon/auth-agent.c is a authorization agent
implementation able to handle device specific and default
·
bluez-gnome passkey/authorization
agent implementation distributed by the BlueZ community
Canceling
For security reason, only the requestor can cancel a
pending authorization operation.
Description
The purpose of bonding is to create a relation
between two Bluetooth devices based on a common link key (a bond). The link key
is created and exchanged(pairing) during the bonding procedure and is expected
to be stored by both Bluetooth devices, to be used for future
authentication[definition from Bluetooth Core Spec].
The Bonding procedure is done through the BlueZ Adapter interface.
Development
warnings/recommendations
1
Just one Bonding request per time is allowed.
2
Bonding is not allowed if there is a discovery
running.
3
Pending remote name is canceled if a CreateBonding
message is received.
4
The Bonding takes some seconds, therefore it is
recommended set a D-Bus callback to handle the message reply for this
operation.
Passkey Agent
Currently, two types of passkey agents are
supported:
5
Device Specific: handles all passkey requests for a
given remote device
6
Default: Handles the remaining requests(not
addressed by device specific agents)
For more information check the BlueZ D-Bus API and
implementation references:
·
utils/daemon/passkey-agent.c is a passkey agent
implementation able to handle device specific and default
·
bluez-gnome passkey agent
implementation distributed by the BlueZ community=