summaryrefslogtreecommitdiff
path: root/device/dev_lookup.c
blob: 746c394b794345daeba6a761e7cdf6ab1875541f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
/* 
 * 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/zalloc.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>

#ifdef i386
#include <i386at/device_emul.h>
#endif

/*
 * 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.
 */
decl_simple_lock_data(,
		dev_number_lock)

zone_t		dev_hdr_zone;

/*
 * Enter device in the number lookup table.
 * The number table lock must be held.
 */
void
dev_number_enter(device)
	register mach_device_t	device;
{
	register 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.
 */
void
dev_number_remove(device)
	register mach_device_t	device;
{
	register 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.
 */
mach_device_t
dev_number_lookup(ops, devnum)
	dev_ops_t	ops;
	int		devnum;
{
	register queue_t	q;
	register 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(name)
	char *		name;
{
	dev_ops_t	dev_ops;
	int		dev_minor;
	register mach_device_t	device;
	register 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) zalloc(dev_hdr_zone);
	    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)
		zfree(dev_hdr_zone, (vm_offset_t)new_device);
	}

	return (device);
}

/*
 * Add a reference to the device.
 */
void
mach_device_reference(device)
	register 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(device)
	register 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);

	zfree(dev_hdr_zone, (vm_offset_t)device);
}

/*

 */
/*
 * port-to-device lookup routines.
 */
decl_simple_lock_data(,
	dev_port_lock)

/*
 * Enter a port-to-device mapping.
 */
void
dev_port_enter(device)
	register mach_device_t	device;
{
	mach_device_reference(device);
#ifdef i386
	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;
	}
#else
	ipc_kobject_set(device->port, (ipc_kobject_t) device, IKOT_DEVICE);
#endif
}

/*
 * Remove a port-to-device mapping.
 */
void
dev_port_remove(device)
	register 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(port)
	ipc_port_t	port;
{
	register 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;
#ifdef i386
	    if (device->emul_ops->reference)
	      (*device->emul_ops->reference)(device->emul_data);
#else
	    mach_device_reference(device);
#endif
	}
	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(device)
	register device_t	device;
{
#ifndef i386
	register ipc_port_t	port;
#endif

	if (device == DEVICE_NULL)
	    return IP_NULL;

#ifdef i386
	return (*device->emul_ops->dev_to_port) (device->emul_data);
#else
	device_lock(device);
	if (device->state == DEV_STATE_OPEN)
	    port = ipc_port_make_send(device->port);
	else
	    port = IP_NULL;
	device_unlock(device);

	mach_device_deallocate(device);
	return port;
#endif
}

/*
 * 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(routine, port)
	boolean_t	(*routine)();
	mach_port_t	port;
{
	register int		i;
	register queue_t	q;
	register 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
 */
#define	NDEVICES	256

void
dev_lookup_init()
{
	register int	i;

	simple_lock_init(&dev_number_lock);

	for (i = 0; i < NDEVHASH; i++)
	    queue_init(&dev_number_hash_table[i]);

	simple_lock_init(&dev_port_lock);

	dev_hdr_zone = zinit(sizeof(struct mach_device),
			     sizeof(struct mach_device) * NDEVICES,
			     PAGE_SIZE,
			     FALSE,
			     "open device entry");
}