1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* ATM driver model support. */ |
3 | |
4 | #include <linux/kernel.h> |
5 | #include <linux/slab.h> |
6 | #include <linux/init.h> |
7 | #include <linux/kobject.h> |
8 | #include <linux/atmdev.h> |
9 | #include "common.h" |
10 | #include "resources.h" |
11 | |
12 | #define to_atm_dev(cldev) container_of(cldev, struct atm_dev, class_dev) |
13 | |
14 | static ssize_t type_show(struct device *cdev, |
15 | struct device_attribute *attr, char *buf) |
16 | { |
17 | struct atm_dev *adev = to_atm_dev(cdev); |
18 | |
19 | return scnprintf(buf, PAGE_SIZE, fmt: "%s\n" , adev->type); |
20 | } |
21 | |
22 | static ssize_t address_show(struct device *cdev, |
23 | struct device_attribute *attr, char *buf) |
24 | { |
25 | struct atm_dev *adev = to_atm_dev(cdev); |
26 | |
27 | return scnprintf(buf, PAGE_SIZE, fmt: "%pM\n" , adev->esi); |
28 | } |
29 | |
30 | static ssize_t atmaddress_show(struct device *cdev, |
31 | struct device_attribute *attr, char *buf) |
32 | { |
33 | unsigned long flags; |
34 | struct atm_dev *adev = to_atm_dev(cdev); |
35 | struct atm_dev_addr *aaddr; |
36 | int count = 0; |
37 | |
38 | spin_lock_irqsave(&adev->lock, flags); |
39 | list_for_each_entry(aaddr, &adev->local, entry) { |
40 | count += scnprintf(buf: buf + count, PAGE_SIZE - count, |
41 | fmt: "%1phN.%2phN.%10phN.%6phN.%1phN\n" , |
42 | &aaddr->addr.sas_addr.prv[0], |
43 | &aaddr->addr.sas_addr.prv[1], |
44 | &aaddr->addr.sas_addr.prv[3], |
45 | &aaddr->addr.sas_addr.prv[13], |
46 | &aaddr->addr.sas_addr.prv[19]); |
47 | } |
48 | spin_unlock_irqrestore(lock: &adev->lock, flags); |
49 | |
50 | return count; |
51 | } |
52 | |
53 | static ssize_t atmindex_show(struct device *cdev, |
54 | struct device_attribute *attr, char *buf) |
55 | { |
56 | struct atm_dev *adev = to_atm_dev(cdev); |
57 | |
58 | return scnprintf(buf, PAGE_SIZE, fmt: "%d\n" , adev->number); |
59 | } |
60 | |
61 | static ssize_t carrier_show(struct device *cdev, |
62 | struct device_attribute *attr, char *buf) |
63 | { |
64 | struct atm_dev *adev = to_atm_dev(cdev); |
65 | |
66 | return scnprintf(buf, PAGE_SIZE, fmt: "%d\n" , |
67 | adev->signal == ATM_PHY_SIG_LOST ? 0 : 1); |
68 | } |
69 | |
70 | static ssize_t link_rate_show(struct device *cdev, |
71 | struct device_attribute *attr, char *buf) |
72 | { |
73 | struct atm_dev *adev = to_atm_dev(cdev); |
74 | int link_rate; |
75 | |
76 | /* show the link rate, not the data rate */ |
77 | switch (adev->link_rate) { |
78 | case ATM_OC3_PCR: |
79 | link_rate = 155520000; |
80 | break; |
81 | case ATM_OC12_PCR: |
82 | link_rate = 622080000; |
83 | break; |
84 | case ATM_25_PCR: |
85 | link_rate = 25600000; |
86 | break; |
87 | default: |
88 | link_rate = adev->link_rate * 8 * 53; |
89 | } |
90 | return scnprintf(buf, PAGE_SIZE, fmt: "%d\n" , link_rate); |
91 | } |
92 | |
93 | static DEVICE_ATTR_RO(address); |
94 | static DEVICE_ATTR_RO(atmaddress); |
95 | static DEVICE_ATTR_RO(atmindex); |
96 | static DEVICE_ATTR_RO(carrier); |
97 | static DEVICE_ATTR_RO(type); |
98 | static DEVICE_ATTR_RO(link_rate); |
99 | |
100 | static struct device_attribute *atm_attrs[] = { |
101 | &dev_attr_atmaddress, |
102 | &dev_attr_address, |
103 | &dev_attr_atmindex, |
104 | &dev_attr_carrier, |
105 | &dev_attr_type, |
106 | &dev_attr_link_rate, |
107 | NULL |
108 | }; |
109 | |
110 | |
111 | static int atm_uevent(const struct device *cdev, struct kobj_uevent_env *env) |
112 | { |
113 | const struct atm_dev *adev; |
114 | |
115 | if (!cdev) |
116 | return -ENODEV; |
117 | |
118 | adev = to_atm_dev(cdev); |
119 | |
120 | if (add_uevent_var(env, format: "NAME=%s%d" , adev->type, adev->number)) |
121 | return -ENOMEM; |
122 | |
123 | return 0; |
124 | } |
125 | |
126 | static void atm_release(struct device *cdev) |
127 | { |
128 | struct atm_dev *adev = to_atm_dev(cdev); |
129 | |
130 | kfree(objp: adev); |
131 | } |
132 | |
133 | static struct class atm_class = { |
134 | .name = "atm" , |
135 | .dev_release = atm_release, |
136 | .dev_uevent = atm_uevent, |
137 | }; |
138 | |
139 | int atm_register_sysfs(struct atm_dev *adev, struct device *parent) |
140 | { |
141 | struct device *cdev = &adev->class_dev; |
142 | int i, j, err; |
143 | |
144 | cdev->class = &atm_class; |
145 | cdev->parent = parent; |
146 | dev_set_drvdata(dev: cdev, data: adev); |
147 | |
148 | dev_set_name(dev: cdev, name: "%s%d" , adev->type, adev->number); |
149 | err = device_register(dev: cdev); |
150 | if (err < 0) |
151 | return err; |
152 | |
153 | for (i = 0; atm_attrs[i]; i++) { |
154 | err = device_create_file(device: cdev, entry: atm_attrs[i]); |
155 | if (err) |
156 | goto err_out; |
157 | } |
158 | |
159 | return 0; |
160 | |
161 | err_out: |
162 | for (j = 0; j < i; j++) |
163 | device_remove_file(dev: cdev, attr: atm_attrs[j]); |
164 | device_del(dev: cdev); |
165 | return err; |
166 | } |
167 | |
168 | void atm_unregister_sysfs(struct atm_dev *adev) |
169 | { |
170 | struct device *cdev = &adev->class_dev; |
171 | |
172 | device_del(dev: cdev); |
173 | } |
174 | |
175 | int __init atm_sysfs_init(void) |
176 | { |
177 | return class_register(class: &atm_class); |
178 | } |
179 | |
180 | void __exit atm_sysfs_exit(void) |
181 | { |
182 | class_unregister(class: &atm_class); |
183 | } |
184 | |