From b36b654a7e82308cea063cdf909a7f246105c2a3 Mon Sep 17 00:00:00 2001 From: Matthias Urlichs Date: Sat, 16 Aug 2008 00:09:24 +0200 Subject: mISDN: Create /sys/class/mISDN Create /sys/class/mISDN and implement functions to handle device renames. Signed-Off-By: Matthias Urlichs Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/hfcmulti.c | 6 +- drivers/isdn/hardware/mISDN/hfcpci.c | 100 ++++++------ drivers/isdn/mISDN/core.c | 271 ++++++++++++++++++++++++++------- drivers/isdn/mISDN/l1oip_core.c | 3 +- include/linux/mISDNif.h | 8 +- 5 files changed, 273 insertions(+), 115 deletions(-) diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index cd3f3994b7f8..97f4708b3879 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -4718,7 +4718,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) } else hc->chan[hc->dslot].jitter = 2; /* default */ snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1); - ret = mISDN_register_device(&dch->dev, name); + ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name); if (ret) goto free_chan; hc->created[0] = 1; @@ -4826,9 +4826,9 @@ init_multi_port(struct hfc_multi *hc, int pt) test_and_set_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i + 2].cfg); } - snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d/%d", + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d-%d", hc->type, HFC_cnt + 1, pt + 1); - ret = mISDN_register_device(&dch->dev, name); + ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name); if (ret) goto free_chan; hc->created[pt] = 1; diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index cd5d26c9909f..8df12bf02af3 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -64,9 +64,6 @@ MODULE_LICENSE("GPL"); module_param(debug, uint, 0); module_param(poll, uint, S_IRUGO | S_IWUSR); -static LIST_HEAD(HFClist); -static DEFINE_RWLOCK(HFClock); - enum { HFC_CCD_2BD0, HFC_CCD_B000, @@ -136,7 +133,6 @@ struct hfcPCI_hw { struct hfc_pci { - struct list_head list; u_char subtype; u_char chanlimit; u_char initdone; @@ -1227,41 +1223,6 @@ hfcpci_int(int intno, void *dev_id) return IRQ_HANDLED; } -static void -hfcpci_softirq(void *arg) -{ - u_long flags; - struct bchannel *bch; - struct hfc_pci *hc; - - write_lock_irqsave(&HFClock, flags); - list_for_each_entry(hc, &HFClist, list) { - if (hc->hw.int_m2 & HFCPCI_IRQ_ENABLE) { - spin_lock(&hc->lock); - bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); - if (bch && bch->state == ISDN_P_B_RAW) { /* B1 rx&tx */ - main_rec_hfcpci(bch); - tx_birq(bch); - } - bch = Sel_BCS(hc, hc->hw.bswapped ? 1 : 2); - if (bch && bch->state == ISDN_P_B_RAW) { /* B2 rx&tx */ - main_rec_hfcpci(bch); - tx_birq(bch); - } - spin_unlock(&hc->lock); - } - } - write_unlock_irqrestore(&HFClock, flags); - - /* if next event would be in the past ... */ - if ((s32)(hfc_jiffies + tics - jiffies) <= 0) - hfc_jiffies = jiffies + 1; - else - hfc_jiffies += tics; - hfc_tl.expires = hfc_jiffies; - add_timer(&hfc_tl); -} - /* * timer callback for D-chan busy resolution. Currently no function */ @@ -2131,7 +2092,6 @@ release_card(struct hfc_pci *hc) { mISDN_freebchannel(&hc->bch[1]); mISDN_freebchannel(&hc->bch[0]); mISDN_freedchannel(&hc->dch); - list_del(&hc->list); pci_set_drvdata(hc->pdev, NULL); kfree(hc); } @@ -2141,7 +2101,6 @@ setup_card(struct hfc_pci *card) { int err = -EINVAL; u_int i; - u_long flags; char name[MISDN_MAX_IDLEN]; card->dch.debug = debug; @@ -2169,13 +2128,10 @@ setup_card(struct hfc_pci *card) if (err) goto error; snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-pci.%d", HFC_cnt + 1); - err = mISDN_register_device(&card->dch.dev, name); + err = mISDN_register_device(&card->dch.dev, &card->pdev->dev, name); if (err) goto error; HFC_cnt++; - write_lock_irqsave(&HFClock, flags); - list_add_tail(&card->list, &HFClist); - write_unlock_irqrestore(&HFClock, flags); printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt); return 0; error: @@ -2311,15 +2267,12 @@ static void __devexit hfc_remove_pci(struct pci_dev *pdev) { struct hfc_pci *card = pci_get_drvdata(pdev); - u_long flags; - if (card) { - write_lock_irqsave(&HFClock, flags); + if (card) release_card(card); - write_unlock_irqrestore(&HFClock, flags); - } else + else if (debug) - printk(KERN_WARNING "%s: drvdata allready removed\n", + printk(KERN_WARNING "%s: drvdata already removed\n", __func__); } @@ -2331,6 +2284,46 @@ static struct pci_driver hfc_driver = { .id_table = hfc_ids, }; +static int +_hfcpci_softirq(struct device *dev, void *arg) +{ + struct hfc_pci *hc = dev_get_drvdata(dev); + struct bchannel *bch; + if (hc == NULL) + return 0; + + if (hc->hw.int_m2 & HFCPCI_IRQ_ENABLE) { + spin_lock(&hc->lock); + bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); + if (bch && bch->state == ISDN_P_B_RAW) { /* B1 rx&tx */ + main_rec_hfcpci(bch); + tx_birq(bch); + } + bch = Sel_BCS(hc, hc->hw.bswapped ? 1 : 2); + if (bch && bch->state == ISDN_P_B_RAW) { /* B2 rx&tx */ + main_rec_hfcpci(bch); + tx_birq(bch); + } + spin_unlock(&hc->lock); + } + return 0; +} + +static void +hfcpci_softirq(void *arg) +{ + (void) driver_for_each_device(&hfc_driver.driver, NULL, arg, + _hfcpci_softirq); + + /* if next event would be in the past ... */ + if ((s32)(hfc_jiffies + tics - jiffies) <= 0) + hfc_jiffies = jiffies + 1; + else + hfc_jiffies += tics; + hfc_tl.expires = hfc_jiffies; + add_timer(&hfc_tl); +} + static int __init HFC_init(void) { @@ -2375,14 +2368,9 @@ HFC_init(void) static void __exit HFC_cleanup(void) { - struct hfc_pci *card, *next; - if (timer_pending(&hfc_tl)) del_timer(&hfc_tl); - list_for_each_entry_safe(card, next, &HFClist, list) { - release_card(card); - } pci_unregister_driver(&hfc_driver); } diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c index 9116f54def2c..9426c9827e47 100644 --- a/drivers/isdn/mISDN/core.c +++ b/drivers/isdn/mISDN/core.c @@ -25,39 +25,183 @@ MODULE_AUTHOR("Karsten Keil"); MODULE_LICENSE("GPL"); module_param(debug, uint, S_IRUGO | S_IWUSR); -static LIST_HEAD(devices); -static DEFINE_RWLOCK(device_lock); static u64 device_ids; #define MAX_DEVICE_ID 63 static LIST_HEAD(Bprotocols); static DEFINE_RWLOCK(bp_lock); +static void mISDN_dev_release(struct device *dev) +{ + /* nothing to do: the device is part of its parent's data structure */ +} + +static ssize_t _show_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return -ENODEV; + return sprintf(buf, "%d\n", mdev->id); +} + +static ssize_t _show_nrbchan(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return -ENODEV; + return sprintf(buf, "%d\n", mdev->nrbchan); +} + +static ssize_t _show_d_protocols(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return -ENODEV; + return sprintf(buf, "%d\n", mdev->Dprotocols); +} + +static ssize_t _show_b_protocols(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return -ENODEV; + return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols()); +} + +static ssize_t _show_protocol(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return -ENODEV; + return sprintf(buf, "%d\n", mdev->D.protocol); +} + +static ssize_t _show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + strcpy(buf, dev_name(dev)); + return strlen(buf); +} + +#if 0 /* hangs */ +static ssize_t _set_name(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = 0; + char *out = kmalloc(count + 1, GFP_KERNEL); + + if (!out) + return -ENOMEM; + + memcpy(out, buf, count); + if (count && out[count - 1] == '\n') + out[--count] = 0; + if (count) + err = device_rename(dev, out); + kfree(out); + + return (err < 0) ? err : count; +} +#endif + +static ssize_t _show_channelmap(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + char *bp = buf; + int i; + + for (i = 0; i <= mdev->nrbchan; i++) + *bp++ = test_channelmap(i, mdev->channelmap) ? '1' : '0'; + + return bp - buf; +} + +static struct device_attribute mISDN_dev_attrs[] = { + __ATTR(id, S_IRUGO, _show_id, NULL), + __ATTR(d_protocols, S_IRUGO, _show_d_protocols, NULL), + __ATTR(b_protocols, S_IRUGO, _show_b_protocols, NULL), + __ATTR(protocol, S_IRUGO, _show_protocol, NULL), + __ATTR(channelmap, S_IRUGO, _show_channelmap, NULL), + __ATTR(nrbchan, S_IRUGO, _show_nrbchan, NULL), + __ATTR(name, S_IRUGO, _show_name, NULL), +/* __ATTR(name, S_IRUGO|S_IWUSR, _show_name, _set_name), */ + {} +}; + +#ifdef CONFIG_HOTPLUG +static int mISDN_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return 0; + + if (add_uevent_var(env, "nchans=%d", mdev->nrbchan)) + return -ENOMEM; + + return 0; +} +#endif + +static void mISDN_class_release(struct class *cls) +{ + /* do nothing, it's static */ +} + +static struct class mISDN_class = { + .name = "mISDN", + .owner = THIS_MODULE, +#ifdef CONFIG_HOTPLUG + .dev_uevent = mISDN_uevent, +#endif + .dev_attrs = mISDN_dev_attrs, + .dev_release = mISDN_dev_release, + .class_release = mISDN_class_release, +}; + +static int +_get_mdevice(struct device *dev, void *id) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return 0; + if (mdev->id != *(u_int *)id) + return 0; + return 1; +} + struct mISDNdevice *get_mdevice(u_int id) { - struct mISDNdevice *dev; + return dev_to_mISDN(class_find_device(&mISDN_class, NULL, &id, + _get_mdevice)); +} - read_lock(&device_lock); - list_for_each_entry(dev, &devices, D.list) - if (dev->id == id) { - read_unlock(&device_lock); - return dev; - } - read_unlock(&device_lock); - return NULL; +static int +_get_mdevice_count(struct device *dev, void *cnt) +{ + *(int *)cnt += 1; + return 0; } int get_mdevice_count(void) { - struct mISDNdevice *dev; - int cnt = 0; + int cnt = 0; - read_lock(&device_lock); - list_for_each_entry(dev, &devices, D.list) - cnt++; - read_unlock(&device_lock); + class_for_each_device(&mISDN_class, NULL, &cnt, _get_mdevice_count); return cnt; } @@ -68,19 +212,24 @@ get_free_devid(void) for (i = 0; i <= MAX_DEVICE_ID; i++) if (!test_and_set_bit(i, (u_long *)&device_ids)) - return i; - return -1; + break; + if (i > MAX_DEVICE_ID) + return -1; + return i; } int -mISDN_register_device(struct mISDNdevice *dev, char *name) +mISDN_register_device(struct mISDNdevice *dev, + struct device *parent, char *name) { - u_long flags; int err; dev->id = get_free_devid(); + err = -EBUSY; if (dev->id < 0) - return -EBUSY; + goto error1; + + device_initialize(&dev->dev); if (name && name[0]) dev_set_name(&dev->dev, "%s", name); else @@ -90,26 +239,39 @@ mISDN_register_device(struct mISDNdevice *dev, char *name) dev_name(&dev->dev), dev->id); err = create_stack(dev); if (err) - return err; - write_lock_irqsave(&device_lock, flags); - list_add_tail(&dev->D.list, &devices); - write_unlock_irqrestore(&device_lock, flags); + goto error1; + + dev->dev.class = &mISDN_class; + dev->dev.platform_data = dev; + dev->dev.parent = parent; + dev_set_drvdata(&dev->dev, dev); + + err = device_add(&dev->dev); + if (err) + goto error3; return 0; + +error3: + delete_stack(dev); + return err; +error1: + return err; + } EXPORT_SYMBOL(mISDN_register_device); void mISDN_unregister_device(struct mISDNdevice *dev) { - u_long flags; - if (debug & DEBUG_CORE) printk(KERN_DEBUG "mISDN_unregister %s %d\n", dev_name(&dev->dev), dev->id); - write_lock_irqsave(&device_lock, flags); - list_del(&dev->D.list); - write_unlock_irqrestore(&device_lock, flags); + /* sysfs_remove_link(&dev->dev.kobj, "device"); */ + device_del(&dev->dev); + dev_set_drvdata(&dev->dev, NULL); + test_and_clear_bit(dev->id, (u_long *)&device_ids); delete_stack(dev); + put_device(&dev->dev); } EXPORT_SYMBOL(mISDN_unregister_device); @@ -201,42 +363,43 @@ mISDNInit(void) MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE); mISDN_init_clock(&debug); mISDN_initstack(&debug); + err = class_register(&mISDN_class); + if (err) + goto error1; err = mISDN_inittimer(&debug); if (err) - goto error; + goto error2; err = l1_init(&debug); - if (err) { - mISDN_timer_cleanup(); - goto error; - } + if (err) + goto error3; err = Isdnl2_Init(&debug); - if (err) { - mISDN_timer_cleanup(); - l1_cleanup(); - goto error; - } + if (err) + goto error4; err = misdn_sock_init(&debug); - if (err) { - mISDN_timer_cleanup(); - l1_cleanup(); - Isdnl2_cleanup(); - } -error: + if (err) + goto error5; + return 0; + +error5: + Isdnl2_cleanup(); +error4: + l1_cleanup(); +error3: + mISDN_timer_cleanup(); +error2: + class_unregister(&mISDN_class); +error1: return err; } static void mISDN_cleanup(void) { misdn_sock_cleanup(); - mISDN_timer_cleanup(); - l1_cleanup(); Isdnl2_cleanup(); + l1_cleanup(); + mISDN_timer_cleanup(); + class_unregister(&mISDN_class); - if (!list_empty(&devices)) - printk(KERN_ERR "%s devices still registered\n", __func__); - - if (!list_empty(&Bprotocols)) - printk(KERN_ERR "%s Bprotocols still registered\n", __func__); printk(KERN_DEBUG "mISDNcore unloaded\n"); } diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c index a6a9e1af2ad6..abe574989572 100644 --- a/drivers/isdn/mISDN/l1oip_core.c +++ b/drivers/isdn/mISDN/l1oip_core.c @@ -1433,7 +1433,8 @@ init_card(struct l1oip *hc, int pri, int bundle) hc->chan[i + ch].bch = bch; set_channelmap(bch->nr, dch->dev.channelmap); } - ret = mISDN_register_device(&dch->dev, hc->name); + /* TODO: create a parent device for this driver */ + ret = mISDN_register_device(&dch->dev, NULL, hc->name); if (ret) return ret; hc->registered = 1; diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h index 3f9988849f32..d4229aebf648 100644 --- a/include/linux/mISDNif.h +++ b/include/linux/mISDNif.h @@ -531,7 +531,8 @@ _queue_data(struct mISDNchannel *ch, u_int prim, /* global register/unregister functions */ -extern int mISDN_register_device(struct mISDNdevice *, char *name); +extern int mISDN_register_device(struct mISDNdevice *, + struct device *parent, char *name); extern void mISDN_unregister_device(struct mISDNdevice *); extern int mISDN_register_Bprotocol(struct Bprotocol *); extern void mISDN_unregister_Bprotocol(struct Bprotocol *); @@ -539,6 +540,11 @@ extern struct mISDNclock *mISDN_register_clock(char *, int, clockctl_func_t *, void *); extern void mISDN_unregister_clock(struct mISDNclock *); +static inline struct mISDNdevice *dev_to_mISDN(struct device *dev) +{ + return dev_get_drvdata(dev); +} + extern void set_channel_address(struct mISDNchannel *, u_int, u_int); extern void mISDN_clock_update(struct mISDNclock *, int, struct timeval *); extern unsigned short mISDN_clock_get(void); -- cgit v1.2.2