1 /* Copyright 2007-2012 Fredrik Wikstrom. All rights reserved.
3 ** Redistribution and use in source and binary forms, with or without
4 ** modification, are permitted provided that the following conditions
7 ** 1. Redistributions of source code must retain the above copyright
8 ** notice, this list of conditions and the following disclaimer.
10 ** 2. Redistributions in binary form must reproduce the above copyright
11 ** notice, this list of conditions and the following disclaimer in the
12 ** documentation and/or other materials provided with the distribution.
14 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
15 ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 ** POSSIBILITY OF SUCH DAMAGE.
28 #include "diskimage_device.h"
30 #include <exec/exec.h>
31 #include <proto/exec.h>
32 #include <proto/expat.h>
34 #include "rev/diskimage.device_rev.h"
36 #define LIBNAME "diskimage.device"
37 CONST TEXT USED_VAR verstag
[] = VERSTAG
;
39 struct Library
*SysBase
;
40 struct Library
*DOSBase
;
41 struct Library
*IntuitionBase
;
43 static void FreeBaseVars (struct DiskImageBase
*libBase
);
44 static struct DiskImageUnit
*InitUnit (struct DiskImageBase
*libBase
, ULONG unit_number
);
45 static void FreeUnit (struct DiskImageBase
*libBase
, struct DiskImageUnit
*unit
);
48 This routine gets called after the device has been allocated.
49 The device pointer is in D0. The AmigaDOS segment list is in a0.
50 If it returns the device pointer, then the device will be linked
51 into the device list. If it returns NULL, then the device
55 If you don't use the "RTF_AUTOINIT" feature, there is an additional
56 caveat. If you allocate memory in your Open function, remember that
57 allocating memory can cause an Expunge... including an expunge of your
58 device. This must not be fatal. The easy solution is don't add your
59 device to the list until after it is ready for action.
61 This call is single-threaded by exec; please read the description for
64 /* The ROMTAG Init Function */
66 static AROS_UFH3(struct DiskImageBase
*, LibInit
,
67 AROS_UFHA(struct DiskImageBase
*, libBase
, D0
),
68 AROS_UFHA(BPTR
, seglist
, A0
),
69 AROS_UFHA(struct Library
*, exec_base
, A6
)
74 static struct DiskImageBase
*LibInit (REG(d0
, struct DiskImageBase
*libBase
),
75 REG(a0
, BPTR seglist
), REG(a6
, struct Library
*exec_base
))
78 libBase
->LibNode
.lib_Node
.ln_Type
= NT_DEVICE
;
79 libBase
->LibNode
.lib_Node
.ln_Pri
= 0;
80 libBase
->LibNode
.lib_Node
.ln_Name
= LIBNAME
;
81 libBase
->LibNode
.lib_Flags
= LIBF_SUMUSED
|LIBF_CHANGED
;
82 libBase
->LibNode
.lib_Version
= VERSION
;
83 libBase
->LibNode
.lib_Revision
= REVISION
;
84 libBase
->LibNode
.lib_IdString
= VSTRING
;
85 libBase
->SysBase
= exec_base
;
88 /* Save pointer to our loaded code (the SegList) */
89 libBase
->SegList
= seglist
;
91 if ((DOSBase
= libBase
->DOSBase
= OpenLibrary("dos.library", MIN_OS_VERSION
)) &&
92 (libBase
->UtilityBase
= OpenLibrary("utility.library", MIN_OS_VERSION
)) &&
93 (IntuitionBase
= libBase
->IntuitionBase
= OpenLibrary("intuition.library", MIN_OS_VERSION
)))
95 if ((libBase
->UnitSemaphore
= CreateSemaphore()) &&
96 (libBase
->PluginSemaphore
= CreateSemaphore()) &&
97 (libBase
->DiskChangeSemaphore
= CreateSemaphore()) &&
98 (libBase
->Units
= CreateList(TRUE
)) &&
99 (libBase
->Plugins
= CreateList(TRUE
)) &&
100 (libBase
->ReloadPluginsHooks
= CreateList(TRUE
)) &&
101 (libBase
->DiskChangeHooks
= CreateList(TRUE
)))
107 FreeBaseVars(libBase
);
108 DeleteLibrary((struct Library
*)libBase
);
115 static void FreeBaseVars (struct DiskImageBase
*libBase
) {
116 struct Library
*SysBase
= libBase
->SysBase
;
117 DeleteList(libBase
->DiskChangeHooks
);
118 DeleteList(libBase
->ReloadPluginsHooks
);
119 DeleteList(libBase
->Plugins
);
120 DeleteList(libBase
->Units
);
121 DeleteSemaphore(libBase
->DiskChangeSemaphore
);
122 DeleteSemaphore(libBase
->PluginSemaphore
);
123 DeleteSemaphore(libBase
->UnitSemaphore
);
124 if (libBase
->IntuitionBase
) CloseLibrary(libBase
->IntuitionBase
);
125 if (libBase
->UtilityBase
) CloseLibrary(libBase
->UtilityBase
);
126 if (libBase
->DOSBase
) CloseLibrary(libBase
->DOSBase
);
129 /* Here begins the system interface commands. When the user calls
130 OpenDevice/CloseDevice/RemDevice, this eventually gets translated
131 into a call to the following routines (Open/Close/Expunge).
132 Exec has already put our device pointer in a6 for us.
135 These calls are guaranteed to be single-threaded; only one task
136 will execute your Open/Close/Expunge at a time.
138 For Kickstart V33/34, the single-threading method involves "Forbid".
139 There is a good chance this will change. Anything inside your
140 Open/Close/Expunge that causes a direct or indirect Wait() will break
141 the Forbid(). If the Forbid() is broken, some other task might
142 manage to enter your Open/Close/Expunge code at the same time.
145 Since exec has turned off task switching while in these routines
146 (via Forbid/Permit), we should not take too long in them. */
148 /* dev_open() sets the io_Error field on an error. If it was successful,
149 we should also set up the io_Unit and ln_Type fields.
150 Exec takes care of setting up io_Device. */
152 /* Open the library */
154 static AROS_LH3(LONG
, LibOpen
,
155 AROS_LHA(struct IORequest
*, io
, A1
),
156 AROS_LHA(ULONG
, unit_number
, D0
),
157 AROS_LHA(ULONG
, flags
, D1
),
158 struct DiskImageBase
*, libBase
, 1, DiskImage
163 static LONG
LibOpen(REG(a1
, struct IORequest
*io
), REG(d0
, ULONG unit_number
), REG(d1
, ULONG flags
),
164 REG(a6
, struct DiskImageBase
*libBase
))
167 struct Library
*SysBase
= libBase
->SysBase
;
168 struct Library
*DOSBase
= libBase
->DOSBase
;
169 struct DiskImageUnit
*unit
;
171 /* Subtle point: any AllocMem() call can cause a call to this device's
172 expunge vector. If lib_OpenCnt is zero, the device might get expunged. */
174 ObtainSemaphore(libBase
->UnitSemaphore
);
176 io
->io_Device
= (struct Device
*)libBase
;
178 io
->io_Error
= IOERR_SUCCESS
;
181 if (libBase
->LibNode
.lib_OpenCnt
++ == 0) {
182 ObtainSemaphore(libBase
->PluginSemaphore
);
183 InitLocaleInfo(SysBase
, &libBase
->LocaleInfo
, "diskimagedevice.catalog");
184 LoadPlugins(libBase
);
185 ReleaseSemaphore(libBase
->PluginSemaphore
);
189 if (unit_number
== ~0) {
190 io
->io_Message
.mn_Node
.ln_Type
= NT_REPLYMSG
;
192 libBase
->LibNode
.lib_Flags
&= ~LIBF_DELEXP
;
193 ReleaseSemaphore(libBase
->UnitSemaphore
);
195 return IOERR_SUCCESS
;
198 unit
= (struct DiskImageUnit
*)libBase
->Units
->lh_Head
;
199 while (unit
->Node
.ln_Succ
) {
200 if (unit
->UnitNum
== unit_number
) {
203 io
->io_Unit
= (struct Unit
*)unit
;
204 io
->io_Message
.mn_Node
.ln_Type
= NT_REPLYMSG
;
206 libBase
->LibNode
.lib_Flags
&= ~LIBF_DELEXP
;
207 ReleaseSemaphore(libBase
->UnitSemaphore
);
209 return IOERR_SUCCESS
;
211 unit
= (struct DiskImageUnit
*)unit
->Node
.ln_Succ
;
214 unit
= InitUnit(libBase
, unit_number
);
218 stdin
= Open("NIL:", MODE_OLDFILE
);
219 stdout
= Open("NIL:", MODE_NEWFILE
);
221 if (stdin
&& stdout
) {
222 unit
->UnitProc
= CreateNewProcTags(
223 NP_Name
, unit
->Node
.ln_Name
,
228 NP_Entry
, UnitProcEntry
,
233 if (unit
->UnitProc
) {
234 struct MsgPort
*replyport
= unit
->ReplyPort
;
235 struct DiskImageMsg
*msg
= unit
->DiskImageMsg
;
236 struct MsgPort
*port
;
238 replyport
->mp_SigTask
= FindTask(NULL
);
239 replyport
->mp_Flags
= PA_SIGNAL
;
240 msg
->dim_Unit
= unit
;
241 msg
->dim_Command
= DICMD_STARTUP
;
242 msg
->dim_Tags
= NULL
;
244 port
= GetProcMsgPort(unit
->UnitProc
);
245 PutMsg(port
, &msg
->dim_Msg
);
248 replyport
->mp_Flags
= PA_IGNORE
;
249 replyport
->mp_SigTask
= NULL
;
251 /* Check that it's not DeathMessage */
252 if (GetMsg(replyport
) == &msg
->dim_Msg
) {
253 AddTail(libBase
->Units
, &unit
->Node
);
254 io
->io_Unit
= (struct Unit
*)unit
;
255 io
->io_Message
.mn_Node
.ln_Type
= NT_REPLYMSG
;
257 libBase
->LibNode
.lib_Flags
&= ~LIBF_DELEXP
;
258 ReleaseSemaphore(libBase
->UnitSemaphore
);
260 return IOERR_SUCCESS
;
262 unit
->UnitProc
= NULL
;
269 FreeUnit(libBase
, unit
);
272 /* IMPORTANT: Mark IORequest as "complete" */
273 io
->io_Message
.mn_Node
.ln_Type
= NT_REPLYMSG
;
275 /* IMPORTANT: trash io_Device on open failure */
276 io
->io_Device
= NULL
;
278 if (io
->io_Error
== IOERR_SUCCESS
) io
->io_Error
= TDERR_NotSpecified
;
280 libBase
->LibNode
.lib_OpenCnt
--; /* End of expunge protection */
282 ReleaseSemaphore(libBase
->UnitSemaphore
);
291 /* There are two different things that might be returned from the dev_close()
292 routine. If the device wishes to be unloaded, then dev_close() must return
293 the segment list (as given to dev_init()). Otherwise dev_close() MUST
296 /* Close the library */
298 static AROS_LH1(BPTR
, LibClose
,
299 AROS_LHA(struct IORequest
*, io
, A1
),
300 struct DiskImageBase
*, libBase
, 2, DiskImage
305 static BPTR
LibClose(REG(a1
, struct IORequest
*io
), REG(a6
, struct DiskImageBase
*libBase
))
308 struct Library
*SysBase
= libBase
->SysBase
;
309 struct DiskImageUnit
*unit
= (struct DiskImageUnit
*)io
->io_Unit
;
311 ObtainSemaphore(libBase
->UnitSemaphore
);
313 /* IMPORTANT: make sure the IORequest is not used again
314 with a -1 in io_Device, any BeginIO() attempt will
315 immediatly halt (which is better than a subtle corruption
316 that will lead to hard-to-trace crashes!!! */
317 io
->io_Unit
= (struct Unit
*)-1;
318 io
->io_Device
= (struct Device
*)-1;
320 /* see if the unit is still in use */
321 if(unit
&& --unit
->OpenCnt
== 0) {
322 struct MsgPort
*replyport
= unit
->ReplyPort
;
323 struct DiskImageMsg
*msg
= unit
->DiskImageMsg
;
327 replyport
->mp_SigTask
= FindTask(NULL
);
328 replyport
->mp_Flags
= PA_SIGNAL
;
329 msg
->dim_Command
= DICMD_DIE
;
330 msg
->dim_Tags
= NULL
;
332 PutMsg(unit
->MsgPort
, &msg
->dim_Msg
);
336 replyport
->mp_Flags
= PA_IGNORE
;
337 replyport
->mp_SigTask
= NULL
;
339 FreeUnit(libBase
, unit
);
342 /* mark us as having one fewer openers */
343 if (--libBase
->LibNode
.lib_OpenCnt
== 0) {
344 ObtainSemaphore(libBase
->PluginSemaphore
);
345 FreePlugins(libBase
);
346 FreeLocaleInfo(SysBase
, &libBase
->LocaleInfo
);
347 ReleaseSemaphore(libBase
->PluginSemaphore
);
350 ReleaseSemaphore(libBase
->UnitSemaphore
);
358 static struct DiskImageUnit
*InitUnit (struct DiskImageBase
*libBase
, ULONG unit_number
) {
359 struct DiskImageUnit
*unit
;
360 struct Library
*SysBase
= libBase
->SysBase
;
362 unit
= AllocVec(sizeof(*unit
), MEMF_CLEAR
);
368 unit
->UnitNum
= unit_number
;
369 unit
->LibBase
= libBase
;
371 if ((unit
->Node
.ln_Name
= ASPrintf("diskimage.device unit %ld", unit_number
)) &&
372 (unit
->IOSemaphore
= CreateSemaphore()) &&
373 (unit
->MsgSemaphore
= CreateSemaphore()) &&
374 (unit
->ReplyPort
= CreatePortNoSignal()) &&
375 (unit
->DiskImageMsg
= (struct DiskImageMsg
*)CreateMsg(sizeof(*unit
->DiskImageMsg
))) &&
376 (unit
->DeathMsg
= (struct DeathMessage
*)CreateMsg(sizeof(*unit
->DeathMsg
))) &&
377 (unit
->ChangeInts
= CreateList(TRUE
)))
379 unit
->DiskImageMsg
->dim_Msg
.mn_Node
.ln_Name
= unit
->Node
.ln_Name
;
380 unit
->DeathMsg
->dm_Msg
.mn_Node
.ln_Name
= unit
->Node
.ln_Name
;
381 unit
->DiskImageMsg
->dim_Msg
.mn_ReplyPort
= unit
->ReplyPort
;
382 unit
->DeathMsg
->dm_Msg
.mn_ReplyPort
= unit
->ReplyPort
;
386 FreeUnit(libBase
, unit
);
390 static void FreeUnit (struct DiskImageBase
*libBase
, struct DiskImageUnit
*unit
) {
391 struct Library
*SysBase
= libBase
->SysBase
;
393 DeleteList(unit
->ChangeInts
);
394 DeleteMsg(&unit
->DeathMsg
->dm_Msg
);
395 DeleteMsg(&unit
->DiskImageMsg
->dim_Msg
);
396 DeletePortNoSignal(unit
->ReplyPort
);
397 DeleteSemaphore(unit
->MsgSemaphore
);
398 DeleteSemaphore(unit
->IOSemaphore
);
399 FreeVec(unit
->Node
.ln_Name
);
404 /* dev_expunge() is called by the memory allocator when the system is low on
407 There are two different things that might be returned from the dev_expunge()
408 routine. If the device is no longer open then dev_expunge() may return the
409 segment list (as given to dev_init()). Otherwise dev_expunge() may set the
410 delayed expunge flag and return NULL.
412 One other important note: because dev_expunge() is called from the memory
413 allocator, it may NEVER Wait() or otherwise take long time to complete. */
415 /* Expunge the library */
417 static AROS_LH0(BPTR
, LibExpunge
,
418 struct DiskImageBase
*, libBase
, 3, DiskImage
423 static BPTR
LibExpunge(REG(a6
, struct DiskImageBase
*libBase
))
426 struct Library
*SysBase
= libBase
->SysBase
;
429 /* see if anyone has us open */
430 if (libBase
->LibNode
.lib_OpenCnt
> 0) {
431 /* it is still open. set the delayed expunge flag */
432 libBase
->LibNode
.lib_Flags
|= LIBF_DELEXP
;
434 /* go ahead and get rid of us. */
435 result
= libBase
->SegList
;
437 /* unlink from device list */
438 Remove((struct Node
*)libBase
); /* Remove first (before FreeMem) */
440 /* ...device specific closings here... */
441 FreeBaseVars(libBase
);
443 /* free our memory */
444 DeleteLibrary((struct Library
*)libBase
);
454 static AROS_LH0(APTR
, LibReserved
,
455 struct DiskImageBase
*, libBase
, 4, DiskImage
463 static APTR
LibReserved (void) {
470 #define LIB_ENTRY(a,b) AROS_SLIB_ENTRY(a, DiskImage, b)
472 #define LIB_ENTRY(a,b) AROS_SLIB_ENTRY(a, DiskImage)
475 #define LIB_ENTRY(a,b) a
478 const CONST_APTR LibVectors
[] = {
479 (APTR
)LIB_ENTRY(LibOpen
, 1),
480 (APTR
)LIB_ENTRY(LibClose
, 2),
481 (APTR
)LIB_ENTRY(LibExpunge
, 3),
482 (APTR
)LIB_ENTRY(LibReserved
, 4),
483 (APTR
)LIB_ENTRY(LibBeginIO
, 5),
484 (APTR
)LIB_ENTRY(LibAbortIO
, 6),
485 (APTR
)LIB_ENTRY(MountImage
, 7),
486 (APTR
)LIB_ENTRY(UnitInfo
, 8),
487 (APTR
)LIB_ENTRY(WriteProtect
, 9),
488 (APTR
)LIB_ENTRY(UnitControlA
, 10),
489 (APTR
)LIB_ENTRY(ReloadPlugins
, 11),
490 (APTR
)LIB_ENTRY(DoHookPlugins
, 12),
491 (APTR
)LIB_ENTRY(AddDiskChangeHook
, 13),
492 (APTR
)LIB_ENTRY(AddReloadPluginsHook
, 14),
496 const IPTR LibInitTab
[] = {
497 sizeof(struct DiskImageBase
),
503 const struct Resident USED_VAR ROMTag
= {
505 (struct Resident
*)&ROMTag
,
507 RTF_AUTOINIT
, /* Add RTF_COLDSTART if you want to be resident */
509 NT_DEVICE
, /* Make this NT_DEVICE if needed */
510 0, /* PRI, usually not needed unless you're resident */