随笔-118  评论-133  文章-4  trackbacks-0
 
ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx
posted @ 2010-03-11 17:13 lfc 阅读(947) | 评论 (0)编辑 收藏
环境:Ubuntu 8.04
前提:安装好svn+apache2+php环境

1、不要使用ubuntu自带的,自行下载最新的websvn:
http://websvn.tigris.org/

2、解压到/var/www/websvn目录下

3、填写apache2配置文件,例如:/etc/apache2/mods-available/dav_svn.conf
添加:

<Location /websvn/>
Options FollowSymLinks
order allow,deny
allow from all
AuthType Basic
AuthName "Subversion Repository"   
</Location>

4、这时候你可以通过http://localhost/websvn进行访问,然后根据提示修改websvn配置文件(指定svn库路径等)

5、如果你希望直接使用http://localhost访问,可修改apache2默认配置文件:/etc/apache2/sites-available/default

修改“DocumentRoot /var/www”为“DocumentRoot /var/www/websvn”即可。


后记:

websvn使用php脚本进行解释,默认配置下可能会存在php分配内存不足的情况,需要修改php配置文件:

/etc/php5/apache2/php.ini

修改

memory_limit = 16M

memory_limit = 512M

即可


posted @ 2010-02-11 11:35 lfc 阅读(3273) | 评论 (7)编辑 收藏
前言:

    记得以前曾研究过Linux内核下i2c子系统,了解了i2c总线上,适配器、设备驱动的注册过程与使用方法,详细请查看:
    1、i2c总线上,适配器、设备驱动注册http://www.cnitblog.com/luofuchong/archive/2008/10/05/49881.html
    2、i2c总线使用http://www.cnitblog.com/luofuchong/archive/2008/10/05/49882.html

    最近因为项目的需要,又重新看了一遍linux下的i2c子系统,有了新的体会。俗话说,好记性不如烂笔头,还是记下来比较好,一来当作总结,二来希望起到启发后人的效果^_^

正文:

    Linux的世界充斥着各种各样的想法,i2c子系统同样,新、旧架构并存。至于旧的架构,网上有非常详细的介绍,本人不打算再去重复,咱们直奔新的架构去。

    Linux下的设备驱动,顾名思义,就是由设备和驱动来组成的,i2c总线上的设备驱动也不例外。

一、i2c设备的注册

第一步:
    记得以前的i2c设备驱动,设备部分喜欢驱动运行的时候动态创建,新式的驱动倾向于向传统的linux下设备驱动看齐,采用静态定义的方式来注册设备,使用接口为:
int __init
i2c_register_board_info(int busnum,
    struct i2c_board_info const *info, unsigned len)
{
    int status;

    mutex_lock(&__i2c_board_lock);

    /* dynamic bus numbers will be assigned after the last static one */
    if (busnum >= __i2c_first_dynamic_bus_num)
        __i2c_first_dynamic_bus_num = busnum + 1;//????

    for (status = 0; len; len--, info++) {
        struct i2c_devinfo    *devinfo;

        devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);//申请表示i2c设备的结构体空间
        if (!devinfo) {
            pr_debug("i2c-core: can't register boardinfo!\n");
            status = -ENOMEM;
            break;
        }
        /* 填写i2c设备描述结构 */
        devinfo->busnum = busnum;
        devinfo->board_info = *info;
        list_add_tail(&devinfo->list, &__i2c_board_list);//添加到全局链表__i2c_board_list中
    }

    mutex_unlock(&__i2c_board_lock);

    return status;
}

第二步:
    系统初始化的时候,会根据板级i2c设备配置信息,创建i2c客户端设备(i2c_client),添加到i2c子系统中:
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
    struct i2c_devinfo    *devinfo;

    mutex_lock(&__i2c_board_lock);
    list_for_each_entry(devinfo, &__i2c_board_list, list) {    //遍历全局链表__i2c_board_list
        if (devinfo->busnum == adapter->nr
                && !i2c_new_device(adapter,
                        &devinfo->board_info))
            printk(KERN_ERR "i2c-core: can't create i2c%d-%04x\n",
                i2c_adapter_id(adapter),
                devinfo->board_info.addr);
    }
    mutex_unlock(&__i2c_board_lock);
}

struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
    struct i2c_client    *client;
    int            status;

    client = kzalloc(sizeof *client, GFP_KERNEL);//创建i2c_client设备
    if (!client)
        return NULL;

    client->adapter = adap;

    client->dev.platform_data = info->platform_data;

    if (info->archdata)
        client->dev.archdata = *info->archdata;

    client->flags = info->flags;
    client->addr = info->addr;
    client->irq = info->irq;

    strlcpy(client->name, info->type, sizeof(client->name));

    /* a new style driver may be bound to this device when we
     * return from this function, or any later moment (e.g. maybe
     * hotplugging will load the driver module).  and the device
     * refcount model is the standard driver model one.
     */
    status = i2c_attach_client(client);//接入设备到i2c总线上
    if (status < 0) {
        kfree(client);
        client = NULL;
    }
    return client;
}

int i2c_attach_client(struct i2c_client *client)
{
    struct i2c_adapter *adapter = client->adapter;
    int res;

    /* Check for address business */
    res = i2c_check_addr(adapter, client->addr);//地址检测
    if (res)
        return res;

    client->dev.parent = &client->adapter->dev;
    client->dev.bus = &i2c_bus_type;

    if (client->driver)
        client->dev.driver = &client->driver->driver;

    if (client->driver && !is_newstyle_driver(client->driver)) {
        client->dev.release = i2c_client_release;
        client->dev.uevent_suppress = 1;
    } else
        client->dev.release = i2c_client_dev_release;

    snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
        "%d-%04x", i2c_adapter_id(adapter), client->addr);
    res = device_register(&client->dev);    //注册设备
    if (res)
        goto out_err;

    mutex_lock(&adapter->clist_lock);
    list_add_tail(&client->list, &adapter->clients);
    mutex_unlock(&adapter->clist_lock);

    dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
        client->name, client->dev.bus_id);

    if (adapter->client_register)  {
        if (adapter->client_register(client)) {
            dev_dbg(&adapter->dev, "client_register "
                "failed for client [%s] at 0x%02x\n",
                client->name, client->addr);
        }
    }

    return 0;

out_err:
    dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "
        "(%d)\n", client->name, client->addr, res);
    return res;
}

注:
    特别要提一下的是这个“i2c_check_addr”,引用<<i2c 源代码情景分析>>里的话:“i2c 设备的7 位地址是就当前i2c 总线而言的,是“相对地址”。不同的i2c 总线上的设备可以使用相同的7 位地址,但是它们所在的i2c 总线不同。所以在系统中一个i2c 设备的“绝对地址”由二元组(i2c 适配器的ID 和设备在该总线上的7 位地址)表示。”,所以这个函数的作用主要是排除同一i2c总线上出现多个地址相同的设备。

二、驱动的注册:

第一步:

static inline int i2c_add_driver(struct i2c_driver *driver)
{
    return i2c_register_driver(THIS_MODULE, driver);
}

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
    int res;

    /* Can't register until after driver model init */
    if (unlikely(WARN_ON(!i2c_bus_type.p)))
        return -EAGAIN;

    /* 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_lock);

    pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

    INIT_LIST_HEAD(&driver->clients);
    /* Walk the adapters that are already present */
    class_for_each_device(&i2c_adapter_class, NULL, driver,
                  __attach_adapter);

    mutex_unlock(&core_lock);
    return 0;
}

第二步:
    还记得在第一部分,我们注册了一i2c设备,其总线类型为i2c_bus_type,现在我们又在这总线上注册一驱动,那不得了,满足了设备+驱动的条件,i2c_bus_type总线上探测函数要被执行了。

struct bus_type i2c_bus_type = {
    .name        = "i2c",
    .dev_attrs    = i2c_dev_attrs,
    .match        = i2c_device_match,
    .uevent        = i2c_device_uevent,
    .probe        = i2c_device_probe,
    .remove        = i2c_device_remove,
    .shutdown    = i2c_device_shutdown,
    .suspend    = i2c_device_suspend,
    .resume        = i2c_device_resume,
};

