高分求助:关于device_driver中probe函数的编写
小菜鸟最近在写一个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 mutexlock;
};
/*******************************************************************************
* 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;
buf = reg;
buf = 0;
spi_write_then_read(spi, buf, 1, buf, 2);
return buf << 8 | buf;
}
/*******************************************************************************
* 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_spispidev
#
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函数还要哪些? 这个事啊. 只能靠你自己多试几次..... $ cd drivers
$ find ./ | grep 'ad78'
./input/touchscreen/ad7879.c
./input/touchscreen/ad7877.c
可以参考下以上两个SPI驱动。既然用linux,就充分的利用已经提供有的源码。 我已经试的汗流浃背了,但总是觉得不是很清楚。
芯片还没买回来,但又总是感觉不那么对劲。
撇开具体的设备,抽象一点,照我的理解,:
以这个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; /* 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;
};
这个结构体,
我对驱动理解很肤浅,各位提提建议 sinanjj,我试的快晕倒了,呵呵,但是又只能继续试。
goosen,我的是2.6.27的,没有你说的那2个文件
$ 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
很奇怪,不知道为什么会多了几个文件,红色部分。
其实我有参考过其他的,但是还是摸不着门路。 本帖最后由 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 atmel没有2.6.31的补丁,我先下个2.6.30的看下 atmel没有2.6.31的补丁,我先下个2.6.30的看下
tiger84 发表于 2010-3-15 16:36 https://bbs.21ic.com/images/common/back.gif
这里已更至2.6.32
http://maxim.org.za/at91_26.html 谢谢goosen,2.6.30里面也有这2个设备的驱动了,不过还是蛮复杂的。不管了,先参照这2个文件改改吧。
打算把SPI相关再仔细看一遍,然后根据已有文件修改,争取今天能把文件改完 简单的改了下
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;
}
明天看是否能拿到器件,飞线调试。 拿到器件,也飞线了,少飞了一根线,实验室关了,明天测试。 最后是用class写的,经过2天的调试,OK了。
后来才发现已经有现成的可以用了,spidev。不过也收获了不少
页:
[1]