aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/dock.c
diff options
context:
space:
mode:
authorKristen Accardi <kristen.c.accardi@intel.com>2006-06-28 03:07:16 -0400
committerLen Brown <len.brown@intel.com>2006-06-28 03:07:16 -0400
commita5e1b94008f2a96abf4a0c0371a55a56b320c13e (patch)
tree3e3a254fdbc565c4f85a66cc6dfaa60319e919a9 /drivers/acpi/dock.c
parenta6a888b3c20cf559c8a2e6e4d86c570dda2ef0f5 (diff)
ACPI: dock driver
Create a driver which lives in the acpi subsystem to handle dock events. This driver is not an "ACPI" driver, because acpi drivers require that the object be present when the driver is loaded. Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com> Cc: Dave Hansen <haveblue@us.ibm.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi/dock.c')
-rw-r--r--drivers/acpi/dock.c739
1 files changed, 739 insertions, 0 deletions
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
new file mode 100644
index 000000000000..510a94524298
--- /dev/null
+++ b/drivers/acpi/dock.c
@@ -0,0 +1,739 @@
1/*
2 * dock.c - ACPI dock station driver
3 *
4 * Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com>
5 *
6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or (at
11 * your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 *
22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 */
24
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/init.h>
28#include <linux/types.h>
29#include <linux/notifier.h>
30#include <acpi/acpi_bus.h>
31#include <acpi/acpi_drivers.h>
32
33#define ACPI_DOCK_DRIVER_NAME "ACPI Dock Station Driver"
34
35ACPI_MODULE_NAME("dock")
36MODULE_AUTHOR("Kristen Carlson Accardi");
37MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_NAME);
38MODULE_LICENSE("GPL");
39
40static struct atomic_notifier_head dock_notifier_list;
41
42struct dock_station {
43 acpi_handle handle;
44 unsigned long last_dock_time;
45 u32 flags;
46 spinlock_t dd_lock;
47 spinlock_t hp_lock;
48 struct list_head dependent_devices;
49 struct list_head hotplug_devices;
50};
51
52struct dock_dependent_device {
53 struct list_head list;
54 struct list_head hotplug_list;
55 acpi_handle handle;
56 acpi_notify_handler handler;
57 void *context;
58};
59
60#define DOCK_DOCKING 0x00000001
61#define DOCK_EVENT KOBJ_DOCK
62#define UNDOCK_EVENT KOBJ_UNDOCK
63
64static struct dock_station *dock_station;
65
66/*****************************************************************************
67 * Dock Dependent device functions *
68 *****************************************************************************/
69/**
70 * alloc_dock_dependent_device - allocate and init a dependent device
71 * @handle: the acpi_handle of the dependent device
72 *
73 * Allocate memory for a dependent device structure for a device referenced
74 * by the acpi handle
75 */
76static struct dock_dependent_device *
77alloc_dock_dependent_device(acpi_handle handle)
78{
79 struct dock_dependent_device *dd;
80
81 dd = kzalloc(sizeof(*dd), GFP_KERNEL);
82 if (dd) {
83 dd->handle = handle;
84 INIT_LIST_HEAD(&dd->list);
85 INIT_LIST_HEAD(&dd->hotplug_list);
86 }
87 return dd;
88}
89
90/**
91 * add_dock_dependent_device - associate a device with the dock station
92 * @ds: The dock station
93 * @dd: The dependent device
94 *
95 * Add the dependent device to the dock's dependent device list.
96 */
97static void
98add_dock_dependent_device(struct dock_station *ds,
99 struct dock_dependent_device *dd)
100{
101 spin_lock(&ds->dd_lock);
102 list_add_tail(&dd->list, &ds->dependent_devices);
103 spin_unlock(&ds->dd_lock);
104}
105
106/**
107 * dock_add_hotplug_device - associate a hotplug handler with the dock station
108 * @ds: The dock station
109 * @dd: The dependent device struct
110 *
111 * Add the dependent device to the dock's hotplug device list
112 */
113static void
114dock_add_hotplug_device(struct dock_station *ds,
115 struct dock_dependent_device *dd)
116{
117 spin_lock(&ds->hp_lock);
118 list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
119 spin_unlock(&ds->hp_lock);
120}
121
122/**
123 * dock_del_hotplug_device - remove a hotplug handler from the dock station
124 * @ds: The dock station
125 * @dd: the dependent device struct
126 *
127 * Delete the dependent device from the dock's hotplug device list
128 */
129static void
130dock_del_hotplug_device(struct dock_station *ds,
131 struct dock_dependent_device *dd)
132{
133 spin_lock(&ds->hp_lock);
134 list_del(&dd->hotplug_list);
135 spin_unlock(&ds->hp_lock);
136}
137
138/**
139 * find_dock_dependent_device - get a device dependent on this dock
140 * @ds: the dock station
141 * @handle: the acpi_handle of the device we want
142 *
143 * iterate over the dependent device list for this dock. If the
144 * dependent device matches the handle, return.
145 */
146static struct dock_dependent_device *
147find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
148{
149 struct dock_dependent_device *dd;
150
151 spin_lock(&ds->dd_lock);
152 list_for_each_entry(dd, &ds->dependent_devices, list) {
153 if (handle == dd->handle) {
154 spin_unlock(&ds->dd_lock);
155 return dd;
156 }
157 }
158 spin_unlock(&ds->dd_lock);
159 return NULL;
160}
161
162/*****************************************************************************
163 * Dock functions *
164 *****************************************************************************/
165/**
166 * is_dock - see if a device is a dock station
167 * @handle: acpi handle of the device
168 *
169 * If an acpi object has a _DCK method, then it is by definition a dock
170 * station, so return true.
171 */
172static int is_dock(acpi_handle handle)
173{
174 acpi_status status;
175 acpi_handle tmp;
176
177 status = acpi_get_handle(handle, "_DCK", &tmp);
178 if (ACPI_FAILURE(status))
179 return 0;
180 return 1;
181}
182
183/**
184 * is_dock_device - see if a device is on a dock station
185 * @handle: acpi handle of the device
186 *
187 * If this device is either the dock station itself,
188 * or is a device dependent on the dock station, then it
189 * is a dock device
190 */
191int is_dock_device(acpi_handle handle)
192{
193 if (!dock_station)
194 return 0;
195
196 if (is_dock(handle) || find_dock_dependent_device(dock_station, handle))
197 return 1;
198
199 return 0;
200}
201
202EXPORT_SYMBOL_GPL(is_dock_device);
203
204/**
205 * dock_present - see if the dock station is present.
206 * @ds: the dock station
207 *
208 * execute the _STA method. note that present does not
209 * imply that we are docked.
210 */
211static int dock_present(struct dock_station *ds)
212{
213 unsigned long sta;
214 acpi_status status;
215
216 if (ds) {
217 status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
218 if (ACPI_SUCCESS(status) && sta)
219 return 1;
220 }
221 return 0;
222}
223
224
225
226/**
227 * dock_create_acpi_device - add new devices to acpi
228 * @handle - handle of the device to add
229 *
230 * This function will create a new acpi_device for the given
231 * handle if one does not exist already. This should cause
232 * acpi to scan for drivers for the given devices, and call
233 * matching driver's add routine.
234 *
235 * Returns a pointer to the acpi_device corresponding to the handle.
236 */
237static struct acpi_device * dock_create_acpi_device(acpi_handle handle)
238{
239 struct acpi_device *device = NULL;
240 struct acpi_device *parent_device;
241 acpi_handle parent;
242 int ret;
243
244 if (acpi_bus_get_device(handle, &device)) {
245 /*
246 * no device created for this object,
247 * so we should create one.
248 */
249 acpi_get_parent(handle, &parent);
250 if (acpi_bus_get_device(parent, &parent_device))
251 parent_device = NULL;
252
253 ret = acpi_bus_add(&device, parent_device, handle,
254 ACPI_BUS_TYPE_DEVICE);
255 if (ret) {
256 pr_debug("error adding bus, %x\n",
257 -ret);
258 return NULL;
259 }
260 }
261 return device;
262}
263
264/**
265 * dock_remove_acpi_device - remove the acpi_device struct from acpi
266 * @handle - the handle of the device to remove
267 *
268 * Tell acpi to remove the acpi_device. This should cause any loaded
269 * driver to have it's remove routine called.
270 */
271static void dock_remove_acpi_device(acpi_handle handle)
272{
273 struct acpi_device *device;
274 int ret;
275
276 if (!acpi_bus_get_device(handle, &device)) {
277 ret = acpi_bus_trim(device, 1);
278 if (ret)
279 pr_debug("error removing bus, %x\n", -ret);
280 }
281}
282
283
284/**
285 * hotplug_dock_devices - insert or remove devices on the dock station
286 * @ds: the dock station
287 * @event: either bus check or eject request
288 *
289 * Some devices on the dock station need to have drivers called
290 * to perform hotplug operations after a dock event has occurred.
291 * Traverse the list of dock devices that have registered a
292 * hotplug handler, and call the handler.
293 */
294static void hotplug_dock_devices(struct dock_station *ds, u32 event)
295{
296 struct dock_dependent_device *dd;
297
298 spin_lock(&ds->hp_lock);
299
300 /*
301 * First call driver specific hotplug functions
302 */
303 list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
304 if (dd->handler)
305 dd->handler(dd->handle, event, dd->context);
306 }
307
308 /*
309 * Now make sure that an acpi_device is created for each
310 * dependent device, or removed if this is an eject request.
311 * This will cause acpi_drivers to be stopped/started if they
312 * exist
313 */
314 list_for_each_entry(dd, &ds->dependent_devices, list) {
315 if (event == ACPI_NOTIFY_EJECT_REQUEST)
316 dock_remove_acpi_device(dd->handle);
317 else
318 dock_create_acpi_device(dd->handle);
319 }
320 spin_unlock(&ds->hp_lock);
321}
322
323static void dock_event(struct dock_station *ds, u32 event, int num)
324{
325 struct acpi_device *device;
326
327 device = dock_create_acpi_device(ds->handle);
328 if (device)
329 kobject_uevent(&device->kobj, num);
330}
331
332/**
333 * eject_dock - respond to a dock eject request
334 * @ds: the dock station
335 *
336 * This is called after _DCK is called, to execute the dock station's
337 * _EJ0 method.
338 */
339static void eject_dock(struct dock_station *ds)
340{
341 struct acpi_object_list arg_list;
342 union acpi_object arg;
343 acpi_status status;
344 acpi_handle tmp;
345
346 /* all dock devices should have _EJ0, but check anyway */
347 status = acpi_get_handle(ds->handle, "_EJ0", &tmp);
348 if (ACPI_FAILURE(status)) {
349 pr_debug("No _EJ0 support for dock device\n");
350 return;
351 }
352
353 arg_list.count = 1;
354 arg_list.pointer = &arg;
355 arg.type = ACPI_TYPE_INTEGER;
356 arg.integer.value = 1;
357
358 if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
359 &arg_list, NULL)))
360 pr_debug("Failed to evaluate _EJ0!\n");
361}
362
363/**
364 * handle_dock - handle a dock event
365 * @ds: the dock station
366 * @dock: to dock, or undock - that is the question
367 *
368 * Execute the _DCK method in response to an acpi event
369 */
370static void handle_dock(struct dock_station *ds, int dock)
371{
372 acpi_status status;
373 struct acpi_object_list arg_list;
374 union acpi_object arg;
375 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
376 struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
377 union acpi_object *obj;
378
379 acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &name_buffer);
380 obj = name_buffer.pointer;
381
382 printk(KERN_INFO PREFIX "%s\n", dock ? "docking" : "undocking");
383
384 /* _DCK method has one argument */
385 arg_list.count = 1;
386 arg_list.pointer = &arg;
387 arg.type = ACPI_TYPE_INTEGER;
388 arg.integer.value = dock;
389 status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer);
390 if (ACPI_FAILURE(status))
391 pr_debug("%s: failed to execute _DCK\n", obj->string.pointer);
392 kfree(buffer.pointer);
393 kfree(name_buffer.pointer);
394}
395
396static inline void dock(struct dock_station *ds)
397{
398 handle_dock(ds, 1);
399}
400
401static inline void undock(struct dock_station *ds)
402{
403 handle_dock(ds, 0);
404}
405
406static inline void begin_dock(struct dock_station *ds)
407{
408 ds->flags |= DOCK_DOCKING;
409}
410
411static inline void complete_dock(struct dock_station *ds)
412{
413 ds->flags &= ~(DOCK_DOCKING);
414 ds->last_dock_time = jiffies;
415}
416
417/**
418 * dock_in_progress - see if we are in the middle of handling a dock event
419 * @ds: the dock station
420 *
421 * Sometimes while docking, false dock events can be sent to the driver
422 * because good connections aren't made or some other reason. Ignore these
423 * if we are in the middle of doing something.
424 */
425static int dock_in_progress(struct dock_station *ds)
426{
427 if ((ds->flags & DOCK_DOCKING) ||
428 time_before(jiffies, (ds->last_dock_time + HZ)))
429 return 1;
430 return 0;
431}
432
433/**
434 * register_dock_notifier - add yourself to the dock notifier list
435 * @nb: the callers notifier block
436 *
437 * If a driver wishes to be notified about dock events, they can
438 * use this function to put a notifier block on the dock notifier list.
439 * this notifier call chain will be called after a dock event, but
440 * before hotplugging any new devices.
441 */
442int register_dock_notifier(struct notifier_block *nb)
443{
444 return atomic_notifier_chain_register(&dock_notifier_list, nb);
445}
446
447EXPORT_SYMBOL_GPL(register_dock_notifier);
448
449/**
450 * unregister_dock_notifier - remove yourself from the dock notifier list
451 * @nb: the callers notifier block
452 */
453void unregister_dock_notifier(struct notifier_block *nb)
454{
455 atomic_notifier_chain_unregister(&dock_notifier_list, nb);
456}
457
458EXPORT_SYMBOL_GPL(unregister_dock_notifier);
459
460/**
461 * register_hotplug_dock_device - register a hotplug function
462 * @handle: the handle of the device
463 * @handler: the acpi_notifier_handler to call after docking
464 * @context: device specific data
465 *
466 * If a driver would like to perform a hotplug operation after a dock
467 * event, they can register an acpi_notifiy_handler to be called by
468 * the dock driver after _DCK is executed.
469 */
470int
471register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler,
472 void *context)
473{
474 struct dock_dependent_device *dd;
475
476 if (!dock_station)
477 return -ENODEV;
478
479 /*
480 * make sure this handle is for a device dependent on the dock,
481 * this would include the dock station itself
482 */
483 dd = find_dock_dependent_device(dock_station, handle);
484 if (dd) {
485 dd->handler = handler;
486 dd->context = context;
487 dock_add_hotplug_device(dock_station, dd);
488 return 0;
489 }
490
491 return -EINVAL;
492}
493
494EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
495
496/**
497 * unregister_hotplug_dock_device - remove yourself from the hotplug list
498 * @handle: the acpi handle of the device
499 */
500void unregister_hotplug_dock_device(acpi_handle handle)
501{
502 struct dock_dependent_device *dd;
503
504 if (!dock_station)
505 return;
506
507 dd = find_dock_dependent_device(dock_station, handle);
508 if (dd)
509 dock_del_hotplug_device(dock_station, dd);
510}
511
512EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
513
514/**
515 * dock_notify - act upon an acpi dock notification
516 * @handle: the dock station handle
517 * @event: the acpi event
518 * @data: our driver data struct
519 *
520 * If we are notified to dock, then check to see if the dock is
521 * present and then dock. Notify all drivers of the dock event,
522 * and then hotplug and devices that may need hotplugging. For undock
523 * check to make sure the dock device is still present, then undock
524 * and hotremove all the devices that may need removing.
525 */
526static void dock_notify(acpi_handle handle, u32 event, void *data)
527{
528 struct dock_station *ds = (struct dock_station *)data;
529
530 switch (event) {
531 case ACPI_NOTIFY_BUS_CHECK:
532 if (!dock_in_progress(ds) && dock_present(ds)) {
533 begin_dock(ds);
534 dock(ds);
535 if (!dock_present(ds)) {
536 printk(KERN_ERR PREFIX "Unable to dock!\n");
537 break;
538 }
539 atomic_notifier_call_chain(&dock_notifier_list,
540 event, NULL);
541 hotplug_dock_devices(ds, event);
542 complete_dock(ds);
543 dock_event(ds, event, DOCK_EVENT);
544 }
545 break;
546 case ACPI_NOTIFY_DEVICE_CHECK:
547 /*
548 * According to acpi spec 3.0a, if a DEVICE_CHECK notification
549 * is sent and _DCK is present, it is assumed to mean an
550 * undock request. This notify routine will only be called
551 * for objects defining _DCK, so we will fall through to eject
552 * request here. However, we will pass an eject request through
553 * to the driver who wish to hotplug.
554 */
555 case ACPI_NOTIFY_EJECT_REQUEST:
556 if (!dock_in_progress(ds) && dock_present(ds)) {
557 /*
558 * here we need to generate the undock
559 * event prior to actually doing the undock
560 * so that the device struct still exists.
561 */
562 dock_event(ds, event, UNDOCK_EVENT);
563 hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST);
564 undock(ds);
565 eject_dock(ds);
566 if (dock_present(ds))
567 printk(KERN_ERR PREFIX "Unable to undock!\n");
568 }
569 break;
570 default:
571 printk(KERN_ERR PREFIX "Unknown dock event %d\n", event);
572 }
573}
574
575/**
576 * find_dock_devices - find devices on the dock station
577 * @handle: the handle of the device we are examining
578 * @lvl: unused
579 * @context: the dock station private data
580 * @rv: unused
581 *
582 * This function is called by acpi_walk_namespace. It will
583 * check to see if an object has an _EJD method. If it does, then it
584 * will see if it is dependent on the dock station.
585 */
586static acpi_status
587find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
588{
589 acpi_status status;
590 acpi_handle tmp;
591 struct dock_station *ds = (struct dock_station *)context;
592 struct dock_dependent_device *dd;
593
594 status = acpi_bus_get_ejd(handle, &tmp);
595 if (ACPI_FAILURE(status))
596 return AE_OK;
597
598 if (tmp == ds->handle) {
599 dd = alloc_dock_dependent_device(handle);
600 if (dd)
601 add_dock_dependent_device(ds, dd);
602 }
603
604 return AE_OK;
605}
606
607/**
608 * dock_add - add a new dock station
609 * @handle: the dock station handle
610 *
611 * allocated and initialize a new dock station device. Find all devices
612 * that are on the dock station, and register for dock event notifications.
613 */
614static int dock_add(acpi_handle handle)
615{
616 int ret;
617 acpi_status status;
618 struct dock_dependent_device *dd;
619
620 /* allocate & initialize the dock_station private data */
621 dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL);
622 if (!dock_station)
623 return -ENOMEM;
624 dock_station->handle = handle;
625 dock_station->last_dock_time = jiffies - HZ;
626 INIT_LIST_HEAD(&dock_station->dependent_devices);
627 INIT_LIST_HEAD(&dock_station->hotplug_devices);
628 spin_lock_init(&dock_station->dd_lock);
629 spin_lock_init(&dock_station->hp_lock);
630
631 /* Find dependent devices */
632 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
633 ACPI_UINT32_MAX, find_dock_devices, dock_station,
634 NULL);
635
636 /* add the dock station as a device dependent on itself */
637 dd = alloc_dock_dependent_device(handle);
638 if (!dd) {
639 kfree(dock_station);
640 return -ENOMEM;
641 }
642 add_dock_dependent_device(dock_station, dd);
643
644 /* register for dock events */
645 status = acpi_install_notify_handler(dock_station->handle,
646 ACPI_SYSTEM_NOTIFY,
647 dock_notify, dock_station);
648
649 if (ACPI_FAILURE(status)) {
650 printk(KERN_ERR PREFIX "Error installing notify handler\n");
651 ret = -ENODEV;
652 goto dock_add_err;
653 }
654
655 printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_NAME);
656
657 return 0;
658
659dock_add_err:
660 kfree(dock_station);
661 kfree(dd);
662 return ret;
663}
664
665/**
666 * dock_remove - free up resources related to the dock station
667 */
668static int dock_remove(void)
669{
670 struct dock_dependent_device *dd, *tmp;
671 acpi_status status;
672
673 if (!dock_station)
674 return 0;
675
676 /* remove dependent devices */
677 list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices,
678 list)
679 kfree(dd);
680
681 /* remove dock notify handler */
682 status = acpi_remove_notify_handler(dock_station->handle,
683 ACPI_SYSTEM_NOTIFY,
684 dock_notify);
685 if (ACPI_FAILURE(status))
686 printk(KERN_ERR "Error removing notify handler\n");
687
688 /* free dock station memory */
689 kfree(dock_station);
690 return 0;
691}
692
693/**
694 * find_dock - look for a dock station
695 * @handle: acpi handle of a device
696 * @lvl: unused
697 * @context: counter of dock stations found
698 * @rv: unused
699 *
700 * This is called by acpi_walk_namespace to look for dock stations.
701 */
702static acpi_status
703find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
704{
705 int *count = (int *)context;
706 acpi_status status = AE_OK;
707
708 if (is_dock(handle)) {
709 if (dock_add(handle) >= 0) {
710 (*count)++;
711 status = AE_CTRL_TERMINATE;
712 }
713 }
714 return status;
715}
716
717static int __init dock_init(void)
718{
719 int num = 0;
720
721 dock_station = NULL;
722
723 /* look for a dock station */
724 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
725 ACPI_UINT32_MAX, find_dock, &num, NULL);
726
727 if (!num)
728 return -ENODEV;
729
730 return 0;
731}
732
733static void __exit dock_exit(void)
734{
735 dock_remove();
736}
737
738postcore_initcall(dock_init);
739module_exit(dock_exit);