static int i2c_device_probe(struct device *dev)
{
    struct i2c_client    *client = to_i2c_client(dev);
    struct i2c_driver    *driver = to_i2c_driver(dev->driver);
    int status;

    if (!driver->probe || !driver->id_table)
        return -ENODEV;
    client->driver = driver;
    if (!device_can_wakeup(&client->dev))
        device_init_wakeup(&client->dev,
                    client->flags & I2C_CLIENT_WAKE);
    dev_dbg(dev, "probe\n");

    status = driver->probe(client, i2c_match_id(driver->id_table, client));//调用具体i2c设备驱动的探测函数
    if (status)
        client->driver = NULL;
    return status;
}

    OK,剩下的就是具体i2c设备驱动的功夫了,在此不作深入研究。
posted @ 2009-10-27 17:54 lfc 阅读(5888) | 评论 (2)编辑 收藏
1、Linux设备驱动之USB HUB驱动:
http://blog.chinaunix.net/u1/51562/showart_1226612.html




posted @ 2009-10-18 22:40 lfc 阅读(931) | 评论 (0)编辑 收藏
又是好久没有更新了,同样记录下这段时间干的事情。
来到公司报道之后我就去了工厂,呆了一个多月,熟悉公司各种产品的生产过程。当然,就是和生产线上的小MM们聊聊天,哈哈,开玩笑了。这期间我的导师给我了个任务——一个UI的低成本实现。这个UI必须具有LCD显示、USB主机、网络等功能,要求就是低成本低成本再低成本;很自然选型选完之后就又变成搞ARM了。买了开发板,硬件就没有什么好说的了。任务的重点落在了软件上,刚开始我和导师达成的意见是代码裸奔(省去授权费用),或者穿个自己写的小OS。于是我花了2个星期的时间写了0.8(写了任务切换,其他没有最后写完)个类似uCOS功能的小OS,发现这种东西跑在ARM920T上实在发挥不出920T的功能,于是我又花了2个星期写了存储管理,打算加入MMU;但是写完发现就算系统能工作,我怎么调试应用程序是个大问题,因为用了 MMU,用户进程就不能和内核一起玩了。经过和导师的商量,我再次回到了Linux;目前的打算是使用免费的内核,自己写个类似TC的图形库,或者使用 Microwindows + FLTK之类的不要钱的东西。很浪费时间啊,花了4个星期才发现原来自己写系统真的是非常困难的。但是这4个星期也没有白花,有些附属产品,例如自己写的不需要stdlib库的malloc。其实我是第二次写这个东西了,第一次的不太成熟,用了几次就发现局限性了。现在写的这个是我啃了几天操作系统原理写的,应该相对好用,以后有空我会贴出代码来。
 
然后就是我做过的Linux开发过程了,其实说起来,这次开发我发现自己Linux真的什么都不懂。过去偷懒没有花时间好好看看Linux内核,现在造成很多麻烦,今天这些问题,熟悉内核的人只需要1分钟就能搞定了,我花了1天多。写下这个笔记,以便以后查阅使用。
 
第一个问题:启动Linux的时候LCD会全屏花屏大约0.5秒,然后左上角出现一块不明花斑。
 
这个问题相对简单。因为我在Bootloader里面打开了液晶显示,缓冲区映射在某个地址上,当内核初始化MMU的时候,LCD控制寄存器里缓冲区的位置信息就不对了,或者是Bootloader使用的缓冲区被内核的数据或代码覆盖,导致在内核初始化LCD之前,LCD花屏。
那个不明花斑其实是Linux的可爱小企鹅图片,但是可能因为缓冲区像素位宽和格式、LCD调色板设置等问题显示不出来。
 
花屏解决方法:在Bootloader加载系统之前关掉LCD控制器或者关掉LCD的背光,这样做比较简单。复杂的,就得改MMU映射部分代码,在修改了MMU映射之后,立即修改LCD缓冲的位置。反正我就关掉LCD控制器了,因为内核很快就会初始化LCD,Windows启动都黑屏呢,我们黑那么2秒钟也没有什么大不了的。
花斑解决方法:相信如果做产品的话,不需要显示什么企鹅给用户看,所以可以在内核选项里将这个企鹅logo关掉。具体位置在Device Drivers>Graphics support>Logo configuration,对应的宏相信在.config里面很容易找到。如果非要显示这个企鹅,那么可以在/drivers/video /console/fbcon.c里面找找,我也不知道怎么弄。
 
