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, Thomas Petazzoni
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_sync(struct sos_fs_node * this);
066 static sos_ret_t
067 chardev_helper_new_opened_file(struct sos_fs_node * this,
068                                const struct sos_process * owner,
069                                sos_ui32_t open_flags,
070                                struct sos_fs_opened_file ** result_of);
071 static sos_ret_t
072 chardev_helper_close_opened_file(struct sos_fs_node * this,
073                                  struct sos_fs_opened_file * of);
074 static sos_ret_t
075 duplicate_opened_chardev(struct sos_fs_opened_file *this,
076                          const struct sos_process  * for_owner,
077                          struct sos_fs_opened_file **result);
078 
079 
080 /**
081  * Return the device descriptionn structure for the corresponding
082  * device class, or NULL when none found.
083  */
084 static struct sos_chardev_class * lookup_chardev_class(sos_ui32_t device_class)
085 {
086   struct sos_chardev_class *chardev;
087   int nb;
088 
089   list_foreach (registered_chardev_classes, chardev, nb)
090     {
091       if (chardev->device_class == device_class)
092         return chardev;
093     }
094 
095   return NULL;
096 }
097 
098 
099 sos_ret_t sos_chardev_register_class (sos_ui32_t device_class,
100                                       struct sos_chardev_ops *ops,
101                                       void * chardev_class_custom_data)
102 {
103   struct sos_chardev_class *chardev;
104 
105   /* Make sure this device class is not already registered */
106   chardev = lookup_chardev_class(device_class);
107   if (NULL != chardev)
108     return -SOS_EBUSY;
109 
110   /* Allocate and initialize a new device description */
111   chardev = (struct sos_chardev_class *)
112     sos_kmalloc(sizeof(struct sos_chardev_class), 0);
113   if (chardev == NULL)
114     return -SOS_ENOMEM;
115 
116   chardev->device_class = device_class;
117   chardev->custom_data  = chardev_class_custom_data;
118   chardev->ops          = ops;
119   chardev->ref_cnt      = 1;
120 
121   /* insert it into the list */
122   list_add_tail (registered_chardev_classes, chardev);
123 
124   return SOS_OK;
125 }
126 
127 
128 sos_ret_t sos_chardev_unregister_class (sos_ui32_t device_class)
129 {
130   struct sos_chardev_class *chardev;
131 
132   /* Make sure this device class is already registered */
133   chardev = lookup_chardev_class(device_class);
134   if (NULL == chardev)
135     return -SOS_ENODEV;
136 
137   /* Make sure no files are already opened for it */
138   if (chardev->ref_cnt != 1)
139     return -SOS_EBUSY;
140 
141   /* remove it from the list */
142   list_delete (registered_chardev_classes, chardev);
143   return sos_kfree((sos_vaddr_t)chardev);
144 }
145 
146 
147 
148 sos_ret_t sos_chardev_helper_ref_new_fsnode(struct sos_fs_node * this)
149 {
150   this->sync              = chardev_helper_sync;
151   this->new_opened_file   = chardev_helper_new_opened_file;
152   this->close_opened_file = chardev_helper_close_opened_file;
153 
154   return SOS_OK;
155 }
156 
157 
158 sos_ret_t sos_chardev_helper_release_fsnode(struct sos_fs_node * this)
159 {
160   return SOS_OK;
161 }
162 
163 
164 /** No synchronization to anything needed for a character device */
165 static sos_ret_t
166 chardev_helper_sync(struct sos_fs_node * this)
167 {
168   return SOS_OK;
169 }
170 
171 
172 /** Callback called each time an FS-node is opened by a user process:
173     create a new sos_chardev_opened_file object associated to the
174     correct major number, and call the device driver's open method */
175 static sos_ret_t
176 chardev_helper_new_opened_file(struct sos_fs_node * this,
177                                const struct sos_process * owner,
178                                sos_ui32_t open_flags,
179                                struct sos_fs_opened_file ** result_of)
180 {
181   sos_ret_t retval;
182   struct sos_chardev_opened_file *chardev_of;
183 
184   /* Lookup the character device description structure */
185   struct sos_chardev_class * chardev
186     = lookup_chardev_class(this->dev_id.device_class);
187   if (NULL == chardev)
188     return -SOS_ENODEV;
189 
190   /* Alloocate the new "open file" description structure */
191   chardev_of = (struct sos_chardev_opened_file*)
192     sos_kmalloc(sizeof(struct sos_chardev_opened_file), 0);
193   if (NULL == chardev_of)
194     return -SOS_ENOMEM;
195 
196   memset(chardev_of, 0x0, sizeof(struct sos_chardev_opened_file));
197   chardev_of->class = chardev;
198   *result_of = & chardev_of->super;
199 
200   /* Increase the reference coount for that node */
201   SOS_ASSERT_FATAL(chardev->ref_cnt >= 1);
202   chardev->ref_cnt ++;
203 
204   /* Initialize the read/write/seek callbacks */
205   (*result_of)->owner       = owner;
206   (*result_of)->open_flags  = open_flags;
207   (*result_of)->ops_file    = & chardev_ops_opened_file;
208   (*result_of)->ops_chardev = & chardev_ops_opened_chardev;
209 
210   /* Call the open callback */
211   retval = chardev->ops->open(this, & chardev_of->super, chardev->custom_data);
212   if (SOS_OK != retval)
213     {
214       sos_kfree((sos_vaddr_t) chardev_of);
215       chardev->ref_cnt --;
216       *result_of = NULL;
217       return retval;
218     }
219 
220   /* Specify the duplicate method */
221   (*result_of)->duplicate = duplicate_opened_chardev;
222 
223   return retval;
224 }
225 
226 
227 /** Callback called each time an opened file is closed. Un-allocate
228     the associated sos_chardev_opened_file object */
229 static sos_ret_t
230 chardev_helper_close_opened_file(struct sos_fs_node * this,
231                                  struct sos_fs_opened_file * of)
232 {
233   sos_ret_t retval;
234   struct sos_chardev_opened_file *chardev_of
235     = ((struct sos_chardev_opened_file*)of);
236 
237   struct sos_chardev_class * chardev = chardev_of->class;
238   SOS_ASSERT_FATAL(NULL != chardev);
239 
240   /* Free the new "open file" description structure */
241   if (NULL != chardev->ops->close)
242     retval = chardev->ops->close(& chardev_of->super, chardev->custom_data);
243   else
244     retval = SOS_OK;
245   if (SOS_OK != retval)
246     return retval;
247 
248   /* Decrease the reference coount for that node */
249   SOS_ASSERT_FATAL(chardev->ref_cnt > 1);
250   chardev->ref_cnt --;
251   
252   sos_kfree((sos_vaddr_t) chardev_of);
253   return retval;
254 }
255 
256 
257 /**
258  * Callback called each time a process is "forked": create a new
259  * sos_chardev_opened_file for the new process.
260  *
261  * @note Almost identical to the open callback.
262  */
263 static sos_ret_t
264 duplicate_opened_chardev(struct sos_fs_opened_file *this,
265                          const struct sos_process  * for_owner,
266                          struct sos_fs_opened_file **result)
267 {
268   sos_ret_t retval;
269   struct sos_chardev_opened_file *chardev_of
270     = ((struct sos_chardev_opened_file*)this);
271   struct sos_chardev_opened_file *new_chardev_of;
272   struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(this->direntry);
273 
274   *result = NULL;
275 
276   /* Lookup the character device description structure */
277   struct sos_chardev_class * chardev = chardev_of->class;
278   SOS_ASSERT_FATAL(NULL != chardev);
279 
280   /* Allocate a new duplicate copy of the original opened file */
281   new_chardev_of = (struct sos_chardev_opened_file*)
282     sos_kmalloc(sizeof(struct sos_chardev_opened_file), 0);
283   if (NULL == new_chardev_of)
284     return -SOS_ENOMEM;
285 
286   memcpy(new_chardev_of, chardev_of, sizeof(*new_chardev_of));
287   new_chardev_of->super.owner    = for_owner;
288   new_chardev_of->super.direntry = NULL; /* Reset the direntry
289                                             for the new opened file */
290   SOS_ASSERT_FATAL(chardev->ref_cnt > 1);
291   chardev->ref_cnt ++;
292 
293   retval = chardev->ops->open(fsnode,
294                               & new_chardev_of->super, chardev->custom_data);
295   if (SOS_OK != retval)
296     {
297       sos_kfree((sos_vaddr_t) new_chardev_of);
298       chardev->ref_cnt --;
299       return retval;
300     }
301 
302   /* Make sure the required methods are overloaded */
303   SOS_ASSERT_FATAL(NULL != new_chardev_of->super.ops_file);
304   SOS_ASSERT_FATAL(NULL != new_chardev_of->super.ops_file->seek);
305   SOS_ASSERT_FATAL(NULL != new_chardev_of->super.ops_file->read);
306 
307   *result = & new_chardev_of->super;
308   return retval;
309 }
310 
311 
312 /*
313  * FS generic character device wrapper functions
314  */
315 
316 /**
317  * Callback called to change the position in the opened file: call the
318  * seek method of the device driver.
319  */
320 static sos_ret_t chardev_wrap_seek(struct sos_fs_opened_file *this,
321                                    sos_lsoffset_t offset,
322                                    sos_seek_whence_t whence,
323                                    /* out */ sos_lsoffset_t * result_position)
324 {
325   sos_ret_t retval = -SOS_ENOSYS;
326   struct sos_chardev_opened_file *chardev_of
327     = ((struct sos_chardev_opened_file*)this);
328 
329   struct sos_chardev_class * chardev = chardev_of->class;
330   SOS_ASSERT_FATAL(NULL != chardev);
331   SOS_ASSERT_FATAL(NULL != chardev->ops);
332 
333   if (NULL != chardev->ops->seek)
334     retval = chardev->ops->seek(this, offset, whence, result_position);
335   
336   return retval;
337 }
338 
339 
340 /**
341  * Callback called to read the contents of the opened file: call the
342  * read method of the device driver.
343  */
344 static sos_ret_t chardev_wrap_read(struct sos_fs_opened_file *this,
345                                    sos_uaddr_t dest_buf,
346                                    sos_size_t * /* in/out */len)
347 {
348   sos_ret_t retval = -SOS_ENOSYS;
349   struct sos_chardev_opened_file *chardev_of
350     = ((struct sos_chardev_opened_file*)this);
351 
352   struct sos_chardev_class * chardev = chardev_of->class;
353   SOS_ASSERT_FATAL(NULL != chardev);
354   SOS_ASSERT_FATAL(NULL != chardev->ops);
355 
356   if (NULL != chardev->ops->read)
357     retval = chardev->ops->read(this, dest_buf, len);
358   
359   return retval;
360 }
361 
362 
363 /**
364  * Callback called to write bytes to the opened file: call the write
365  * method of the device driver.
366  */
367 static sos_ret_t chardev_wrap_write(struct sos_fs_opened_file *this,
368                                     sos_uaddr_t src_buf,
369                                     sos_size_t * /* in/out */len)
370 {
371   sos_ret_t retval = -SOS_ENOSYS;
372   struct sos_chardev_opened_file *chardev_of
373     = ((struct sos_chardev_opened_file*)this);
374 
375   struct sos_chardev_class * chardev = chardev_of->class;
376   SOS_ASSERT_FATAL(NULL != chardev);
377   SOS_ASSERT_FATAL(NULL != chardev->ops);
378 
379   if (NULL != chardev->ops->write)
380     retval = chardev->ops->write(this, src_buf, len);
381   
382   return retval;
383 }
384 
385 
386 /**
387  * Callback called to map the contents of the opened file: call the
388  * map method of the device driver.
389  */
390 static sos_ret_t chardev_wrap_mmap(struct sos_fs_opened_file *this,
391                                    sos_uaddr_t *uaddr, sos_size_t size,
392                                    sos_ui32_t access_rights,
393                                    sos_ui32_t flags,
394                                    sos_luoffset_t offset)
395 {
396   sos_ret_t retval = -SOS_ENOSYS;
397   struct sos_chardev_opened_file *chardev_of
398     = ((struct sos_chardev_opened_file*)this);
399 
400   struct sos_chardev_class * chardev = chardev_of->class;
401   SOS_ASSERT_FATAL(NULL != chardev);
402   SOS_ASSERT_FATAL(NULL != chardev->ops);
403 
404   if (NULL != chardev->ops->mmap)
405     retval = chardev->ops->mmap(this, uaddr, size,
406                                 access_rights, flags, offset);
407   
408   return retval;
409 }
410 
411 
412 /**
413  * Callback called to change the state of the opened file: call the
414  * fcntl method of the device driver.
415  */
416 static sos_ret_t chardev_wrap_fcntl(struct sos_fs_opened_file *this,
417                                     int req_id,
418                                     sos_ui32_t req_arg)
419 {
420   sos_ret_t retval = -SOS_ENOSYS;
421   struct sos_chardev_opened_file *chardev_of
422     = ((struct sos_chardev_opened_file*)this);
423 
424   struct sos_chardev_class * chardev = chardev_of->class;
425   SOS_ASSERT_FATAL(NULL != chardev);
426   SOS_ASSERT_FATAL(NULL != chardev->ops);
427 
428   if (NULL != chardev->ops->fcntl)
429     retval = chardev->ops->fcntl(this, req_id, req_arg);
430   
431   return retval;
432 }
433 
434 
435 /**
436  * Callback called to control the underlying device: call the ioctl
437  * method of the device driver.
438  */
439 static sos_ret_t chardev_wrap_ioctl(struct sos_fs_opened_file *this,
440                                     int req_id,
441                                     sos_ui32_t req_arg)
442 {
443   sos_ret_t retval = -SOS_ENOSYS;
444   struct sos_chardev_opened_file *chardev_of
445     = ((struct sos_chardev_opened_file*)this);
446 
447   struct sos_chardev_class * chardev = chardev_of->class;
448   SOS_ASSERT_FATAL(NULL != chardev);
449   SOS_ASSERT_FATAL(NULL != chardev->ops);
450 
451   if (NULL != chardev->ops->ioctl)
452     retval = chardev->ops->ioctl(this, req_id, req_arg);
453   
454   return retval;
455 }
456 
457 
458 /**
459  * Gather the callbacks for a "character device" opened file
460  */
461 static struct sos_fs_ops_opened_file chardev_ops_opened_file
462   = (struct sos_fs_ops_opened_file) {
463     .seek  = chardev_wrap_seek,
464     .read  = chardev_wrap_read,
465     .write = chardev_wrap_write,
466     .mmap  = chardev_wrap_mmap,
467     .fcntl = chardev_wrap_fcntl
468   };
469 
470 
471 static struct sos_fs_ops_opened_chardev chardev_ops_opened_chardev
472   = (struct sos_fs_ops_opened_chardev) {
473     .ioctl = chardev_wrap_ioctl
474   };

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