打印
[嵌入式linux]

高分求助:关于device_driver中probe函数的编写

[复制链接]
7362|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tiger84|  楼主 | 2010-3-15 13:31 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
小菜鸟最近在写一个SPI接口的AD驱动,用的是AD7888,主芯片是at91sam9260,在文件board-sam9260ek.c中,把结构体改成如下了
static struct spi_board_info ek_spi_devices[] = {
#if defined(CONFIG_AD7888)
        {   /* spi ad */
            .modalias   = "ad7888",
            .chip_select    = 0,
            .max_speed_hz   = 15 * 1000 * 1000,
            .bus_num    = 0,
            .mode  = SPI_MODE_0,
        },
#endif

#if defined(CONFIG_MMC_SPI)
{ /* DataFlash card */
  .modalias = "mmc_spi",
  .chip_select = 1,
  .max_speed_hz = 15 * 1000 * 1000,
  .bus_num = 0,  
},
#endif

};
然后参照其他的驱动,自己编写了一个AD7888的驱动,如下
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>

/*
* NOTE: this is an *AD* driver.  
* Handle SPI chips with the drivers/spi/
*
*/
struct ad7888_data {
struct spi_device *spi;
struct mutex  lock;
};

/*******************************************************************************
* Function Name  : void ad7888_init(void)
* Description    : ad7888 init
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
static void ad7888_init(void)
{
   
}
/*******************************************************************************
* Function Name  : void ad7888_exit(void)
* Description    : ad7888 exit
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
static void ad7888_exit(void)
{
   
}
/*******************************************************************************
* Function Name  : static int ad7888_read_reg(struct spi_device *spi, int reg)
* Description    : depend on the channel to read ad7888 ad data
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
static int ad7888_read_reg(struct spi_device *spi, int reg)
{
    char buf[2];
    buf[0] = reg;
    buf[1] = 0;
    spi_write_then_read(spi, buf, 1, buf, 2);
    return buf[1] << 8 | buf[0];
}
/*******************************************************************************
* Function Name  : static int ad7888_read_reg(struct spi_device *spi, int reg)
* Description    : depend on the channel to read ad7888 ad data
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
static int ad7888_probe(struct spi_device *spi)
{
    struct ad7888_data *ts = NULL;   
int         ret;
  
/* Chip description 暂时不加*/
//AD7888 上升沿接收数据,下降沿发送数据,选择模式0,16位数据(前4为0),MSB在前 默认可以不写  
spi->mode = SPI_MODE_0;
spi->bits_per_word = 8;
ret = spi_setup(spi);
if (ret < 0)
  return ret;
   
ts = kzalloc(sizeof(struct ad7888_data), GFP_KERNEL);
if (!ts)
{
     ret = -ENOMEM;
     return ret;
}  
mutex_init(&ts->lock);
ts->spi = spi_dev_get(spi);
dev_set_drvdata(&spi->dev, ts);

/* Ping the chip ... the status register is pretty portable,
  * unlike probing manufacturer IDs.  We do expect that system
  * firmware didn't write it in the past few milliseconds!
  */
  /*
ret = spi_w8r16(spi, AD_ADD8_REG);
if (ret <= 0) {
  dev_dbg(&spi->dev, "rdsr --> %d (%02x)\n", ret, ret);
  ret = -ENXIO;
  goto fail;
}
*/
    // 注册设备
   
   // ret = device_register(&ts->spi->dev);
// if (ret)
//  goto fail;
  