第二个问题:Linux启动之后,只要一段时间不动键盘(开发板上用IO扩展出来的键盘),LCD就会自动关闭(黑屏、显示慢慢消失之类),只要按下键盘就能恢复。
 
这个问题让我花了一天多的时间。其实如果是手持设备,这样也没有什么。但是我们公司的产品是要一直显示东西的,必须解决这个问题。我看了很多论坛,有不少人也遇到了这个问题,但是我刚才是搜索的时候,关键词不对,总找不到正确的答案。如果你遇到了同样的问题,而且不想看我的三脚猫分析,那么就在百度上搜索“blankinterval”、“setterm -blank 0”之类的,马上你就能找到简单的解决方案。
 
这个问题很容易让人想到屏幕保护和电源管理。的确,这是一种电源管理。但是,你却无法从Linux内核选项的电源管理中解决这个问题。我们一步步来。
 
首先,我测量到LCD的PCLK时钟消失了,这意味着内核把LCD控制器关掉了。于是,从LCD驱动程序着手。我用的是S3C2440,这是 2410的升级版,但是LCD控制器是一样的,在我拿到的开发板厂商给我做好驱动的内核里,驱动的位置在/drivers/video /s3c2410fb.c。为什么后面有个fb呢?这是Framebuffer的缩写,百度下你能找到很多关于它的解释。Framebuffer是所有 Linux下GUI程序对硬件操作的设备接口,位于/dev中,一般为fb0。在s3c2410fb.c中可以找到一个类似 s3c2410_disable_controller()这样名称的函数,我的驱动里叫pxafb_disable_controller(),可以看出这个驱动是从pxa处理器改的,当然厂家不一样名字也叫得不一样。里面有一句类似这样写的 __raw_writel(fbi->reg.lcdcon1 & ~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);,把这句话删掉LCD就不会关掉了。这是第一个层次,我也看到有人是这样做的。但是,这有问题,按键盘恢复后,原本显示在屏幕上的东西如果你不重画会消失,就算你重画了,也会看到屏幕的某些部分先黑了下,然后恢复了。当然如果你可以接受,那么就这样吧。
 
然后,可以很自然的想到是谁调用了这个函数,从源头把这个问题消除掉。但是情况却不是这样的。我搜索这个函数名,找到了一个 set_ctrlr_state()的函数调用了pxafb_disable_controller(),搜索set_ctrlr_state(),发现有个pxafb_task()调用了set_ctrlr_state(),但是到了pxafb_task()就没有办法再往上找了,因为这是提供给内核的一个任务,以指针传递函数入口。我对内核了解太不够了,花了很多时间看了很多论坛上的文章,机缘巧合之下,我找到了/drivers/char/vt.c 这个文件。vt.c我感觉应该是2.4内核的console.c和vt.c的结合体,应为它集成了console基本上所有功能函数,就ioctl在 vt_ioctl.c这个文件里。这个文件的主要作用是负责管理控制台,如控制台的模式(图形、字符)、向控制台输出等等。其中能找到一些如 do_blank_screen(),blank_screen_t()这样的函数,就是这些函数关闭了LCD控制器,修改任意一个都可以起作用。网上的一个解决方案是把blank_screen_t()变成空函数,但是我没有这样试过,我觉得已经来到了问题的根源附近,应该能从根本上解决。
 
