Commit fb551405c0f8e15d6fc7ae6e16a5e15382f8b8ac
Committed by
Wim Van Sebroeck
1 parent
e907df3272
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
watchdog: sch56xx: Use watchdog core
Convert sch56xx drivers to the generic watchdog core. Note this patch depends on the "watchdog: Add multiple device support" patch from Alan Cox. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Showing 4 changed files with 61 additions and 316 deletions Side-by-side Diff
drivers/hwmon/sch5627.c
... | ... | @@ -579,7 +579,7 @@ |
579 | 579 | } |
580 | 580 | |
581 | 581 | /* Note failing to register the watchdog is not a fatal error */ |
582 | - data->watchdog = sch56xx_watchdog_register(data->addr, | |
582 | + data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr, | |
583 | 583 | (build_code << 24) | (build_id << 8) | hwmon_rev, |
584 | 584 | &data->update_lock, 1); |
585 | 585 |
drivers/hwmon/sch5636.c
... | ... | @@ -510,7 +510,7 @@ |
510 | 510 | } |
511 | 511 | |
512 | 512 | /* Note failing to register the watchdog is not a fatal error */ |
513 | - data->watchdog = sch56xx_watchdog_register(data->addr, | |
513 | + data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr, | |
514 | 514 | (revision[0] << 8) | revision[1], |
515 | 515 | &data->update_lock, 0); |
516 | 516 |
drivers/hwmon/sch56xx-common.c
... | ... | @@ -66,15 +66,9 @@ |
66 | 66 | |
67 | 67 | struct sch56xx_watchdog_data { |
68 | 68 | u16 addr; |
69 | - u32 revision; | |
70 | 69 | struct mutex *io_lock; |
71 | - struct mutex watchdog_lock; | |
72 | - struct list_head list; /* member of the watchdog_data_list */ | |
73 | - struct kref kref; | |
74 | - struct miscdevice watchdog_miscdev; | |
75 | - unsigned long watchdog_is_open; | |
76 | - char watchdog_name[10]; /* must be unique to avoid sysfs conflict */ | |
77 | - char watchdog_expect_close; | |
70 | + struct watchdog_info wdinfo; | |
71 | + struct watchdog_device wddev; | |
78 | 72 | u8 watchdog_preset; |
79 | 73 | u8 watchdog_control; |
80 | 74 | u8 watchdog_output_enable; |
... | ... | @@ -82,15 +76,6 @@ |
82 | 76 | |
83 | 77 | static struct platform_device *sch56xx_pdev; |
84 | 78 | |
85 | -/* | |
86 | - * Somewhat ugly :( global data pointer list with all sch56xx devices, so that | |
87 | - * we can find our device data as when using misc_register there is no other | |
88 | - * method to get to ones device data from the open fop. | |
89 | - */ | |
90 | -static LIST_HEAD(watchdog_data_list); | |
91 | -/* Note this lock not only protect list access, but also data.kref access */ | |
92 | -static DEFINE_MUTEX(watchdog_data_mutex); | |
93 | - | |
94 | 79 | /* Super I/O functions */ |
95 | 80 | static inline int superio_inb(int base, int reg) |
96 | 81 | { |
97 | 82 | |
98 | 83 | |
... | ... | @@ -272,22 +257,13 @@ |
272 | 257 | * Watchdog routines |
273 | 258 | */ |
274 | 259 | |
275 | -/* | |
276 | - * Release our data struct when the platform device has been released *and* | |
277 | - * all references to our watchdog device are released. | |
278 | - */ | |
279 | -static void sch56xx_watchdog_release_resources(struct kref *r) | |
260 | +static int watchdog_set_timeout(struct watchdog_device *wddev, | |
261 | + unsigned int timeout) | |
280 | 262 | { |
281 | - struct sch56xx_watchdog_data *data = | |
282 | - container_of(r, struct sch56xx_watchdog_data, kref); | |
283 | - kfree(data); | |
284 | -} | |
285 | - | |
286 | -static int watchdog_set_timeout(struct sch56xx_watchdog_data *data, | |
287 | - int timeout) | |
288 | -{ | |
289 | - int ret, resolution; | |
263 | + struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); | |
264 | + unsigned int resolution; | |
290 | 265 | u8 control; |
266 | + int ret; | |
291 | 267 | |
292 | 268 | /* 1 second or 60 second resolution? */ |
293 | 269 | if (timeout <= 255) |
... | ... | @@ -298,12 +274,6 @@ |
298 | 274 | if (timeout < resolution || timeout > (resolution * 255)) |
299 | 275 | return -EINVAL; |
300 | 276 | |
301 | - mutex_lock(&data->watchdog_lock); | |
302 | - if (!data->addr) { | |
303 | - ret = -ENODEV; | |
304 | - goto leave; | |
305 | - } | |
306 | - | |
307 | 277 | if (resolution == 1) |
308 | 278 | control = data->watchdog_control | SCH56XX_WDOG_TIME_BASE_SEC; |
309 | 279 | else |
... | ... | @@ -316,7 +286,7 @@ |
316 | 286 | control); |
317 | 287 | mutex_unlock(data->io_lock); |
318 | 288 | if (ret) |
319 | - goto leave; | |
289 | + return ret; | |
320 | 290 | |
321 | 291 | data->watchdog_control = control; |
322 | 292 | } |
323 | 293 | |
324 | 294 | |
325 | 295 | |
326 | 296 | |
... | ... | @@ -326,38 +296,17 @@ |
326 | 296 | * the watchdog countdown. |
327 | 297 | */ |
328 | 298 | data->watchdog_preset = DIV_ROUND_UP(timeout, resolution); |
299 | + wddev->timeout = data->watchdog_preset * resolution; | |
329 | 300 | |
330 | - ret = data->watchdog_preset * resolution; | |
331 | -leave: | |
332 | - mutex_unlock(&data->watchdog_lock); | |
333 | - return ret; | |
301 | + return 0; | |
334 | 302 | } |
335 | 303 | |
336 | -static int watchdog_get_timeout(struct sch56xx_watchdog_data *data) | |
304 | +static int watchdog_start(struct watchdog_device *wddev) | |
337 | 305 | { |
338 | - int timeout; | |
339 | - | |
340 | - mutex_lock(&data->watchdog_lock); | |
341 | - if (data->watchdog_control & SCH56XX_WDOG_TIME_BASE_SEC) | |
342 | - timeout = data->watchdog_preset; | |
343 | - else | |
344 | - timeout = data->watchdog_preset * 60; | |
345 | - mutex_unlock(&data->watchdog_lock); | |
346 | - | |
347 | - return timeout; | |
348 | -} | |
349 | - | |
350 | -static int watchdog_start(struct sch56xx_watchdog_data *data) | |
351 | -{ | |
306 | + struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); | |
352 | 307 | int ret; |
353 | 308 | u8 val; |
354 | 309 | |
355 | - mutex_lock(&data->watchdog_lock); | |
356 | - if (!data->addr) { | |
357 | - ret = -ENODEV; | |
358 | - goto leave_unlock_watchdog; | |
359 | - } | |
360 | - | |
361 | 310 | /* |
362 | 311 | * The sch56xx's watchdog cannot really be started / stopped |
363 | 312 | * it is always running, but we can avoid the timer expiring |
364 | 313 | |
365 | 314 | |
366 | 315 | |
367 | 316 | |
368 | 317 | |
369 | 318 | |
370 | 319 | |
... | ... | @@ -405,39 +354,29 @@ |
405 | 354 | |
406 | 355 | leave: |
407 | 356 | mutex_unlock(data->io_lock); |
408 | -leave_unlock_watchdog: | |
409 | - mutex_unlock(&data->watchdog_lock); | |
410 | 357 | return ret; |
411 | 358 | } |
412 | 359 | |
413 | -static int watchdog_trigger(struct sch56xx_watchdog_data *data) | |
360 | +static int watchdog_trigger(struct watchdog_device *wddev) | |
414 | 361 | { |
362 | + struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); | |
415 | 363 | int ret; |
416 | 364 | |
417 | - mutex_lock(&data->watchdog_lock); | |
418 | - if (!data->addr) { | |
419 | - ret = -ENODEV; | |
420 | - goto leave; | |
421 | - } | |
422 | - | |
423 | 365 | /* Reset the watchdog countdown counter */ |
424 | 366 | mutex_lock(data->io_lock); |
425 | 367 | ret = sch56xx_write_virtual_reg(data->addr, SCH56XX_REG_WDOG_PRESET, |
426 | 368 | data->watchdog_preset); |
427 | 369 | mutex_unlock(data->io_lock); |
428 | -leave: | |
429 | - mutex_unlock(&data->watchdog_lock); | |
370 | + | |
430 | 371 | return ret; |
431 | 372 | } |
432 | 373 | |
433 | -static int watchdog_stop_unlocked(struct sch56xx_watchdog_data *data) | |
374 | +static int watchdog_stop(struct watchdog_device *wddev) | |
434 | 375 | { |
376 | + struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); | |
435 | 377 | int ret = 0; |
436 | 378 | u8 val; |
437 | 379 | |
438 | - if (!data->addr) | |
439 | - return -ENODEV; | |
440 | - | |
441 | 380 | if (data->watchdog_output_enable & SCH56XX_WDOG_OUTPUT_ENABLE) { |
442 | 381 | val = data->watchdog_output_enable & |
443 | 382 | ~SCH56XX_WDOG_OUTPUT_ENABLE; |
444 | 383 | |
445 | 384 | |
... | ... | @@ -455,184 +394,19 @@ |
455 | 394 | return ret; |
456 | 395 | } |
457 | 396 | |
458 | -static int watchdog_stop(struct sch56xx_watchdog_data *data) | |
459 | -{ | |
460 | - int ret; | |
461 | - | |
462 | - mutex_lock(&data->watchdog_lock); | |
463 | - ret = watchdog_stop_unlocked(data); | |
464 | - mutex_unlock(&data->watchdog_lock); | |
465 | - | |
466 | - return ret; | |
467 | -} | |
468 | - | |
469 | -static int watchdog_release(struct inode *inode, struct file *filp) | |
470 | -{ | |
471 | - struct sch56xx_watchdog_data *data = filp->private_data; | |
472 | - | |
473 | - if (data->watchdog_expect_close) { | |
474 | - watchdog_stop(data); | |
475 | - data->watchdog_expect_close = 0; | |
476 | - } else { | |
477 | - watchdog_trigger(data); | |
478 | - pr_crit("unexpected close, not stopping watchdog!\n"); | |
479 | - } | |
480 | - | |
481 | - clear_bit(0, &data->watchdog_is_open); | |
482 | - | |
483 | - mutex_lock(&watchdog_data_mutex); | |
484 | - kref_put(&data->kref, sch56xx_watchdog_release_resources); | |
485 | - mutex_unlock(&watchdog_data_mutex); | |
486 | - | |
487 | - return 0; | |
488 | -} | |
489 | - | |
490 | -static int watchdog_open(struct inode *inode, struct file *filp) | |
491 | -{ | |
492 | - struct sch56xx_watchdog_data *pos, *data = NULL; | |
493 | - int ret, watchdog_is_open; | |
494 | - | |
495 | - /* | |
496 | - * We get called from drivers/char/misc.c with misc_mtx hold, and we | |
497 | - * call misc_register() from sch56xx_watchdog_probe() with | |
498 | - * watchdog_data_mutex hold, as misc_register() takes the misc_mtx | |
499 | - * lock, this is a possible deadlock, so we use mutex_trylock here. | |
500 | - */ | |
501 | - if (!mutex_trylock(&watchdog_data_mutex)) | |
502 | - return -ERESTARTSYS; | |
503 | - list_for_each_entry(pos, &watchdog_data_list, list) { | |
504 | - if (pos->watchdog_miscdev.minor == iminor(inode)) { | |
505 | - data = pos; | |
506 | - break; | |
507 | - } | |
508 | - } | |
509 | - /* Note we can never not have found data, so we don't check for this */ | |
510 | - watchdog_is_open = test_and_set_bit(0, &data->watchdog_is_open); | |
511 | - if (!watchdog_is_open) | |
512 | - kref_get(&data->kref); | |
513 | - mutex_unlock(&watchdog_data_mutex); | |
514 | - | |
515 | - if (watchdog_is_open) | |
516 | - return -EBUSY; | |
517 | - | |
518 | - filp->private_data = data; | |
519 | - | |
520 | - /* Start the watchdog */ | |
521 | - ret = watchdog_start(data); | |
522 | - if (ret) { | |
523 | - watchdog_release(inode, filp); | |
524 | - return ret; | |
525 | - } | |
526 | - | |
527 | - return nonseekable_open(inode, filp); | |
528 | -} | |
529 | - | |
530 | -static ssize_t watchdog_write(struct file *filp, const char __user *buf, | |
531 | - size_t count, loff_t *offset) | |
532 | -{ | |
533 | - int ret; | |
534 | - struct sch56xx_watchdog_data *data = filp->private_data; | |
535 | - | |
536 | - if (count) { | |
537 | - if (!nowayout) { | |
538 | - size_t i; | |
539 | - | |
540 | - /* Clear it in case it was set with a previous write */ | |
541 | - data->watchdog_expect_close = 0; | |
542 | - | |
543 | - for (i = 0; i != count; i++) { | |
544 | - char c; | |
545 | - if (get_user(c, buf + i)) | |
546 | - return -EFAULT; | |
547 | - if (c == 'V') | |
548 | - data->watchdog_expect_close = 1; | |
549 | - } | |
550 | - } | |
551 | - ret = watchdog_trigger(data); | |
552 | - if (ret) | |
553 | - return ret; | |
554 | - } | |
555 | - return count; | |
556 | -} | |
557 | - | |
558 | -static long watchdog_ioctl(struct file *filp, unsigned int cmd, | |
559 | - unsigned long arg) | |
560 | -{ | |
561 | - struct watchdog_info ident = { | |
562 | - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, | |
563 | - .identity = "sch56xx watchdog" | |
564 | - }; | |
565 | - int i, ret = 0; | |
566 | - struct sch56xx_watchdog_data *data = filp->private_data; | |
567 | - | |
568 | - switch (cmd) { | |
569 | - case WDIOC_GETSUPPORT: | |
570 | - ident.firmware_version = data->revision; | |
571 | - if (!nowayout) | |
572 | - ident.options |= WDIOF_MAGICCLOSE; | |
573 | - if (copy_to_user((void __user *)arg, &ident, sizeof(ident))) | |
574 | - ret = -EFAULT; | |
575 | - break; | |
576 | - | |
577 | - case WDIOC_GETSTATUS: | |
578 | - case WDIOC_GETBOOTSTATUS: | |
579 | - ret = put_user(0, (int __user *)arg); | |
580 | - break; | |
581 | - | |
582 | - case WDIOC_KEEPALIVE: | |
583 | - ret = watchdog_trigger(data); | |
584 | - break; | |
585 | - | |
586 | - case WDIOC_GETTIMEOUT: | |
587 | - i = watchdog_get_timeout(data); | |
588 | - ret = put_user(i, (int __user *)arg); | |
589 | - break; | |
590 | - | |
591 | - case WDIOC_SETTIMEOUT: | |
592 | - if (get_user(i, (int __user *)arg)) { | |
593 | - ret = -EFAULT; | |
594 | - break; | |
595 | - } | |
596 | - ret = watchdog_set_timeout(data, i); | |
597 | - if (ret >= 0) | |
598 | - ret = put_user(ret, (int __user *)arg); | |
599 | - break; | |
600 | - | |
601 | - case WDIOC_SETOPTIONS: | |
602 | - if (get_user(i, (int __user *)arg)) { | |
603 | - ret = -EFAULT; | |
604 | - break; | |
605 | - } | |
606 | - | |
607 | - if (i & WDIOS_DISABLECARD) | |
608 | - ret = watchdog_stop(data); | |
609 | - else if (i & WDIOS_ENABLECARD) | |
610 | - ret = watchdog_trigger(data); | |
611 | - else | |
612 | - ret = -EINVAL; | |
613 | - break; | |
614 | - | |
615 | - default: | |
616 | - ret = -ENOTTY; | |
617 | - } | |
618 | - return ret; | |
619 | -} | |
620 | - | |
621 | -static const struct file_operations watchdog_fops = { | |
622 | - .owner = THIS_MODULE, | |
623 | - .llseek = no_llseek, | |
624 | - .open = watchdog_open, | |
625 | - .release = watchdog_release, | |
626 | - .write = watchdog_write, | |
627 | - .unlocked_ioctl = watchdog_ioctl, | |
397 | +static const struct watchdog_ops watchdog_ops = { | |
398 | + .owner = THIS_MODULE, | |
399 | + .start = watchdog_start, | |
400 | + .stop = watchdog_stop, | |
401 | + .ping = watchdog_trigger, | |
402 | + .set_timeout = watchdog_set_timeout, | |
628 | 403 | }; |
629 | 404 | |
630 | -struct sch56xx_watchdog_data *sch56xx_watchdog_register( | |
405 | +struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, | |
631 | 406 | u16 addr, u32 revision, struct mutex *io_lock, int check_enabled) |
632 | 407 | { |
633 | 408 | struct sch56xx_watchdog_data *data; |
634 | - int i, err, control, output_enable; | |
635 | - const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 }; | |
409 | + int err, control, output_enable; | |
636 | 410 | |
637 | 411 | /* Cache the watchdog registers */ |
638 | 412 | mutex_lock(io_lock); |
639 | 413 | |
640 | 414 | |
641 | 415 | |
642 | 416 | |
643 | 417 | |
644 | 418 | |
645 | 419 | |
646 | 420 | |
647 | 421 | |
... | ... | @@ -656,82 +430,53 @@ |
656 | 430 | return NULL; |
657 | 431 | |
658 | 432 | data->addr = addr; |
659 | - data->revision = revision; | |
660 | 433 | data->io_lock = io_lock; |
661 | - data->watchdog_control = control; | |
662 | - data->watchdog_output_enable = output_enable; | |
663 | - mutex_init(&data->watchdog_lock); | |
664 | - INIT_LIST_HEAD(&data->list); | |
665 | - kref_init(&data->kref); | |
666 | 434 | |
667 | - err = watchdog_set_timeout(data, 60); | |
668 | - if (err < 0) | |
669 | - goto error; | |
435 | + strlcpy(data->wdinfo.identity, "sch56xx watchdog", | |
436 | + sizeof(data->wdinfo.identity)); | |
437 | + data->wdinfo.firmware_version = revision; | |
438 | + data->wdinfo.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT; | |
439 | + if (!nowayout) | |
440 | + data->wdinfo.options |= WDIOF_MAGICCLOSE; | |
670 | 441 | |
671 | - /* | |
672 | - * We take the data_mutex lock early so that watchdog_open() cannot | |
673 | - * run when misc_register() has completed, but we've not yet added | |
674 | - * our data to the watchdog_data_list. | |
675 | - */ | |
676 | - mutex_lock(&watchdog_data_mutex); | |
677 | - for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) { | |
678 | - /* Register our watchdog part */ | |
679 | - snprintf(data->watchdog_name, sizeof(data->watchdog_name), | |
680 | - "watchdog%c", (i == 0) ? '\0' : ('0' + i)); | |
681 | - data->watchdog_miscdev.name = data->watchdog_name; | |
682 | - data->watchdog_miscdev.fops = &watchdog_fops; | |
683 | - data->watchdog_miscdev.minor = watchdog_minors[i]; | |
684 | - err = misc_register(&data->watchdog_miscdev); | |
685 | - if (err == -EBUSY) | |
686 | - continue; | |
687 | - if (err) | |
688 | - break; | |
442 | + data->wddev.info = &data->wdinfo; | |
443 | + data->wddev.ops = &watchdog_ops; | |
444 | + data->wddev.parent = parent; | |
445 | + data->wddev.timeout = 60; | |
446 | + data->wddev.min_timeout = 1; | |
447 | + data->wddev.max_timeout = 255 * 60; | |
448 | + if (nowayout) | |
449 | + data->wddev.status |= WDOG_NO_WAY_OUT; | |
450 | + if (output_enable & SCH56XX_WDOG_OUTPUT_ENABLE) | |
451 | + data->wddev.status |= WDOG_ACTIVE; | |
689 | 452 | |
690 | - list_add(&data->list, &watchdog_data_list); | |
691 | - pr_info("Registered /dev/%s chardev major 10, minor: %d\n", | |
692 | - data->watchdog_name, watchdog_minors[i]); | |
693 | - break; | |
694 | - } | |
695 | - mutex_unlock(&watchdog_data_mutex); | |
453 | + /* Since the watchdog uses a downcounter there is no register to read | |
454 | + the BIOS set timeout from (if any was set at all) -> | |
455 | + Choose a preset which will give us a 1 minute timeout */ | |
456 | + if (control & SCH56XX_WDOG_TIME_BASE_SEC) | |
457 | + data->watchdog_preset = 60; /* seconds */ | |
458 | + else | |
459 | + data->watchdog_preset = 1; /* minute */ | |
696 | 460 | |
461 | + data->watchdog_control = control; | |
462 | + data->watchdog_output_enable = output_enable; | |
463 | + | |
464 | + watchdog_set_drvdata(&data->wddev, data); | |
465 | + err = watchdog_register_device(&data->wddev); | |
697 | 466 | if (err) { |
698 | 467 | pr_err("Registering watchdog chardev: %d\n", err); |
699 | - goto error; | |
468 | + kfree(data); | |
469 | + return NULL; | |
700 | 470 | } |
701 | - if (i == ARRAY_SIZE(watchdog_minors)) { | |
702 | - pr_warn("Couldn't register watchdog (no free minor)\n"); | |
703 | - goto error; | |
704 | - } | |
705 | 471 | |
706 | 472 | return data; |
707 | - | |
708 | -error: | |
709 | - kfree(data); | |
710 | - return NULL; | |
711 | 473 | } |
712 | 474 | EXPORT_SYMBOL(sch56xx_watchdog_register); |
713 | 475 | |
714 | 476 | void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data) |
715 | 477 | { |
716 | - mutex_lock(&watchdog_data_mutex); | |
717 | - misc_deregister(&data->watchdog_miscdev); | |
718 | - list_del(&data->list); | |
719 | - mutex_unlock(&watchdog_data_mutex); | |
720 | - | |
721 | - mutex_lock(&data->watchdog_lock); | |
722 | - if (data->watchdog_is_open) { | |
723 | - pr_warn("platform device unregistered with watchdog " | |
724 | - "open! Stopping watchdog.\n"); | |
725 | - watchdog_stop_unlocked(data); | |
726 | - } | |
727 | - /* Tell the wdog start/stop/trigger functions our dev is gone */ | |
728 | - data->addr = 0; | |
729 | - data->io_lock = NULL; | |
730 | - mutex_unlock(&data->watchdog_lock); | |
731 | - | |
732 | - mutex_lock(&watchdog_data_mutex); | |
733 | - kref_put(&data->kref, sch56xx_watchdog_release_resources); | |
734 | - mutex_unlock(&watchdog_data_mutex); | |
478 | + watchdog_unregister_device(&data->wddev); | |
479 | + kfree(data); | |
735 | 480 | } |
736 | 481 | EXPORT_SYMBOL(sch56xx_watchdog_unregister); |
737 | 482 |
drivers/hwmon/sch56xx-common.h
... | ... | @@ -27,7 +27,7 @@ |
27 | 27 | int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, |
28 | 28 | int high_nibble); |
29 | 29 | |
30 | -struct sch56xx_watchdog_data *sch56xx_watchdog_register( | |
30 | +struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, | |
31 | 31 | u16 addr, u32 revision, struct mutex *io_lock, int check_enabled); |
32 | 32 | void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data); |