forked from Minki/linux
d9ca676bcb
The ATM subsystem was incorrectly creating the 'device' link for ATM nodes in sysfs. This led to incorrect device/parent relationships exposed by sysfs and udev. Instead of rolling the 'device' link by hand in the generic ATM code, pass each ATM driver's bus device down to the sysfs code and let sysfs do this stuff correctly. Signed-off-by: Dan Williams <dcbw@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
191 lines
3.9 KiB
C
191 lines
3.9 KiB
C
/* ATM driver model support. */
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kobject.h>
|
|
#include <linux/atmdev.h>
|
|
#include "common.h"
|
|
#include "resources.h"
|
|
|
|
#define to_atm_dev(cldev) container_of(cldev, struct atm_dev, class_dev)
|
|
|
|
static ssize_t show_type(struct device *cdev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct atm_dev *adev = to_atm_dev(cdev);
|
|
return sprintf(buf, "%s\n", adev->type);
|
|
}
|
|
|
|
static ssize_t show_address(struct device *cdev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
char *pos = buf;
|
|
struct atm_dev *adev = to_atm_dev(cdev);
|
|
int i;
|
|
|
|
for (i = 0; i < (ESI_LEN - 1); i++)
|
|
pos += sprintf(pos, "%02x:", adev->esi[i]);
|
|
pos += sprintf(pos, "%02x\n", adev->esi[i]);
|
|
|
|
return pos - buf;
|
|
}
|
|
|
|
static ssize_t show_atmaddress(struct device *cdev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
unsigned long flags;
|
|
char *pos = buf;
|
|
struct atm_dev *adev = to_atm_dev(cdev);
|
|
struct atm_dev_addr *aaddr;
|
|
int bin[] = { 1, 2, 10, 6, 1 }, *fmt = bin;
|
|
int i, j;
|
|
|
|
spin_lock_irqsave(&adev->lock, flags);
|
|
list_for_each_entry(aaddr, &adev->local, entry) {
|
|
for (i = 0, j = 0; i < ATM_ESA_LEN; ++i, ++j) {
|
|
if (j == *fmt) {
|
|
pos += sprintf(pos, ".");
|
|
++fmt;
|
|
j = 0;
|
|
}
|
|
pos += sprintf(pos, "%02x",
|
|
aaddr->addr.sas_addr.prv[i]);
|
|
}
|
|
pos += sprintf(pos, "\n");
|
|
}
|
|
spin_unlock_irqrestore(&adev->lock, flags);
|
|
|
|
return pos - buf;
|
|
}
|
|
|
|
static ssize_t show_carrier(struct device *cdev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
char *pos = buf;
|
|
struct atm_dev *adev = to_atm_dev(cdev);
|
|
|
|
pos += sprintf(pos, "%d\n",
|
|
adev->signal == ATM_PHY_SIG_LOST ? 0 : 1);
|
|
|
|
return pos - buf;
|
|
}
|
|
|
|
static ssize_t show_link_rate(struct device *cdev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
char *pos = buf;
|
|
struct atm_dev *adev = to_atm_dev(cdev);
|
|
int link_rate;
|
|
|
|
/* show the link rate, not the data rate */
|
|
switch (adev->link_rate) {
|
|
case ATM_OC3_PCR:
|
|
link_rate = 155520000;
|
|
break;
|
|
case ATM_OC12_PCR:
|
|
link_rate = 622080000;
|
|
break;
|
|
case ATM_25_PCR:
|
|
link_rate = 25600000;
|
|
break;
|
|
default:
|
|
link_rate = adev->link_rate * 8 * 53;
|
|
}
|
|
pos += sprintf(pos, "%d\n", link_rate);
|
|
|
|
return pos - buf;
|
|
}
|
|
|
|
static DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
|
|
static DEVICE_ATTR(atmaddress, S_IRUGO, show_atmaddress, NULL);
|
|
static DEVICE_ATTR(carrier, S_IRUGO, show_carrier, NULL);
|
|
static DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
|
|
static DEVICE_ATTR(link_rate, S_IRUGO, show_link_rate, NULL);
|
|
|
|
static struct device_attribute *atm_attrs[] = {
|
|
&dev_attr_atmaddress,
|
|
&dev_attr_address,
|
|
&dev_attr_carrier,
|
|
&dev_attr_type,
|
|
&dev_attr_link_rate,
|
|
NULL
|
|
};
|
|
|
|
|
|
static int atm_uevent(struct device *cdev, struct kobj_uevent_env *env)
|
|
{
|
|
struct atm_dev *adev;
|
|
|
|
if (!cdev)
|
|
return -ENODEV;
|
|
|
|
adev = to_atm_dev(cdev);
|
|
if (!adev)
|
|
return -ENODEV;
|
|
|
|
if (add_uevent_var(env, "NAME=%s%d", adev->type, adev->number))
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void atm_release(struct device *cdev)
|
|
{
|
|
struct atm_dev *adev = to_atm_dev(cdev);
|
|
|
|
kfree(adev);
|
|
}
|
|
|
|
static struct class atm_class = {
|
|
.name = "atm",
|
|
.dev_release = atm_release,
|
|
.dev_uevent = atm_uevent,
|
|
};
|
|
|
|
int atm_register_sysfs(struct atm_dev *adev, struct device *parent)
|
|
{
|
|
struct device *cdev = &adev->class_dev;
|
|
int i, j, err;
|
|
|
|
cdev->class = &atm_class;
|
|
cdev->parent = parent;
|
|
dev_set_drvdata(cdev, adev);
|
|
|
|
dev_set_name(cdev, "%s%d", adev->type, adev->number);
|
|
err = device_register(cdev);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
for (i = 0; atm_attrs[i]; i++) {
|
|
err = device_create_file(cdev, atm_attrs[i]);
|
|
if (err)
|
|
goto err_out;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_out:
|
|
for (j = 0; j < i; j++)
|
|
device_remove_file(cdev, atm_attrs[j]);
|
|
device_del(cdev);
|
|
return err;
|
|
}
|
|
|
|
void atm_unregister_sysfs(struct atm_dev *adev)
|
|
{
|
|
struct device *cdev = &adev->class_dev;
|
|
|
|
device_del(cdev);
|
|
}
|
|
|
|
int __init atm_sysfs_init(void)
|
|
{
|
|
return class_register(&atm_class);
|
|
}
|
|
|
|
void __exit atm_sysfs_exit(void)
|
|
{
|
|
class_unregister(&atm_class);
|
|
}
|