fail:
dev_dbg(&spi->dev, "probe err %d\n", ret);
dev_set_drvdata(&spi->dev, NULL);
mutex_destroy(&ts->lock);
kfree(ts);
return ret;
}
static int ad7888_remove(struct spi_device *spi)
{
    struct ad7888_data *ts;
ts = dev_get_drvdata(&spi->dev);
if (ts == NULL)
  return -ENODEV;
    dev_set_drvdata(&spi->dev, NULL);
    mutex_destroy(&ts->lock);
     // 注册设备
//   device_unregister(&ts->spi->dev);
   
kfree(ts);
  
return 0;   
}
static struct spi_driver ad7888_spi_driver = {
    .probe  = ad7888_probe,
    .remove = ad7888_remove,
    .driver = {
                    .name   = "ad7888",
                    .owner  = THIS_MODULE,                  
              .bus    = &spi_bus_type,
  
              },
};
static int __init ad7888_spi_init(void)
{   
    ad7888_init();
   
    return spi_register_driver(&ad7888_spi_driver);
}
static void __exit ad7888_spi_exit(void)
{
    ad7888_exit();
    spi_unregister_driver(&ad7888_spi_driver);
}
module_init(ad7888_spi_init);
module_exit(ad7888_spi_exit);
编译进去启动后,在/sys里面
#ls /sys/bus/spi/devices/spi1.0/
/sys/bus/spi/devices/spi1.0/bus/        /sys/bus/spi/devices/spi1.0/subsystem/
/sys/bus/spi/devices/spi1.0/driver/     /sys/bus/spi/devices/spi1.0/uevent
/sys/bus/spi/devices/spi1.0/modalias

# cat /sys/bus/spi/devices/spi1.0/modalias
ad7888

# ls /sys/bus/spi/drivers/
ad7888   mmc_spi  spidev
#


device和driver都找到了,但是我不知道probe函数怎么写,

它主要有2个作用,把这个驱动绑定到具体的SPI设备上,并能探测到存在的SPI设备,可能会配置一些在系统启动配置不了的参数。
以下是我写的probe函数
/*******************************************************************************
* Function Name  : static int ad7888_read_reg(struct spi_device *spi, int reg)
* Description    : depend on the channel to read ad7888 ad data
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
static int ad7888_probe(struct spi_device *spi)
{
    struct ad7888_data        *ts = NULL;         
        int         ret;
               
        /* Chip description 暂时不加*/

        //AD7888 上升沿接收数据,下降沿发送数据,选择模式0,16位数据(前4为0),MSB在前        默认可以不写         
        spi->mode = SPI_MODE_0;
        spi->bits_per_word = 8;

        ret = spi_setup(spi);
        if (ret < 0)
                return ret;

   
        ts = kzalloc(sizeof(struct ad7888_data), GFP_KERNEL);
        if (!ts)
        {
            ret = -ENOMEM;

            return ret;
        }               

        mutex_init(&ts->lock);
        ts->spi = spi_dev_get(spi);
        dev_set_drvdata(&spi->dev, ts);
        
        /* Ping the chip ... the status register is pretty portable,
         * unlike probing manufacturer IDs.  We do expect that system
         * firmware didn't write it in the past few milliseconds!
         */
         /*
        ret = spi_w8r16(spi, AD_ADD8_REG);
        if (ret <= 0) {
                dev_dbg(&spi->dev, "rdsr --> %d (%02x)\n", ret, ret);
                ret = -ENXIO;
                goto fail;
        }        
        */
    // 注册设备
   
   // ret = device_register(&ts->spi->dev);
//        if (ret)
//                goto fail;

               
        fail:
        dev_dbg(&spi->dev, "probe err %d\n", ret);
        dev_set_drvdata(&spi->dev, NULL);
        mutex_destroy(&ts->lock);
        kfree(ts);

        return ret;
}


各位指教一二,看probe函数还要哪些?

相关帖子

沙发
sinanjj| | 2010-3-15 14:50 | 只看该作者
这个事啊. 只能靠你自己多试几次.....

使用特权

评论回复
板凳
goosen| | 2010-3-15 15:19 | 只看该作者
$ cd drivers
$ find ./ | grep 'ad78'
./input/touchscreen/ad7879.c
./input/touchscreen/ad7877.c

可以参考下以上两个SPI驱动。既然用linux,就充分的利用已经提供有的源码。

使用特权

评论回复
地板
tiger84|  楼主 | 2010-3-15 15:36 | 只看该作者
我已经试的汗流浃背了,但总是觉得不是很清楚。
芯片还没买回来,但又总是感觉不那么对劲。

撇开具体的设备,抽象一点,照我的理解,:
以这个SPI设备为例,
系统初始化时,
(1)向内核注册这些板级信息体
spi_register_board_info(devices, nr_devices);
(2)然后是platform_device注册
platform_device_register(&at91sam9260_spi1_device);