我们先看下屏幕关闭问题的真正起因,看这个控制台初始化函数
static int __init con_init(void)
{
 const char *display_desc = NULL;
 struct vc_data *vc;
 unsigned int currcons = 0;
 acquire_console_sem();
 if (conswitchp)
  display_desc = conswitchp->con_startup();
 if (!display_desc) {
  fg_console = 0;
  release_console_sem();
  return 0;
 }
 init_timer(&console_timer);
 console_timer.function = blank_screen_t;
 if (blankinterval) {
  blank_state = blank_normal_wait;
  mod_timer(&console_timer, jiffies + blankinterval);
 }
// 这是对控制台定时器的初始化,定时器事件函数被连接到了blank_screen_t()
 /*
  * kmalloc is not running yet - we use the bootmem allocator.
  */
 for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
  vc_cons[currcons].d = vc = alloc_bootmem(sizeof(struct vc_data));
  visual_init(vc, currcons, 1);
  vc->vc_screenbuf = (unsigned short *)alloc_bootmem(vc->vc_screenbuf_size);
  vc->vc_kmalloced = 0;
  vc_init(vc, vc->vc_rows, vc->vc_cols,
   currcons || !vc->vc_sw->con_save_screen);
 }
 currcons = fg_console = 0;
 master_display_fg = vc = vc_cons[currcons].d;
 set_origin(vc);
 save_screen(vc);
 gotoxy(vc, vc->vc_x, vc->vc_y);
 csi_J(vc, 0);
 update_screen(vc);
 printk("Console: %s %s %dx%d",
  vc->vc_can_do_color ? "colour" : "mono",
  display_desc, vc->vc_cols, vc->vc_rows);
 printable = 1;
 printk("\n");
 release_console_sem();
#ifdef CONFIG_VT_CONSOLE
 register_console(&vt_console_driver);
#endif
 return 0;
}

其中引用了一个叫blankinterval的全局变量和一个console_time,我不知道内核的定时器是具体是怎么工作,但是这样的代码已经很明显了。这个定时器和电源管理宏PM_CONFIG没有任何关系,它是控制台的一部分。再看下blank_screen_t():
 
static void blank_screen_t(unsigned long dummy)
{
 blank_timer_expired = 1;
 schedule_work(&console_work);
}
 
可以发现vt.c开头的宏,static DECLARE_WORK(console_work, console_callback, NULL);,找到了console_callback()这个函数:
 
static void console_callback(void *ignored)
{
 acquire_console_sem();
 if (want_console >= 0) {
  if (want_console != fg_console &&
      vc_cons_allocated(want_console)) {
   hide_cursor(vc_cons[fg_console].d);
   change_console(vc_cons[want_console].d);
   /* we only changed when the console had already
      been allocated - a new console is not created
      in an interrupt routine */
  }
  want_console = -1;
 }
 if (do_poke_blanked_console) { /* do not unblank for a LED change */
  do_poke_blanked_console = 0;
  poke_blanked_console();
 }
 if (scrollback_delta) {
  struct vc_data *vc = vc_cons[fg_console].d;
  clear_selection();
  if (vc->vc_mode == KD_TEXT)
   vc->vc_sw->con_scrolldelta(vc, scrollback_delta);
  scrollback_delta = 0;
 }
 if (blank_timer_expired) {
  do_blank_screen(0);
  blank_timer_expired = 0;
 }
 release_console_sem();
}
 
再看do_blank_screen(),随着struct vc_data中的与fops类似指针跟踪下去,就可以找到驱动里面的相应代码了。写出来太麻烦,让我偷懒把。
 
小总结下,其实在控制台内部就有一个定时器,它负责在一定时间之后将显示关闭,而无视是否打开了电源管理功能。那这和Framebuffer有什么关系呢?我从一个很弱智的角度解释,内核刚启动的时候有这样一句输出:
Console: colour dummy device 80x30
在初始化LCD控制器(Framebuffer)之后,有这样一句输出:
Console: switching to colour frame buffer device 80x30
我就理解:这时候,内核把控制台(也不知道是console还是tty)切换到了Framebuffer上,大虾们赶快跳出来批判我吧,呵呵。
 
回到正题,从代码可以发现,根本的解决之道是让blankinterval = 0,blank_state就不会是blank_off之外的值,也就不会关闭屏幕了。
但是问题到这里还是没有完全解决,如果用户程序希望改变blankinterval来实现屏保(当然在我的系统上用不着);另外,一些程序改变了blankinterval,程序退出之后,屏幕在一段时间之后还是会关闭的。怎么才能在用户程序那头解决这个问题呢,这又耗费了我很多时间。
 
