Kernel - Start on SHM 'driver' (and common in-memory finddir/readdir)
[tpg/acess2.git] / KernelLand / Kernel / drv / fifo.c
1 /* 
2  * Acess2 Kernel
3  * - By John Hodge (thePowersGang)
4  *
5  * drv/fifo.c
6  * - FIFO Pipe Driver
7  */
8 #define DEBUG   0
9 #include <acess.h>
10 #include <modules.h>
11 #include <fs_devfs.h>
12 #include <semaphore.h>
13 #include <memfs_helpers.h>
14
15 // === CONSTANTS ===
16 #define DEFAULT_RING_SIZE       2048
17 #define PF_BLOCKING             1
18
19 // === TYPES ===
20 typedef struct sPipe
21 {
22         tMemFS_FileHdr  FileHdr;
23         tVFS_Node       Node;
24         Uint    Flags;
25          int    ReadPos;
26          int    WritePos;
27          int    BufSize;
28         char    *Buffer;
29 } tPipe;
30
31 // === PROTOTYPES ===
32  int    FIFO_Install(char **Arguments);
33  int    FIFO_IOCtl(tVFS_Node *Node, int Id, void *Data);
34  int    FIFO_ReadDir(tVFS_Node *Node, int Id, char Dest[FILENAME_MAX]);
35 tVFS_Node       *FIFO_FindDir(tVFS_Node *Node, const char *Filename, Uint Flags);
36 tVFS_Node       *FIFO_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
37 void    FIFO_Reference(tVFS_Node *Node);
38 void    FIFO_Close(tVFS_Node *Node);
39  int    FIFO_Unlink(tVFS_Node *Node, const char *OldName);
40 size_t  FIFO_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags);
41 size_t  FIFO_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags);
42 tPipe   *FIFO_Int_NewPipe(int Size, const char *Name);
43
44 // === GLOBALS ===
45 MODULE_DEFINE(0, 0x0032, FIFO, FIFO_Install, NULL, NULL);
46 tMemFS_DirHdr   gFIFO_RootDir = {
47         .FileHdr = { .Name = "FIFO" },
48 };
49 tVFS_NodeType   gFIFO_DirNodeType = {
50         .TypeName = "FIFO Dir Node",
51         .ReadDir = FIFO_ReadDir,
52         .FindDir = FIFO_FindDir,
53         .MkNod = FIFO_MkNod,
54         .Unlink = FIFO_Unlink,
55         .IOCtl = FIFO_IOCtl
56 };
57 tVFS_NodeType   gFIFO_PipeNodeType = {
58         .TypeName = "FIFO Pipe Node",
59         .Read = FIFO_Read,
60         .Write = FIFO_Write,
61         .Close = FIFO_Close,
62         .Reference = FIFO_Reference
63 };
64 tDevFS_Driver   gFIFO_DriverInfo = {
65         NULL, "fifo",
66         {
67         .Size = 1,
68         .NumACLs = 1,
69         .ACLs = &gVFS_ACL_EveryoneRW,
70         .Flags = VFS_FFLAG_DIRECTORY,
71         .Type = &gFIFO_DirNodeType
72         }
73 };
74 tVFS_Node       gFIFO_AnonNode = {
75         .NumACLs = 1,
76         .ACLs = &gVFS_ACL_EveryoneRW,
77         };
78
79 // === CODE ===
80 /**
81  * \fn int FIFO_Install(char **Options)
82  * \brief Installs the FIFO Driver
83  */
84 int FIFO_Install(char **Options)
85 {
86         MemFS_InitDir( &gFIFO_RootDir );
87         DevFS_AddDevice( &gFIFO_DriverInfo );
88         return MODULE_ERR_OK;
89 }
90
91 /**
92  * \fn int FIFO_IOCtl(tVFS_Node *Node, int Id, void *Data)
93  */
94 int FIFO_IOCtl(tVFS_Node *Node, int Id, void *Data)
95 {
96         return 0;
97 }
98
99 /**
100  * \fn char *FIFO_ReadDir(tVFS_Node *Node, int Id)
101  * \brief Reads from the FIFO root
102  */
103 int FIFO_ReadDir(tVFS_Node *Node, int Id, char Dest[FILENAME_MAX])
104 {
105         // Entry 0 is Anon Pipes
106         if(Id == 0)
107         {
108                 strcpy(Dest, "anon");
109                 return 0;
110         }
111         
112         return MemFS_ReadDir(&gFIFO_RootDir, Id-1, Dest);
113 }
114
115 /**
116  * \fn tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename)
117  * \brief Find a file in the FIFO root
118  * \note Creates an anon pipe if anon is requested
119  */
120 tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename, Uint Flags)
121 {
122         ASSERTR(Filename, NULL);
123         ASSERTR(Filename[0], NULL);
124         
125         // Anon Pipe
126         if( strcmp(Filename, "anon") == 0 )
127         {
128                 if( Flags & VFS_FDIRFLAG_STAT ) {
129                         //return &gFIFI_TemplateAnonNode;
130                 }
131                 tPipe *ret = FIFO_Int_NewPipe(DEFAULT_RING_SIZE, "anon");
132                 return &ret->Node;
133         }
134         
135         // Check Named List
136         tPipe *ret = (tPipe*)MemFS_FindDir(&gFIFO_RootDir, Filename);
137         if(!ret)
138                 return NULL;
139         return &ret->Node;
140 }
141
142 /**
143  * \fn int FIFO_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
144  */
145 tVFS_Node *FIFO_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
146 {
147         UNIMPLEMENTED();
148         return NULL;
149 }
150
151 /**
152  * \brief Delete a pipe
153  */
154 int FIFO_Unlink(tVFS_Node *Node, const char *OldName)
155 {
156         if(Node != &gFIFO_DriverInfo.RootNode)  return 0;
157         
158         // Can't relink anon
159         if(strcmp(OldName, "anon"))     return 0;
160         
161         // Find node
162         tPipe* pipe = (tPipe*)MemFS_Remove(&gFIFO_RootDir, OldName);
163         if(!pipe)       return 0;
164         
165         free(pipe);
166         return 0;
167 }
168
169 void FIFO_Reference(tVFS_Node *Node)
170 {
171         if(!Node->ImplPtr)      return ;
172         
173         Node->ReferenceCount ++;
174 }
175
176 /**
177  * \fn void FIFO_Close(tVFS_Node *Node)
178  * \brief Close a FIFO end
179  */
180 void FIFO_Close(tVFS_Node *Node)
181 {
182         if(!Node->ImplPtr)      return ;
183         
184         Node->ReferenceCount --;
185         if(Node->ReferenceCount)        return ;
186         
187         tPipe   *pipe = Node->ImplPtr;
188         
189         if(strcmp(pipe->FileHdr.Name, "anon") == 0)
190         {
191                 Log_Debug("FIFO", "Pipe %p closed", pipe);
192                 free(pipe);
193                 return ;
194         }
195         
196         return ;
197 }
198
199 /**
200  * \brief Read from a fifo pipe
201  */
202 size_t FIFO_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
203 {
204         tPipe   *pipe = Node->ImplPtr;
205         Uint    len;
206         Uint    remaining = Length;
207
208         if(!pipe)       return 0;
209         
210         ENTER("pNode XOffset xLength pBuffer", Node, Offset, Length, Buffer);
211         
212         while(remaining)
213         {
214                 // Wait for buffer to fill
215                 if( (pipe->Flags & PF_BLOCKING) && !(Flags & VFS_IOFLAG_NOBLOCK) )
216                 {
217                         if( pipe->ReadPos == pipe->WritePos )
218                                 VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "FIFO_Read");
219                         
220                 }
221                 else
222                 {
223                         if(pipe->ReadPos == pipe->WritePos)
224                         {
225                                 VFS_MarkAvaliable(Node, 0);
226                                 LEAVE('i', 0);
227                                 return 0;
228                         }
229                 }
230         
231                 len = remaining;
232                 if( pipe->ReadPos < pipe->WritePos )
233                 {
234                          int    avail_bytes = pipe->WritePos - pipe->ReadPos;
235                         if( avail_bytes < remaining )   len = avail_bytes;
236                 }
237                 else
238                 {
239                          int    avail_bytes = pipe->WritePos + pipe->BufSize - pipe->ReadPos;
240                         if( avail_bytes < remaining )   len = avail_bytes;
241                 }
242
243                 LOG("len = %i, remaining = %i", len, remaining);                
244
245                 // Check if read overflows buffer
246                 if(len > pipe->BufSize - pipe->ReadPos)
247                 {
248                         int ofs = pipe->BufSize - pipe->ReadPos;
249                         memcpy(Buffer, &pipe->Buffer[pipe->ReadPos], ofs);
250                         memcpy((Uint8*)Buffer + ofs, &pipe->Buffer, len-ofs);
251                 }
252                 else
253                 {
254                         memcpy(Buffer, &pipe->Buffer[pipe->ReadPos], len);
255                 }
256                 
257                 // Increment read position
258                 pipe->ReadPos += len;
259                 pipe->ReadPos %= pipe->BufSize;
260                 
261                 // Mark some flags
262                 if( pipe->ReadPos == pipe->WritePos ) {
263                         LOG("%i == %i, marking none to read", pipe->ReadPos, pipe->WritePos);
264                         VFS_MarkAvaliable(Node, 0);
265                 }
266                 VFS_MarkFull(Node, 0);  // Buffer can't still be full
267                 
268                 // Decrement Remaining Bytes
269                 remaining -= len;
270                 // Increment Buffer address
271                 Buffer = (Uint8*)Buffer + len;
272                 
273                 // TODO: Option to read differently
274                 LEAVE('i', len);
275                 return len;
276         }
277
278         LEAVE('i', Length);
279         return Length;
280
281 }
282
283 /**
284  * \brief Write to a fifo pipe
285  */
286 size_t FIFO_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags)
287 {
288         tPipe   *pipe = Node->ImplPtr;
289         Uint    len;
290         Uint    remaining = Length;
291         
292         if(!pipe)       return 0;
293
294         ENTER("pNode XOffset xLength pBuffer", Node, Offset, Length, Buffer);
295         
296         while(remaining)
297         {
298                 // Wait for buffer to empty
299                 if( (pipe->Flags & PF_BLOCKING) && !(Flags & VFS_IOFLAG_NOBLOCK) )
300                 {
301                         if( pipe->ReadPos == (pipe->WritePos+1)%pipe->BufSize ) {
302                                 LOG("Blocking write on FIFO");
303                                 VFS_SelectNode(Node, VFS_SELECT_WRITE, NULL, "FIFO_Write");
304                         }
305
306                         len = remaining;
307                         if( pipe->ReadPos > pipe->WritePos )
308                         {
309                                  int    rem_space = pipe->ReadPos - pipe->WritePos;
310                                 if(rem_space < remaining)       len = rem_space;
311                         }
312                         else
313                         {
314                                  int    rem_space = pipe->ReadPos + pipe->BufSize - pipe->WritePos;
315                                 if(rem_space < remaining)       len = rem_space;
316                         }
317                 }
318                 else
319                 {
320                         if(pipe->ReadPos == (pipe->WritePos+1)%pipe->BufSize)
321                         {
322                                 LEAVE('i', 0);
323                                 return 0;
324                         }
325                         // Write buffer
326                         if(pipe->ReadPos - pipe->WritePos < remaining)
327                                 len = pipe->ReadPos - pipe->WritePos;
328                         else
329                                 len = remaining;
330                 }
331                 
332                 // Check if write overflows buffer
333                 if(len > pipe->BufSize - pipe->WritePos)
334                 {
335                         int ofs = pipe->BufSize - pipe->WritePos;
336                         LOG("pipe->Buffer = %p, pipe->WritePos = %i, ofs=%i, len=%i",
337                                 pipe->Buffer, pipe->WritePos, ofs, len);
338                         memcpy(&pipe->Buffer[pipe->WritePos], Buffer, ofs);
339                         memcpy(&pipe->Buffer[0], (Uint8*)Buffer + ofs, len-ofs);
340                 }
341                 else
342                 {
343                         LOG("pipe->Buffer = %p, pipe->WritePos = %i", pipe->Buffer, pipe->WritePos);
344                         memcpy(&pipe->Buffer[pipe->WritePos], Buffer, len);
345                 }
346                 
347                 // Increment read position
348                 pipe->WritePos += len;
349                 pipe->WritePos %= pipe->BufSize;
350                 
351                 // Mark some flags
352                 if( pipe->ReadPos == pipe->WritePos ) {
353                         LOG("Buffer is full");
354                         VFS_MarkFull(Node, 1);  // Buffer full
355                 }
356                 VFS_MarkAvaliable(Node, 1);
357                 
358                 // Decrement Remaining Bytes
359                 remaining -= len;
360                 // Increment Buffer address
361                 Buffer = (Uint8*)Buffer + len;
362         }
363
364         LEAVE('i', Length);
365         return Length;
366 }
367
368 // --- HeLPERS ---
369 /**
370  * \fn tPipe *FIFO_Int_NewPipe(int Size, const char *Name)
371  * \brief Create a new pipe
372  */
373 tPipe *FIFO_Int_NewPipe(int Size, const char *Name)
374 {
375         tPipe   *ret;
376          int    namelen = strlen(Name) + 1;
377          int    allocsize = sizeof(tPipe) + sizeof(tVFS_ACL) + Size + namelen;
378
379         ENTER("iSize sName", Size, Name);       
380
381         ret = calloc(1, allocsize);
382         if(!ret)        LEAVE_RET('n', NULL);
383         
384         // Set default flags
385         ret->Flags = PF_BLOCKING;
386         
387         // Allocate Buffer
388         ret->BufSize = Size;
389         ret->Buffer = (void*)( (Uint)ret + sizeof(tPipe) + sizeof(tVFS_ACL) );
390         
391         // Set name (and FIFO name)
392         ret->FileHdr.Name = ret->Buffer + Size;
393         strcpy((char*)ret->FileHdr.Name, Name);
394         
395         // Set Node
396         ret->Node.ReferenceCount = 1;
397         ret->Node.Size = 0;
398         ret->Node.ImplPtr = ret;
399         ret->Node.UID = Threads_GetUID();
400         ret->Node.GID = Threads_GetGID();
401         ret->Node.NumACLs = 1;
402         ret->Node.ACLs = (void*)( (Uint)ret + sizeof(tPipe) );
403                 ret->Node.ACLs->Ent.Group = 0;
404                 ret->Node.ACLs->Ent.ID = ret->Node.UID;
405                 ret->Node.ACLs->Perm.Inv = 0;
406                 ret->Node.ACLs->Perm.Perms = -1;
407         ret->Node.CTime
408                 = ret->Node.MTime
409                 = ret->Node.ATime = now();
410         ret->Node.Type = &gFIFO_PipeNodeType;
411
412         LEAVE('p', ret);
413         
414         return ret;
415 }

UCC git Repository :: git.ucc.asn.au