因为用的是atmel的SPI控制器,所以有如下代码
static struct platform_driver atmel_spi_driver = {
        .driver                = {
                .name        = "atmel_spi",
                .owner        = THIS_MODULE,
        },
        .suspend        = atmel_spi_suspend,
        .resume                = atmel_spi_resume,
        .remove                = __exit_p(atmel_spi_remove),
};

static int __init atmel_spi_init(void)
{
        return platform_driver_probe(&atmel_spi_driver, atmel_spi_probe);
}
module_init(atmel_spi_init);

static void __exit atmel_spi_exit(void)
{
        platform_driver_unregister(&atmel_spi_driver);
}
这里主要是针对主控制器的,它的probe函数
static int __init atmel_spi_probe(struct platform_device *pdev)
{
        ------------------省略-----------------------
        ret = spi_register_master(master);
        if (ret)
                goto out_reset_hw;

        return 0;

out_reset_hw:
        spi_writel(as, CR, SPI_BIT(SWRST));
        clk_disable(clk);
        free_irq(irq, master);
out_unmap_regs:
        iounmap(as->regs);
out_free_buffer:
        dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer,
                        as->buffer_dma);
out_free:
        clk_put(clk);
        spi_master_put(master);
        return ret;
}
看下它的调用过程
atmel_spi_probe----->spi_register_master----->scan_boardinfo---->spi_new_device,相应的注释如下:
****************************************************************************
/**
* spi_register_master - register SPI master controller
* @master: initialized master, originally from spi_alloc_master()
* Context: can sleep
*
* SPI master controllers connect to their drivers using some non-SPI bus,
* such as the platform bus.  The final stage of probe() in that code
* includes calling spi_register_master() to hook up to this SPI bus glue.
*
* SPI controllers use board specific (often SOC specific) bus numbers,
* and board-specific addressing for SPI devices combines those numbers
* with chip select numbers.  Since SPI does not directly support dynamic
* device identification, boards need configuration tables telling which
* chip is at which address.
*
* This must be called from context that can sleep.  It returns zero on
* success, else a negative error code (dropping the master's refcount).
* After a successful return, the caller is responsible for calling
* spi_unregister_master().
*/
int spi_register_master(struct spi_master *master)
***************************************************************************
/* FIXME someone should add support for a __setup("spi", ...) that
* creates board info from kernel command lines
*/
static void scan_boardinfo(struct spi_master *master)
{
        struct boardinfo        *bi;

        mutex_lock(&board_lock);
        list_for_each_entry(bi, &board_list, list) {
                struct spi_board_info        *chip = bi->board_info;
                unsigned                n;

                for (n = bi->n_board_info; n > 0; n--, chip++) {
                        if (chip->bus_num != master->bus_num)
                                continue;
                        /* NOTE: this relies on spi_new_device to
                         * issue diagnostics when given bogus inputs
                         */
                        (void) spi_new_device(master, chip);
                }
        }
        mutex_unlock(&board_lock);
}
****************************************************************************
/**
* spi_new_device - instantiate one new SPI device
* @master: Controller to which device is connected
* @chip: Describes the SPI device
* Context: can sleep
*
* On typical mainboards, this is purely internal; and it's not needed
* after board init creates the hard-wired devices.  Some development
* platforms may not be able to use spi_register_board_info though, and
* this is exported so that for example a USB or parport based adapter
* driver could add devices (which it would learn about out-of-band).
*
* Returns the new device, or NULL.
*/
struct spi_device *spi_new_device(struct spi_master *master,
                                  struct spi_board_info *chip)
{
        struct spi_device        *proxy;
        int                        status;

        /* NOTE:  caller did any chip->bus_num checks necessary.
         *
         * Also, unless we change the return value convention to use
         * error-or-pointer (not NULL-or-pointer), troubleshootability
         * suggests syslogged diagnostics are best here (ugh).
         */

        proxy = spi_alloc_device(master);
        if (!proxy)
                return NULL;

        WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));

        proxy->chip_select = chip->chip_select;
        proxy->max_speed_hz = chip->max_speed_hz;
        proxy->mode = chip->mode;
        proxy->irq = chip->irq;
        strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
        proxy->dev.platform_data = (void *) chip->platform_data;
        proxy->controller_data = chip->controller_data;
        proxy->controller_state = NULL;

        status = spi_add_device(proxy);
        if (status < 0) {
                spi_dev_put(proxy);
                return NULL;
        }

        return proxy;
}
******************************************************************************
/**
* spi_add_device - Add spi_device allocated with spi_alloc_device
* @spi: spi_device to register
*
* Companion function to spi_alloc_device.  Devices allocated with
* spi_alloc_device can be added onto the spi bus with this function.
*
* Returns 0 on success; negative errno on failure
*/
int spi_add_device(struct spi_device *spi)
{
        static DEFINE_MUTEX(spi_add_lock);
        struct device *dev = spi->master->dev.parent;
        int status;

        /* Chipselects are numbered 0..max; validate. */
        if (spi->chip_select >= spi->master->num_chipselect) {
                dev_err(dev, "cs%d >= max %d\n",
                        spi->chip_select,
                        spi->master->num_chipselect);
                return -EINVAL;
        }

        /* Set the bus ID string */
        snprintf(spi->dev.bus_id, sizeof spi->dev.bus_id,
                        "%s.%u", spi->master->dev.bus_id,
                        spi->chip_select);


        /* We need to make sure there's no other device with this
         * chipselect **BEFORE** we call setup(), else we'll trash
         * its configuration.  Lock against concurrent add() calls.
         */
        mutex_lock(&spi_add_lock);

        if (bus_find_device_by_name(&spi_bus_type, NULL, spi->dev.bus_id)
                        != NULL) {
                dev_err(dev, "chipselect %d already in use\n",
                                spi->chip_select);
                status = -EBUSY;
                goto done;
        }

        /* Drivers may modify this initial i/o setup, but will
         * normally rely on the device being setup.  Devices
         * using SPI_CS_HIGH can't coexist well otherwise...
         */
        status = spi->master->setup(spi);
        if (status < 0) {
                dev_err(dev, "can't %s %s, status %d\n",
                                "setup", spi->dev.bus_id, status);
                goto done;
        }

        /* Device may be bound to an active driver when this returns */
        status = device_add(&spi->dev);
        if (status < 0)
                dev_err(dev, "can't %s %s, status %d\n",
                                "add", spi->dev.bus_id, status);
        else
                dev_dbg(dev, "registered child %s\n", spi->dev.bus_id);

done:
        mutex_unlock(&spi_add_lock);
        return status;
}
*******************************************************************************

有如下几个疑问:
(1)设备已经增加到平台设备里面了,平台设备和平台驱动都已经注册,并且在这个主控制器的probe函数里面已经把具体的device设备加进去了,那么我写spi 设备驱动时,比如这里的AD7888,只需要注册driver就行了,不知道我理解的是否对不?但是我对比了其他具体的设备驱动,发现那些设备又注册了一次设备。

(2)按照这样写驱动的话,一些读写及控制函数怎么加进去,我看了一些其他的驱动,
比如一个eeprom驱动
/*
* NOTE: this is an *EEPROM* driver.  The vagaries of product naming
* mean that some AT25 products are EEPROMs, and others are FLASH.
* Handle FLASH chips with the drivers/mtd/devices/m25p80.c driver,
* not this one!
*/
struct at25_data {
        struct spi_device        *spi;
        struct mutex                lock;
        struct spi_eeprom        chip;
        struct bin_attribute        bin;
        unsigned                addrlen;
};
就把读写函数都放在struct bin_attribute        bin;

一个SPI扩展IO芯片MAX7301是这样定义结构体的:
/*
* Some registers must be read back to modify.
* To save time we cache them here in memory
*/
struct max7301 {
        struct mutex        lock;
        u8                port_config[8];        /* field 0 is unused */
        u32                out_level;        /* cached output levels */
        struct gpio_chip chip;
        struct spi_device *spi;
};

把控制都放在struct gpio_chip chip;里面了。

而DS1305呢
/* register RTC ... from here on, ds1305->ctrl needs locking */
        rtc = rtc_device_register("ds1305", &spi->dev,
                        &ds1305_ops, THIS_MODULE);
这个比较好理解,就放在ds1305_ops里面了。

于是我打算把这个读写函数放在
struct ad7888_data {
        struct spi_device        *spi;
        struct mutex                lock;
};
这个结构体,

