📄 dock.c
字号:
* register_dock_notifier - add yourself to the dock notifier list * @nb: the callers notifier block * * If a driver wishes to be notified about dock events, they can * use this function to put a notifier block on the dock notifier list. * this notifier call chain will be called after a dock event, but * before hotplugging any new devices. */int register_dock_notifier(struct notifier_block *nb){ if (!dock_station) return -ENODEV; return atomic_notifier_chain_register(&dock_notifier_list, nb);}EXPORT_SYMBOL_GPL(register_dock_notifier);/** * unregister_dock_notifier - remove yourself from the dock notifier list * @nb: the callers notifier block */void unregister_dock_notifier(struct notifier_block *nb){ if (!dock_station) return; atomic_notifier_chain_unregister(&dock_notifier_list, nb);}EXPORT_SYMBOL_GPL(unregister_dock_notifier);/** * register_hotplug_dock_device - register a hotplug function * @handle: the handle of the device * @handler: the acpi_notifier_handler to call after docking * @context: device specific data * * If a driver would like to perform a hotplug operation after a dock * event, they can register an acpi_notifiy_handler to be called by * the dock driver after _DCK is executed. */intregister_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler, void *context){ struct dock_dependent_device *dd; if (!dock_station) return -ENODEV; /* * make sure this handle is for a device dependent on the dock, * this would include the dock station itself */ dd = find_dock_dependent_device(dock_station, handle); if (dd) { dd->handler = handler; dd->context = context; dock_add_hotplug_device(dock_station, dd); return 0; } return -EINVAL;}EXPORT_SYMBOL_GPL(register_hotplug_dock_device);/** * unregister_hotplug_dock_device - remove yourself from the hotplug list * @handle: the acpi handle of the device */void unregister_hotplug_dock_device(acpi_handle handle){ struct dock_dependent_device *dd; if (!dock_station) return; dd = find_dock_dependent_device(dock_station, handle); if (dd) dock_del_hotplug_device(dock_station, dd);}EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);/** * handle_eject_request - handle an undock request checking for error conditions * * Check to make sure the dock device is still present, then undock and * hotremove all the devices that may need removing. */static int handle_eject_request(struct dock_station *ds, u32 event){ if (!dock_present(ds)) return -ENODEV; if (dock_in_progress(ds)) return -EBUSY; /* * here we need to generate the undock * event prior to actually doing the undock * so that the device struct still exists. */ dock_event(ds, event, UNDOCK_EVENT); hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST); undock(ds); eject_dock(ds); if (dock_present(ds)) { printk(KERN_ERR PREFIX "Unable to undock!\n"); return -EBUSY; } complete_undock(ds); return 0;}/** * dock_notify - act upon an acpi dock notification * @handle: the dock station handle * @event: the acpi event * @data: our driver data struct * * If we are notified to dock, then check to see if the dock is * present and then dock. Notify all drivers of the dock event, * and then hotplug and devices that may need hotplugging. */static void dock_notify(acpi_handle handle, u32 event, void *data){ struct dock_station *ds = data; switch (event) { case ACPI_NOTIFY_BUS_CHECK: if (!dock_in_progress(ds) && dock_present(ds)) { begin_dock(ds); dock(ds); if (!dock_present(ds)) { printk(KERN_ERR PREFIX "Unable to dock!\n"); break; } atomic_notifier_call_chain(&dock_notifier_list, event, NULL); hotplug_dock_devices(ds, event); complete_dock(ds); dock_event(ds, event, DOCK_EVENT); } break; case ACPI_NOTIFY_DEVICE_CHECK: /* * According to acpi spec 3.0a, if a DEVICE_CHECK notification * is sent and _DCK is present, it is assumed to mean an * undock request. This notify routine will only be called * for objects defining _DCK, so we will fall through to eject * request here. However, we will pass an eject request through * to the driver who wish to hotplug. */ case ACPI_NOTIFY_EJECT_REQUEST: begin_undock(ds); if (immediate_undock) handle_eject_request(ds, event); else dock_event(ds, event, UNDOCK_EVENT); break; default: printk(KERN_ERR PREFIX "Unknown dock event %d\n", event); }}/** * find_dock_devices - find devices on the dock station * @handle: the handle of the device we are examining * @lvl: unused * @context: the dock station private data * @rv: unused * * This function is called by acpi_walk_namespace. It will * check to see if an object has an _EJD method. If it does, then it * will see if it is dependent on the dock station. */static acpi_statusfind_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv){ acpi_status status; acpi_handle tmp, parent; struct dock_station *ds = context; struct dock_dependent_device *dd; status = acpi_bus_get_ejd(handle, &tmp); if (ACPI_FAILURE(status)) { /* try the parent device as well */ status = acpi_get_parent(handle, &parent); if (ACPI_FAILURE(status)) goto fdd_out; /* see if parent is dependent on dock */ status = acpi_bus_get_ejd(parent, &tmp); if (ACPI_FAILURE(status)) goto fdd_out; } if (tmp == ds->handle) { dd = alloc_dock_dependent_device(handle); if (dd) add_dock_dependent_device(ds, dd); }fdd_out: return AE_OK;}/* * show_docked - read method for "docked" file in sysfs */static ssize_t show_docked(struct device *dev, struct device_attribute *attr, char *buf){ return snprintf(buf, PAGE_SIZE, "%d\n", dock_present(dock_station));}DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL);/* * show_flags - read method for flags file in sysfs */static ssize_t show_flags(struct device *dev, struct device_attribute *attr, char *buf){ return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags);}DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL);/* * write_undock - write method for "undock" file in sysfs */static ssize_t write_undock(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ int ret; if (!count) return -EINVAL; ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST); return ret ? ret: count;}DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock);/* * show_dock_uid - read method for "uid" file in sysfs */static ssize_t show_dock_uid(struct device *dev, struct device_attribute *attr, char *buf){ unsigned long lbuf; acpi_status status = acpi_evaluate_integer(dock_station->handle, "_UID", NULL, &lbuf); if (ACPI_FAILURE(status)) return 0; return snprintf(buf, PAGE_SIZE, "%lx\n", lbuf);}DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);/** * dock_add - add a new dock station * @handle: the dock station handle * * allocated and initialize a new dock station device. Find all devices * that are on the dock station, and register for dock event notifications. */static int dock_add(acpi_handle handle){ int ret; acpi_status status; struct dock_dependent_device *dd; /* allocate & initialize the dock_station private data */ dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL); if (!dock_station) return -ENOMEM; dock_station->handle = handle; dock_station->last_dock_time = jiffies - HZ; INIT_LIST_HEAD(&dock_station->dependent_devices); INIT_LIST_HEAD(&dock_station->hotplug_devices); spin_lock_init(&dock_station->dd_lock); mutex_init(&dock_station->hp_lock); ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); /* initialize platform device stuff */ dock_device = platform_device_register_simple(dock_device_name, 0, NULL, 0); if (IS_ERR(dock_device)) { kfree(dock_station); dock_station = NULL; return PTR_ERR(dock_device); } /* we want the dock device to send uevents */ dock_device->dev.uevent_suppress = 0; ret = device_create_file(&dock_device->dev, &dev_attr_docked); if (ret) { printk("Error %d adding sysfs file\n", ret); platform_device_unregister(dock_device); kfree(dock_station); dock_station = NULL; return ret; } ret = device_create_file(&dock_device->dev, &dev_attr_undock); if (ret) { printk("Error %d adding sysfs file\n", ret); device_remove_file(&dock_device->dev, &dev_attr_docked); platform_device_unregister(dock_device); kfree(dock_station); dock_station = NULL; return ret; } ret = device_create_file(&dock_device->dev, &dev_attr_uid); if (ret) { printk("Error %d adding sysfs file\n", ret); device_remove_file(&dock_device->dev, &dev_attr_docked); device_remove_file(&dock_device->dev, &dev_attr_undock); platform_device_unregister(dock_device); kfree(dock_station); dock_station = NULL; return ret; } ret = device_create_file(&dock_device->dev, &dev_attr_flags); if (ret) { printk("Error %d adding sysfs file\n", ret); device_remove_file(&dock_device->dev, &dev_attr_docked); device_remove_file(&dock_device->dev, &dev_attr_undock); device_remove_file(&dock_device->dev, &dev_attr_uid); platform_device_unregister(dock_device); kfree(dock_station); dock_station = NULL; return ret; } /* Find dependent devices */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, find_dock_devices, dock_station, NULL); /* add the dock station as a device dependent on itself */ dd = alloc_dock_dependent_device(handle); if (!dd) { kfree(dock_station); dock_station = NULL; ret = -ENOMEM; goto dock_add_err_unregister; } add_dock_dependent_device(dock_station, dd); /* register for dock events */ status = acpi_install_notify_handler(dock_station->handle, ACPI_SYSTEM_NOTIFY, dock_notify, dock_station); if (ACPI_FAILURE(status)) { printk(KERN_ERR PREFIX "Error installing notify handler\n"); ret = -ENODEV; goto dock_add_err; } printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_DESCRIPTION); return 0;dock_add_err: kfree(dd);dock_add_err_unregister: device_remove_file(&dock_device->dev, &dev_attr_docked); device_remove_file(&dock_device->dev, &dev_attr_undock); device_remove_file(&dock_device->dev, &dev_attr_uid); device_remove_file(&dock_device->dev, &dev_attr_flags); platform_device_unregister(dock_device); kfree(dock_station); dock_station = NULL; return ret;}/** * dock_remove - free up resources related to the dock station */static int dock_remove(void){ struct dock_dependent_device *dd, *tmp; acpi_status status; if (!dock_station) return 0; /* remove dependent devices */ list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices, list) kfree(dd); /* remove dock notify handler */ status = acpi_remove_notify_handler(dock_station->handle, ACPI_SYSTEM_NOTIFY, dock_notify); if (ACPI_FAILURE(status)) printk(KERN_ERR "Error removing notify handler\n"); /* cleanup sysfs */ device_remove_file(&dock_device->dev, &dev_attr_docked); device_remove_file(&dock_device->dev, &dev_attr_undock); device_remove_file(&dock_device->dev, &dev_attr_uid); device_remove_file(&dock_device->dev, &dev_attr_flags); platform_device_unregister(dock_device); /* free dock station memory */ kfree(dock_station); dock_station = NULL; return 0;}/** * find_dock - look for a dock station * @handle: acpi handle of a device * @lvl: unused * @context: counter of dock stations found * @rv: unused * * This is called by acpi_walk_namespace to look for dock stations. */static acpi_statusfind_dock(acpi_handle handle, u32 lvl, void *context, void **rv){ int *count = context; acpi_status status = AE_OK; if (is_dock(handle)) { if (dock_add(handle) >= 0) { (*count)++; status = AE_CTRL_TERMINATE; } } return status;}static int __init dock_init(void){ int num = 0; dock_station = NULL; /* look for a dock station */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, find_dock, &num, NULL); if (!num) printk(KERN_INFO "No dock devices found.\n"); return 0;}static void __exit dock_exit(void){ dock_remove();}postcore_initcall(dock_init);module_exit(dock_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -