From aa66974b8d72320a9a7c854317803384d0570ec7 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 16 Jan 2004 22:03:20 +0000 Subject: [PATCH] [SERIAL] make ACPI serial module unload work Patch from Bjorn Helgaas This patch makes ACPI serial ports work right when the serial driver is built as a module. Previously, loading worked fine, but we didn't clean up on module removal. --- drivers/serial/8250_acpi.c | 64 +++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/drivers/serial/8250_acpi.c b/drivers/serial/8250_acpi.c index c0cf63199bf75..d2be2bd9d0bac 100644 --- a/drivers/serial/8250_acpi.c +++ b/drivers/serial/8250_acpi.c @@ -1,6 +1,7 @@ /* - * serial/acpi.c * Copyright (c) 2002-2003 Matthew Wilcox for Hewlett-Packard + * Copyright (C) 2004 Hewlett-Packard Co + * Bjorn Helgaas * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -13,6 +14,7 @@ #include #include #include +#include #include #include @@ -20,6 +22,11 @@ #include #include +struct serial_private { + int line; + void *iomem_base; +}; + static acpi_status acpi_serial_mmio(struct serial_struct *req, struct acpi_resource_address64 *addr) { @@ -96,39 +103,72 @@ static acpi_status acpi_serial_resource(struct acpi_resource *res, void *data) static int acpi_serial_add(struct acpi_device *device) { + struct serial_private *priv; acpi_status status; struct serial_struct serial_req; - int line; + int result; memset(&serial_req, 0, sizeof(serial_req)); + priv = kmalloc(sizeof(struct serial_private), GFP_KERNEL); + if (!priv) { + result = -ENOMEM; + goto fail; + } + memset(priv, 0, sizeof(*priv)); + status = acpi_walk_resources(device->handle, METHOD_NAME__CRS, acpi_serial_resource, &serial_req); - if (ACPI_FAILURE(status)) - return -ENODEV; + if (ACPI_FAILURE(status)) { + result = -ENODEV; + goto fail; + } - if (!serial_req.iomem_base && !serial_req.port) { + if (serial_req.iomem_base) + priv->iomem_base = serial_req.iomem_base; + else if (!serial_req.port) { printk(KERN_ERR "%s: no iomem or port address in %s _CRS\n", __FUNCTION__, device->pnp.bus_id); - return -ENODEV; + result = -ENODEV; + goto fail; } serial_req.baud_base = BASE_BAUD; - serial_req.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_AUTO_IRQ | - UPF_RESOURCES; + serial_req.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | + UPF_AUTO_IRQ | UPF_RESOURCES; - line = register_serial(&serial_req); - if (line < 0) { + priv->line = register_serial(&serial_req); + if (priv->line < 0) { printk(KERN_WARNING "Couldn't register serial port %s: %d\n", - device->pnp.bus_id, line); - return -ENODEV; + device->pnp.bus_id, priv->line); + result = -ENODEV; + goto fail; } + acpi_driver_data(device) = priv; return 0; + +fail: + if (serial_req.iomem_base) + iounmap(serial_req.iomem_base); + kfree(priv); + + return result; } static int acpi_serial_remove(struct acpi_device *device, int type) { + struct serial_private *priv; + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + priv = acpi_driver_data(device); + unregister_serial(priv->line); + if (priv->iomem_base) + iounmap(priv->iomem_base); + kfree(priv); + return 0; } -- 2.53.0