SimpleOS

LXR

Navigation



Site hébergé par : enix

The LXR Cross Referencer for SOS

source navigation ]
diff markup ]
identifier search ]
general search ]
 
 
Article:1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 6.5 ] [ 7 ] [ 7.5 ] [ 8 ] [ 9 ] [ 9.5 ]

001 /* Copyright (C) 2005      David Decotigny
002 
003    This program is free software; you can redistribute it and/or
004    modify it under the terms of the GNU General Public License
005    as published by the Free Software Foundation; either version 2
006    of the License, or (at your option) any later version.
007 
008    This program is distributed in the hope that it will be useful,
009    but WITHOUT ANY WARRANTY; without even the implied warranty of
010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
011    GNU General Public License for more details.
012 
013    You should have received a copy of the GNU General Public License
014    along with this program; if not, write to the Free Software
015    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
016    USA.
017 */
018 
019 #include <sos/assert.h>
020 #include <sos/types.h>
021 #include <sos/fs.h>
022 #include <sos/list.h>
023 #include <sos/kmalloc.h>
024 
025 #include "chardev.h"
026 
027 struct sos_chardev_class
028 {
029   sos_ui32_t                device_class;
030   struct sos_chardev_ops   *ops;
031 
032   /** This corresponds to the chardev_class_custom_data field passed
033       to open/read/etc. and to sos_chardev_register_class() */
034   void                     *custom_data;
035 
036   sos_count_t               ref_cnt; /**< increased each time a FS
037                                           node is opened */
038 
039   struct sos_chardev_class *next, *prev;
040 };
041 
042 
043 struct sos_chardev_opened_file
044 {
045   /** "normal" VFS opened file structure is available in this
046       structure */
047   struct sos_fs_opened_file  super;
048 
049   /** Additional information for this opened file: the device's class
050       structure */
051   struct sos_chardev_class  *class;
052 };
053 
054 
055 /** The list of registered classes, ie the dictionary major number ->
056     device class description */
057 static struct sos_chardev_class *registered_chardev_classes;
058 
059 
060 /* Forward declarations */
061 static struct sos_fs_ops_opened_file chardev_ops_opened_file;
062 static struct sos_fs_ops_opened_chardev chardev_ops_opened_chardev;
063 
064 static sos_ret_t
065 chardev_helper_new_opened_file(struct sos_fs_node * this,
066                                const struct sos_process * owner,
067                                sos_ui32_t open_flags,
068                                struct sos_fs_opened_file ** result_of);
069 static sos_ret_t
070 chardev_helper_close_opened_file(struct sos_fs_node * this,
071                                  struct sos_fs_opened_file * of);
072 static sos_ret_t
073 duplicate_opened_chardev(struct sos_fs_opened_file *this,
074                          const struct sos_process  * for_owner,
075                          struct sos_fs_opened_file **result);
076 
077 
078 /**
079  * Return the device descriptionn structure for the corresponding
080  * device class, or NULL when none found.
081  */
082 static struct sos_chardev_class * lookup_chardev_class(sos_ui32_t device_class)
083 {
084   struct sos_chardev_class *chardev;
085   int nb;
086 
087   list_foreach (registered_chardev_classes, chardev, nb)
088     {
089       if (chardev->device_class == device_class)
090         return chardev;
091     }
092 
093   return NULL;
094 }
095 
096 
097 sos_ret_t sos_chardev_register_class (sos_ui32_t device_class,
098                                       struct sos_chardev_ops *ops,
099                                       void * chardev_class_custom_data)
100 {
101   struct sos_chardev_class *chardev;
102 
103   /* Make sure this device class is not already registered */
104   chardev = lookup_chardev_class(device_class);
105   if (NULL != chardev)
106     return -SOS_EBUSY;
107 
108   /* Allocate and initialize a new device description */
109   chardev = (struct sos_chardev_class *)
110     sos_kmalloc(sizeof(struct sos_chardev_class), 0);
111   if (chardev == NULL)
112     return -SOS_ENOMEM;
113 
114   chardev->device_class = device_class;
115   chardev->custom_data  = chardev_class_custom_data;
116   chardev->ops          = ops;
117   chardev->ref_cnt      = 1;
118 
119   /* insert it into the list */
120   list_add_tail (registered_chardev_classes, chardev);
121 
122   return SOS_OK;
123 }
124 
125 
126 sos_ret_t sos_chardev_unregister_class (sos_ui32_t device_class)
127 {
128   struct sos_chardev_class *chardev;
129 
130   /* Make sure this device class is already registered */
131   chardev = lookup_chardev_class(device_class);
132   if (NULL == chardev)
133     return -SOS_ENODEV;
134 
135   /* Make sure no files are already opened for it */
136   if (chardev->ref_cnt != 1)
137     return -SOS_EBUSY;
138 
139   /* remove it from the list */
140   list_delete (registered_chardev_classes, chardev);
141   return sos_kfree((sos_vaddr_t)chardev);
142 }
143 
144 
145 
146 sos_ret_t sos_chardev_helper_ref_new_fsnode(struct sos_fs_node * this)
147 {
148   this->new_opened_file   = chardev_helper_new_opened_file;
149   this->close_opened_file = chardev_helper_close_opened_file;
150 
151   return SOS_OK;
152 }
153 
154 
155 sos_ret_t sos_chardev_helper_release_fsnode(struct sos_fs_node * this)
156 {
157   return SOS_OK;
158 }
159 
160 
161 /** Callback called each time an FS-node is opened by a user process:
162     create a new sos_chardev_opened_file object associated to the
163     correct major number, and call the device driver's open method */
164 static sos_ret_t
165 chardev_helper_new_opened_file(struct sos_fs_node * this,
166                                const struct sos_process * owner,
167                                sos_ui32_t open_flags,
168                                struct sos_fs_opened_file ** result_of)
169 {
170   sos_ret_t retval;
171   struct sos_chardev_opened_file *chardev_of;
172 
173   /* Lookup the character device description structure */
174   struct sos_chardev_class * chardev
175     = lookup_chardev_class(this->dev_id.device_class);
176   if (NULL == chardev)
177     return -SOS_ENODEV;
178 
179   /* Alloocate the new "open file" description structure */
180   chardev_of = (struct sos_chardev_opened_file*)
181     sos_kmalloc(sizeof(struct sos_chardev_opened_file), 0);
182   if (NULL == chardev_of)
183     return -SOS_ENOMEM;
184 
185   memset(chardev_of, 0x0, sizeof(struct sos_chardev_opened_file));
186   chardev_of->class = chardev;
187   *result_of = & chardev_of->super;
188 
189   /* Increase the reference coount for that node */
190   SOS_ASSERT_FATAL(chardev->ref_cnt >= 1);
191   chardev->ref_cnt ++;
192 
193   /* Initialize the read/write/seek callbacks */
194   (*result_of)->owner       = owner;
195   (*result_of)->open_flags  = open_flags;
196   (*result_of)->ops_file    = & chardev_ops_opened_file;
197   (*result_of)->ops_chardev = & chardev_ops_opened_chardev;
198 
199   /* Call the open callback */
200   retval = chardev->ops->open(this, & chardev_of->super, chardev->custom_data);
201   if (SOS_OK != retval)
202     {
203       sos_kfree((sos_vaddr_t) chardev_of);
204       chardev->ref_cnt --;
205       *result_of = NULL;
206       return retval;
207     }
208 
209   /* Specify the duplicate method */
210   (*result_of)->duplicate = duplicate_opened_chardev;
211 
212   return retval;
213 }
214 
215 
216 /** Callback called each time an opened file is closed. Un-allocate
217     the associated sos_chardev_opened_file object */
218 static sos_ret_t
219 chardev_helper_close_opened_file(struct sos_fs_node * this,
220                                  struct sos_fs_opened_file * of)
221 {
222   sos_ret_t retval;
223   struct sos_chardev_opened_file *chardev_of
224     = ((struct sos_chardev_opened_file*)of);
225 
226   struct sos_chardev_class * chardev = chardev_of->class;
227   SOS_ASSERT_FATAL(NULL != chardev);
228 
229   /* Free the new "open file" description structure */
230   if (NULL != chardev->ops->close)
231     retval = chardev->ops->close(& chardev_of->super, chardev->custom_data);
232   else
233     retval = SOS_OK;
234   if (SOS_OK != retval)
235     return retval;
236 
237   /* Decrease the reference coount for that node */
238   SOS_ASSERT_FATAL(chardev->ref_cnt > 1);
239   chardev->ref_cnt --;
240   
241   sos_kfree((sos_vaddr_t) chardev_of);
242   return retval;
243 }
244 
245 
246 /**
247  * Callback called each time a process is "forked": create a new
248  * sos_chardev_opened_file for the new process.
249  *
250  * @note Almost identical to the open callback.
251  */
252 static sos_ret_t
253 duplicate_opened_chardev(struct sos_fs_opened_file *this,
254                          const struct sos_process  * for_owner,
255                          struct sos_fs_opened_file **result)
256 {
257   sos_ret_t retval;
258   struct sos_chardev_opened_file *chardev_of
259     = ((struct sos_chardev_opened_file*)this);
260   struct sos_chardev_opened_file *new_chardev_of;
261   struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(this->direntry);
262 
263   *result = NULL;
264 
265   /* Lookup the character device description structure */
266   struct sos_chardev_class * chardev = chardev_of->class;
267   SOS_ASSERT_FATAL(NULL != chardev);
268 
269   /* Allocate a new duplicate copy of the original opened file */
270   new_chardev_of = (struct sos_chardev_opened_file*)
271     sos_kmalloc(sizeof(struct sos_chardev_opened_file), 0);
272   if (NULL == new_chardev_of)
273     return -SOS_ENOMEM;
274 
275   memcpy(new_chardev_of, chardev_of, sizeof(*new_chardev_of));
276   new_chardev_of->super.owner    = for_owner;
277   new_chardev_of->super.direntry = NULL; /* Reset the direntry
278                                             for the new opened file */
279   SOS_ASSERT_FATAL(chardev->ref_cnt > 1);
280   chardev->ref_cnt ++;
281 
282   retval = chardev->ops->open(fsnode,
283                               & new_chardev_of->super, chardev->custom_data);
284   if (SOS_OK != retval)
285     {
286       sos_kfree((sos_vaddr_t) new_chardev_of);
287       chardev->ref_cnt --;
288       return retval;
289     }
290 
291   /* Make sure the required methods are overloaded */
292   SOS_ASSERT_FATAL(NULL != new_chardev_of->super.ops_file);
293   SOS_ASSERT_FATAL(NULL != new_chardev_of->super.ops_file->seek);
294   SOS_ASSERT_FATAL(NULL != new_chardev_of->super.ops_file->read);
295 
296   *result = & new_chardev_of->super;
297   return retval;
298 }
299 
300 
301 /*
302  * FS generic character device wrapper functions
303  */
304 
305 /**
306  * Callback called to change the position in the opened file: call the
307  * seek method of the device driver.
308  */
309 static sos_ret_t chardev_wrap_seek(struct sos_fs_opened_file *this,
310                                    sos_lsoffset_t offset,
311                                    sos_seek_whence_t whence,
312                                    /* out */ sos_lsoffset_t * result_position)
313 {
314   sos_ret_t retval = -SOS_ENOSYS;
315   struct sos_chardev_opened_file *chardev_of
316     = ((struct sos_chardev_opened_file*)this);
317 
318   struct sos_chardev_class * chardev = chardev_of->class;
319   SOS_ASSERT_FATAL(NULL != chardev);
320   SOS_ASSERT_FATAL(NULL != chardev->ops);
321 
322   if (NULL != chardev->ops->seek)
323     retval = chardev->ops->seek(this, offset, whence, result_position);
324   
325   return retval;
326 }
327 
328 
329 /**
330  * Callback called to read the contents of the opened file: call the
331  * read method of the device driver.
332  */
333 static sos_ret_t chardev_wrap_read(struct sos_fs_opened_file *this,
334                                    sos_uaddr_t dest_buf,
335                                    sos_size_t * /* in/out */len)
336 {
337   sos_ret_t retval = -SOS_ENOSYS;
338   struct sos_chardev_opened_file *chardev_of
339     = ((struct sos_chardev_opened_file*)this);
340 
341   struct sos_chardev_class * chardev = chardev_of->class;
342   SOS_ASSERT_FATAL(NULL != chardev);
343   SOS_ASSERT_FATAL(NULL != chardev->ops);
344 
345   if (NULL != chardev->ops->read)
346     retval = chardev->ops->read(this, dest_buf, len);
347   
348   return retval;
349 }
350 
351 
352 /**
353  * Callback called to write bytes to the opened file: call the write
354  * method of the device driver.
355  */
356 static sos_ret_t chardev_wrap_write(struct sos_fs_opened_file *this,
357                                     sos_uaddr_t src_buf,
358                                     sos_size_t * /* in/out */len)
359 {
360   sos_ret_t retval = -SOS_ENOSYS;
361   struct sos_chardev_opened_file *chardev_of
362     = ((struct sos_chardev_opened_file*)this);
363 
364   struct sos_chardev_class * chardev = chardev_of->class;
365   SOS_ASSERT_FATAL(NULL != chardev);
366   SOS_ASSERT_FATAL(NULL != chardev->ops);
367 
368   if (NULL != chardev->ops->write)
369     retval = chardev->ops->write(this, src_buf, len);
370   
371   return retval;
372 }
373 
374 
375 /**
376  * Callback called to map the contents of the opened file: call the
377  * map method of the device driver.
378  */
379 static sos_ret_t chardev_wrap_mmap(struct sos_fs_opened_file *this,
380                                    sos_uaddr_t *uaddr, sos_size_t size,
381                                    sos_ui32_t access_rights,
382                                    sos_ui32_t flags,
383                                    sos_luoffset_t offset)
384 {
385   sos_ret_t retval = -SOS_ENOSYS;
386   struct sos_chardev_opened_file *chardev_of
387     = ((struct sos_chardev_opened_file*)this);
388 
389   struct sos_chardev_class * chardev = chardev_of->class;
390   SOS_ASSERT_FATAL(NULL != chardev);
391   SOS_ASSERT_FATAL(NULL != chardev->ops);
392 
393   if (NULL != chardev->ops->mmap)
394     retval = chardev->ops->mmap(this, uaddr, size,
395                                 access_rights, flags, offset);
396   
397   return retval;
398 }
399 
400 
401 /**
402  * Callback called to change the state of the opened file: call the
403  * fcntl method of the device driver.
404  */
405 static sos_ret_t chardev_wrap_fcntl(struct sos_fs_opened_file *this,
406                                     int req_id,
407                                     sos_ui32_t req_arg)
408 {
409   sos_ret_t retval = -SOS_ENOSYS;
410   struct sos_chardev_opened_file *chardev_of
411     = ((struct sos_chardev_opened_file*)this);
412 
413   struct sos_chardev_class * chardev = chardev_of->class;
414   SOS_ASSERT_FATAL(NULL != chardev);
415   SOS_ASSERT_FATAL(NULL != chardev->ops);
416 
417   if (NULL != chardev->ops->fcntl)
418     retval = chardev->ops->fcntl(this, req_id, req_arg);
419   
420   return retval;
421 }
422 
423 
424 /**
425  * Callback called to control the underlying device: call the ioctl
426  * method of the device driver.
427  */
428 static sos_ret_t chardev_wrap_ioctl(struct sos_fs_opened_file *this,
429                                     int req_id,
430                                     sos_ui32_t req_arg)
431 {
432   sos_ret_t retval = -SOS_ENOSYS;
433   struct sos_chardev_opened_file *chardev_of
434     = ((struct sos_chardev_opened_file*)this);
435 
436   struct sos_chardev_class * chardev = chardev_of->class;
437   SOS_ASSERT_FATAL(NULL != chardev);
438   SOS_ASSERT_FATAL(NULL != chardev->ops);
439 
440   if (NULL != chardev->ops->ioctl)
441     retval = chardev->ops->ioctl(this, req_id, req_arg);
442   
443   return retval;
444 }
445 
446 
447 /**
448  * Gather the callbacks for a "character device" opened file
449  */
450 static struct sos_fs_ops_opened_file chardev_ops_opened_file
451   = (struct sos_fs_ops_opened_file) {
452     .seek  = chardev_wrap_seek,
453     .read  = chardev_wrap_read,
454     .write = chardev_wrap_write,
455     .mmap  = chardev_wrap_mmap,
456     .fcntl = chardev_wrap_fcntl
457   };
458 
459 
460 static struct sos_fs_ops_opened_chardev chardev_ops_opened_chardev
461   = (struct sos_fs_ops_opened_chardev) {
462     .ioctl = chardev_wrap_ioctl
463   };

source navigation ] diff markup ] identifier search ] general search ]