SysFS Channel representation changes
* Kernel (and SysFS) objects for channels. * A bus of their own. * Dynamically allocate a minor (no conflict with /dev/dahdi/timer et. al.). * Device files located under /dev/dahdi/channels instead of /dev/dahdi/spans [e.g: /dev/dahdi/channels/2/4) - A link back should is doable with a simple udev rule. * Bus name is dahdi_channels instead of dahdi_chans [e.g: /sys/bus/dahdi_channels] * Driver name is dahdi instead of dahdi_chans [e.g: /sys/bus/dahdi_channels/drivers/dahdi] * Attributes are all strings (or lists of strings), including 'sig' and 'sigcap'. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQEcBAABCAAGBQJQyL3jAAoJEMZ1bgEN3R/0YHEH/RFKSftR0GWNo8TkRbDnojjK bU9YwnLucAKe2AgwRKcKLStmEHYK/e2Jar1TVuDBlgvE3hdYRzddjbB045Ff4un2 0483Yx3JMQFYQep3W5DNpT1R7YU9rn9iurObZXnXryWe0Zxw57sKC7ixCl3tEv0m x58cl9UDMhknf+LtY8b/QNMneIu0MbzBgxiow6yj4g8x9Vv1obnPFbfui871Yvlt 2H4LdVchZCfmYYTcT93AxfV6G/1RuF9wWGbSUN+VigcXuvhUWERsjSVlq0c8GAWO pocXlxxwN1W8gYC/yhNKKLZHVTBi8YLvNAjUCCr/N7JxX9YnMljCcBjuiljDVlg= =sZHz -----END PGP SIGNATURE----- Merge tag 'review-sysfs-chan' of http://git.tzafrir.org.il/git/dahdi-linux into for-trunk SysFS Channel representation changes * Kernel (and SysFS) objects for channels. * A bus of their own. * Dynamically allocate a minor (no conflict with /dev/dahdi/timer et. al.). * Device files located under /dev/dahdi/channels instead of /dev/dahdi/spans [e.g: /dev/dahdi/channels/2/4) - A link back should is doable with a simple udev rule. * Bus name is dahdi_channels instead of dahdi_chans [e.g: /sys/bus/dahdi_channels] * Driver name is dahdi instead of dahdi_chans [e.g: /sys/bus/dahdi_channels/drivers/dahdi] * Attributes are all strings (or lists of strings), including 'sig' and 'sigcap'.
This commit is contained in:
commit
6846663d1e
59
README
59
README
@ -521,9 +521,12 @@ or dynamically through the udev system.
|
||||
|
||||
* /dev/dahdi/ctl (196:0) - a general device file for various information and
|
||||
control operations on the DAHDI channels.
|
||||
* /dev/dahdi/channels/N/M - A device file for channel M in span N
|
||||
(M is chanpos - numbering relative to the current span).
|
||||
* /dev/dahdi/NNN (196:NNN) - for NNN in the range 1-249. A device file for
|
||||
DAHDI channel NNN. It can be used to read data from the channel
|
||||
and write data to the channel.
|
||||
and write data to the channel. It is not generated by default but may
|
||||
be generated as a symlink using udev rules.
|
||||
* /dev/dahdi/transcode (196:250) - Used to connect to a DAHDI transcoding
|
||||
device.
|
||||
* /dev/dahdi/timer (196:253) - Allows setting timers. Used anywhere?
|
||||
@ -851,6 +854,60 @@ A very short type string.
|
||||
Current sync source.
|
||||
|
||||
|
||||
Channels Bus
|
||||
^^^^^^^^^^^^
|
||||
Each DAHDI channel is represented by a node under
|
||||
/sys/bus/dahdi_channels/devices with the name 'dahdi!channels!N!M'
|
||||
(where N is the number of the span and M is the number of the channel
|
||||
in the span - chanpos). Channels of each span also reside under the node
|
||||
of the span.
|
||||
|
||||
Useful attributes in the channel node (All attributed listed below are
|
||||
read-only):
|
||||
|
||||
===== /sys/bus/dahdi_spans/devices/span-N/dahdi!channels!N!M/alarms
|
||||
List of names of the current active alarms (space separated). Normally
|
||||
(no alarms) empty. Example:
|
||||
|
||||
RED YELLOW
|
||||
|
||||
===== /sys/bus/dahdi_spans/devices/span-N/dahdi!channels!N!M/blocksize
|
||||
The block size set by userspace.
|
||||
|
||||
===== /sys/bus/dahdi_spans/devices/span-N/dahdi!channels!N!M/channo
|
||||
The (global) channel number.
|
||||
|
||||
===== /sys/bus/dahdi_spans/devices/span-N/dahdi!channels!N!M/chanpos
|
||||
The channel number within the span.
|
||||
|
||||
===== /sys/bus/dahdi_spans/devices/span-N/dahdi!channels!N!M/dev
|
||||
Major and minor device numbers.
|
||||
|
||||
===== /sys/bus/dahdi_spans/devices/span-N/dahdi!channels!N!M/ec_factory
|
||||
The name of the echo canceller to be used in the channel, if one is
|
||||
configured. Example:
|
||||
|
||||
MG2
|
||||
|
||||
===== /sys/bus/dahdi_spans/devices/span-N/dahdi!channels!N!M/ec_state
|
||||
State of the echo canceller. ACTIVE: configured and inuse. INACTIVE
|
||||
otherwise.
|
||||
|
||||
===== /sys/bus/dahdi_spans/devices/span-N/dahdi!channels!N!M/in_use
|
||||
1 if the channel is in use (was opepend by userspace), 0 otherwise.
|
||||
|
||||
===== /sys/bus/dahdi_spans/devices/span-N/dahdi!channels!N!M/name
|
||||
A name string for the channel
|
||||
|
||||
===== /sys/bus/dahdi_spans/devices/span-N/dahdi!channels!N!M/sig
|
||||
The signalling types set for the channel. A space-separated list of
|
||||
signalling types.
|
||||
|
||||
===== /sys/bus/dahdi_spans/devices/span-N/dahdi!channels!N!M/sigcap
|
||||
The signalling types this channel may be configured to handle. A space-
|
||||
separated list of signalling types.
|
||||
|
||||
|
||||
User-space Interface
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
User-space programs can only work with DAHDI channels. The basic
|
||||
|
@ -811,7 +811,7 @@ ssize_t lineconfig_str(int lineconfig, char buf[], size_t size)
|
||||
EXPORT_SYMBOL(lineconfig_str);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static const char *sigstr(int sig)
|
||||
const char *sigstr(int sig)
|
||||
{
|
||||
switch (sig) {
|
||||
case DAHDI_SIG_FXSLS:
|
||||
@ -858,7 +858,7 @@ static const char *sigstr(int sig)
|
||||
}
|
||||
}
|
||||
|
||||
static int fill_alarm_string(char *buf, int count, int alarms)
|
||||
int fill_alarm_string(char *buf, int count, int alarms)
|
||||
{
|
||||
int len;
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <dahdi/kernel.h>
|
||||
#include "dahdi.h"
|
||||
#include "dahdi-sysfs.h"
|
||||
@ -38,22 +39,222 @@
|
||||
|
||||
static struct class *dahdi_class;
|
||||
|
||||
static dev_t dahdi_channels_devt; /*!< Device number of first channel */
|
||||
static struct cdev dahdi_channels_cdev; /*!< Channels chardev's */
|
||||
|
||||
/*
|
||||
* Flags to remember what initializations already
|
||||
* succeeded.
|
||||
*/
|
||||
static struct {
|
||||
int channel_driver:1;
|
||||
int channels_bus:1;
|
||||
int cdev:1;
|
||||
} should_cleanup;
|
||||
|
||||
#define chan_attr(field, format_string) \
|
||||
static BUS_ATTR_READER(field##_show, dev, buf) \
|
||||
{ \
|
||||
struct dahdi_chan *chan; \
|
||||
\
|
||||
chan = dev_to_chan(dev); \
|
||||
return sprintf(buf, format_string, chan->field); \
|
||||
}
|
||||
|
||||
chan_attr(name, "%s\n");
|
||||
chan_attr(channo, "%d\n");
|
||||
chan_attr(chanpos, "%d\n");
|
||||
chan_attr(blocksize, "%d\n");
|
||||
#ifdef OPTIMIZE_CHANMUTE
|
||||
chan_attr(chanmute, "%d\n");
|
||||
#endif
|
||||
|
||||
static BUS_ATTR_READER(sigcap_show, dev, buf)
|
||||
{
|
||||
struct dahdi_chan *chan;
|
||||
int len = 0;
|
||||
int i;
|
||||
uint sigtypes[] = {
|
||||
DAHDI_SIG_FXSLS,
|
||||
DAHDI_SIG_FXSGS,
|
||||
DAHDI_SIG_FXSKS,
|
||||
DAHDI_SIG_FXOLS,
|
||||
DAHDI_SIG_FXOGS,
|
||||
DAHDI_SIG_FXOKS,
|
||||
DAHDI_SIG_EM,
|
||||
DAHDI_SIG_CLEAR,
|
||||
DAHDI_SIG_HDLCRAW,
|
||||
DAHDI_SIG_HDLCFCS,
|
||||
DAHDI_SIG_HDLCNET,
|
||||
DAHDI_SIG_SLAVE,
|
||||
DAHDI_SIG_SF,
|
||||
DAHDI_SIG_CAS,
|
||||
DAHDI_SIG_EM_E1,
|
||||
DAHDI_SIG_DACS_RBS,
|
||||
DAHDI_SIG_HARDHDLC,
|
||||
DAHDI_SIG_MTP2,
|
||||
};
|
||||
chan = dev_to_chan(dev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sigtypes); i++) {
|
||||
uint x = chan->sigcap & sigtypes[i];
|
||||
if (x == sigtypes[i])
|
||||
len += sprintf(buf + len, "%s ", sigstr(x));
|
||||
}
|
||||
while (len > 0 && isspace(buf[len - 1])) /* trim */
|
||||
len--;
|
||||
len += sprintf(buf + len, "\n");
|
||||
return len;
|
||||
}
|
||||
|
||||
static BUS_ATTR_READER(sig_show, dev, buf)
|
||||
{
|
||||
struct dahdi_chan *chan;
|
||||
|
||||
chan = dev_to_chan(dev);
|
||||
return sprintf(buf, "%s\n", sigstr(chan->sig));
|
||||
}
|
||||
|
||||
static BUS_ATTR_READER(in_use_show, dev, buf)
|
||||
{
|
||||
struct dahdi_chan *chan;
|
||||
|
||||
chan = dev_to_chan(dev);
|
||||
return sprintf(buf, "%d\n", test_bit(DAHDI_FLAGBIT_OPEN, &chan->flags));
|
||||
}
|
||||
|
||||
static BUS_ATTR_READER(alarms_show, dev, buf)
|
||||
{
|
||||
struct dahdi_chan *chan;
|
||||
int len;
|
||||
|
||||
chan = dev_to_chan(dev);
|
||||
len = fill_alarm_string(buf, PAGE_SIZE, chan->chan_alarms);
|
||||
buf[len++] = '\n';
|
||||
return len;
|
||||
}
|
||||
|
||||
static BUS_ATTR_READER(ec_factory_show, dev, buf)
|
||||
{
|
||||
struct dahdi_chan *chan;
|
||||
int len = 0;
|
||||
|
||||
chan = dev_to_chan(dev);
|
||||
if (chan->ec_factory)
|
||||
len += sprintf(buf, "%s", chan->ec_factory->get_name(chan));
|
||||
buf[len++] = '\n';
|
||||
return len;
|
||||
}
|
||||
|
||||
static BUS_ATTR_READER(ec_state_show, dev, buf)
|
||||
{
|
||||
struct dahdi_chan *chan;
|
||||
int len = 0;
|
||||
|
||||
chan = dev_to_chan(dev);
|
||||
if (chan->ec_factory)
|
||||
len += sprintf(buf, "%sACTIVE", (chan->ec_state) ? "" : "IN");
|
||||
buf[len++] = '\n';
|
||||
return len;
|
||||
}
|
||||
|
||||
static struct device_attribute chan_dev_attrs[] = {
|
||||
__ATTR_RO(name),
|
||||
__ATTR_RO(channo),
|
||||
__ATTR_RO(chanpos),
|
||||
__ATTR_RO(sig),
|
||||
__ATTR_RO(sigcap),
|
||||
__ATTR_RO(alarms),
|
||||
__ATTR_RO(ec_factory),
|
||||
__ATTR_RO(ec_state),
|
||||
__ATTR_RO(blocksize),
|
||||
#ifdef OPTIMIZE_CHANMUTE
|
||||
__ATTR_RO(chanmute),
|
||||
#endif
|
||||
__ATTR_RO(in_use),
|
||||
__ATTR_NULL,
|
||||
};
|
||||
|
||||
static void chan_release(struct device *dev)
|
||||
{
|
||||
struct dahdi_chan *chan;
|
||||
|
||||
BUG_ON(!dev);
|
||||
chan = dev_to_chan(dev);
|
||||
chan_dbg(DEVICES, chan, "SYSFS\n");
|
||||
}
|
||||
|
||||
static int chan_match(struct device *dev, struct device_driver *driver)
|
||||
{
|
||||
struct dahdi_chan *chan;
|
||||
|
||||
chan = dev_to_chan(dev);
|
||||
chan_dbg(DEVICES, chan, "SYSFS\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct bus_type chan_bus_type = {
|
||||
.name = "dahdi_channels",
|
||||
.match = chan_match,
|
||||
.dev_attrs = chan_dev_attrs,
|
||||
};
|
||||
|
||||
static int chan_probe(struct device *dev)
|
||||
{
|
||||
struct dahdi_chan *chan;
|
||||
|
||||
chan = dev_to_chan(dev);
|
||||
chan_dbg(DEVICES, chan, "SYSFS\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chan_remove(struct device *dev)
|
||||
{
|
||||
struct dahdi_chan *chan;
|
||||
|
||||
chan = dev_to_chan(dev);
|
||||
chan_dbg(DEVICES, chan, "SYSFS\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct device_driver chan_driver = {
|
||||
.name = "dahdi",
|
||||
.bus = &chan_bus_type,
|
||||
#ifndef OLD_HOTPLUG_SUPPORT
|
||||
.owner = THIS_MODULE,
|
||||
#endif
|
||||
.probe = chan_probe,
|
||||
.remove = chan_remove
|
||||
};
|
||||
|
||||
int chan_sysfs_create(struct dahdi_chan *chan)
|
||||
{
|
||||
char chan_name[32];
|
||||
void *dummy;
|
||||
int res = 0;
|
||||
struct device *dev;
|
||||
struct dahdi_span *span;
|
||||
int res;
|
||||
dev_t devt;
|
||||
|
||||
if (chan->channo >= 250)
|
||||
return 0;
|
||||
chan_dbg(DEVICES, chan, "Creating channel %d\n", chan->channo);
|
||||
if (test_bit(DAHDI_FLAGBIT_DEVFILE, &chan->flags))
|
||||
return 0;
|
||||
snprintf(chan_name, sizeof(chan_name), "dahdi!%d", chan->channo);
|
||||
dummy = (void *)MAKE_DAHDI_DEV(chan->channo, chan_name);
|
||||
if (IS_ERR(dummy)) {
|
||||
res = PTR_ERR(dummy);
|
||||
chan_err(chan, "Failed creating sysfs device: %d\n",
|
||||
res);
|
||||
span = chan->span;
|
||||
devt = MKDEV(MAJOR(dahdi_channels_devt), chan->channo);
|
||||
dev = &chan->chan_device;
|
||||
dev->devt = devt;
|
||||
dev->class = dahdi_class;
|
||||
dev->bus = &chan_bus_type;
|
||||
dev->parent = span->span_device;
|
||||
/*
|
||||
* FIXME: the name cannot be longer than KOBJ_NAME_LEN
|
||||
*/
|
||||
dev_set_name(dev, "dahdi!channels!%d!%d", span->spanno, chan->chanpos);
|
||||
dev_set_drvdata(dev, chan);
|
||||
dev->release = chan_release;
|
||||
res = device_register(dev);
|
||||
if (res) {
|
||||
chan_err(chan, "%s: device_register failed: %d\n",
|
||||
__func__, res);
|
||||
dev_set_drvdata(dev, NULL);
|
||||
return res;
|
||||
}
|
||||
set_bit(DAHDI_FLAGBIT_DEVFILE, &chan->flags);
|
||||
@ -62,9 +263,19 @@ int chan_sysfs_create(struct dahdi_chan *chan)
|
||||
|
||||
void chan_sysfs_remove(struct dahdi_chan *chan)
|
||||
{
|
||||
struct device *dev = &chan->chan_device;
|
||||
|
||||
chan_dbg(DEVICES, chan, "Destroying channel %d\n", chan->channo);
|
||||
if (!dev_get_drvdata(dev))
|
||||
return;
|
||||
if (!test_bit(DAHDI_FLAGBIT_DEVFILE, &chan->flags))
|
||||
return;
|
||||
DEL_DAHDI_DEV(chan->channo);
|
||||
dev = &chan->chan_device;
|
||||
BUG_ON(dev_get_drvdata(dev) != chan);
|
||||
device_unregister(dev);
|
||||
dev_set_drvdata(dev, NULL);
|
||||
/* FIXME: should have been done earlier in dahdi_chan_unreg */
|
||||
chan->channo = -1;
|
||||
clear_bit(DAHDI_FLAGBIT_DEVFILE, &chan->flags);
|
||||
}
|
||||
|
||||
@ -77,7 +288,7 @@ int dahdi_register_chardev(struct dahdi_chardev *dev)
|
||||
char *udevname;
|
||||
|
||||
udevname = kzalloc(strlen(dev->name) + sizeof(DAHDI_STRING) + 1,
|
||||
GFP_KERNEL);
|
||||
GFP_KERNEL);
|
||||
if (!udevname)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -181,28 +392,85 @@ cleanup:
|
||||
*/
|
||||
static void sysfs_channels_cleanup(void)
|
||||
{
|
||||
if (should_cleanup.cdev) {
|
||||
dahdi_dbg(DEVICES, "removing channels cdev\n");
|
||||
cdev_del(&dahdi_channels_cdev);
|
||||
should_cleanup.cdev = 0;
|
||||
}
|
||||
if (dahdi_channels_devt) {
|
||||
dahdi_dbg(DEVICES, "unregistering chrdev_region\n");
|
||||
unregister_chrdev_region(dahdi_channels_devt,
|
||||
DAHDI_MAX_CHANNELS);
|
||||
}
|
||||
|
||||
fixed_devfiles_remove();
|
||||
if (dahdi_class) {
|
||||
dahdi_dbg(DEVICES, "Destroying DAHDI class:\n");
|
||||
class_destroy(dahdi_class);
|
||||
dahdi_class = NULL;
|
||||
}
|
||||
if (should_cleanup.channel_driver) {
|
||||
dahdi_dbg(DEVICES, "Removing channel driver\n");
|
||||
driver_unregister(&chan_driver);
|
||||
should_cleanup.channel_driver = 0;
|
||||
}
|
||||
if (should_cleanup.channels_bus) {
|
||||
dahdi_dbg(DEVICES, "Removing channels bus\n");
|
||||
bus_unregister(&chan_bus_type);
|
||||
should_cleanup.channels_bus = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int __init dahdi_sysfs_chan_init(const struct file_operations *fops)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
dahdi_dbg(DEVICES, "Registering channels bus\n");
|
||||
res = bus_register(&chan_bus_type);
|
||||
if (res) {
|
||||
dahdi_err("%s: bus_register(%s) failed. Error number %d\n",
|
||||
__func__, chan_bus_type.name, res);
|
||||
goto cleanup;
|
||||
}
|
||||
should_cleanup.channels_bus = 1;
|
||||
|
||||
dahdi_dbg(DEVICES, "Registering channel driver\n");
|
||||
res = driver_register(&chan_driver);
|
||||
if (res) {
|
||||
dahdi_err("%s: driver_register(%s) failed. Error number %d",
|
||||
__func__, chan_driver.name, res);
|
||||
goto cleanup;
|
||||
}
|
||||
should_cleanup.channel_driver = 1;
|
||||
|
||||
dahdi_class = class_create(THIS_MODULE, "dahdi");
|
||||
if (IS_ERR(dahdi_class)) {
|
||||
res = PTR_ERR(dahdi_class);
|
||||
dahdi_err("%s: class_create(dahi_chan) failed. Error: %d\n",
|
||||
__func__, res);
|
||||
__func__, res);
|
||||
goto cleanup;
|
||||
}
|
||||
res = fixed_devfiles_create();
|
||||
if (res)
|
||||
goto cleanup;
|
||||
dahdi_dbg(DEVICES, "allocating chrdev_region\n");
|
||||
res = alloc_chrdev_region(&dahdi_channels_devt,
|
||||
0,
|
||||
DAHDI_MAX_CHANNELS,
|
||||
"dahdi_channels");
|
||||
if (res) {
|
||||
dahdi_err("%s: Failed allocating chrdev for %d channels (%d)",
|
||||
__func__, DAHDI_MAX_CHANNELS, res);
|
||||
goto cleanup;
|
||||
}
|
||||
dahdi_dbg(DEVICES, "adding channels cdev\n");
|
||||
res = cdev_add(&dahdi_channels_cdev, dahdi_channels_devt,
|
||||
DAHDI_MAX_CHANNELS);
|
||||
if (res) {
|
||||
dahdi_err("%s: cdev_add() failed (%d)", __func__, res);
|
||||
goto cleanup;
|
||||
}
|
||||
should_cleanup.cdev = 1;
|
||||
return 0;
|
||||
cleanup:
|
||||
sysfs_channels_cleanup();
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <linux/version.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
@ -605,6 +606,8 @@ struct dahdi_chan {
|
||||
#else
|
||||
unsigned char *lin2x;
|
||||
#endif
|
||||
struct device chan_device; /*!< Kernel object for this chan */
|
||||
#define dev_to_chan(dev) container_of(dev, struct dahdi_chan, chan_device)
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DAHDI_NET
|
||||
@ -1227,6 +1230,12 @@ void dahdi_init_tone_state(struct dahdi_tone_state *ts, struct dahdi_tone *zt);
|
||||
/*! \brief Get a given MF tone struct, suitable for dahdi_tone_nextsample. */
|
||||
struct dahdi_tone *dahdi_mf_tone(const struct dahdi_chan *chan, char digit, int digitmode);
|
||||
|
||||
/*! \brief Convert signalling bits to human readable string */
|
||||
const char *sigstr(int sig);
|
||||
|
||||
/*! \brief Convert alarm bits to human readable string */
|
||||
int fill_alarm_string(char *buf, int count, int alarms);
|
||||
|
||||
/* Echo cancel a receive and transmit chunk for a given channel. This
|
||||
should be called by the low-level driver as close to the interface
|
||||
as possible. ECHO CANCELLATION IS NO LONGER AUTOMATICALLY DONE
|
||||
|
Loading…
Reference in New Issue
Block a user