Hello All,
I am in the process of porting a 2.4 temperature sensor device driver (the National
Semiconductor LM70CILD-3 temperature sensor eval board) to the 2.6 Linux kernel
(specifically to v 2.6.15.3 <http://2.6.15.3>), with the intention of submitting it for inclusion.
All ok, except this: am stuck on inserting an entry in /sys instead of /proc for the
driver (as that is suggested as the new "correct" interface to userspace).
I have read some documentation on sysfs and Rubini's lddbus example in
the LDD3 book; however, i am still a little confused: do we really need
to create a new /sys/bus/<driver_name> for each device inserted into
the lernel at runtime? if (probably) not, where _exactly_ do i create
my entry, and of course, _how _ exactly?
FYI, my driver is a char driver and does not require a major/minor pair as the UI is via proc,
and hopefully now, sysfs.
(For those interested, pl find the source here: http://www.designergraphix.com/kaiwan/projects/lm70CILD3.c )
So i guess what i'm also trying to say is this: as i don't require a major/minor pair, i'm abviously
not using register_chrdev() or the cdev() interface..hence i don't have a kobject and auto-inclusion in
the sysfs tree (isn't that right?). So... how exactly do i get my sysfs hooks in - as the
device_create_file() API requires struct device and struct device_attribute parameters.
Have any of you come across sample code/howto/tutorial out there that demonstrates just this (creating
arbitrary sysfs hooks)? Request your help as i'm stuck here...(i also looked through
Documentation/filesystems/sysfs.txt but was unable to properly map it to code... ).
Perhaps, as the usual googling did not turn up a full-fledged howto on this topic, it's time for a
knowledgeable person(s) out there to write one? I feel this would be v useful in the Documentation/ branch..
Just a suggestion..
TIA, kaiwan.
On Wed, 15 Feb 2006 13:24:28 +0530, Kaiwan N Billimoria wrote:
>
>
>Hello All,
>
>I am in the process of porting a 2.4 temperature sensor device driver (the National
>Semiconductor LM70CILD-3 temperature sensor eval board) to the 2.6 Linux kernel
>(specifically to v 2.6.15.3 <http://2.6.15.3>), with the intention of submitting it for inclusion.
>All ok, except this: am stuck on inserting an entry in /sys instead of /proc for the
>driver (as that is suggested as the new "correct" interface to userspace).
>
>I have read some documentation on sysfs and Rubini's lddbus example in
>the LDD3 book; however, i am still a little confused: do we really need
>to create a new /sys/bus/<driver_name> for each device inserted into
>the lernel at runtime? if (probably) not, where _exactly_ do i create
>my entry, and of course, _how _ exactly?
>
>FYI, my driver is a char driver and does not require a major/minor pair as the UI is via proc,
>and hopefully now, sysfs.
>(For those interested, pl find the source here: http://www.designergraphix.com/kaiwan/projects/lm70CILD3.c )
>
>So i guess what i'm also trying to say is this: as i don't require a major/minor pair, i'm abviously
>not using register_chrdev() or the cdev() interface..hence i don't have a kobject and auto-inclusion in
>the sysfs tree (isn't that right?). So... how exactly do i get my sysfs hooks in - as the
>device_create_file() API requires struct device and struct device_attribute parameters.
>
>Have any of you come across sample code/howto/tutorial out there that demonstrates just this (creating
>arbitrary sysfs hooks)? Request your help as i'm stuck here...(i also looked through
>Documentation/filesystems/sysfs.txt but was unable to properly map it to code... ).
>
>Perhaps, as the usual googling did not turn up a full-fledged howto on this topic, it's time for a
>knowledgeable person(s) out there to write one? I feel this would be v useful in the Documentation/ branch..
>Just a suggestion..
>
>TIA, kaiwan.
>
>
Hmmm...
I don't know if this'll really help, but have a look at
drivers/firmware/edd.c
On Wed, Feb 15, 2006 at 01:24:28PM +0530, Kaiwan N Billimoria wrote:
> Hello All,
>
> I am in the process of porting a 2.4 temperature sensor device driver (the
> National Semiconductor LM70CILD-3 temperature sensor eval board) to the 2.6
> Linux kernel (specifically to v 2.6.15.3 <http://2.6.15.3>), with the
> intention of submitting it for inclusion. All ok, except this: am stuck on
> inserting an entry in /sys instead of /proc for the
> driver (as that is suggested as the new "correct" interface to userspace).
Have you read Documentation/hwmon/sysfs-interface? I think that,
combined with using the hwmon class code is what you want to use here.
Hope this helps,
greg k-h
>Seewer Philippe wrote:
>Hmmm...
>
>I don't know if this'll really help, but have a look at
>drivers/firmware/edd.c
>Greg KH wrote:
>Have you read Documentation/hwmon/sysfs-interface? I think that,
>combined with using the hwmon class code is what you want to use here.
>
>Hope this helps,
>
>greg k-h
>
>
>
Thanks, yes I shall look up both these..at first glance they do look
promising.
One thing i'd like to point out though, Greg: the LM70 is an
SPI/Microwire based system and not i2c; so straight away, the i2c
interface by itself will not be used...; also, the specific board
(LM70CILD-3, which i've written the 2.4 driver for & am now porting to
2.6), comes with a built-in parport interface..so that's what the driver
takes into account of course..
Also it's a relatively simple temperature sensor - it does not seem to
support hysteresis temperature, i/p voltages, etc. I'm saying all this
as the sysfs interface i envision is just a simple read-only hook: the
o/p value (after a little userspace massaging) is the temperature in
Celsius correct to 0.25 degrees. So it looks to me that this particular
driver necessitates a kind-of "custom" entry under /sys/class/hwmon with
it's own userspace support. Do I move ahead in this direction?
Regards,
kaiwan.
> Also it's a relatively simple temperature sensor - it does not seem to
> support hysteresis temperature, i/p voltages, etc. I'm saying all this
> as the sysfs interface i envision is just a simple read-only hook: the
> o/p value (after a little userspace massaging) is the temperature in
> Celsius correct to 0.25 degrees. So it looks to me that this particular
> driver necessitates a kind-of "custom" entry under /sys/class/hwmon with
> it's own userspace support.
try to avoid this!
It is very useful for EVERYONE to only have ONE interface to report such
temperatures. That way *all* applications will just work, and nobody
needs to do 5 to 50 different interfaces in their application.
Please try to see if it's possible to use the existing userspace
interface. Pretty please with sugar on top ;)
On Thu, Feb 16, 2006 at 05:03:45PM +0530, Kaiwan N Billimoria wrote:
> One thing i'd like to point out though, Greg: the LM70 is an
> SPI/Microwire based system and not i2c; so straight away, the i2c
> interface by itself will not be used...; also, the specific board
> (LM70CILD-3, which i've written the 2.4 driver for & am now porting to
> 2.6), comes with a built-in parport interface..so that's what the driver
> takes into account of course..
That's fine, you do not have to be a i2c driver to use the hwmon
interface. There are other drivers in the drivers/hwmon/ directory
today that are not i2c drivers. It is not a requirement at all.
> Also it's a relatively simple temperature sensor - it does not seem to
> support hysteresis temperature, i/p voltages, etc. I'm saying all this
> as the sysfs interface i envision is just a simple read-only hook: the
> o/p value (after a little userspace massaging) is the temperature in
> Celsius correct to 0.25 degrees. So it looks to me that this particular
> driver necessitates a kind-of "custom" entry under /sys/class/hwmon with
> it's own userspace support. Do I move ahead in this direction?
No. Use the same names for your files as is described in the document.
If you can't provide all of the different files, that's fine, just
provide what you can. The userspace tools will handle this properly.
This also keeps you from having to write custom userspace tools for
every individual program that wants to know this information.
thanks,
greg k-h
Hi Kaiwan,
> One thing i'd like to point out though, Greg: the LM70 is an
> SPI/Microwire based system and not i2c; so straight away, the i2c
> interface by itself will not be used...; also, the specific board
> (LM70CILD-3, which i've written the 2.4 driver for & am now porting to
> 2.6), comes with a built-in parport interface..so that's what the driver
> takes into account of course..
You must stay away from writing a driver for the board itself. What you
must write is in fact two different drivers:
1* A driver for the SPI interface of your board (basically a parallel
port <-> SPI bridge). This driver will expose the device as an SPI bus
to the rest of the kernel. This driver doesn't care about what chip is
plugged on it.
2* A driver for the LM70 temperature sensor chip, which doesn't care
about the chip location. This driver will use generic SPI commands as
offered by the spi kernel interface.
This modular approach makes it possible to then reuse each of the
drivers. If you later have a similar board for a different chip, the
first driver will still work (assuming the new board uses SPI and the
same wiring conventions). If you later have an LM70 chip on a different
physical interface, the second driver will still work.
You should take a look at how this was done for the Analog Devices
ADM1032 evaluation board, as this is really similar, except that the
ADM1032 uses I2C/SMBus instead of SPI.
The board itself is driven by the i2c-parport driver. This driver
exposes the board as a generic i2c interface to the rest of the kernel.
The bit-banging logic is common to various I2C bus driver and can be
found in i2c-algo-bit. The i2c-parport really only defines which bits
must be played with to control the I2C bus lines. SPI has an equivalent
helper driver named spi_bitbang. I've never used it myself but it must
be very similar. So your first driver would be similar to i2c-parport
except that it would use spi_bitbang instead of i2c-algo-bit. There
doesn't seem to be any in-tree user of spi_bitbang right now so you are
walking a relatively new path. Maybe the spi folks will be able to
guide you.
The ADM1032 chip is driven by the lm90 driver (National Semiconductor's
LM90 and Analog Devices' ADM1032 are fully compatible). This driver
uses the generic i2c/smbus interface. Thanks to this, it works with the
chip on the parallel port evaluation board, but also with compatible
chips found in laptops or desktop SMBus. Your lm70 driver would be
similar, except that it would use spi commands instead of i2c/smbus
commands. The sysfs interface should be very similar, except that yours
would be more simple if the LM70 is a single temperature chip.
> Also it's a relatively simple temperature sensor - it does not seem to
> support hysteresis temperature, i/p voltages, etc. I'm saying all this
> as the sysfs interface i envision is just a simple read-only hook: the
> o/p value (after a little userspace massaging) is the temperature in
> Celsius correct to 0.25 degrees. So it looks to me that this particular
> driver necessitates a kind-of "custom" entry under /sys/class/hwmon with
> it's own userspace support. Do I move ahead in this direction?
No, just do what every other hardware monitoring chip does, so that
support can be added for the lm70 chip in libsensors - then you win
instant support in all hardware monitoring application which rely on
libsensors, and even a few which do not.
It's really not a matter of how many features a chip has. Look at the
lm75 or w83l785ts driver, you'll see they have very few features as
well. It's a matter of having a common standard for exporting the
values to user-space, so that the same library or application can
handle all sources with minimum effort.
Thanks,
--
Jean Delvare
Jean Delvare wrote:
>Hi Kaiwan,
>
>You must stay away from writing a driver for the board itself. What you
>must write is in fact two different drivers:
>
>1* A driver for the SPI interface of your board (basically a parallel
>port <-> SPI bridge). This driver will expose the device as an SPI bus
>to the rest of the kernel. This driver doesn't care about what chip is
>plugged on it.
>
>2* A driver for the LM70 temperature sensor chip, which doesn't care
>about the chip location. This driver will use generic SPI commands as
>offered by the spi kernel interface.
>
>
>
Ok, i see your point..
>It's really not a matter of how many features a chip has. Look at the
>lm75 or w83l785ts driver, you'll see they have very few features as
>well. It's a matter of having a common standard for exporting the
>values to user-space, so that the same library or application can
>handle all sources with minimum effort.
>
>
>
Yes, again..
I shall start looking into these aspects & workin on it in the coming
week..am up to my ears in other stuff right now..
though how exactly i don;t know now :) ; will certainly require your
(and others) help on this..
>Thanks,
>
>
Thank you, your long reply was very enlightening;
Kaiwan.
Jean Delvare wrote:
>Hi Kaiwan,
>
>
>
--snip--
>You must stay away from writing a driver for the board itself. What you
>must write is in fact two different drivers:
>
>1* A driver for the SPI interface of your board (basically a parallel
>port <-> SPI bridge). This driver will expose the device as an SPI bus
>to the rest of the kernel. This driver doesn't care about what chip is
>plugged on it.
>
>
>
Hi Jean,
1. Yes, i know, long time since the above message..yet i'm happy to say
that i have built a (lightweight version) of the
SPI<->parport bridge (file pasted below), which is based on your
i2c-parport driver bridge code.
It is built as a header file: the driver developer basically appends
his/her adapter entry into the adapter_parm[] data structure and
includes this file in the device driver.
>2* A driver for the LM70 temperature sensor chip, which doesn't care
>about the chip location. This driver will use generic SPI commands as
>offered by the spi kernel interface.
>
>This modular approach makes it possible to then reuse each of the
>drivers. If you later have a similar board for a different chip, the
>first driver will still work (assuming the new board uses SPI and the
>same wiring conventions). If you later have an LM70 chip on a different
>physical interface, the second driver will still work.
>
>
>
2. I have also done this; i now have a working driver for a specific
chip (the NS lm70CILD-3 eval board) based on the (above mentioned)
SPi<->parport bridge. Basically, the "type 0" entry in the
adapter_parm[] data structure is the lm70CILD-3 entry. The driver
handles the other things, including creation of a sysfs hook (used to
query the temperature from userspace).
>No, just do what every other hardware monitoring chip does, so that
>support can be added for the lm70 chip in libsensors - then you win
>instant support in all hardware monitoring application which rely on
>libsensors, and even a few which do not.
>
>It's really not a matter of how many features a chip has. Look at the
>lm75 or w83l785ts driver, you'll see they have very few features as
>well. It's a matter of having a common standard for exporting the
>values to user-space, so that the same library or application can
>handle all sources with minimum effort.
>
>Thanks,
>
>
3. I still have to work on integrating the userspace conversion into
libsensors (as recommended by yourself & others).
4. My intention at this point of time is for you (and others) to take a
look at the spi-parport-light.h code and pl give me your feedback. I'm
not posting a patch at this time..
5. Also, because it's a header, am not sure what approach to take when
patching into the kernel; i mean, i can place it under the spi tree
(either drivers/spi or include/linux/spi) but can't add the usual
"obj-$(CONFIG_xxx) += xxx.o"
line (as it's a header). Or can I do something like this?? i'm really
quite uncertain, forgive me.. any suggestions on how i should go about this?
Thanks very much,
Kaiwan.
PS> If you'd like to see a usage example, i can post the lm70CILD3.c
driver code..
+++++++++++++++++++++ File spi-parport-light.h
+++++++++++++++++++++++++++++++++++++++
---
/*
------------------------------------------------------------------------ *
*
spi-parport-light.h *
* SPI bus over parallel
port *
*
------------------------------------------------------------------------ *
Copyright (C) 2006 Kaiwan N Billimoria <[email protected]>
Heavily based on i2c-parport-light.c
Copyright (C) 2003-2004 Jean Delvare <[email protected]>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
------------------------------------------------------------------------ */
#ifdef DATA
#undef DATA
#endif
#define DATA 0
#define STAT 1
#define CTRL 2
struct lineop {
u8 val;
u8 port;
u8 inverted;
};
struct adapter_parm {
char *name;
struct lineop setsda;
struct lineop setscl;
struct lineop setcsl; // chip select
struct lineop getsda;
struct lineop getscl;
struct lineop init;
struct spi_device *spidev;
};
static struct adapter_parm adapter_parm[] = {
/* type 0: National Semiconductor LM70CILD-3 evaluation board */
{
.name = "lm70CILD-3",
.setscl = { 0x40, DATA, 0 },
.setcsl = { 0x20, DATA, 1 },
.getsda = { 0x10, STAT, 0 }, // SI/O
.init = { 0xFE, DATA, 0 },
},
/* add adapters here */
};
/*----------------Module parameters-----------------------------*/
static int type;
module_param(type, int, 0);
MODULE_PARM_DESC(type,
"Type of adapter: (defaults to 0)\n"
" 0 = LM70CILD-3 (National Semiconductor) evaluation board\n");
/* add new type(s) here */
static u16 base;
module_param(base, ushort, 0);
MODULE_PARM_DESC(base, "Parport Base I/O address");
/*--------------------------------------------------------------*/
#include <linux/ioport.h>
#include <linux/delay.h>
#include <asm/io.h>
/* ----- Low-level parallel port access
----------------------------------- */
#define DEFAULT_BASE 0x378
static inline void port_write(unsigned char p, unsigned char d)
{
outb(d, base+p);
}
static inline unsigned char port_read(unsigned char p)
{
return inb(base+p);
}
/* ----- Unified line operation functions
--------------------------------- */
static inline void line_set(int state, const struct lineop *op)
{
u8 oldval = port_read(op->port);
/* Touch only the bit(s) needed */
if ((op->inverted && !state) || (!op->inverted && state))
port_write(op->port, oldval | op->val);
else
port_write(op->port, oldval & ~op->val);
}
static inline int line_get(const struct lineop *op)
{
u8 oldval = port_read(op->port);
return ((op->inverted && (oldval & op->val) != op->val)
|| (!op->inverted && (oldval & op->val) == op->val));
}
/* ----- SPI call-back functions and structures ----------------- */
static void parport_setscl(void *data, int state)
{
line_set(state, &adapter_parm[type].setscl);
}
static void parport_setsda(void *data, int state)
{
line_set(state, &adapter_parm[type].setsda);
}
static int parport_getscl(void *data)
{
return line_get(&adapter_parm[type].getscl);
}
static int parport_getsda(void *data)
{
return line_get(&adapter_parm[type].getsda);
}
static void parport_setcsl(void *data, int state)
{
line_set(state, &adapter_parm[type].setcsl);
}
/* ----- Module init and
exit---------------------------------------------- */
/* Module init : to be called from the specific driver init routine */
static int spi_parport_init(char *name)
{
/* adapter_parm set in the spi-parport-light.h header; type is a
module parameter */
if (type < 0 || type >= ARRAY_SIZE(adapter_parm)) {
printk(KERN_WARNING "%s: invalid parameter \"type\" (%d)\n\
defaulting to type 0\n", name, type);
type = 0;
}
if (base == 0)
base = DEFAULT_BASE;
if (!request_region(base, 3, "spi-parport-light"))
return -EBUSY;
/* Reset hardware to a sane state (SCL and SDA high) */
parport_setsda(NULL, 1);
parport_setscl(NULL, 1);
/* Other init if needed (power on...) */
if (adapter_parm[type].init.val)
line_set(1, &adapter_parm[type].init);
/* CS deselect, if necessary.. */
if (adapter_parm[type].setcsl.val)
line_set(0, &adapter_parm[type].setcsl);
/* Memory for the spi_device struct for this adapter */
adapter_parm[type].spidev = kzalloc(sizeof(struct spi_device),
GFP_KERNEL);
if (!adapter_parm[type].spidev) {
printk(KERN_ERR "%s: out of memory\n", name);
release_region(base, 3);
return -ENOMEM;
}
printk(KERN_INFO "%s loaded: adapter type %d (device '%s'), using
base address 0x%x\n",
name, type, adapter_parm[type].name, base);
return 0;
}
/* Module exit : to be called from the specific driver cleanup routine */
static void spi_parport_exit(char *name)
{
if (adapter_parm[type].spidev)
kfree (adapter_parm[type].spidev);
/* Un-init if needed (power off...) */
if (adapter_parm[type].init.val)
line_set(0, &adapter_parm[type].init);
release_region(base, 3);
printk(KERN_INFO "%s unloaded.\n", name);
}