我在追查代码的过程中走了个弯路,认为修改控制台的模式可以不让黑屏现象出现,但是后来发现,这样可能会使控制台没有办法画图,不知道对不对。
忽略弯路,直接正解。vt.c中不是有很多操作控制台的函数么?看看是谁修改了blankinterval。于是搜索 blankinterval,发现setterm_command()修改了它,然后搜索setterm_command,找到了 do_con_trol()函数,搜索do_con_trol,找到了do_con_write()函数,搜索do_con_write,终于最终 BOSS现身了:con_write()函数。为什么说它是最终BOSS呢?看看这段:
static struct tty_operations con_ops = {
 .open = con_open,
 .close = con_close,
 .write = con_write,
 .write_room = con_write_room,
 .put_char = con_put_char,
 .flush_chars = con_flush_chars,
 .chars_in_buffer = con_chars_in_buffer,
 .ioctl = vt_ioctl,
 .stop = con_stop,
 .start = con_start,
 .throttle = con_throttle,
 .unthrottle = con_unthrottle,
};
熟悉fops的话你就能看出来了,这是对tty设备的文件操作函数的表。也就是说,在用户程序里,通过open函数打开/dev/tty,然后再用write函数就可以修改blankinterval了。原理是找到了,实践上有很大困难,那么多重函数调用,再看看do_con_trol()里面的switch语句,正常人都要发晕。好在伟大的百度为我们提供了很多信息:在命令行下,可以使用setterm -blank 0指令来设置blankinterval。哈哈,救星来了,赶快看看setterm的源代码。setterm属于util-linux包,搜索一下很容易找到。其中的perform_sequence()函数里有这样一段:
 
 /* -blank [0-60]. */
 if (opt_blank && vcterm)
  printf("\033[9;%d]", opt_bl_min);
 
真得很神奇啊,用个printf就可以在用户程序里解决这个问题,本来我是打算只说用printf解决的,看到原理我想会更舒服一些;况且,在我的系统上用printf是不行的。
但是!问题还没有完,往往在我们的系统中,LCD的虚拟控制台和控制台TTY不是同一个设备,也就是说,如果在程序里单纯的printf是不行的!这样只能修改你正在使用的TTY的blankinterval,而你用的却是文本方式的设备,不存在黑屏问题。
于是,就需要仔细比较/dev/console、/dev/tty、/dev/ttyn的设备号,在我的系统里,用户程序里/dev /console和/dev/tty都是5,说明他们是一个东西,/dev/ttyn是4,这才是FB上的虚拟控制台。但是/dev/ttyn不是正在使用的TTY,那么怎么printf呢?只好用write函数来解决了。
 
写这样一段代码:
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
 
void some_function()
{
 int f;
 f = open("/dev/tty0", O_RDWR);
 write(f, "\033[9;0]", 8);
 close(f);
}
问题终于解决了。
 
总结下,第二个问题有很多种解决方法:
1.修改LCD驱动,把关闭LCD控制器的函数变为空(不推荐)
2.修改vt.c中的blank_screen_t()函数,让其为空(在系统不需要使用关闭显示功能时推荐)
3.修改vt.c中的blankinterval,让其为0(系统可能需要使用关闭显示功能,而且希望系统上电后正常状态下不会关闭显示时推荐)
4.修改用户程序,加入设置blankinterval的代码(推荐)


注:
  佩服作者的求实精神,以前都是直接采用总结2来解决的,如果是现在的话可能会采用总结3来解决,偷懒嘛^_^
  另外,上面的总结4改成使用sys用户接口来修改定时器的话会更好^_^
posted @ 2009-09-07 09:50 lfc 阅读(1545) | 评论 (1)编辑 收藏
作者:刘旭晖 Raymond转载请注明出处
Email:colorant@163.com
BLOG:http://blog.csdn.net/colorant/
主页:http://sites.google.com/site/rgbbones/

原帖 http://blog.csdn.net/colorant/archive/2008/08/07/2782541.aspx

电阻式触摸屏的Controller从原理上,简单的说就是一个可切换通道的AD转换器,常见的有类似TSC2046这一系列的独立芯片,也有 Buildin在MPU里的集成模块。这里记录的主要是这类触摸屏驱动的调试中我所遇到过的一些问题,因为具体硬件平台和环境不同,有些问题,现象和原因 可能不一定与你的实际情况相同,仅供参考。

