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

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