brintos

brintos / linux-shallow public Read only

0
0
Text · 16.2 KiB · 34affbd Raw
635 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  dock.c - ACPI dock station driver4 *5 *  Copyright (C) 2006, 2014, Intel Corp.6 *  Author: Kristen Carlson Accardi <kristen.c.accardi@intel.com>7 *          Rafael J. Wysocki <rafael.j.wysocki@intel.com>8 */9 10#include <linux/kernel.h>11#include <linux/moduleparam.h>12#include <linux/slab.h>13#include <linux/init.h>14#include <linux/types.h>15#include <linux/notifier.h>16#include <linux/platform_device.h>17#include <linux/jiffies.h>18#include <linux/stddef.h>19#include <linux/acpi.h>20 21#include "internal.h"22 23static bool immediate_undock = 1;24module_param(immediate_undock, bool, 0644);25MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "26	"undock immediately when the undock button is pressed, 0 will cause"27	" the driver to wait for userspace to write the undock sysfs file "28	" before undocking");29 30struct dock_station {31	acpi_handle handle;32	unsigned long last_dock_time;33	u32 flags;34	struct list_head dependent_devices;35 36	struct list_head sibling;37	struct platform_device *dock_device;38};39static LIST_HEAD(dock_stations);40static int dock_station_count;41 42struct dock_dependent_device {43	struct list_head list;44	struct acpi_device *adev;45};46 47#define DOCK_DOCKING	0x0000000148#define DOCK_UNDOCKING  0x0000000249#define DOCK_IS_DOCK	0x0000001050#define DOCK_IS_ATA	0x0000002051#define DOCK_IS_BAT	0x0000004052#define DOCK_EVENT	353#define UNDOCK_EVENT	254 55enum dock_callback_type {56	DOCK_CALL_HANDLER,57	DOCK_CALL_FIXUP,58	DOCK_CALL_UEVENT,59};60 61/*****************************************************************************62 *                         Dock Dependent device functions                   *63 *****************************************************************************/64/**65 * add_dock_dependent_device - associate a device with the dock station66 * @ds: Dock station.67 * @adev: Dependent ACPI device object.68 *69 * Add the dependent device to the dock's dependent device list.70 */71static int add_dock_dependent_device(struct dock_station *ds,72				     struct acpi_device *adev)73{74	struct dock_dependent_device *dd;75 76	dd = kzalloc(sizeof(*dd), GFP_KERNEL);77	if (!dd)78		return -ENOMEM;79 80	dd->adev = adev;81	INIT_LIST_HEAD(&dd->list);82	list_add_tail(&dd->list, &ds->dependent_devices);83 84	return 0;85}86 87static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event,88			       enum dock_callback_type cb_type)89{90	struct acpi_device *adev = dd->adev;91	acpi_hp_fixup fixup = NULL;92	acpi_hp_uevent uevent = NULL;93	acpi_hp_notify notify = NULL;94 95	acpi_lock_hp_context();96 97	if (adev->hp) {98		if (cb_type == DOCK_CALL_FIXUP)99			fixup = adev->hp->fixup;100		else if (cb_type == DOCK_CALL_UEVENT)101			uevent = adev->hp->uevent;102		else103			notify = adev->hp->notify;104	}105 106	acpi_unlock_hp_context();107 108	if (fixup)109		fixup(adev);110	else if (uevent)111		uevent(adev, event);112	else if (notify)113		notify(adev, event);114}115 116static struct dock_station *find_dock_station(acpi_handle handle)117{118	struct dock_station *ds;119 120	list_for_each_entry(ds, &dock_stations, sibling)121		if (ds->handle == handle)122			return ds;123 124	return NULL;125}126 127/**128 * find_dock_dependent_device - get a device dependent on this dock129 * @ds: the dock station130 * @adev: ACPI device object to find.131 *132 * iterate over the dependent device list for this dock.  If the133 * dependent device matches the handle, return.134 */135static struct dock_dependent_device *136find_dock_dependent_device(struct dock_station *ds, struct acpi_device *adev)137{138	struct dock_dependent_device *dd;139 140	list_for_each_entry(dd, &ds->dependent_devices, list)141		if (adev == dd->adev)142			return dd;143 144	return NULL;145}146 147void register_dock_dependent_device(struct acpi_device *adev,148				    acpi_handle dshandle)149{150	struct dock_station *ds = find_dock_station(dshandle);151 152	if (ds && !find_dock_dependent_device(ds, adev))153		add_dock_dependent_device(ds, adev);154}155 156/*****************************************************************************157 *                         Dock functions                                    *158 *****************************************************************************/159 160/**161 * is_dock_device - see if a device is on a dock station162 * @adev: ACPI device object to check.163 *164 * If this device is either the dock station itself,165 * or is a device dependent on the dock station, then it166 * is a dock device167 */168int is_dock_device(struct acpi_device *adev)169{170	struct dock_station *dock_station;171 172	if (!dock_station_count)173		return 0;174 175	if (acpi_dock_match(adev->handle))176		return 1;177 178	list_for_each_entry(dock_station, &dock_stations, sibling)179		if (find_dock_dependent_device(dock_station, adev))180			return 1;181 182	return 0;183}184EXPORT_SYMBOL_GPL(is_dock_device);185 186/**187 * dock_present - see if the dock station is present.188 * @ds: the dock station189 *190 * execute the _STA method.  note that present does not191 * imply that we are docked.192 */193static int dock_present(struct dock_station *ds)194{195	unsigned long long sta;196	acpi_status status;197 198	if (ds) {199		status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);200		if (ACPI_SUCCESS(status) && sta)201			return 1;202	}203	return 0;204}205 206/**207 * hot_remove_dock_devices - Remove dock station devices.208 * @ds: Dock station.209 */210static void hot_remove_dock_devices(struct dock_station *ds)211{212	struct dock_dependent_device *dd;213 214	/*215	 * Walk the list in reverse order so that devices that have been added216	 * last are removed first (in case there are some indirect dependencies217	 * between them).218	 */219	list_for_each_entry_reverse(dd, &ds->dependent_devices, list)220		dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST,221				   DOCK_CALL_HANDLER);222 223	list_for_each_entry_reverse(dd, &ds->dependent_devices, list)224		acpi_bus_trim(dd->adev);225}226 227/**228 * hotplug_dock_devices - Insert devices on a dock station.229 * @ds: the dock station230 * @event: either bus check or device check request231 *232 * Some devices on the dock station need to have drivers called233 * to perform hotplug operations after a dock event has occurred.234 * Traverse the list of dock devices that have registered a235 * hotplug handler, and call the handler.236 */237static void hotplug_dock_devices(struct dock_station *ds, u32 event)238{239	struct dock_dependent_device *dd;240 241	/* Call driver specific post-dock fixups. */242	list_for_each_entry(dd, &ds->dependent_devices, list)243		dock_hotplug_event(dd, event, DOCK_CALL_FIXUP);244 245	/* Call driver specific hotplug functions. */246	list_for_each_entry(dd, &ds->dependent_devices, list)247		dock_hotplug_event(dd, event, DOCK_CALL_HANDLER);248 249	/*250	 * Check if all devices have been enumerated already.  If not, run251	 * acpi_bus_scan() for them and that will cause scan handlers to be252	 * attached to device objects or acpi_drivers to be stopped/started if253	 * they are present.254	 */255	list_for_each_entry(dd, &ds->dependent_devices, list) {256		struct acpi_device *adev = dd->adev;257 258		if (!acpi_device_enumerated(adev)) {259			int ret = acpi_bus_scan(adev->handle);260 261			if (ret)262				dev_dbg(&adev->dev, "scan error %d\n", -ret);263		}264	}265}266 267static void dock_event(struct dock_station *ds, u32 event, int num)268{269	struct device *dev = &ds->dock_device->dev;270	char event_string[13];271	char *envp[] = { event_string, NULL };272	struct dock_dependent_device *dd;273 274	if (num == UNDOCK_EVENT)275		sprintf(event_string, "EVENT=undock");276	else277		sprintf(event_string, "EVENT=dock");278 279	/*280	 * Indicate that the status of the dock station has281	 * changed.282	 */283	if (num == DOCK_EVENT)284		kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);285 286	list_for_each_entry(dd, &ds->dependent_devices, list)287		dock_hotplug_event(dd, event, DOCK_CALL_UEVENT);288 289	if (num != DOCK_EVENT)290		kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);291}292 293/**294 * handle_dock - handle a dock event295 * @ds: the dock station296 * @dock: to dock, or undock - that is the question297 *298 * Execute the _DCK method in response to an acpi event299 */300static void handle_dock(struct dock_station *ds, int dock)301{302	acpi_status status;303	struct acpi_object_list arg_list;304	union acpi_object arg;305	unsigned long long value;306 307	acpi_handle_info(ds->handle, "%s\n", dock ? "docking" : "undocking");308 309	/* _DCK method has one argument */310	arg_list.count = 1;311	arg_list.pointer = &arg;312	arg.type = ACPI_TYPE_INTEGER;313	arg.integer.value = dock;314	status = acpi_evaluate_integer(ds->handle, "_DCK", &arg_list, &value);315	if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)316		acpi_handle_err(ds->handle, "Failed to execute _DCK (0x%x)\n",317				status);318}319 320static inline void dock(struct dock_station *ds)321{322	handle_dock(ds, 1);323}324 325static inline void undock(struct dock_station *ds)326{327	handle_dock(ds, 0);328}329 330static inline void begin_dock(struct dock_station *ds)331{332	ds->flags |= DOCK_DOCKING;333}334 335static inline void complete_dock(struct dock_station *ds)336{337	ds->flags &= ~(DOCK_DOCKING);338	ds->last_dock_time = jiffies;339}340 341static inline void begin_undock(struct dock_station *ds)342{343	ds->flags |= DOCK_UNDOCKING;344}345 346static inline void complete_undock(struct dock_station *ds)347{348	ds->flags &= ~(DOCK_UNDOCKING);349}350 351/**352 * dock_in_progress - see if we are in the middle of handling a dock event353 * @ds: the dock station354 *355 * Sometimes while docking, false dock events can be sent to the driver356 * because good connections aren't made or some other reason.  Ignore these357 * if we are in the middle of doing something.358 */359static int dock_in_progress(struct dock_station *ds)360{361	if ((ds->flags & DOCK_DOCKING) ||362	    time_before(jiffies, (ds->last_dock_time + HZ)))363		return 1;364	return 0;365}366 367/**368 * handle_eject_request - handle an undock request checking for error conditions369 * @ds: The dock station to undock.370 * @event: The ACPI event number associated with the undock request.371 *372 * Check to make sure the dock device is still present, then undock and373 * hotremove all the devices that may need removing.374 */375static int handle_eject_request(struct dock_station *ds, u32 event)376{377	if (dock_in_progress(ds))378		return -EBUSY;379 380	/*381	 * here we need to generate the undock382	 * event prior to actually doing the undock383	 * so that the device struct still exists.384	 * Also, even send the dock event if the385	 * device is not present anymore386	 */387	dock_event(ds, event, UNDOCK_EVENT);388 389	hot_remove_dock_devices(ds);390	undock(ds);391	acpi_evaluate_lck(ds->handle, 0);392	acpi_evaluate_ej0(ds->handle);393	if (dock_present(ds)) {394		acpi_handle_err(ds->handle, "Unable to undock!\n");395		return -EBUSY;396	}397	complete_undock(ds);398	return 0;399}400 401/**402 * dock_notify - Handle ACPI dock notification.403 * @adev: Dock station's ACPI device object.404 * @event: Event code.405 *406 * If we are notified to dock, then check to see if the dock is407 * present and then dock.  Notify all drivers of the dock event,408 * and then hotplug and devices that may need hotplugging.409 */410int dock_notify(struct acpi_device *adev, u32 event)411{412	acpi_handle handle = adev->handle;413	struct dock_station *ds = find_dock_station(handle);414	int surprise_removal = 0;415 416	if (!ds)417		return -ENODEV;418 419	/*420	 * According to acpi spec 3.0a, if a DEVICE_CHECK notification421	 * is sent and _DCK is present, it is assumed to mean an undock422	 * request.423	 */424	if ((ds->flags & DOCK_IS_DOCK) && event == ACPI_NOTIFY_DEVICE_CHECK)425		event = ACPI_NOTIFY_EJECT_REQUEST;426 427	/*428	 * dock station: BUS_CHECK - docked or surprise removal429	 *		 DEVICE_CHECK - undocked430	 * other device: BUS_CHECK/DEVICE_CHECK - added or surprise removal431	 *432	 * To simplify event handling, dock dependent device handler always433	 * get ACPI_NOTIFY_BUS_CHECK/ACPI_NOTIFY_DEVICE_CHECK for add and434	 * ACPI_NOTIFY_EJECT_REQUEST for removal435	 */436	switch (event) {437	case ACPI_NOTIFY_BUS_CHECK:438	case ACPI_NOTIFY_DEVICE_CHECK:439		if (!dock_in_progress(ds) && !acpi_device_enumerated(adev)) {440			begin_dock(ds);441			dock(ds);442			if (!dock_present(ds)) {443				acpi_handle_err(handle, "Unable to dock!\n");444				complete_dock(ds);445				break;446			}447			hotplug_dock_devices(ds, event);448			complete_dock(ds);449			dock_event(ds, event, DOCK_EVENT);450			acpi_evaluate_lck(ds->handle, 1);451			acpi_update_all_gpes();452			break;453		}454		if (dock_present(ds) || dock_in_progress(ds))455			break;456		/* This is a surprise removal */457		surprise_removal = 1;458		event = ACPI_NOTIFY_EJECT_REQUEST;459		/* Fall back */460		fallthrough;461	case ACPI_NOTIFY_EJECT_REQUEST:462		begin_undock(ds);463		if ((immediate_undock && !(ds->flags & DOCK_IS_ATA))464		   || surprise_removal)465			handle_eject_request(ds, event);466		else467			dock_event(ds, event, UNDOCK_EVENT);468		break;469	}470	return 0;471}472 473/*474 * show_docked - read method for "docked" file in sysfs475 */476static ssize_t docked_show(struct device *dev,477			   struct device_attribute *attr, char *buf)478{479	struct dock_station *dock_station = dev->platform_data;480	struct acpi_device *adev = acpi_fetch_acpi_dev(dock_station->handle);481 482	return sysfs_emit(buf, "%u\n", acpi_device_enumerated(adev));483}484static DEVICE_ATTR_RO(docked);485 486/*487 * show_flags - read method for flags file in sysfs488 */489static ssize_t flags_show(struct device *dev,490			  struct device_attribute *attr, char *buf)491{492	struct dock_station *dock_station = dev->platform_data;493 494	return sysfs_emit(buf, "%d\n", dock_station->flags);495 496}497static DEVICE_ATTR_RO(flags);498 499/*500 * write_undock - write method for "undock" file in sysfs501 */502static ssize_t undock_store(struct device *dev, struct device_attribute *attr,503			    const char *buf, size_t count)504{505	int ret;506	struct dock_station *dock_station = dev->platform_data;507 508	if (!count)509		return -EINVAL;510 511	acpi_scan_lock_acquire();512	begin_undock(dock_station);513	ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST);514	acpi_scan_lock_release();515	return ret ? ret : count;516}517static DEVICE_ATTR_WO(undock);518 519/*520 * show_dock_uid - read method for "uid" file in sysfs521 */522static ssize_t uid_show(struct device *dev,523			struct device_attribute *attr, char *buf)524{525	unsigned long long lbuf;526	struct dock_station *dock_station = dev->platform_data;527 528	acpi_status status = acpi_evaluate_integer(dock_station->handle,529					"_UID", NULL, &lbuf);530	if (ACPI_FAILURE(status))531		return 0;532 533	return sysfs_emit(buf, "%llx\n", lbuf);534}535static DEVICE_ATTR_RO(uid);536 537static ssize_t type_show(struct device *dev,538			 struct device_attribute *attr, char *buf)539{540	struct dock_station *dock_station = dev->platform_data;541	char *type;542 543	if (dock_station->flags & DOCK_IS_DOCK)544		type = "dock_station";545	else if (dock_station->flags & DOCK_IS_ATA)546		type = "ata_bay";547	else if (dock_station->flags & DOCK_IS_BAT)548		type = "battery_bay";549	else550		type = "unknown";551 552	return sysfs_emit(buf, "%s\n", type);553}554static DEVICE_ATTR_RO(type);555 556static struct attribute *dock_attributes[] = {557	&dev_attr_docked.attr,558	&dev_attr_flags.attr,559	&dev_attr_undock.attr,560	&dev_attr_uid.attr,561	&dev_attr_type.attr,562	NULL563};564 565static const struct attribute_group dock_attribute_group = {566	.attrs = dock_attributes567};568 569/**570 * acpi_dock_add - Add a new dock station571 * @adev: Dock station ACPI device object.572 *573 * allocated and initialize a new dock station device.574 */575void acpi_dock_add(struct acpi_device *adev)576{577	struct dock_station *dock_station, ds = { NULL, };578	struct platform_device_info pdevinfo;579	acpi_handle handle = adev->handle;580	struct platform_device *dd;581	int ret;582 583	memset(&pdevinfo, 0, sizeof(pdevinfo));584	pdevinfo.name = "dock";585	pdevinfo.id = dock_station_count;586	pdevinfo.fwnode = acpi_fwnode_handle(adev);587	pdevinfo.data = &ds;588	pdevinfo.size_data = sizeof(ds);589	dd = platform_device_register_full(&pdevinfo);590	if (IS_ERR(dd))591		return;592 593	dock_station = dd->dev.platform_data;594 595	dock_station->handle = handle;596	dock_station->dock_device = dd;597	dock_station->last_dock_time = jiffies - HZ;598 599	INIT_LIST_HEAD(&dock_station->sibling);600	INIT_LIST_HEAD(&dock_station->dependent_devices);601 602	/* we want the dock device to send uevents */603	dev_set_uevent_suppress(&dd->dev, 0);604 605	if (acpi_dock_match(handle))606		dock_station->flags |= DOCK_IS_DOCK;607	if (acpi_ata_match(handle))608		dock_station->flags |= DOCK_IS_ATA;609	if (acpi_device_is_battery(adev))610		dock_station->flags |= DOCK_IS_BAT;611 612	ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group);613	if (ret)614		goto err_unregister;615 616	/* add the dock station as a device dependent on itself */617	ret = add_dock_dependent_device(dock_station, adev);618	if (ret)619		goto err_rmgroup;620 621	dock_station_count++;622	list_add(&dock_station->sibling, &dock_stations);623	adev->flags.is_dock_station = true;624	dev_info(&adev->dev, "ACPI dock station (docks/bays count: %d)\n",625		 dock_station_count);626	return;627 628err_rmgroup:629	sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group);630 631err_unregister:632	platform_device_unregister(dd);633	acpi_handle_err(handle, "%s encountered error %d\n", __func__, ret);634}635