diff options
Diffstat (limited to 'device/dev_lookup.c')
-rw-r--r-- | device/dev_lookup.c | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/device/dev_lookup.c b/device/dev_lookup.c new file mode 100644 index 0000000..c9c39f8 --- /dev/null +++ b/device/dev_lookup.c @@ -0,0 +1,364 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 3/89 + */ + +#include <mach/port.h> +#include <mach/vm_param.h> + +#include <kern/queue.h> +#include <kern/slab.h> + +#include <device/device_types.h> +#include <device/dev_hdr.h> +#include <device/conf.h> +#include <device/param.h> /* DEV_BSIZE, as default */ + +#include <ipc/ipc_port.h> +#include <kern/ipc_kobject.h> + +#include <device/device_emul.h> +#include <device/ds_routines.h> + +/* + * Device structure routines: reference counting, port->device. + */ + +/* + * Lookup/enter by device number. + */ +#define NDEVHASH 8 +#define DEV_NUMBER_HASH(dev) ((dev) & (NDEVHASH-1)) +queue_head_t dev_number_hash_table[NDEVHASH]; + +/* + * Lock for device-number to device lookup. + * Must be held before device-ref_count lock. + */ +def_simple_lock_data(static, dev_number_lock) + +struct kmem_cache dev_hdr_cache; + +/* + * Enter device in the number lookup table. + * The number table lock must be held. + */ +static void +dev_number_enter(const mach_device_t device) +{ + queue_t q; + + q = &dev_number_hash_table[DEV_NUMBER_HASH(device->dev_number)]; + queue_enter(q, device, mach_device_t, number_chain); +} + +/* + * Remove device from the device-number lookup table. + * The device-number table lock must be held. + */ +static void +dev_number_remove(const mach_device_t device) +{ + queue_t q; + + q = &dev_number_hash_table[DEV_NUMBER_HASH(device->dev_number)]; + queue_remove(q, device, mach_device_t, number_chain); +} + +/* + * Lookup a device by device operations and minor number. + * The number table lock must be held. + */ +static mach_device_t +dev_number_lookup(const dev_ops_t ops, int devnum) +{ + queue_t q; + mach_device_t device; + + q = &dev_number_hash_table[DEV_NUMBER_HASH(devnum)]; + queue_iterate(q, device, mach_device_t, number_chain) { + if (device->dev_ops == ops && device->dev_number == devnum) { + return (device); + } + } + return (MACH_DEVICE_NULL); +} + +/* + * Look up a device by name, and create the device structure + * if it does not exist. Enter it in the dev_number lookup + * table. + */ +mach_device_t +device_lookup(const char *name) +{ + dev_ops_t dev_ops; + int dev_minor; + mach_device_t device; + mach_device_t new_device; + + /* + * Get the device and unit number from the name. + */ + if (!dev_name_lookup(name, &dev_ops, &dev_minor)) + return (MACH_DEVICE_NULL); + + /* + * Look up the device in the hash table. If it is + * not there, enter it. + */ + new_device = MACH_DEVICE_NULL; + simple_lock(&dev_number_lock); + while ((device = dev_number_lookup(dev_ops, dev_minor)) + == MACH_DEVICE_NULL) { + /* + * Must unlock to allocate the structure. If + * the structure has appeared after we have allocated, + * release the new structure. + */ + if (new_device != MACH_DEVICE_NULL) + break; /* allocated */ + + simple_unlock(&dev_number_lock); + + new_device = (mach_device_t) kmem_cache_alloc(&dev_hdr_cache); + simple_lock_init(&new_device->ref_lock); + new_device->ref_count = 1; + simple_lock_init(&new_device->lock); + new_device->state = DEV_STATE_INIT; + new_device->flag = 0; + new_device->open_count = 0; + new_device->io_in_progress = 0; + new_device->io_wait = FALSE; + new_device->port = IP_NULL; + new_device->dev_ops = dev_ops; + new_device->dev_number = dev_minor; + new_device->bsize = DEV_BSIZE; /* change later */ + + simple_lock(&dev_number_lock); + } + + if (device == MACH_DEVICE_NULL) { + /* + * No existing device structure. Insert the + * new one. + */ + assert(new_device != MACH_DEVICE_NULL); + device = new_device; + + dev_number_enter(device); + simple_unlock(&dev_number_lock); + } + else { + /* + * Have existing device. + */ + mach_device_reference(device); + simple_unlock(&dev_number_lock); + + if (new_device != MACH_DEVICE_NULL) + kmem_cache_free(&dev_hdr_cache, (vm_offset_t)new_device); + } + + return (device); +} + +/* + * Add a reference to the device. + */ +void +mach_device_reference(mach_device_t device) +{ + simple_lock(&device->ref_lock); + device->ref_count++; + simple_unlock(&device->ref_lock); +} + +/* + * Remove a reference to the device, and deallocate the + * structure if no references are left. + */ +void +mach_device_deallocate(mach_device_t device) +{ + simple_lock(&device->ref_lock); + if (--device->ref_count > 0) { + simple_unlock(&device->ref_lock); + return; + } + device->ref_count = 1; + simple_unlock(&device->ref_lock); + + simple_lock(&dev_number_lock); + simple_lock(&device->ref_lock); + if (--device->ref_count > 0) { + simple_unlock(&device->ref_lock); + simple_unlock(&dev_number_lock); + return; + } + + dev_number_remove(device); + simple_unlock(&device->ref_lock); + simple_unlock(&dev_number_lock); + + kmem_cache_free(&dev_hdr_cache, (vm_offset_t)device); +} + +/* + + */ +/* + * port-to-device lookup routines. + */ + +/* + * Enter a port-to-device mapping. + */ +void +dev_port_enter(mach_device_t device) +{ + mach_device_reference(device); + + ipc_kobject_set(device->port, + (ipc_kobject_t) &device->dev, IKOT_DEVICE); + device->dev.emul_data = device; + { + extern struct device_emulation_ops mach_device_emulation_ops; + + device->dev.emul_ops = &mach_device_emulation_ops; + } +} + +/* + * Remove a port-to-device mapping. + */ +void +dev_port_remove(mach_device_t device) +{ + ipc_kobject_set(device->port, IKO_NULL, IKOT_NONE); + mach_device_deallocate(device); +} + +/* + * Lookup a device by its port. + * Doesn't consume the naked send right; produces a device reference. + */ +device_t +dev_port_lookup(ipc_port_t port) +{ + device_t device; + + if (!IP_VALID(port)) + return (DEVICE_NULL); + + ip_lock(port); + if (ip_active(port) && (ip_kotype(port) == IKOT_DEVICE)) { + device = (device_t) port->ip_kobject; + if (device->emul_ops->reference) + (*device->emul_ops->reference)(device->emul_data); + } + else + device = DEVICE_NULL; + + ip_unlock(port); + return (device); +} + +/* + * Get the port for a device. + * Consumes a device reference; produces a naked send right. + */ +ipc_port_t +convert_device_to_port(const device_t device) +{ + if (device == DEVICE_NULL) + return IP_NULL; + + return (*device->emul_ops->dev_to_port) (device->emul_data); +} + +/* + * Call a supplied routine on each device, passing it + * the port as an argument. If the routine returns TRUE, + * stop the search and return TRUE. If none returns TRUE, + * return FALSE. + */ +boolean_t +dev_map( + dev_map_fn routine, + mach_port_t port) +{ + int i; + queue_t q; + mach_device_t dev, prev_dev; + + for (i = 0, q = &dev_number_hash_table[0]; + i < NDEVHASH; + i++, q++) { + prev_dev = MACH_DEVICE_NULL; + simple_lock(&dev_number_lock); + queue_iterate(q, dev, mach_device_t, number_chain) { + mach_device_reference(dev); + simple_unlock(&dev_number_lock); + if (prev_dev != MACH_DEVICE_NULL) + mach_device_deallocate(prev_dev); + + if ((*routine)(dev, port)) { + /* + * Done + */ + mach_device_deallocate(dev); + return (TRUE); + } + + simple_lock(&dev_number_lock); + prev_dev = dev; + } + simple_unlock(&dev_number_lock); + if (prev_dev != MACH_DEVICE_NULL) + mach_device_deallocate(prev_dev); + } + return (FALSE); +} + +/* + * Initialization + */ +void +dev_lookup_init(void) +{ + int i; + + simple_lock_init(&dev_number_lock); + + for (i = 0; i < NDEVHASH; i++) + queue_init(&dev_number_hash_table[i]); + + kmem_cache_init(&dev_hdr_cache, "mach_device", + sizeof(struct mach_device), 0, NULL, 0); +} |