数据线上无法得到正确的数据

症状

读取的测量数据变化不定,几乎没有规律

分析

Tsc2046控制器在接收到8个bit的测量指令后,需要一段的时间完成测量和AD转换工作,这期间在其busy线上会输出高电平,直到电平拉低后在数 据线上才会输出数据。 而这期间,其片选信号从spec上看,应该要保持有效,所以在使用SPI接口时候,需要采用比如32位传送的方式,使得在传完8位指令后,继续保持片选和 时钟,已接收后续的测量结果数据,因为这期间无法测量busy信号,假定其为某一恒定时间,位移得到所需测量结果。分析数据不稳定的原因在于这段busy 等待时间在不同的测量情况下,长度可能不一定是确定的,导致无法准确判断应该提取SDO口上的哪段数据作为测量结果。

所以主要矛盾在于我所使用的CPU的SPI口的片选和时钟信号无法与数据线分开独立自由控制。

解决

采用GPIO口模拟SPI时序,从而可以自己控制clk和片选信号,因此此时可以测量busy线的信号,从而准确的得知何时tsc2046开始完成测量并输出测量结果。

busy线长期处于忙等待状态

症状

送完指令,等待busy拉低的过程中,busy信号始终为高

分析

仔细分析spec,发现在busy状态下,tsc2046依然需要clk来完成AD转换工作,所以在等待busy信号的时候,要同时继续保持CLK信号

解决

每读一次busy信号的同时,继续用GPIO口模拟一个周期的CLK信号

不断产生pendown中断

症状

在第一次触摸屏幕,pendown中断产生,并完成测量后,即使放开触摸屏,依然连续不断的有pendown中断信号产生,反复进入测量过程。

分析

测量触摸屏X+ pin 上的电平发现,即使在等待中断的过程中,X+ pin 也始终为低电平,而理论上,此时该pin应该为高电平,由于Y- pin 接地,在触摸屏被按下的时候,X+ pin 电压被拉低,从而产生pendown中断。

反复调试发现该pin电压为低的原因在于在上一次测量中,最后一次测量的是Z1,在触摸屏放开的时候,测量Z1的过程中,X+为低电平。而后回到等待中断 状态的时候,X+ pin 由于外部电容的原因,被上拉电阻重新拉高为高电平需要一段的时间,在此之前,如果打开中断,就会误判,错误的收到中断信号

解决

有几种办法可以解决:

减小外部电容
将中断由电平触发改为下降沿触发
在完成测量之后,打开中断之前,延迟一段时间。等待X+ Pin回到高电平状态

改变电容涉及到硬件电路改变,而改变触发方式则因为目前使用的中断线无法改成边沿触发,不可行,所以目前是用最后一种方式。


可以尝试改变测量顺序,先测Z1再测X,Y看是否这样可以避免最后一种方式所需的延迟。减小cpu占用率。

采集的数据有剧烈抖动

症状

触摸屏压下以后,即使保持位置不动,所取得的XY坐标也会不时发生较大范围的跳动,幅度达到十几个到几十个像素

分析

通常触摸屏的抖动是不可避免的,毕竟是一个模拟信号的AD转换的过程,但是正常抖动应该在几个像素以下。大范围的坐标抖动,说明AD转换采样取得的电压值有较大变化。理论上大致会有两类原因造成:

AD转换的参考电压跳动
取样PIN脚输入电压跳动

因此,外推一下,不外乎是:

参考电压或取样PIN脚电压受到干扰
取样时刻,对应PIN脚的电压值还未稳定
触摸屏本身质量原因(如贴合存在空隙等),触压时电阻值变化较大,造成输出电压的抖动

解决

具体解决时就要具体分析跳动的现象和规律了
通常如果是内置触摸屏控制模块的MPU,取样PIN脚数据受到干扰的可能性较大,因为这时候,电阻式触摸屏的4个PIN脚的走线相对独立的触摸屏控制芯片来说,可能会比较长,周边的其它信号线可能也比较多。要注意保护好信号。
后两者对应的措施包括延迟消抖,合理安排采样时间,间隔。 改进触摸屏本身等。

