]>
Commit | Line | Data |
---|---|---|
47fdf097 ML |
1 | /* |
2 | * Copyright (C) 2010 B.A.T.M.A.N. contributors: | |
3 | * | |
4 | * Marek Lindner | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of version 2 of the GNU General Public | |
8 | * License as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
18 | * 02110-1301, USA | |
19 | * | |
20 | */ | |
21 | ||
22 | #include "main.h" | |
23 | #include "bat_sysfs.h" | |
24 | #include "translation-table.h" | |
25 | #include "originator.h" | |
26 | #include "hard-interface.h" | |
14741240 | 27 | #include "vis.h" |
47fdf097 ML |
28 | |
29 | #define to_dev(obj) container_of(obj, struct device, kobj) | |
30 | ||
47fdf097 ML |
31 | #define BAT_ATTR(_name, _mode, _show, _store) \ |
32 | struct bat_attribute bat_attr_##_name = { \ | |
33 | .attr = {.name = __stringify(_name), \ | |
34 | .mode = _mode }, \ | |
35 | .show = _show, \ | |
36 | .store = _store, \ | |
37 | }; | |
38 | ||
3a632cc7 | 39 | static ssize_t show_aggr_ogms(struct kobject *kobj, struct attribute *attr, |
47fdf097 ML |
40 | char *buff) |
41 | { | |
42 | struct device *dev = to_dev(kobj->parent); | |
43 | struct bat_priv *bat_priv = netdev_priv(to_net_dev(dev)); | |
44 | int aggr_status = atomic_read(&bat_priv->aggregation_enabled); | |
45 | ||
3a632cc7 | 46 | return sprintf(buff, "%s\n", |
47fdf097 ML |
47 | aggr_status == 0 ? "disabled" : "enabled"); |
48 | } | |
49 | ||
3a632cc7 | 50 | static ssize_t store_aggr_ogms(struct kobject *kobj, struct attribute *attr, |
47fdf097 ML |
51 | char *buff, size_t count) |
52 | { | |
53 | struct device *dev = to_dev(kobj->parent); | |
54 | struct net_device *net_dev = to_net_dev(dev); | |
55 | struct bat_priv *bat_priv = netdev_priv(net_dev); | |
56 | int aggr_tmp = -1; | |
57 | ||
58 | if (((count == 2) && (buff[0] == '1')) || | |
59 | (strncmp(buff, "enable", 6) == 0)) | |
60 | aggr_tmp = 1; | |
61 | ||
62 | if (((count == 2) && (buff[0] == '0')) || | |
63 | (strncmp(buff, "disable", 7) == 0)) | |
64 | aggr_tmp = 0; | |
65 | ||
66 | if (aggr_tmp < 0) { | |
67 | if (buff[count - 1] == '\n') | |
68 | buff[count - 1] = '\0'; | |
69 | ||
c1641862 SE |
70 | bat_info(net_dev, |
71 | "Invalid parameter for 'aggregate OGM' setting" | |
72 | "received: %s\n", buff); | |
47fdf097 ML |
73 | return -EINVAL; |
74 | } | |
75 | ||
76 | if (atomic_read(&bat_priv->aggregation_enabled) == aggr_tmp) | |
77 | return count; | |
78 | ||
c1641862 SE |
79 | bat_info(net_dev, "Changing aggregation from: %s to: %s\n", |
80 | atomic_read(&bat_priv->aggregation_enabled) == 1 ? | |
81 | "enabled" : "disabled", aggr_tmp == 1 ? "enabled" : | |
82 | "disabled"); | |
47fdf097 ML |
83 | |
84 | atomic_set(&bat_priv->aggregation_enabled, (unsigned)aggr_tmp); | |
85 | return count; | |
86 | } | |
87 | ||
e35fd5ec SW |
88 | static ssize_t show_bond(struct kobject *kobj, struct attribute *attr, |
89 | char *buff) | |
90 | { | |
91 | struct device *dev = to_dev(kobj->parent); | |
92 | struct bat_priv *bat_priv = netdev_priv(to_net_dev(dev)); | |
93 | int bond_status = atomic_read(&bat_priv->bonding_enabled); | |
94 | ||
95 | return sprintf(buff, "%s\n", | |
96 | bond_status == 0 ? "disabled" : "enabled"); | |
97 | } | |
98 | ||
99 | static ssize_t store_bond(struct kobject *kobj, struct attribute *attr, | |
100 | char *buff, size_t count) | |
101 | { | |
102 | struct device *dev = to_dev(kobj->parent); | |
103 | struct net_device *net_dev = to_net_dev(dev); | |
104 | struct bat_priv *bat_priv = netdev_priv(net_dev); | |
105 | int bonding_enabled_tmp = -1; | |
106 | ||
107 | if (((count == 2) && (buff[0] == '1')) || | |
108 | (strncmp(buff, "enable", 6) == 0)) | |
109 | bonding_enabled_tmp = 1; | |
110 | ||
111 | if (((count == 2) && (buff[0] == '0')) || | |
112 | (strncmp(buff, "disable", 7) == 0)) | |
113 | bonding_enabled_tmp = 0; | |
114 | ||
115 | if (bonding_enabled_tmp < 0) { | |
116 | if (buff[count - 1] == '\n') | |
117 | buff[count - 1] = '\0'; | |
118 | ||
c1641862 SE |
119 | bat_err(net_dev, |
120 | "Invalid parameter for 'bonding' setting received: " | |
121 | "%s\n", buff); | |
e35fd5ec SW |
122 | return -EINVAL; |
123 | } | |
124 | ||
125 | if (atomic_read(&bat_priv->bonding_enabled) == bonding_enabled_tmp) | |
126 | return count; | |
127 | ||
c1641862 SE |
128 | bat_info(net_dev, "Changing bonding from: %s to: %s\n", |
129 | atomic_read(&bat_priv->bonding_enabled) == 1 ? | |
130 | "enabled" : "disabled", | |
131 | bonding_enabled_tmp == 1 ? "enabled" : "disabled"); | |
e35fd5ec SW |
132 | |
133 | atomic_set(&bat_priv->bonding_enabled, (unsigned)bonding_enabled_tmp); | |
134 | return count; | |
135 | } | |
136 | ||
14741240 ML |
137 | static ssize_t show_vis_mode(struct kobject *kobj, struct attribute *attr, |
138 | char *buff) | |
139 | { | |
140 | struct device *dev = to_dev(kobj->parent); | |
141 | struct bat_priv *bat_priv = netdev_priv(to_net_dev(dev)); | |
142 | int vis_mode = atomic_read(&bat_priv->vis_mode); | |
143 | ||
3a632cc7 | 144 | return sprintf(buff, "%s\n", |
14741240 | 145 | vis_mode == VIS_TYPE_CLIENT_UPDATE ? |
3a632cc7 | 146 | "client" : "server"); |
14741240 ML |
147 | } |
148 | ||
149 | static ssize_t store_vis_mode(struct kobject *kobj, struct attribute *attr, | |
150 | char *buff, size_t count) | |
151 | { | |
152 | struct device *dev = to_dev(kobj->parent); | |
153 | struct net_device *net_dev = to_net_dev(dev); | |
154 | struct bat_priv *bat_priv = netdev_priv(net_dev); | |
155 | unsigned long val; | |
156 | int ret, vis_mode_tmp = -1; | |
157 | ||
158 | ret = strict_strtoul(buff, 10, &val); | |
159 | ||
160 | if (((count == 2) && (!ret) && (val == VIS_TYPE_CLIENT_UPDATE)) || | |
3a632cc7 ML |
161 | (strncmp(buff, "client", 6) == 0) || |
162 | (strncmp(buff, "off", 3) == 0)) | |
14741240 ML |
163 | vis_mode_tmp = VIS_TYPE_CLIENT_UPDATE; |
164 | ||
165 | if (((count == 2) && (!ret) && (val == VIS_TYPE_SERVER_SYNC)) || | |
166 | (strncmp(buff, "server", 6) == 0)) | |
167 | vis_mode_tmp = VIS_TYPE_SERVER_SYNC; | |
168 | ||
169 | if (vis_mode_tmp < 0) { | |
170 | if (buff[count - 1] == '\n') | |
171 | buff[count - 1] = '\0'; | |
172 | ||
c1641862 SE |
173 | bat_info(net_dev, |
174 | "Invalid parameter for 'vis mode' setting received: " | |
175 | "%s\n", buff); | |
14741240 ML |
176 | return -EINVAL; |
177 | } | |
178 | ||
179 | if (atomic_read(&bat_priv->vis_mode) == vis_mode_tmp) | |
180 | return count; | |
181 | ||
c1641862 SE |
182 | bat_info(net_dev, "Changing vis mode from: %s to: %s\n", |
183 | atomic_read(&bat_priv->vis_mode) == VIS_TYPE_CLIENT_UPDATE ? | |
184 | "client" : "server", vis_mode_tmp == VIS_TYPE_CLIENT_UPDATE ? | |
185 | "client" : "server"); | |
14741240 ML |
186 | |
187 | atomic_set(&bat_priv->vis_mode, (unsigned)vis_mode_tmp); | |
188 | return count; | |
189 | } | |
190 | ||
1d59f82c ML |
191 | static ssize_t show_orig_interval(struct kobject *kobj, struct attribute *attr, |
192 | char *buff) | |
193 | { | |
194 | struct device *dev = to_dev(kobj->parent); | |
195 | struct bat_priv *bat_priv = netdev_priv(to_net_dev(dev)); | |
196 | ||
3a632cc7 | 197 | return sprintf(buff, "%i\n", |
1d59f82c ML |
198 | atomic_read(&bat_priv->orig_interval)); |
199 | } | |
200 | ||
201 | static ssize_t store_orig_interval(struct kobject *kobj, struct attribute *attr, | |
202 | char *buff, size_t count) | |
203 | { | |
204 | struct device *dev = to_dev(kobj->parent); | |
205 | struct net_device *net_dev = to_net_dev(dev); | |
206 | struct bat_priv *bat_priv = netdev_priv(net_dev); | |
207 | unsigned long orig_interval_tmp; | |
208 | int ret; | |
209 | ||
210 | ret = strict_strtoul(buff, 10, &orig_interval_tmp); | |
211 | if (ret) { | |
c1641862 SE |
212 | bat_info(net_dev, "Invalid parameter for 'orig_interval' " |
213 | "setting received: %s\n", buff); | |
1d59f82c ML |
214 | return -EINVAL; |
215 | } | |
216 | ||
5a391957 | 217 | if (orig_interval_tmp < JITTER * 2) { |
c1641862 SE |
218 | bat_info(net_dev, "New originator interval too small: %li " |
219 | "(min: %i)\n", orig_interval_tmp, JITTER * 2); | |
1d59f82c ML |
220 | return -EINVAL; |
221 | } | |
222 | ||
223 | if (atomic_read(&bat_priv->orig_interval) == orig_interval_tmp) | |
224 | return count; | |
225 | ||
c1641862 SE |
226 | bat_info(net_dev, "Changing originator interval from: %i to: %li\n", |
227 | atomic_read(&bat_priv->orig_interval), | |
228 | orig_interval_tmp); | |
1d59f82c ML |
229 | |
230 | atomic_set(&bat_priv->orig_interval, orig_interval_tmp); | |
231 | return count; | |
232 | } | |
233 | ||
84ec0864 ML |
234 | #ifdef CONFIG_BATMAN_ADV_DEBUG |
235 | static ssize_t show_log_level(struct kobject *kobj, struct attribute *attr, | |
236 | char *buff) | |
47fdf097 ML |
237 | { |
238 | struct device *dev = to_dev(kobj->parent); | |
84ec0864 ML |
239 | struct bat_priv *bat_priv = netdev_priv(to_net_dev(dev)); |
240 | int log_level = atomic_read(&bat_priv->log_level); | |
47fdf097 | 241 | |
84ec0864 | 242 | return sprintf(buff, "%d\n", log_level); |
47fdf097 ML |
243 | } |
244 | ||
84ec0864 ML |
245 | static ssize_t store_log_level(struct kobject *kobj, struct attribute *attr, |
246 | char *buff, size_t count) | |
47fdf097 ML |
247 | { |
248 | struct device *dev = to_dev(kobj->parent); | |
249 | struct net_device *net_dev = to_net_dev(dev); | |
84ec0864 ML |
250 | struct bat_priv *bat_priv = netdev_priv(net_dev); |
251 | unsigned long log_level_tmp; | |
252 | int ret; | |
47fdf097 | 253 | |
84ec0864 ML |
254 | ret = strict_strtoul(buff, 10, &log_level_tmp); |
255 | if (ret) { | |
c1641862 SE |
256 | bat_info(net_dev, "Invalid parameter for 'log_level' " |
257 | "setting received: %s\n", buff); | |
84ec0864 ML |
258 | return -EINVAL; |
259 | } | |
47fdf097 | 260 | |
84ec0864 | 261 | if (log_level_tmp > 3) { |
c1641862 SE |
262 | bat_info(net_dev, "New log level too big: %li " |
263 | "(max: %i)\n", log_level_tmp, 3); | |
84ec0864 ML |
264 | return -EINVAL; |
265 | } | |
47fdf097 | 266 | |
84ec0864 ML |
267 | if (atomic_read(&bat_priv->log_level) == log_level_tmp) |
268 | return count; | |
47fdf097 | 269 | |
466122df SE |
270 | bat_info(net_dev, "Changing log level from: %i to: %li\n", |
271 | atomic_read(&bat_priv->log_level), | |
272 | log_level_tmp); | |
273 | ||
84ec0864 ML |
274 | atomic_set(&bat_priv->log_level, (unsigned)log_level_tmp); |
275 | return count; | |
47fdf097 | 276 | } |
84ec0864 | 277 | #endif |
47fdf097 | 278 | |
3a632cc7 ML |
279 | static BAT_ATTR(aggregated_ogms, S_IRUGO | S_IWUSR, |
280 | show_aggr_ogms, store_aggr_ogms); | |
e35fd5ec | 281 | static BAT_ATTR(bonding, S_IRUGO | S_IWUSR, show_bond, store_bond); |
14741240 | 282 | static BAT_ATTR(vis_mode, S_IRUGO | S_IWUSR, show_vis_mode, store_vis_mode); |
1d59f82c ML |
283 | static BAT_ATTR(orig_interval, S_IRUGO | S_IWUSR, |
284 | show_orig_interval, store_orig_interval); | |
84ec0864 ML |
285 | #ifdef CONFIG_BATMAN_ADV_DEBUG |
286 | static BAT_ATTR(log_level, S_IRUGO | S_IWUSR, show_log_level, store_log_level); | |
287 | #endif | |
47fdf097 | 288 | |
47fdf097 | 289 | static struct bat_attribute *mesh_attrs[] = { |
3a632cc7 | 290 | &bat_attr_aggregated_ogms, |
e35fd5ec | 291 | &bat_attr_bonding, |
14741240 | 292 | &bat_attr_vis_mode, |
1d59f82c | 293 | &bat_attr_orig_interval, |
84ec0864 ML |
294 | #ifdef CONFIG_BATMAN_ADV_DEBUG |
295 | &bat_attr_log_level, | |
296 | #endif | |
47fdf097 ML |
297 | NULL, |
298 | }; | |
299 | ||
300 | int sysfs_add_meshif(struct net_device *dev) | |
301 | { | |
302 | struct kobject *batif_kobject = &dev->dev.kobj; | |
303 | struct bat_priv *bat_priv = netdev_priv(dev); | |
304 | struct bat_attribute **bat_attr; | |
47fdf097 ML |
305 | int err; |
306 | ||
307 | /* FIXME: should be done in the general mesh setup | |
308 | routine as soon as we have it */ | |
309 | atomic_set(&bat_priv->aggregation_enabled, 1); | |
e35fd5ec | 310 | atomic_set(&bat_priv->bonding_enabled, 0); |
14741240 | 311 | atomic_set(&bat_priv->vis_mode, VIS_TYPE_CLIENT_UPDATE); |
1d59f82c | 312 | atomic_set(&bat_priv->orig_interval, 1000); |
84ec0864 ML |
313 | atomic_set(&bat_priv->log_level, 0); |
314 | ||
208e13e4 ML |
315 | bat_priv->primary_if = NULL; |
316 | bat_priv->num_ifaces = 0; | |
47fdf097 ML |
317 | |
318 | bat_priv->mesh_obj = kobject_create_and_add(SYSFS_IF_MESH_SUBDIR, | |
319 | batif_kobject); | |
320 | if (!bat_priv->mesh_obj) { | |
c1641862 SE |
321 | bat_err(dev, "Can't add sysfs directory: %s/%s\n", dev->name, |
322 | SYSFS_IF_MESH_SUBDIR); | |
47fdf097 ML |
323 | goto out; |
324 | } | |
325 | ||
326 | for (bat_attr = mesh_attrs; *bat_attr; ++bat_attr) { | |
327 | err = sysfs_create_file(bat_priv->mesh_obj, | |
328 | &((*bat_attr)->attr)); | |
329 | if (err) { | |
c1641862 SE |
330 | bat_err(dev, "Can't add sysfs file: %s/%s/%s\n", |
331 | dev->name, SYSFS_IF_MESH_SUBDIR, | |
332 | ((*bat_attr)->attr).name); | |
47fdf097 ML |
333 | goto rem_attr; |
334 | } | |
335 | } | |
336 | ||
47fdf097 ML |
337 | return 0; |
338 | ||
47fdf097 ML |
339 | rem_attr: |
340 | for (bat_attr = mesh_attrs; *bat_attr; ++bat_attr) | |
341 | sysfs_remove_file(bat_priv->mesh_obj, &((*bat_attr)->attr)); | |
342 | ||
343 | kobject_put(bat_priv->mesh_obj); | |
344 | bat_priv->mesh_obj = NULL; | |
345 | out: | |
346 | return -ENOMEM; | |
347 | } | |
348 | ||
349 | void sysfs_del_meshif(struct net_device *dev) | |
350 | { | |
351 | struct bat_priv *bat_priv = netdev_priv(dev); | |
352 | struct bat_attribute **bat_attr; | |
47fdf097 ML |
353 | |
354 | for (bat_attr = mesh_attrs; *bat_attr; ++bat_attr) | |
355 | sysfs_remove_file(bat_priv->mesh_obj, &((*bat_attr)->attr)); | |
356 | ||
357 | kobject_put(bat_priv->mesh_obj); | |
358 | bat_priv->mesh_obj = NULL; | |
359 | } | |
208e13e4 ML |
360 | |
361 | static ssize_t show_mesh_iface(struct kobject *kobj, struct attribute *attr, | |
362 | char *buff) | |
363 | { | |
364 | struct device *dev = to_dev(kobj->parent); | |
365 | struct net_device *net_dev = to_net_dev(dev); | |
366 | struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); | |
367 | ||
368 | if (!batman_if) | |
369 | return 0; | |
370 | ||
3a632cc7 | 371 | return sprintf(buff, "%s\n", |
208e13e4 ML |
372 | batman_if->if_status == IF_NOT_IN_USE ? |
373 | "none" : "bat0"); | |
374 | } | |
375 | ||
376 | static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr, | |
377 | char *buff, size_t count) | |
378 | { | |
379 | struct device *dev = to_dev(kobj->parent); | |
380 | struct net_device *net_dev = to_net_dev(dev); | |
381 | struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); | |
382 | int status_tmp = -1; | |
383 | ||
384 | if (!batman_if) | |
385 | return count; | |
386 | ||
387 | if (strncmp(buff, "none", 4) == 0) | |
388 | status_tmp = IF_NOT_IN_USE; | |
389 | ||
390 | if (strncmp(buff, "bat0", 4) == 0) | |
391 | status_tmp = IF_I_WANT_YOU; | |
392 | ||
393 | if (status_tmp < 0) { | |
394 | if (buff[count - 1] == '\n') | |
395 | buff[count - 1] = '\0'; | |
396 | ||
c1641862 SE |
397 | pr_err("Invalid parameter for 'mesh_iface' setting received: " |
398 | "%s\n", buff); | |
208e13e4 ML |
399 | return -EINVAL; |
400 | } | |
401 | ||
402 | if ((batman_if->if_status == status_tmp) || | |
403 | ((status_tmp == IF_I_WANT_YOU) && | |
404 | (batman_if->if_status != IF_NOT_IN_USE))) | |
405 | return count; | |
406 | ||
407 | if (status_tmp == IF_I_WANT_YOU) | |
408 | status_tmp = hardif_enable_interface(batman_if); | |
409 | else | |
410 | hardif_disable_interface(batman_if); | |
411 | ||
412 | return (status_tmp < 0 ? status_tmp : count); | |
413 | } | |
414 | ||
415 | static ssize_t show_iface_status(struct kobject *kobj, struct attribute *attr, | |
416 | char *buff) | |
417 | { | |
418 | struct device *dev = to_dev(kobj->parent); | |
419 | struct net_device *net_dev = to_net_dev(dev); | |
420 | struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); | |
421 | ||
422 | if (!batman_if) | |
423 | return 0; | |
424 | ||
425 | switch (batman_if->if_status) { | |
426 | case IF_TO_BE_REMOVED: | |
427 | return sprintf(buff, "disabling\n"); | |
428 | case IF_INACTIVE: | |
429 | return sprintf(buff, "inactive\n"); | |
430 | case IF_ACTIVE: | |
431 | return sprintf(buff, "active\n"); | |
432 | case IF_TO_BE_ACTIVATED: | |
433 | return sprintf(buff, "enabling\n"); | |
434 | case IF_NOT_IN_USE: | |
435 | default: | |
436 | return sprintf(buff, "not in use\n"); | |
437 | } | |
438 | } | |
439 | ||
186305aa ML |
440 | static BAT_ATTR(mesh_iface, S_IRUGO | S_IWUSR, |
441 | show_mesh_iface, store_mesh_iface); | |
442 | static BAT_ATTR(iface_status, S_IRUGO, show_iface_status, NULL); | |
208e13e4 | 443 | |
186305aa ML |
444 | static struct bat_attribute *batman_attrs[] = { |
445 | &bat_attr_mesh_iface, | |
446 | &bat_attr_iface_status, | |
208e13e4 ML |
447 | NULL, |
448 | }; | |
449 | ||
450 | int sysfs_add_hardif(struct kobject **hardif_obj, struct net_device *dev) | |
451 | { | |
452 | struct kobject *hardif_kobject = &dev->dev.kobj; | |
186305aa | 453 | struct bat_attribute **bat_attr; |
208e13e4 ML |
454 | int err; |
455 | ||
456 | *hardif_obj = kobject_create_and_add(SYSFS_IF_BAT_SUBDIR, | |
457 | hardif_kobject); | |
458 | ||
459 | if (!*hardif_obj) { | |
c1641862 SE |
460 | bat_err(dev, "Can't add sysfs directory: %s/%s\n", dev->name, |
461 | SYSFS_IF_BAT_SUBDIR); | |
208e13e4 ML |
462 | goto out; |
463 | } | |
464 | ||
186305aa ML |
465 | for (bat_attr = batman_attrs; *bat_attr; ++bat_attr) { |
466 | err = sysfs_create_file(*hardif_obj, &((*bat_attr)->attr)); | |
208e13e4 | 467 | if (err) { |
c1641862 SE |
468 | bat_err(dev, "Can't add sysfs file: %s/%s/%s\n", |
469 | dev->name, SYSFS_IF_BAT_SUBDIR, | |
470 | ((*bat_attr)->attr).name); | |
208e13e4 ML |
471 | goto rem_attr; |
472 | } | |
473 | } | |
474 | ||
475 | return 0; | |
476 | ||
477 | rem_attr: | |
186305aa ML |
478 | for (bat_attr = batman_attrs; *bat_attr; ++bat_attr) |
479 | sysfs_remove_file(*hardif_obj, &((*bat_attr)->attr)); | |
208e13e4 ML |
480 | out: |
481 | return -ENOMEM; | |
482 | } | |
483 | ||
484 | void sysfs_del_hardif(struct kobject **hardif_obj) | |
485 | { | |
486 | kobject_put(*hardif_obj); | |
487 | *hardif_obj = NULL; | |
488 | } |