/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.driver; import java.util.ArrayList; import java.util.HashMap; import java.util.Set; import org.jnode.bootlog.BootLogInstance; import org.jnode.system.resource.ResourceOwner; import org.jnode.util.StopWatch; /** * A software representation of a hardware device. * <p/> * Every device is controlled by a Driver. These drivers are found by DeviceToDriverMapper * instances. * * @author Ewout Prangsma (epr@users.sourceforge.net) * @see org.jnode.driver.Driver * @see org.jnode.driver.DeviceToDriverMapper */ public class Device implements ResourceOwner { /** * The bus that i'm connected to */ private final Bus bus; /** * My driver */ private Driver driver; /** * My identifier */ private String id; /** * Has this device been started? */ private boolean started = false; /** * The API's implemented by this device */ private final HashMap<Class<? extends DeviceAPI>, DeviceAPI> apis = new HashMap<Class<? extends DeviceAPI>, DeviceAPI>(); /** * My listeners */ private final ArrayList<DeviceListener> listeners = new ArrayList<DeviceListener>(); /** * The manager */ private AbstractDeviceManager manager; /** * Create a new instance * * @param bus * @param id */ public Device(Bus bus, String id) { this.id = id; this.bus = bus; } /** * Gets the driver of this device. * * @return My driver, can be null */ public final Driver getDriver() { return driver; } /** * Gets the classname of my driver. * * @return String can be null. */ public final String getDriverClassName() { final Driver driver = this.driver; if (driver != null) { return driver.getClass().getName(); } else { return null; } } /** * Gets the bus this device is connected to. * * @return My parent bus */ public final Bus getBus() { return this.bus; } /** * @param driver * @throws DriverException * @see org.jnode.driver.Device#setDriver(org.jnode.driver.Driver) */ public void setDriver(Driver driver) throws DriverException { try { driver.connect(this); this.driver = driver; } catch (DriverException ex) { this.driver = null; throw new DriverException("Cannot set driver", ex); } } /** * @return The id of this device * @see org.jnode.driver.Device#getId() */ public final String getId() { return id; } /** * Change the id of this device, only called by devicemanager * * @param newId */ final void setId(String newId) { this.id = newId; } /** * Start this device. * * @throws DriverException */ final void start() throws DriverException { if (driver == null) { throw new DriverException("Cannot start without a driver"); } else if (manager == null) { throw new DriverException("Cannot start without being registered"); } else if (!started) { // Let extensions do their start work onStartDevice(); // Let the driver start me driver.startDevice(); // I'm started started = true; // Notify my listeners fireStartedEvent(); } } /** * Start this device. * * @throws DriverException */ final void stop(boolean unsetDriver) throws DriverException { if (driver == null) { throw new DriverException("Cannot stop without a driver"); } else if (manager == null) { throw new DriverException("Cannot stop without being registered"); } else if (started) { // Notify my listeners fireStopEvent(); // Let the driver stop me driver.stopDevice(); // Let extensions do their stop work onStopDevice(); // Remove the driver connection if requested if (unsetDriver) { this.driver = null; } // I'm stopped now. started = false; } } /** * Has this device been started? * * @return boolean */ public final boolean isStarted() { return started; } /** * Add an API implementation to the list of API's implemented by this device. * * @param apiInterface * @param apiImplementation */ public final <T extends DeviceAPI> void registerAPI(Class<T> apiInterface, T apiImplementation) { if (!apiInterface.isInstance(apiImplementation)) { throw new IllegalArgumentException("API implementation does not implement API interface"); } if (!apiInterface.isInterface()) { throw new IllegalArgumentException("API interface must be an interface"); } apis.put(apiInterface, apiImplementation); final Class[] interfaces = apiInterface.getInterfaces(); if (interfaces != null) { for (Class intf : interfaces) { if (DeviceAPI.class.isAssignableFrom(intf)) { if (!apis.containsKey(intf)) { apis.put((Class<? extends DeviceAPI>) intf, apiImplementation); } } } } } /** * Remove an API implementation from the list of API's implemented by this device. * * @param apiInterface */ public final void unregisterAPI(Class<? extends DeviceAPI> apiInterface) { apis.remove(apiInterface); } /** * Does this device implement the given API? * * @param apiInterface * @return boolean */ public final boolean implementsAPI(Class<? extends DeviceAPI> apiInterface) { //lookup is classname based to handle multi isolate uscases for (Class clazz : apis.keySet()) { if (clazz.getName().equals(apiInterface.getName())) { return true; } } return false; } /** * Gets all implemented API's? * * @return A set of Class instances */ public final Set<Class<? extends DeviceAPI>> implementedAPIs() { return apis.keySet(); } /** * Gets the implementation of a given API. * * @param apiInterface * @return The api implementation (guaranteed not null) * @throws ApiNotFoundException The given api has not been found */ public final <T extends DeviceAPI> T getAPI(Class<T> apiInterface) throws ApiNotFoundException { //lookup is classname based to handle multi isolate uscases Class apiInterface2 = null; for (Class clazz : apis.keySet()) { if (clazz.getName().equals(apiInterface.getName())) { apiInterface2 = clazz; break; } } final T impl = apiInterface.cast(apis.get(apiInterface2)); if (impl == null) { throw new ApiNotFoundException(apiInterface.getName()); } return impl; } /** * Add a listener * * @param listener */ public final void addListener(DeviceListener listener) { listeners.add(listener); } /** * Remove a listener * * @param listener */ public final void removeListener(DeviceListener listener) { listeners.remove(listener); } /** * This method is called during the start of the device. Just before the call to startDevice of * the connected driver. * * @throws DriverException */ protected void onStartDevice() throws DriverException { } /** * This method is called during the stop of the device. Just after the call to stopDevice of * the connected driver. * * @throws DriverException */ protected void onStopDevice() throws DriverException { } /** * Fire a deviceStarted event to all my listeners */ protected final void fireStartedEvent() { final StopWatch sw = new StopWatch(); for (DeviceListener l : listeners) { sw.start(); l.deviceStarted(this); if (sw.isElapsedLongerThen(100)) { BootLogInstance.get() .error("DeviceListener took " + sw + " in deviceStarted: " + l.getClass().getName()); } } manager.fireStartedEvent(this); } /** * Fire a deviceStop event to all my listeners */ protected final void fireStopEvent() { manager.fireStopEvent(this); final StopWatch sw = new StopWatch(); for (DeviceListener l : listeners) { sw.start(); l.deviceStop(this); if (sw.isElapsedLongerThen(100)) { BootLogInstance.get().error("DeviceListener took " + sw + " in deviceStop: " + l.getClass().getName()); } } } /** * @return The short description * @see org.jnode.system.resource.ResourceOwner#getShortDescription() */ public String getShortDescription() { return getId(); } /** * @return Returns the manager. */ public final DeviceManager getManager() { return this.manager; } /** * @param manager The manager to set. */ final void setManager(AbstractDeviceManager manager) { if (this.manager != null) { throw new SecurityException("Cannot overwrite the device manager"); } else { this.manager = manager; } } }