home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD v1.2 / amidev_cd_12.iso / reference / amiga_mail_vol2 / ii-77 / asyncio.c < prev    next >
C/C++ Source or Header  |  1996-01-30  |  14KB  |  436 lines

  1. ;/* ASyncIO.c - Execute me to compile with SAS/C 6.56
  2. sc NMINC STRMERGE NOSTKCHK IGNORE=73 asyncio.c
  3. quit
  4. */
  5.  
  6. /*
  7. (c)  Copyright 1992 Commodore-Amiga, Inc.   All rights reserved.
  8. The information contained herein is subject to change without notice,
  9. and is provided "as is" without warranty of any kind, either expressed
  10. or implied.  The entire risk as to the use of this information is
  11. assumed by the user.
  12. */
  13.  
  14. #include <exec/types.h>
  15. #include <exec/exec.h>
  16. #include <dos/dos.h>
  17. #include <dos/dosextens.h>
  18. #include <stdio.h>
  19.  
  20. #include <clib/exec_protos.h>
  21. #include <clib/dos_protos.h>
  22.  
  23. #include "asyncio.h"
  24.  
  25. /*****************************************************************************/
  26.  
  27. static VOID SendAsync(struct AsyncFile *file, APTR arg2)
  28. {
  29.     /* send out an async packet to the file system. */
  30.  
  31.     file->af_Packet.sp_Pkt.dp_Port = &file->af_PacketPort;
  32.     file->af_Packet.sp_Pkt.dp_Arg2 = (LONG)arg2;
  33.     PutMsg(file->af_Handler, &file->af_Packet.sp_Msg);
  34.     file->af_PacketPending = TRUE;
  35. }
  36.  
  37. /*****************************************************************************/
  38.  
  39.  
  40. static VOID WaitPacket(struct AsyncFile *file)
  41. {
  42.     /* This enables signalling when a packet comes back to the port */
  43.     file->af_PacketPort.mp_Flags = PA_SIGNAL;
  44.  
  45.     /* Wait for the packet to come back, and remove it from the message
  46.      * list. Since we know no other packets can come in to the port, we can
  47.      * safely use Remove() instead of GetMsg(). If other packets could come in,
  48.      * we would have to use GetMsg(), which correctly arbitrates access in such
  49.      * a case
  50.      */
  51.     Remove((struct Node *)WaitPort(&file->af_PacketPort));
  52.  
  53.     /* set the port type back to PA_IGNORE so we won't be bothered with
  54.      * spurious signals
  55.      */
  56.     file->af_PacketPort.mp_Flags = PA_IGNORE;
  57.  
  58.     /* packet is no longer pending, we got it */
  59.     file->af_PacketPending = FALSE;
  60. }
  61.  
  62. /*****************************************************************************/
  63.  
  64. struct AsyncFile *OpenAsync(STRPTR fileName, UBYTE mode, LONG bufferSize)
  65. {
  66. struct AsyncFile  *file;
  67. struct FileHandle *fh;
  68.  
  69.     /* The buffer size is rounded to a multiple of 32 bytes. This will make
  70.      * DMA as fast as can be
  71.      */
  72.  
  73.     bufferSize = (bufferSize + 31) & 0xffffffe0;
  74.  
  75.     /* now allocate the ASyncFile structure, as well as the read buffer. Add
  76.      * 15 bytes to the total size in order to allow for later quad-longword
  77.      * alignement of the buffers
  78.      */
  79.  
  80.     if (file = AllocVec(sizeof(struct AsyncFile) + bufferSize + 15,
  81.                         MEMF_ANY|MEMF_CLEAR))
  82.     {
  83.         if (mode == MODE_READ)
  84.         {
  85.             file->af_File     = Open(fileName,MODE_OLDFILE);
  86.             file->af_ReadMode = TRUE;
  87.         }
  88.         else if (mode == MODE_WRITE)
  89.         {
  90.             file->af_File = Open(fileName,MODE_NEWFILE);
  91.         }
  92.         else if (mode == MODE_APPEND)
  93.         {
  94.             /* in append mode, we open for writing, and then seek to the
  95.              * end of the file. That way, the initial write will happen at
  96.              * the end of the file, thus extending it
  97.              */
  98.  
  99.             if (file->af_File = Open(fileName,MODE_READWRITE))
  100.             {
  101.                 if (Seek(file->af_File,0,OFFSET_END) < 0)
  102.                 {
  103.                     Close(file->af_File);
  104.                     file->af_File = NULL;
  105.                 }
  106.             }
  107.         }
  108.  
  109.         if (!file->af_File)
  110.         {
  111.             /* file didn't open, free stuff and leave */
  112.             FreeVec(file);
  113.             return(NULL);
  114.         }
  115.  
  116.         /* initialize the ASyncFile structure. We do as much as we can here,
  117.          * in order to avoid doing it in more critical sections
  118.          *
  119.          * Note how the two buffers used are quad-longword aligned. This helps
  120.          * performance on 68040 systems with copyback cache. Aligning the data
  121.          * avoids a nasty side-effect of the 040 caches on DMA. Not aligning
  122.          * the data causes the device driver to have to do some magic to avoid
  123.          * the cache problem. This magic will generally involve flushing the
  124.          * CPU caches. This is very costly on an 040. Aligning things avoids
  125.          * the need for magic, at the cost of at most 15 bytes of ram.
  126.          */
  127.  
  128.         fh                  = BADDR(file->af_File);
  129.         file->af_Handler    = fh->fh_Type;
  130.         file->af_BufferSize = bufferSize / 2;
  131.         file->af_Buffers[0] =
  132.                 (APTR)(((ULONG)file + sizeof(struct AsyncFile) + 15) & 0xfffffff0);
  133.         file->af_Buffers[1] =
  134.                 (APTR)((ULONG)file->af_Buffers[0] + file->af_BufferSize);
  135.         file->af_Offset     = file->af_Buffers[0];
  136.  
  137.         /* this is the port used to get the packets we send out back.
  138.          * It is initialized to PA_IGNORE, which means that no signal is
  139.          * generated when a message comes in to the port. The signal bit number
  140.          * is initialized to SIGB_SINGLE, which is the special bit that can
  141.          * be used for one-shot signalling. The signal will never be set,
  142.          * since the port is of type PA_IGNORE. We'll change the type of the
  143.          * port later on to PA_SIGNAL whenever we need to wait for a message
  144.          * to come in.
  145.          *
  146.          * The trick used here avoids the need to allocate an extra signal bit
  147.          * for the port. It is quite efficient.
  148.          */
  149.  
  150.         file->af_PacketPort.mp_MsgList.lh_Head     =
  151.                 (struct Node *)&file->af_PacketPort.mp_MsgList.lh_Tail;
  152.         file->af_PacketPort.mp_MsgList.lh_TailPred =
  153.                 (struct Node *)&file->af_PacketPort.mp_MsgList.lh_Head;
  154.         file->af_PacketPort.mp_Node.ln_Type        = NT_MSGPORT;
  155.         file->af_PacketPort.mp_Flags               = PA_IGNORE;
  156.         file->af_PacketPort.mp_SigBit              = SIGB_SINGLE;
  157.         file->af_PacketPort.mp_SigTask             = FindTask(NULL);
  158.  
  159.         file->af_Packet.sp_Pkt.dp_Link          = &file->af_Packet.sp_Msg;
  160.         file->af_Packet.sp_Pkt.dp_Arg1          = fh->fh_Arg1;
  161.         file->af_Packet.sp_Pkt.dp_Arg3          = file->af_BufferSize;
  162.         file->af_Packet.sp_Msg.mn_Node.ln_Name  = (STRPTR)&file->af_Packet.sp_Pkt;
  163.         file->af_Packet.sp_Msg.mn_Node.ln_Type  = NT_MESSAGE;
  164.         file->af_Packet.sp_Msg.mn_Length        = sizeof(struct StandardPacket);
  165.  
  166.         if (mode == MODE_READ)
  167.         {
  168.             /* if we are in read mode, send out the first read packet to the
  169.              * file system. While the application is getting ready to read
  170.              * data, the file system will happily fill in this buffer with
  171.              * DMA transfer, so that by the time the application needs the data,
  172.              * it will be in the buffer waiting
  173.              */
  174.  
  175.             file->af_Packet.sp_Pkt.dp_Type = ACTION_READ;
  176.             if (file->af_Handler)
  177.                 SendAsync(file,file->af_Buffers[0]);
  178.         }
  179.         else
  180.         {
  181.             file->af_Packet.sp_Pkt.dp_Type = ACTION_WRITE;
  182.             file->af_BytesLeft             = file->af_BufferSize;
  183.         }
  184.     }
  185.  
  186.     return(file);
  187. }
  188.  
  189. /*****************************************************************************/
  190.  
  191. LONG CloseAsync(struct AsyncFile *file)
  192. {
  193. LONG result;
  194. LONG result2;
  195.  
  196.     result = 0;
  197.     if (file)
  198.     {
  199.         if (file->af_PacketPending)
  200.             WaitPacket(file);
  201.  
  202.         result  = file->af_Packet.sp_Pkt.dp_Res1;
  203.         result2 = file->af_Packet.sp_Pkt.dp_Res2;
  204.         if (result >= 0)
  205.         {
  206.             if (!file->af_ReadMode)
  207.             {
  208.                 /* this will flush out any pending data in the write buffer */
  209.                 result  = Write(file->af_File,
  210.                           file->af_Buffers[file->af_CurrentBuf],
  211.                           file->af_BufferSize - file->af_BytesLeft);
  212.                 result2 = IoErr();
  213.             }
  214.         }
  215.  
  216.         Close(file->af_File);
  217.         FreeVec(file);
  218.  
  219.         SetIoErr(result2);
  220.     }
  221.  
  222.     return(result);
  223. }
  224.  
  225. /*****************************************************************************/
  226.  
  227. LONG ReadAsync(struct AsyncFile *file, APTR buf, LONG numBytes)
  228. {
  229. LONG totalBytes;
  230. LONG bytesArrived;
  231.  
  232.     totalBytes = 0;
  233.  
  234.     /* if we need more bytes than there are in the current buffer, enter the
  235.      * read loop
  236.      */
  237.  
  238.     while (numBytes > file->af_BytesLeft)
  239.     {
  240.         /* this takes care of NIL: */
  241.         if (!file->af_Handler)
  242.             return(0);
  243.  
  244.         WaitPacket(file);
  245.  
  246.         bytesArrived = file->af_Packet.sp_Pkt.dp_Res1;
  247.         if (bytesArrived <= 0)
  248.         {
  249.             /* error, get out of here */
  250.             SetIoErr(file->af_Packet.sp_Pkt.dp_Res2);
  251.             return(-1);
  252.         }
  253.  
  254.         /* enable this section of code if you want special processing for
  255.          * reads bigger than the buffer size
  256.          */
  257. #ifdef OPTIMIZE_BIG_READS
  258.         if (numBytes > file->af_BytesLeft + bytesArrived + file->af_BufferSize)
  259.         {
  260.             if (file->af_BytesLeft)
  261.             {
  262.                 CopyMem(file->af_Offset,buf,file->af_BytesLeft);
  263.  
  264.                 numBytes   -= file->af_BytesLeft;
  265.                 buf         = (APTR)((ULONG)buf + file->af_BytesLeft);
  266.                 totalBytes += file->af_BytesLeft;
  267.                 file->af_BytesLeft = 0;
  268.             }
  269.  
  270.             if (bytesArrived)
  271.             {
  272.                 CopyMem(file->af_Buffers[file->af_CurrentBuf],buf,bytesArrived);
  273.  
  274.                 numBytes   -= bytesArrived;
  275.                 buf         = (APTR)((ULONG)buf + bytesArrived);
  276.                 totalBytes += bytesArrived;
  277.             }
  278.  
  279.             bytesArrived = Read(file->af_File,buf,numBytes);
  280.  
  281.             if (bytesArrived <= 0)
  282.                 return(-1);
  283.  
  284.             SendAsync(file,file->af_Buffers[0]);
  285.             file->af_CurrentBuf = 0;
  286.             file->af_BytesLeft  = 0;
  287.  
  288.             return(totalBytes + bytesArrived);
  289.         }
  290. #endif
  291.  
  292.         if (file->af_BytesLeft)
  293.         {
  294.             CopyMem(file->af_Offset,buf,file->af_BytesLeft);
  295.  
  296.             numBytes   -= file->af_BytesLeft;
  297.             buf         = (APTR)((ULONG)buf + file->af_BytesLeft);
  298.             totalBytes += file->af_BytesLeft;
  299.         }
  300.  
  301.         /* ask that the buffer be filled */
  302.         SendAsync(file,file->af_Buffers[1-file->af_CurrentBuf]);
  303.  
  304.         file->af_Offset     = file->af_Buffers[file->af_CurrentBuf];
  305.         file->af_CurrentBuf = 1 - file->af_CurrentBuf;
  306.         file->af_BytesLeft  = bytesArrived;
  307.     }
  308.  
  309.     if (numBytes)
  310.     {
  311.         CopyMem(file->af_Offset,buf,numBytes);
  312.         file->af_BytesLeft -= numBytes;
  313.         file->af_Offset     = (APTR)((ULONG)file->af_Offset + numBytes);
  314.     }
  315.  
  316.     return (totalBytes + numBytes);
  317. }
  318.  
  319. /*****************************************************************************/
  320.  
  321. LONG ReadCharAsync(struct AsyncFile *file)
  322. {
  323. char ch;
  324.  
  325.     if (file->af_BytesLeft)
  326.     {
  327.         /* if there is at least a byte left in the current buffer, get it
  328.          * directly. Also update all counters
  329.          */
  330.  
  331.         ch = *(char *)file->af_Offset;
  332.         file->af_BytesLeft--;
  333.         file->af_Offset = (APTR)((ULONG)file->af_Offset + 1);
  334.  
  335.         return((LONG)ch);
  336.     }
  337.  
  338.     /* there were no characters in the current buffer, so call the main read
  339.      * routine. This has the effect of sending a request to the file system to
  340.      * have the current buffer refilled. After that request is done, the
  341.      * character is extracted for the alternate buffer, which at that point
  342.      * becomes the "current" buffer
  343.      */
  344.  
  345.     if (ReadAsync(file,&ch,1) > 0)
  346.         return((LONG)ch);
  347.  
  348.     /* We couldn't read above, so fail */
  349.  
  350.     return(-1);
  351. }
  352.  
  353. /*****************************************************************************/
  354.  
  355. LONG WriteAsync(struct AsyncFile *file, APTR buf, LONG numBytes)
  356. {
  357. LONG totalBytes;
  358.  
  359.     totalBytes = 0;
  360.  
  361.     while (numBytes > file->af_BytesLeft)
  362.     {
  363.         /* this takes care of NIL: */
  364.         if (!file->af_Handler)
  365.         {
  366.             file->af_Offset    = file->af_Buffers[file->af_CurrentBuf];
  367.             file->af_BytesLeft = file->af_BufferSize;
  368.             return(numBytes + totalBytes);
  369.         }
  370.  
  371.         if (file->af_BytesLeft)
  372.         {
  373.             CopyMem(buf,file->af_Offset,numBytes);
  374.  
  375.             numBytes   -= file->af_BytesLeft;
  376.             buf         = (APTR)((ULONG)buf + file->af_BytesLeft);
  377.             totalBytes += file->af_BytesLeft;
  378.         }
  379.  
  380.         if (file->af_PacketPending)
  381.         {
  382.             WaitPacket(file);
  383.  
  384.             if (file->af_Packet.sp_Pkt.dp_Res1 <= 0)
  385.             {
  386.                 /* an error occurred, leave */
  387.                 SetIoErr(file->af_Packet.sp_Pkt.dp_Res2);
  388.                 return(-1);
  389.             }
  390.         }
  391.  
  392.         /* send the current buffer out to disk */
  393.         SendAsync(file,file->af_Buffers[file->af_CurrentBuf]);
  394.  
  395.         file->af_CurrentBuf   = 1 - file->af_CurrentBuf;
  396.         file->af_Offset       = file->af_Buffers[file->af_CurrentBuf];
  397.         file->af_BytesLeft    = file->af_BufferSize;
  398.     }
  399.  
  400.     if (numBytes)
  401.     {
  402.         CopyMem(buf,file->af_Offset,numBytes);
  403.         file->af_BytesLeft -= numBytes;
  404.         file->af_Offset     = (APTR)((ULONG)file->af_Offset + numBytes);
  405.     }
  406.  
  407.     return (totalBytes + numBytes);
  408. }
  409.  
  410. /*****************************************************************************/
  411.  
  412. LONG WriteCharAsync(struct AsyncFile *file, char ch)
  413. {
  414.     if (file->af_BytesLeft)
  415.     {
  416.         /* if there's any room left in the current buffer, directly write
  417.          * the byte into it, updating counters and stuff.
  418.          */
  419.  
  420.         *(char *)file->af_Offset = ch;
  421.         file->af_BytesLeft--;
  422.         file->af_Offset = (APTR)((ULONG)file->af_Offset + 1);
  423.  
  424.         /* one byte written */
  425.         return(1);
  426.     }
  427.  
  428.     /* there was no room in the current buffer, so call the main write
  429.      * routine. This will effectively send the current buffer out to disk,
  430.      * wait for the other buffer to come back, and then put the byte into
  431.      * it.
  432.      */
  433.  
  434.     return(WriteAsync(file,&ch,1));
  435. }
  436.