我对驱动理解很肤浅,各位提提建议

使用特权

评论回复
5
tiger84|  楼主 | 2010-3-15 15:46 | 只看该作者
sinanjj,我试的快晕倒了,呵呵,但是又只能继续试。
goosen,我的是2.6.27的,没有你说的那2个文件
[zhh@localhost linux-2.6.27]$ find ./ -iname *ad7*
./drivers/spi/ad7888.o
./drivers/spi/ad7888.c
./drivers/spi/.ad7888.o.cmd
./drivers/hwmon/ad7414.c
./drivers/hwmon/ad7418.c
./include/linux/spi/ad7877.h
./include/config/ad7888.h
./include/config/spi/ad7888.h


很奇怪,不知道为什么会多了几个文件,红色部分。

其实我有参考过其他的,但是还是摸不着门路。

使用特权

评论回复
6
goosen| | 2010-3-15 16:11 | 只看该作者
本帖最后由 goosen 于 2010-3-15 16:18 编辑

我的是2.6.31。找参考驱动的时候,优先找同一公司同一系列芯片的驱动。

或这里,
http://www.google.com/codesearch?hl=zh-CN&lr=&q=ad7877.c&sbtn=%E6%90%9C%E7%B4%A2

使用特权

评论回复
7
tiger84|  楼主 | 2010-3-15 16:36 | 只看该作者
atmel没有2.6.31的补丁,我先下个2.6.30的看下

使用特权

评论回复
8
goosen| | 2010-3-15 17:05 | 只看该作者
atmel没有2.6.31的补丁,我先下个2.6.30的看下
tiger84 发表于 2010-3-15 16:36


这里已更至2.6.32
http://maxim.org.za/at91_26.html

使用特权

评论回复
9
tiger84|  楼主 | 2010-3-15 17:22 | 只看该作者
谢谢goosen,2.6.30里面也有这2个设备的驱动了,不过还是蛮复杂的。不管了,先参照这2个文件改改吧。
打算把SPI相关再仔细看一遍,然后根据已有文件修改,争取今天能把文件改完

使用特权

评论回复
10
tiger84|  楼主 | 2010-3-15 22:01 | 只看该作者
简单的改了下
static int ad7888_probe(struct spi_device *spi)
{
    struct ad7888_data              *chip;
    struct ad7888_platform_data     *pdata;
    int    ret;

    //AD7888 上升沿接收数据,下降沿发送数据,选择模式0,16位数据(前4为0),MSB在前        默认可以不写       
    // 若需要更改,请在下面配置,配置后需要调用spi_setup
//        spi->mode = SPI_MODE_0;
//        spi->bits_per_word = 8;
//    ret = spi_setup(spi);
//        if (ret < 0)
//            return ret;


     /* assuming the driver requires board-specific data: */
    pdata = &spi->dev.platform_data;
    if (!pdata)
            return -ENODEV;            

    /* get memory for driver's per-chip state, set up driver data */
    chip = kzalloc(sizeof *chip, GFP_KERNEL);
    if (!chip)
            return -ENOMEM;   
    spi_set_drvdata(spi, chip);

    chip->spi = spi_dev_get(spi);

    /*
     // 用来确认器件在板子上
        ret = spi_w8r16(spi, AD_ADD8_REG);
        if (ret <= 0) {
                dev_dbg(&spi->dev, "rdsr --> %d (%02x)\n", ret, ret);
                ret = -ENXIO;
                goto fail;
        }       
        */

        fail:
        dev_dbg(&spi->dev, "probe err %d\n", ret);
        dev_set_drvdata(&spi->dev, NULL);
        kfree(chip);

        return ret;   
   
}
明天看是否能拿到器件,飞线调试。

使用特权

评论回复
11
tiger84|  楼主 | 2010-3-16 21:13 | 只看该作者
拿到器件,也飞线了,少飞了一根线,实验室关了,明天测试。

使用特权

评论回复
12
tiger84|  楼主 | 2010-3-19 13:27 | 只看该作者
最后是用class写的,经过2天的调试,OK了。

后来才发现已经有现成的可以用了,spidev。不过也收获了不少

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:专注ARM及linux性能优化

101

主题

862

帖子

0

粉丝