mISDN/drivers/isdn/hardware/mISDN/sysfs_st.c

271 lines
6.8 KiB
C

/* $Id$
*
* Author Karsten Keil (keil@isdn4linux.de)
*
* mISDN sysfs stack stuff
*
* This file is (c) under GNU PUBLIC LICENSE
*
*/
#include "core.h"
#include "sysfs.h"
#define to_mISDNstack(d) container_of(d, mISDNstack_t, class_dev)
static ssize_t show_st_id(struct class_device *class_dev, char *buf)
{
mISDNstack_t *st = to_mISDNstack(class_dev);
return sprintf(buf, "%08x\n", st->id);
}
static CLASS_DEVICE_ATTR(id, S_IRUGO, show_st_id, NULL);
static ssize_t show_st_status(struct class_device *class_dev, char *buf)
{
mISDNstack_t *st = to_mISDNstack(class_dev);
return sprintf(buf, "0x%08lx\n", st->status);
}
static ssize_t store_st_status(struct class_device *class_dev, const char *buf, size_t count)
{
mISDNstack_t *st = to_mISDNstack(class_dev);
ulong status;
int err;
status = simple_strtol(buf, NULL, 0);
printk(KERN_DEBUG "%s: status %08lx\n", __FUNCTION__, status);
if (status == (1<<mISDN_STACK_INIT)) {
/* we want to make st->new_pid activ */
err = clear_stack(st, 1);
if (err) {
int_errtxt("clear_stack:%d", err);
return(err);
}
err = set_stack(st ,&st->new_pid);
if (err) {
int_errtxt("set_stack:%d", err);
return(err);
}
return(count);
} else if (status == (1<<mISDN_STACK_THREADSTART)) {
/* we want to start a new process after abort */
err = mISDN_start_stack_thread(st);
if (err) {
int_errtxt("start_stack_thread:%d", err);
return(err);
}
return(count);
}
st->status = status;
wake_up_interruptible(&st->workq);
return(count);
}
static CLASS_DEVICE_ATTR(status, S_IRUGO | S_IWUSR, show_st_status, store_st_status);
static ssize_t store_st_protocol(struct class_device *class_dev, const char *buf, size_t count)
{
mISDNstack_t *st = to_mISDNstack(class_dev);
ulong tmp;
char *p = (char *)buf;
u_int i;
memset(&st->new_pid.protocol, 0, (MAX_LAYER_NR + 1)*sizeof(st->new_pid.protocol[0]));
for (i=0; i<=MAX_LAYER_NR; i++) {
if (!*p)
break;
tmp = simple_strtol(p, &p, 0);
st->new_pid.protocol[i] = tmp;
if (*p)
p++;
}
if (*p)
int_errtxt("overflow");
return(count);
}
static ssize_t store_st_layermask(struct class_device *class_dev, const char *buf, size_t count)
{
mISDNstack_t *st = to_mISDNstack(class_dev);
ulong mask = (1<<(MAX_LAYER_NR + 1)) -1;
st->new_pid.layermask = simple_strtol(buf, NULL, 0);
if (st->new_pid.layermask > mask) {
int_errtxt("overflow");
st->new_pid.layermask &= mask;
}
return(count);
}
static ssize_t store_st_parameter(struct class_device *class_dev, const char *buf, size_t count)
{
mISDNstack_t *st = to_mISDNstack(class_dev);
ulong tmp;
char *d, *p = (char *)buf;
u_int i, j, l;
memset(&st->new_pid.param, 0, (MAX_LAYER_NR + 1)*sizeof(st->new_pid.param[0]));
kfree(st->new_pid.pbuf);
l = 0;
for (i=0; i<=MAX_LAYER_NR; i++) {
if (!*p)
break;
tmp = simple_strtol(p, &p, 0);
if (*p)
p++;
if (tmp) {
j = tmp;
l += j+1;
while(j--) {
if (!*p)
break;
tmp = simple_strtol(p, &p, 0);
if (*p)
p++;
else
break;
}
}
}
if (*p)
int_errtxt("overflow");
if (l == 0) {
st->new_pid.maxplen = 0;
return(count);
}
st->new_pid.pbuf = kmalloc(l, GFP_ATOMIC);
if (!st->new_pid.pbuf)
return(-ENOMEM);
st->new_pid.maxplen = l;
d = st->new_pid.pbuf;
memset(d, 0, l);
p = (char *)buf;
for (i=0; i<=MAX_LAYER_NR; i++) {
if (!*p)
break;
tmp = simple_strtol(p, &p, 0);
if (*p)
p++;
if (tmp) {
j = tmp;
st->new_pid.param[i] = d;
*d++ = tmp & 0xff;
while(j--) {
if (!*p)
break;
tmp = simple_strtol(p, &p, 0);
*d++ = tmp & 0xff;
if (*p)
p++;
else
break;
}
}
}
return(count);
}
MISDN_PROTO(mISDNstack, pid, S_IRUGO);
MISDN_PROTO(mISDNstack, new_pid, S_IRUGO);
static ssize_t show_st_qlen(struct class_device *class_dev, char *buf)
{
mISDNstack_t *st = to_mISDNstack(class_dev);
return sprintf(buf, "%d\n", skb_queue_len(&st->msgq));
}
static CLASS_DEVICE_ATTR(qlen, S_IRUGO, show_st_qlen, NULL);
static void release_mISDN_stack(struct class_device *dev)
{
mISDNstack_t *st = to_mISDNstack(dev);
char name[12];
sysfs_remove_group(&st->class_dev.kobj, &pid_group);
sysfs_remove_group(&st->class_dev.kobj, &new_pid_group);
printk(KERN_INFO "release stack class dev %s\n", dev->class_id);
if (st->parent) {
sysfs_remove_link(&dev->kobj, "parent");
snprintf(name, 12, "child%d", (CHILD_ID_MASK & st->id) >> 16);
sysfs_remove_link(&st->parent->class_dev.kobj, name);
}
if (st->master) {
sysfs_remove_link(&dev->kobj, "master");
snprintf(name, 12, "clone%d", (CLONE_ID_MASK & st->id) >> 16);
sysfs_remove_link(&st->master->class_dev.kobj, name);
}
}
static struct class stack_dev_class = {
.name = "mISDN-stacks",
#ifndef CLASS_WITHOUT_OWNER
.owner = THIS_MODULE,
#endif
.release = &release_mISDN_stack,
};
int
mISDN_register_sysfs_stack(mISDNstack_t *st)
{
int err;
char name[12];
st->class_dev.class = &stack_dev_class;
if (st->id & FLG_CHILD_STACK)
snprintf(st->class_dev.class_id, BUS_ID_SIZE, "chst-%08x", st->id);
else if (st->id & FLG_CLONE_STACK)
snprintf(st->class_dev.class_id, BUS_ID_SIZE, "clst-%08x", st->id);
else
snprintf(st->class_dev.class_id, BUS_ID_SIZE, "st-%08x", st->id);
if (st->mgr)
st->class_dev.dev = st->mgr->class_dev.dev;
err = class_device_register(&st->class_dev);
if (err)
return(err);
err = sysfs_create_group(&st->class_dev.kobj, &pid_group);
if (err)
goto out_unreg;
mISDNstack_attr_protocol_new_pid.attr.mode |= S_IWUSR;
mISDNstack_attr_protocol_new_pid.store = store_st_protocol;
mISDNstack_attr_parameter_new_pid.attr.mode |= S_IWUSR;
mISDNstack_attr_parameter_new_pid.store = store_st_parameter;
mISDNstack_attr_layermask_new_pid.attr.mode |= S_IWUSR;
mISDNstack_attr_layermask_new_pid.store = store_st_layermask;
err = sysfs_create_group(&st->class_dev.kobj, &new_pid_group);
if (err)
goto out_unreg;
class_device_create_file(&st->class_dev, &class_device_attr_id);
class_device_create_file(&st->class_dev, &class_device_attr_qlen);
class_device_create_file(&st->class_dev, &class_device_attr_status);
if (st->parent) {
sysfs_create_link(&st->class_dev.kobj, &st->parent->class_dev.kobj, "parent");
snprintf(name, 12, "child%d", (CHILD_ID_MASK & st->id) >> 16);
sysfs_create_link(&st->parent->class_dev.kobj, &st->class_dev.kobj, name);
}
if (st->master) {
sysfs_create_link(&st->class_dev.kobj, &st->master->class_dev.kobj, "master");
snprintf(name, 12, "clone%d", (CLONE_ID_MASK & st->id) >> 16);
sysfs_create_link(&st->master->class_dev.kobj, &st->class_dev.kobj, name);
}
return(err);
out_unreg:
class_device_unregister(&st->class_dev);
return(err);
}
void
mISDN_unregister_sysfs_st(mISDNstack_t *st)
{
class_device_unregister(&st->class_dev);
}
int
mISDN_sysfs_st_init(void)
{
return(class_register(&stack_dev_class));
}
void
mISDN_sysfs_st_cleanup(void)
{
class_unregister(&stack_dev_class);
}