最后,抖动有时候是不能完全消除的,可以通过数据平滑的措施在一定程度上减弱抖动带来的干扰。

CPU占用率超高

症状

触摸笔压下后,CPU占用率迅速攀高到一个不合理的地步,松开后降低

分析

通常这种情况都是由于使用了不合理的查询手段来获取采样数据,例如忙等待AD转换的结束,采样频率过快等等

解决

理论上,所有这类IO设备都应该采用中断驱动的方式来获取数据。很遗憾的是,有些内置的触摸屏控制模块,转换结束后并不产生中断信号,只是设置一个状态寄 存器,需要由软件查询得到。这种情况下,如果转换完成时间不定,又没有较高精度的定时中断源,只能牺牲相应速度,在每次查询间隔之间睡眠一段足够长的时 间,让出CPU。 通常来说,在jiffies值为10ms间隔的系统上,最快每秒查询50-100次,也基本能够满足像手写输入这样的应用的需求了。
posted @ 2009-07-24 11:17 lfc 阅读(1734) | 评论 (1)编辑 收藏
SDL:
./configure --host=arm-none-linux-gnueabi --disable-video-qtopia --disable-video-dummy --disable-video-dga --disable-arts --disable-esd --disable-cdrom --disable-nasm --disable-video-x11 --enable-video-fbcon --enable-input-tslib --prefix=/home/luofc/work/toolchain/arm-2007q3/arm-none-linux-gnueabi

directfb:
1、configure
修改 MODULEDIR=$libdir/$MODULEDIRNAME 为 MODULEDIR=/lib/$MODULEDIRNAME
2、配置编译
export PKG_CONFIG_PATH=/home/luofc/work/toolchain/arm-2007q3/arm-none-linux-gnueabi/lib/pkgconfig
./configure --host=arm-none-linux-gnueabi --prefix=/home/luofc/work/toolchain/arm-2007q3/arm-none-linux-gnueabi --with-gfxdrivers=none --with-inputdrivers=tslib --enable-png=no --enable-jpeg=no --disable-tiff --enable-zlib --enable-sdl=no --enable-vnc=no --enable-gif=no --disable-x11

mplayer:
1、mplayer对于嵌入式下directfb的检测有问题,需要修改configure文件
1)修改
#include <directfb_version.h>为#include <directfb/directfb_version.h>
2)添加
--with-directfb-config=PATH path to directfb*-config

--with-directfb-config=*)
    _directfbconfig=`echo $ac_option | cut -d '=' -f 2`
    ;;

if test "$_directfb" = yes ; then
  echocheck "check for directfb"

  if test -z "$_directfbconfig" ; then
      die "please set the --with-gtk-config"
  else
    _inc_extra="$_inc_extra `$_directfbconfig --cflags 2>&1`"
    _libs_mplayer="$_libs_mplayer `$_directfbconfig --libs 2>&1`"
    echores "$_directfb (using $_directfbconfig)"
  fi
fi

2、配置编译
./configure --cc=arm-none-linux-gnueabi-gcc --host-cc=gcc --target=arm-linux --enable-cross-compile --disable-win32dll --disable-dvdread --enable-fbdev --disable-mencoder --disable-mp3lib --disable-live --enable-freetype --enable-ass --enable-directfb --with-freetype-config=/home/luofc/work/toolchain/arm-2007q3/arm-none-linux-gnueabi/bin/freetype-config --with-sdl-config=/home/luofc/work/toolchain/arm-2007q3/arm-none-linux-gnueabi/bin/sdl-config --with-directfb-config=/home/luofc/work/toolchain/arm-2007q3/arm-none-linux-gnueabi/bin/directfb-config


posted @ 2009-07-16 10:36 lfc 阅读(2597) | 评论 (0)编辑 收藏
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 阅读(2889) | 评论 (3)编辑 收藏
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 阅读(6029) | 评论 (0)编辑 收藏
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 阅读(2281) | 评论 (0)编辑 收藏
仅列出标题
共12页: First 4 5 6 7 8 9 10 11 12