|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.1 (the "License"). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License.
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24: /* Copyright (c) 1991 NeXT Computer, Inc. All rights reserved.
25: *
26: * volCheck.m - common volume check logic for all DiskObjects.
27: *
28: * This only does useful work in the kernel, since it relies heavily
29: * on the vol driver functionality. Someday we'll have to come up
30: * with a new interface bewteen WS and a user-level version of this.
31: *
32: * HISTORY
33: * 07-May-91 Doug Mitchell at NeXT
34: * Created.
35: */
36: #import <driverkit/IODisk.h>
37: #import <driverkit/IODiskPartition.h>
38: #import <machkit/NXLock.h>
39: #import <kernserv/queue.h>
40: #import <driverkit/generalFuncs.h>
41: #import <driverkit/Device_ddm.h>
42: #import <driverkit/IODeviceDescription.h>
43: #import <bsd/dev/disk.h>
44:
45: #undef DRIVER_PRIVATE
46: #define DRIVER_PRIVATE
47:
48: #ifdef KERNEL
49: #import <kernserv/prototypes.h>
50: #import <bsd/dev/voldev.h>
51: #else KERNEL
52: #import <bsd/libc.h>
53: #import "volDriver.h"
54: #endif KERNEL
55:
56: #import <driverkit/volCheck.h>
57: #import "volCheckPrivate.h"
58:
59: /*
60: * Static data.
61: */
62: /*
63: * Queue of volCheckEntry_t's. One per removable disk drive. It's not
64: * protected with a lock; all accesses to this queue are by the
65: * volCheckThread.
66: */
67: static queue_head_t volCheckEntryQ;
68:
69: /*
70: * Input queue. Commands are queued here by public routines to ensure
71: * single-threaded response to change in device state.
72: */
73: static queue_head_t volCheckCmdQ;
74: static id volCheckCmdLock;
75:
76: /*
77: * Prototypes of static functions.
78: */
79: static void volCheckThread(void *foo);
80: static void volCheckResponse(void *param,
81: int tag,
82: int response_value);
83: static volCheckEntry_t *getVCEntry(id diskObj);
84: static void vcEnqueueCmd(volCheckOp_t op,
85: id diskObj,
86: int diskType,
87: dev_t blockDev,
88: dev_t rawDev);
89: static void volCheckCmdHandler();
90: static void volDriverNotify(id diskObj,
91: dev_t blockDev,
92: dev_t rawDev);
93:
94: /*
95: * Public functions.
96: */
97:
98: /*
99: * One-time only initialization.
100: */
101: void volCheckInit()
102: {
103: volCheckCmdLock = [NXSpinLock new];
104: queue_init(&volCheckEntryQ);
105: queue_init(&volCheckCmdQ);
106:
107: /*
108: * FIXME - only start up a thread when we get the first
109: * volCheckRegister().
110: */
111: IOForkThread((IOThreadFunc)volCheckThread, NULL);
112: #ifndef KERNEL
113: volDriverInit();
114: #endif KERNEL
115: }
116:
117: /*
118: * These public routines queue up a command on volCheckCmdQ to ensure
119: * single-threaded handling of lastReadyState transitions.
120: */
121: /*
122: * Register a removable media drive. This should be called once for the
123: * physical disk object, not for any LogicalDisks which sit on top of it.
124: */
125: void volCheckRegister(id diskObj,
126: dev_t blockDev,
127: dev_t rawDev)
128: {
129: xpr_vc("volCheckRegister: disk %s\n", [diskObj name], 2,3,4,5);
130: if(![diskObj isPhysical]) {
131: IOLog("volCheckRegister: %s is not a physical device\n",
132: [diskObj name]);
133: return;
134: }
135: vcEnqueueCmd(VC_REGISTER,
136: diskObj,
137: 0,
138: blockDev,
139: rawDev);
140: }
141:
142: /*
143: * Unregister a removable media drive. This probably should never happen,
144: * at least not in the kernel...
145: */
146: void volCheckUnregister(id diskObj)
147: {
148: xpr_vc("volCheckUnregister: disk %s\n",
149: [diskObj name], 2,3,4,5);
150: vcEnqueueCmd(VC_UNREGISTER,
151: diskObj,
152: 0,
153: 0,
154: 0);
155: }
156:
157: /*
158: * Request a "please insert disk" panel. Called by physDevice DiskObject.
159: *
160: * For now, we don't have a connection with WS unless we're in the
161: * kernel.
162: */
163: void volCheckRequest(id diskObj, // requestor
164: int diskType) // PR_DRIVE_FLOPPY, etc.
165: {
166: xpr_vc("volCheckRequest: disk %s\n",
167: [diskObj name], 2,3,4,5);
168: vcEnqueueCmd(VC_REQUEST,
169: diskObj,
170: diskType,
171: 0,
172: 0);
173: }
174:
175: /*
176: * Notify volCheck that specified device is in a "disk ejecting" state.
177: * Called by physDevice DiskObject in ejectPhysical:. Only necessary
178: * for drives which may not respond to autoeject. Thus floppy drivers shouldn't
179: * have to call this.
180: *
181: * NOTE: we force the low-level driver to pass in diskType in this call
182: * and in volCheckRequest() to avoid having DiskObject know this
183: * detail at registerDisk: time...
184: */
185: void volCheckEjecting(id diskObj,
186: int diskType) // PR_DRIVE_FLOPPY, etc.
187: {
188: xpr_vc("volCheckEjecting: disk %s\n", [diskObj name], 2,3,4,5);
189:
190: vcEnqueueCmd(VC_EJECTING,
191: diskObj,
192: diskType,
193: 0,
194: 0);
195: }
196:
197: /*
198: * Notify volCheck that disk has gone not ready. Typically called on
199: * gross error detection.
200: */
201: void volCheckNotReady(id diskObj)
202: {
203: xpr_vc("volCheckNotReady: disk %s\n", [diskObj name], 2,3,4,5);
204: vcEnqueueCmd(VC_NOTREADY,
205: diskObj,
206: 0,
207: 0,
208: 0);
209: }
210:
211: /*
212: * Private functions.
213: */
214: /*
215: * Get the volCheckEntry_t for specified DiskObject id.
216: */
217: static volCheckEntry_t *getVCEntry(id diskObj)
218: {
219: volCheckEntry_t *vcEntry;
220:
221: vcEntry = (volCheckEntry_t *)queue_first(&volCheckEntryQ);
222: while(!queue_end(&volCheckEntryQ, (queue_t)vcEntry)) {
223: if(vcEntry->diskObj == diskObj)
224: return(vcEntry);
225: vcEntry = (volCheckEntry_t *)vcEntry->link.next;
226: }
227: xpr_err("getVCEntry: ENTRY NOT FOUND\n", 1,2,3,4,5);
228: return(NULL);
229: }
230:
231: /*
232: * Enqueue a command on volCheckCmdQ. Called by public routines.
233: */
234: static void vcEnqueueCmd(volCheckOp_t op,
235: id diskObj,
236: int diskType,
237: dev_t blockDev,
238: dev_t rawDev)
239: {
240: volCheckCmd_t *vcCmd;
241:
242: vcCmd = IOMalloc(sizeof(*vcCmd));
243: vcCmd->op = op;
244: vcCmd->diskObj = diskObj;
245: vcCmd->diskType = diskType;
246: vcCmd->blockDev = blockDev;
247: vcCmd->rawDev = rawDev;
248: [volCheckCmdLock lock];
249: queue_enter(&volCheckCmdQ,
250: vcCmd,
251: volCheckCmd_t *,
252: link);
253: [volCheckCmdLock unlock];
254: }
255:
256: /*
257: * Called by vol driver upon reception of panel response. The only
258: * response we know about is an abort...do we need others? All we do
259: * is forward this notification to the appropriate DiskObject.
260: */
261: static void volCheckResponse(void *param,
262: int tag,
263: int response_value)
264: {
265: id diskObj = (id)param;
266:
267: xpr_vc("volCheckResponse disk %s\n", [diskObj name], 2,3,4,5);
268: vcEnqueueCmd(VC_RESPONSE,
269: diskObj,
270: 0,
271: 0,
272: 0);
273: }
274:
275: /*
276: * volCheck thread function. Max of two per system (one in the kernel,
277: * one in user space).
278: */
279: static void volCheckThread(void *foo)
280: {
281:
282: volCheckEntry_t *vcEntry;
283: IODiskReadyState lastReadyState;
284: IODiskReadyState newReadyState = IO_NotReady; // damned compiler!
285: const char *devname;
286: id diskObj;
287: int manualPoll;
288:
289: while(1) {
290:
291: /*
292: * First handle any queued commands.
293: */
294: volCheckCmdHandler();
295:
296: /*
297: * Process each registered disk.
298: */
299: vcEntry = (volCheckEntry_t *)queue_first(&volCheckEntryQ);
300: manualPoll = vol_check_manual_poll();
301: while(!queue_end(&volCheckEntryQ, (queue_t)vcEntry)) {
302:
303: devname = [vcEntry->diskObj name];
304: diskObj = vcEntry->diskObj;
305: lastReadyState = [diskObj lastReadyState];
306: if(lastReadyState != IO_Ready) {
307: /*
308: * Poll for transition if:
309: * -- this is a normal once-per-second polling
310: * drive, or
311: * -- vol driver tells us it's time to poll, or
312: * -- ready state is Ejecting
313: */
314: if(![vcEntry->diskObj needsManualPolling] ||
315: manualPoll ||
316: (lastReadyState == IO_Ejecting)) {
317: newReadyState = [diskObj updateReadyState];
318: }
319: else {
320: /*
321: * Don't poll; assume state unchanged.
322: */
323: newReadyState = lastReadyState;
324: }
325: }
326: switch(lastReadyState) {
327: case IO_Ready:
328: break; // nothing to do here
329:
330: case IO_Ejecting:
331: /*
332: * We hope to see a transition to not ready or
333: * no disk...when we see "ready" enough times,
334: * we'll ask the WS to put up a "please eject disk"
335: * panel.
336: */
337: if(newReadyState == IO_Ready) {
338: if(--vcEntry->ejectCounter == 0) {
339: xpr_vc("volCheck: requesting disk eject"
340: " panel for disk %s\n", devname,
341: 2,3,4,5);
342: vol_panel_request(NULL, // no callback
343: PR_RT_EJECT_REQ,
344: PR_RT_CANCEL, // leave up 'til
345: // we take it
346: // down
347: 0, // p1, not used
348: vcEntry->diskType, // p2
349: [diskObj unit], // p3
350: 0, // p4, not used
351: "", // string1
352: "", // string2
353: NULL, // no callback
354: &vcEntry->tag);
355: vcEntry->ejectRequestPending = YES;
356:
357: } /* counter 0 */
358: } /* still ready */
359: else {
360:
361: /*
362: * Allright! It went not ready. Remove
363: * eject request panel if present.
364: */
365: xpr_vc("volCheck: %s: Eject Complete\n",
366: devname, 2,3,4,5);
367: [diskObj setLastReadyState:IO_NoDisk];
368: if(vcEntry->ejectRequestPending) {
369: vcEntry->ejectRequestPending = NO;
370: vol_panel_remove(vcEntry->tag);
371: }
372: } /* not ready/not present */
373: break; /* from case IO_Ejecting */
374:
375: case IO_NotReady:
376: case IO_NoDisk:
377: /*
378: * The drive wasn't ready the last time we checked.
379: * We're interested in transitions to a ready state,
380: * indicating disk insertion.
381: */
382: if(newReadyState == IO_Ready) {
383:
384: id descr;
385:
386: [diskObj setLastReadyState:IO_Ready];
387: if(vcEntry->diskRequestPending) {
388: /*
389: * Cancel the panel.
390: */
391: xpr_vc("volCheck: cancelling request panel"
392: " for %s\n", devname, 2,3,4,5);
393: vol_panel_remove(vcEntry->tag);
394: }
395:
396: /*
397: * Have the driver get other physical parameters.
398: */
399: [diskObj updatePhysicalParameters];
400:
401: /*
402: * Notify driver whether or not we think it
403: * is waiting for this disk to cover race
404: * conditions between panels and insertions.
405: * Then take care of layering some
406: * IODiskPartitions on top of this new disk.
407: * We have to do this before a possible
408: * vol_notify_dev().
409: */
410: [diskObj diskBecameReady];
411: descr = [IODeviceDescription new];
412: [descr setDirectDevice:diskObj];
413: if(![IODiskPartition probe:descr]) {
414: [descr free];
415: }
416:
417: if(vcEntry->diskRequestPending) {
418: vcEntry->diskRequestPending = NO;
419: }
420: else {
421: /*
422: * Unexpected disk insertion. Notify WS.
423: * We are the one to determine whether it has
424: * a valid label. We also have to gather
425: * other cruft like write protect for use by
426: * WS.
427: */
428: volDriverNotify(diskObj, vcEntry->blockDev,
429: vcEntry->rawDev);
430:
431: } /* new disk insertion */
432: } /* new state == ready */
433: /* else no action (wasn't ready, still
434: * isn't) */
435: break; /* from case not ready/no disk */
436: } /* switch lastReadyState */
437:
438: /*
439: * Next disk.
440: */
441: vcEntry = (volCheckEntry_t *)vcEntry->link.next;
442: }
443:
444: /*
445: * OK, sleep a while.
446: */
447: IOSleep(1000);
448:
449: } /* while 1 */
450:
451: /* NOT REACHED */
452:
453: }
454:
455: /*
456: * Handle queued commands.
457: */
458: static void volCheckCmdHandler()
459: {
460: volCheckCmd_t *vcCmd;
461: volCheckEntry_t *vcEntry = NULL;
462: id diskObj;
463: IODiskReadyState readyState;
464:
465: [volCheckCmdLock lock];
466: while(!queue_empty(&volCheckCmdQ)) {
467: vcCmd = (volCheckCmd_t *)queue_first(&volCheckCmdQ);
468: queue_remove(&volCheckCmdQ,
469: vcCmd,
470: volCheckCmd_t *,
471: link);
472: [volCheckCmdLock unlock];
473: diskObj = vcCmd->diskObj;
474: if(vcCmd->op != VC_REGISTER) {
475: vcEntry = getVCEntry(diskObj);
476: if(vcEntry == NULL) {
477: IOLog("volCheck: disk %s not "
478: "registered, cmd = %d\n",
479: [diskObj name], vcCmd->op);
480: goto freeCmd;
481: }
482:
483: }
484: switch(vcCmd->op) {
485: case VC_REGISTER:
486: xpr_vc("volCheck: registering disk %s\n",
487: [diskObj name], 2,3,4,5);
488: #if 1
489: /*
490: * We should use the current state since that
491: * indicates whether or the disk has been probed
492: * at registration. It may have come ready after
493: * probe, but before we get here.
494: */
495: readyState = [diskObj lastReadyState];
496: #else
497: readyState = [diskObj updateReadyState];
498: /*
499: * IODiskPartition does this too, but we have to do
500: * it here as well in case we run before
501: * IODiskPartition does this.
502: */
503: [diskObj setLastReadyState:readyState];
504: #endif
505:
506: /*
507: * If ready now, go ahead and notify vol driver.
508: */
509: if(readyState == IO_Ready) {
510: /*
511: * If this disk is not removable and
512: * it's currently ready, we have no
513: * reason to do anything other than
514: * the initial notify.
515: */
516: volDriverNotify(diskObj, vcCmd->blockDev,
517: vcCmd->rawDev);
518:
519:
520: if(![diskObj isRemovable]) {
521: /*
522: * If ready now, go ahead and
523: * notify vol driver.
524: */
525: break;
526: };
527: }
528:
529: vcEntry = IOMalloc(sizeof(*vcEntry));
530: vcEntry->diskObj = diskObj;
531: vcEntry->blockDev = vcCmd->blockDev;
532: vcEntry->rawDev = vcCmd->rawDev;
533: vcEntry->ejectCounter = 0;
534: vcEntry->ejectRequestPending = NO;
535: vcEntry->diskRequestPending = NO;
536: vcEntry->diskType = vcCmd->diskType;
537: queue_enter(&volCheckEntryQ,
538: vcEntry,
539: volCheckEntry_t *,
540: link);
541: break;
542:
543: case VC_UNREGISTER:
544: xpr_vc("volCheck: unregistering disk %s\n",
545: [vcCmd->diskObj name], 2,3,4,5);
546: queue_remove(&volCheckEntryQ,
547: vcEntry,
548: volCheckEntry_t *,
549: link);
550: IOFree(vcEntry, sizeof(*vcEntry));
551: break;
552:
553: case VC_REQUEST:
554: /*
555: * Panel request.
556: */
557: {
558: kern_return_t krtn;
559: int unit = [diskObj unit];
560:
561: xpr_vc("volCheck VC_REQUEST: disk %s\n",
562: [diskObj name], 2,3,4,5);
563:
564: /*
565: * If there is already a panel up for this drive,
566: * this is a nop.
567: */
568: if(vcEntry->diskRequestPending) {
569: xpr_vc("volCheckRequest: Panel already "
570: "pending\n", 1,2,3,4,5);
571: break;
572: }
573: if([diskObj lastReadyState] == IO_Ready) {
574: /*
575: * This can be the result of a call to
576: * volCheckRequest() follwed by immediate
577: * insertion of the desired disk.
578: * No error. No panel.
579: */
580: xpr_vc("volCheckRequest: Panel Not "
581: "Necessary\n", 1,2,3,4,5);
582: break;
583: }
584: krtn = vol_panel_disk_num(volCheckResponse,
585: 0, // volume number
586: vcCmd->diskType,
587: unit, // drive number
588: diskObj, // param for callback
589: NO, // wrong disk
590: &vcEntry->tag);
591: /*
592: * Record this request in *vcEntry.
593: */
594: vcEntry->diskRequestPending = YES;
595: break;
596: }
597:
598: case VC_EJECTING:
599: xpr_vc("volCheck VC_EJECTING: disk %s\n",
600: [diskObj name], 2,3,4,5);
601: [diskObj setLastReadyState:IO_Ejecting];
602: #if i386
603: /*
604: * FIXME - this should only be done for
605: * i386 *floppy* drives!
606: */
607: vcEntry->ejectCounter = 1;
608: #else
609: vcEntry->ejectCounter = VC_EJECT_DELAY;
610: #endif
611: vcEntry->ejectRequestPending = NO;
612: vcEntry->diskType = vcCmd->diskType;
613: break;
614:
615: case VC_NOTREADY:
616: xpr_vc("volCheck VC_NOTREADY: disk %s\n",
617: [diskObj name], 2,3,4,5);
618: [diskObj setLastReadyState:IO_NotReady];
619: break;
620:
621: case VC_RESPONSE:
622: /*
623: * This comes from the vol driver - it's a panel
624: * response, which can only mean "disk not available".
625: */
626: xpr_vc("volCheck VC_RESPONSE: disk %s\n",
627: [diskObj name], 2,3,4,5);
628: if(!vcEntry->diskRequestPending) {
629: /*
630: * This can happen if the disk is inserted
631: * at around the same time as the user hit
632: * "cancel".
633: */
634: break;
635: }
636: vcEntry->diskRequestPending = NO;
637: [diskObj abortRequest];
638: break;
639:
640: } /* switch op */
641:
642: freeCmd:
643: IOFree(vcCmd, sizeof(*vcCmd));
644:
645: /*
646: * We need to hold this lock at the top of the loop, and
647: * we'll hold it when we drop out.
648: */
649: [volCheckCmdLock lock];
650: } /* for each cmd */
651:
652: [volCheckCmdLock unlock];
653: xpr_vc("volCheckCmdHandler: done\n", 1,2,3,4,5);
654: }
655:
656: /*
657: * Common means by which vol driver is notified of presence of disk, either
658: * at disk insertion time or at volCheckRegister() time.
659: */
660: static void volDriverNotify(id diskObj,
661: dev_t blockDev,
662: dev_t rawDev)
663: {
664: struct disk_label *label = IOMalloc(sizeof(struct disk_label));
665: int vol_state;
666: int flags;
667: id logicalDisk;
668: IOReturn rtn;
669:
670: xpr_vc("volDriverNotify: dev=%s\n", [diskObj name], 2,3,4,5);
671: if([diskObj isRemovable]) {
672: flags = IND_FLAGS_REMOVABLE;
673: }
674: else {
675: flags = IND_FLAGS_FIXED;
676: }
677: logicalDisk = [diskObj nextLogicalDisk];
678: if(logicalDisk != nil) {
679: rtn = [logicalDisk readLabel:label];
680: }
681: else {
682: /*
683: * This is pretty weird. IODiskPartitionProbe:
684: * should have created an IODiskPartition object as partition
685: * 0's raw device...
686: */
687: IOLog("volCheck: physDev with no logicalDisk!!\n");
688: rtn = IO_R_NO_LABEL;
689: }
690: if(rtn == IO_R_SUCCESS) {
691: vol_state = IND_VS_LABEL;
692: }
693: else {
694: if([diskObj isFormatted]) {
695: vol_state = IND_VS_FORMATTED;
696: }
697: else {
698: vol_state = IND_VS_UNFORMATTED;
699: }
700: }
701: if([diskObj isWriteProtected]) {
702: flags |= IND_FLAGS_WP;
703: }
704: vol_notify_dev(blockDev,
705: rawDev,
706: "", // form_type - what was this for
707: // again???
708: vol_state,
709: [diskObj name], // "sd0", etc.
710: flags); // WP, removable
711:
712: IOFree(label, sizeof(struct disk_label));
713: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.