Subversion Repositories HelenOS

Rev

Rev 4345 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (c) 2007 Josef Cejka
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * - Redistributions of source code must retain the above copyright
  10.  *   notice, this list of conditions and the following disclaimer.
  11.  * - Redistributions in binary form must reproduce the above copyright
  12.  *   notice, this list of conditions and the following disclaimer in the
  13.  *   documentation and/or other materials provided with the distribution.
  14.  * - The name of the author may not be used to endorse or promote products
  15.  *   derived from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28.  
  29. /**
  30.  * @defgroup devmap Device mapper.
  31.  * @brief HelenOS device mapper.
  32.  * @{
  33.  */
  34.  
  35. /** @file
  36.  */
  37.  
  38. #include <ipc/services.h>
  39. #include <ipc/ns.h>
  40. #include <async.h>
  41. #include <stdio.h>
  42. #include <errno.h>
  43. #include <bool.h>
  44. #include <futex.h>
  45. #include <stdlib.h>
  46. #include <string.h>
  47. #include <ipc/devmap.h>
  48.  
  49. #define NAME  "devmap"
  50.  
  51. /** Pending lookup structure. */
  52. typedef struct {
  53.     link_t link;
  54.     char *name;              /**< Device name */
  55.     ipc_callid_t callid;     /**< Call ID waiting for the lookup */
  56. } pending_req_t;
  57.  
  58. LIST_INITIALIZE(devices_list);
  59. LIST_INITIALIZE(drivers_list);
  60. LIST_INITIALIZE(pending_req);
  61.  
  62. /* Locking order:
  63.  *  drivers_list_futex
  64.  *  devices_list_futex
  65.  *  (devmap_driver_t *)->devices_futex
  66.  *  create_handle_futex
  67.  **/
  68.  
  69. static atomic_t devices_list_futex = FUTEX_INITIALIZER;
  70. static atomic_t drivers_list_futex = FUTEX_INITIALIZER;
  71. static atomic_t create_handle_futex = FUTEX_INITIALIZER;
  72.  
  73. static int devmap_create_handle(void)
  74. {
  75.     static int last_handle = 0;
  76.     int handle;
  77.    
  78.     /* TODO: allow reusing old handles after their unregistration
  79.      * and implement some version of LRU algorithm
  80.      */
  81.    
  82.     /* FIXME: overflow */
  83.     futex_down(&create_handle_futex);
  84.    
  85.     last_handle += 1;
  86.     handle = last_handle;
  87.    
  88.     futex_up(&create_handle_futex);
  89.    
  90.     return handle;
  91. }
  92.  
  93.  
  94. /** Initialize device mapper.
  95.  *
  96.  *
  97.  */
  98. static int devmap_init()
  99. {
  100.     /* TODO: */
  101.    
  102.     return EOK;
  103. }
  104.  
  105. /** Find device with given name.
  106.  *
  107.  */
  108. static devmap_device_t *devmap_device_find_name(const char *name)
  109. {
  110.     link_t *item = devices_list.next;
  111.     devmap_device_t *device = NULL;
  112.    
  113.     while (item != &devices_list) {
  114.         device = list_get_instance(item, devmap_device_t, devices);
  115.         if (0 == str_cmp(device->name, name))
  116.             break;
  117.         item = item->next;
  118.     }
  119.    
  120.     if (item == &devices_list)
  121.         return NULL;
  122.    
  123.     device = list_get_instance(item, devmap_device_t, devices);
  124.     return device;
  125. }
  126.  
  127. /** Find device with given handle.
  128.  *
  129.  * @todo: use hash table
  130.  *
  131.  */
  132. static devmap_device_t *devmap_device_find_handle(int handle)
  133. {
  134.     futex_down(&devices_list_futex);
  135.    
  136.     link_t *item = (&devices_list)->next;
  137.     devmap_device_t *device = NULL;
  138.    
  139.     while (item != &devices_list) {
  140.         device = list_get_instance(item, devmap_device_t, devices);
  141.         if (device->handle == handle)
  142.             break;
  143.         item = item->next;
  144.     }
  145.    
  146.     if (item == &devices_list) {
  147.         futex_up(&devices_list_futex);
  148.         return NULL;
  149.     }
  150.    
  151.     device = list_get_instance(item, devmap_device_t, devices);
  152.    
  153.     futex_up(&devices_list_futex);
  154.    
  155.     return device;
  156. }
  157.  
  158. /**
  159.  *
  160.  * Unregister device and free it. It's assumed that driver's device list is
  161.  * already locked.
  162.  *
  163.  */
  164. static int devmap_device_unregister_core(devmap_device_t *device)
  165. {
  166.     list_remove(&(device->devices));
  167.     list_remove(&(device->driver_devices));
  168.    
  169.     free(device->name);
  170.     free(device);
  171.    
  172.     return EOK;
  173. }
  174.  
  175. /**
  176.  *
  177.  * Read info about new driver and add it into linked list of registered
  178.  * drivers.
  179.  *
  180.  */
  181. static void devmap_driver_register(devmap_driver_t **odriver)
  182. {
  183.     *odriver = NULL;
  184.    
  185.     ipc_call_t icall;
  186.     ipc_callid_t iid = async_get_call(&icall);
  187.    
  188.     if (IPC_GET_METHOD(icall) != DEVMAP_DRIVER_REGISTER) {
  189.         ipc_answer_0(iid, EREFUSED);
  190.         return;
  191.     }
  192.    
  193.     devmap_driver_t *driver = (devmap_driver_t *) malloc(sizeof(devmap_driver_t));
  194.    
  195.     if (driver == NULL) {
  196.         ipc_answer_0(iid, ENOMEM);
  197.         return;
  198.     }
  199.    
  200.     /*
  201.      * Get driver name
  202.      */
  203.     ipc_callid_t callid;
  204.     size_t name_size;
  205.     if (!ipc_data_write_receive(&callid, &name_size)) {
  206.         free(driver);
  207.         ipc_answer_0(callid, EREFUSED);
  208.         ipc_answer_0(iid, EREFUSED);
  209.         return;
  210.     }
  211.    
  212.     if (name_size > DEVMAP_NAME_MAXLEN) {
  213.         free(driver);
  214.         ipc_answer_0(callid, EINVAL);
  215.         ipc_answer_0(iid, EREFUSED);
  216.         return;
  217.     }
  218.    
  219.     /*
  220.      * Allocate buffer for device name.
  221.      */
  222.     driver->name = (char *) malloc(name_size + 1);
  223.     if (driver->name == NULL) {
  224.         free(driver);
  225.         ipc_answer_0(callid, ENOMEM);
  226.         ipc_answer_0(iid, EREFUSED);
  227.         return;
  228.     }
  229.    
  230.     /*
  231.      * Send confirmation to sender and get data into buffer.
  232.      */
  233.     if (EOK != ipc_data_write_finalize(callid, driver->name, name_size)) {
  234.         free(driver->name);
  235.         free(driver);
  236.         ipc_answer_0(iid, EREFUSED);
  237.         return;
  238.     }
  239.    
  240.     driver->name[name_size] = 0;
  241.    
  242.     /* Initialize futex for list of devices owned by this driver */
  243.     futex_initialize(&(driver->devices_futex), 1);
  244.    
  245.     /*
  246.      * Initialize list of asociated devices
  247.      */
  248.     list_initialize(&(driver->devices));
  249.    
  250.     /*
  251.      * Create connection to the driver
  252.      */
  253.     ipc_call_t call;
  254.     callid = async_get_call(&call);
  255.    
  256.     if (IPC_M_CONNECT_TO_ME != IPC_GET_METHOD(call)) {
  257.         ipc_answer_0(callid, ENOTSUP);
  258.        
  259.         free(driver->name);
  260.         free(driver);
  261.         ipc_answer_0(iid, ENOTSUP);
  262.         return;
  263.     }
  264.    
  265.     driver->phone = IPC_GET_ARG5(call);
  266.    
  267.     ipc_answer_0(callid, EOK);
  268.    
  269.     list_initialize(&(driver->drivers));
  270.    
  271.     futex_down(&drivers_list_futex);
  272.    
  273.     /* TODO:
  274.      * check that no driver with name equal to driver->name is registered
  275.      */
  276.    
  277.     /*
  278.      * Insert new driver into list of registered drivers
  279.      */
  280.     list_append(&(driver->drivers), &drivers_list);
  281.     futex_up(&drivers_list_futex);
  282.    
  283.     ipc_answer_0(iid, EOK);
  284.    
  285.     *odriver = driver;
  286. }
  287.  
  288. /**
  289.  * Unregister device driver, unregister all its devices and free driver
  290.  * structure.
  291.  *
  292.  */
  293. static int devmap_driver_unregister(devmap_driver_t *driver)
  294. {
  295.     if (driver == NULL)
  296.         return EEXISTS;
  297.    
  298.     futex_down(&drivers_list_futex);
  299.    
  300.     ipc_hangup(driver->phone);
  301.    
  302.     /* remove it from list of drivers */
  303.     list_remove(&(driver->drivers));
  304.    
  305.     /* unregister all its devices */
  306.    
  307.     futex_down(&devices_list_futex);
  308.     futex_down(&(driver->devices_futex));
  309.    
  310.     while (!list_empty(&(driver->devices))) {
  311.         devmap_device_t *device = list_get_instance(driver->devices.next,
  312.             devmap_device_t, driver_devices);
  313.         devmap_device_unregister_core(device);
  314.     }
  315.    
  316.     futex_up(&(driver->devices_futex));
  317.     futex_up(&devices_list_futex);
  318.     futex_up(&drivers_list_futex);
  319.    
  320.     /* free name and driver */
  321.     if (NULL != driver->name)
  322.         free(driver->name);
  323.    
  324.     free(driver);
  325.    
  326.     return EOK;
  327. }
  328.  
  329.  
  330. /** Process pending lookup requests */
  331. static void process_pending_lookup()
  332. {
  333.     link_t *cur;
  334.    
  335. loop:
  336.     for (cur = pending_req.next; cur != &pending_req; cur = cur->next) {
  337.         pending_req_t *pr = list_get_instance(cur, pending_req_t, link);
  338.        
  339.         const devmap_device_t *dev = devmap_device_find_name(pr->name);
  340.         if (!dev)
  341.             continue;
  342.        
  343.         ipc_answer_1(pr->callid, EOK, dev->handle);
  344.        
  345.         free(pr->name);
  346.         list_remove(cur);
  347.         free(pr);
  348.         goto loop;
  349.     }
  350. }
  351.  
  352.  
  353. /** Register instance of device
  354.  *
  355.  */
  356. static void devmap_device_register(ipc_callid_t iid, ipc_call_t *icall,
  357.     devmap_driver_t *driver)
  358. {
  359.     if (driver == NULL) {
  360.         ipc_answer_0(iid, EREFUSED);
  361.         return;
  362.     }
  363.    
  364.     /* Create new device entry */
  365.     devmap_device_t *device = (devmap_device_t *) malloc(sizeof(devmap_device_t));
  366.     if (device == NULL) {
  367.         ipc_answer_0(iid, ENOMEM);
  368.         return;
  369.     }
  370.    
  371.     /* Get device name */
  372.     ipc_callid_t callid;
  373.     size_t size;
  374.     if (!ipc_data_write_receive(&callid, &size)) {
  375.         free(device);
  376.         ipc_answer_0(iid, EREFUSED);
  377.         return;
  378.     }
  379.    
  380.     if (size > DEVMAP_NAME_MAXLEN) {
  381.         free(device);
  382.         ipc_answer_0(callid, EINVAL);
  383.         ipc_answer_0(iid, EREFUSED);
  384.         return;
  385.     }
  386.    
  387.     /* +1 for terminating \0 */
  388.     device->name = (char *) malloc(size + 1);
  389.    
  390.     if (device->name == NULL) {
  391.         free(device);
  392.         ipc_answer_0(callid, ENOMEM);
  393.         ipc_answer_0(iid, EREFUSED);
  394.         return;
  395.     }
  396.    
  397.     ipc_data_write_finalize(callid, device->name, size);
  398.     device->name[size] = 0;
  399.    
  400.     list_initialize(&(device->devices));
  401.     list_initialize(&(device->driver_devices));
  402.    
  403.     futex_down(&devices_list_futex);
  404.    
  405.     /* Check that device with such name is not already registered */
  406.     if (NULL != devmap_device_find_name(device->name)) {
  407.         printf(NAME ": Device '%s' already registered\n", device->name);
  408.         futex_up(&devices_list_futex); 
  409.         free(device->name);
  410.         free(device);
  411.         ipc_answer_0(iid, EEXISTS);
  412.         return;
  413.     }
  414.    
  415.     /* Get unique device handle */
  416.     device->handle = devmap_create_handle();
  417.    
  418.     device->driver = driver;
  419.    
  420.     /* Insert device into list of all devices  */
  421.     list_append(&device->devices, &devices_list);
  422.    
  423.     /* Insert device into list of devices that belog to one driver */
  424.     futex_down(&device->driver->devices_futex);
  425.    
  426.     list_append(&device->driver_devices, &device->driver->devices);
  427.    
  428.     futex_up(&device->driver->devices_futex);
  429.     futex_up(&devices_list_futex);
  430.    
  431.     ipc_answer_1(iid, EOK, device->handle);
  432.    
  433.     process_pending_lookup();
  434. }
  435.  
  436. /**
  437.  *
  438.  */
  439. static int devmap_device_unregister(ipc_callid_t iid, ipc_call_t *icall,
  440.     devmap_driver_t *driver)
  441. {
  442.     /* TODO */
  443.     return EOK;
  444. }
  445.  
  446. /** Connect client to the device.
  447.  *
  448.  * Find device driver owning requested device and forward
  449.  * the message to it.
  450.  *
  451.  */
  452. static void devmap_forward(ipc_callid_t callid, ipc_call_t *call)
  453. {
  454.     /*
  455.      * Get handle from request
  456.      */
  457.     int handle = IPC_GET_ARG2(*call);
  458.     devmap_device_t *dev = devmap_device_find_handle(handle);
  459.    
  460.     if (NULL == dev) {
  461.         ipc_answer_0(callid, ENOENT);
  462.         return;
  463.     }
  464.    
  465.     ipc_forward_fast(callid, dev->driver->phone, (ipcarg_t)(dev->handle),
  466.         IPC_GET_ARG3(*call), 0, IPC_FF_NONE);
  467. }
  468.  
  469. /** Find handle for device instance identified by name.
  470.  *
  471.  * In answer will be send EOK and device handle in arg1 or a error
  472.  * code from errno.h.
  473.  *
  474.  */
  475. static void devmap_get_handle(ipc_callid_t iid, ipc_call_t *icall)
  476. {
  477.     /*
  478.      * Wait for incoming message with device name (but do not
  479.      * read the name itself until the buffer is allocated).
  480.      */
  481.     ipc_callid_t callid;
  482.     size_t size;
  483.     if (!ipc_data_write_receive(&callid, &size)) {
  484.         ipc_answer_0(callid, EREFUSED);
  485.         ipc_answer_0(iid, EREFUSED);
  486.         return;
  487.     }
  488.    
  489.     if ((size < 1) || (size > DEVMAP_NAME_MAXLEN)) {
  490.         ipc_answer_0(callid, EINVAL);
  491.         ipc_answer_0(iid, EREFUSED);
  492.         return;
  493.     }
  494.    
  495.     /*
  496.      * Allocate buffer for device name.
  497.      */
  498.     char *name = (char *) malloc(size);
  499.     if (name == NULL) {
  500.         ipc_answer_0(callid, ENOMEM);
  501.         ipc_answer_0(iid, EREFUSED);
  502.         return;
  503.     }
  504.    
  505.     /*
  506.      * Send confirmation to sender and get data into buffer.
  507.      */
  508.     ipcarg_t retval = ipc_data_write_finalize(callid, name, size);
  509.     if (retval != EOK) {
  510.         ipc_answer_0(iid, EREFUSED);
  511.         free(name);
  512.         return;
  513.     }
  514.     name[size] = '\0';
  515.    
  516.     /*
  517.      * Find device name in linked list of known devices.
  518.      */
  519.     const devmap_device_t *dev = devmap_device_find_name(name);
  520.    
  521.     /*
  522.      * Device was not found.
  523.      */
  524.     if (dev == NULL) {
  525.         if (IPC_GET_ARG1(*icall) & IPC_FLAG_BLOCKING) {
  526.             /* Blocking lookup, add to pending list */
  527.             pending_req_t *pr = (pending_req_t *) malloc(sizeof(pending_req_t));
  528.             if (!pr) {
  529.                 ipc_answer_0(iid, ENOMEM);
  530.                 free(name);
  531.                 return;
  532.             }
  533.            
  534.             pr->name = name;
  535.             pr->callid = iid;
  536.             list_append(&pr->link, &pending_req);
  537.             return;
  538.         }
  539.        
  540.         ipc_answer_0(iid, ENOENT);
  541.         free(name);
  542.         return;
  543.     }
  544.    
  545.     ipc_answer_1(iid, EOK, dev->handle);
  546.     free(name);
  547. }
  548.  
  549. /** Find name of device identified by id and send it to caller.
  550.  *
  551.  */
  552. static void devmap_get_name(ipc_callid_t iid, ipc_call_t *icall)
  553. {
  554.     const devmap_device_t *device = devmap_device_find_handle(IPC_GET_ARG1(*icall));
  555.    
  556.     /*
  557.      * Device not found.
  558.      */
  559.     if (device == NULL) {
  560.         ipc_answer_0(iid, ENOENT);
  561.         return;
  562.     }
  563.    
  564.     ipc_answer_0(iid, EOK);
  565.    
  566.     size_t name_size = str_size(device->name);
  567.    
  568.     /* FIXME:
  569.      * We have no channel from DEVMAP to client, therefore
  570.      * sending must be initiated by client.
  571.      *
  572.      * int rc = ipc_data_write_send(phone, device->name, name_size);
  573.      * if (rc != EOK) {
  574.      *     async_wait_for(req, NULL);
  575.      *     return rc;
  576.      * }
  577.      */
  578.    
  579.     /* TODO: send name in response */
  580. }
  581.  
  582. /** Handle connection with device driver.
  583.  *
  584.  */
  585. static void devmap_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
  586. {
  587.     /* Accept connection */
  588.     ipc_answer_0(iid, EOK);
  589.    
  590.     devmap_driver_t *driver = NULL;
  591.     devmap_driver_register(&driver);
  592.    
  593.     if (NULL == driver)
  594.         return;
  595.    
  596.     bool cont = true;
  597.     while (cont) {
  598.         ipc_call_t call;
  599.         ipc_callid_t callid = async_get_call(&call);
  600.        
  601.         switch (IPC_GET_METHOD(call)) {
  602.         case IPC_M_PHONE_HUNGUP:
  603.             cont = false;
  604.             /* Exit thread */
  605.             continue;
  606.         case DEVMAP_DRIVER_UNREGISTER:
  607.             if (NULL == driver)
  608.                 ipc_answer_0(callid, ENOENT);
  609.             else
  610.                 ipc_answer_0(callid, EOK);
  611.             break;
  612.         case DEVMAP_DEVICE_REGISTER:
  613.             /* Register one instance of device */
  614.             devmap_device_register(callid, &call, driver);
  615.             break;
  616.         case DEVMAP_DEVICE_UNREGISTER:
  617.             /* Remove instance of device identified by handler */
  618.             devmap_device_unregister(callid, &call, driver);
  619.             break;
  620.         case DEVMAP_DEVICE_GET_HANDLE:
  621.             devmap_get_handle(callid, &call);
  622.             break;
  623.         case DEVMAP_DEVICE_GET_NAME:
  624.             devmap_get_handle(callid, &call);
  625.             break;
  626.         default:
  627.             if (!(callid & IPC_CALLID_NOTIFICATION))
  628.                 ipc_answer_0(callid, ENOENT);
  629.         }
  630.     }
  631.    
  632.     if (NULL != driver) {
  633.         /*
  634.          * Unregister the device driver and all its devices.
  635.          */
  636.         devmap_driver_unregister(driver);
  637.         driver = NULL;
  638.     }
  639. }
  640.  
  641. /** Handle connection with device client.
  642.  *
  643.  */
  644. static void devmap_connection_client(ipc_callid_t iid, ipc_call_t *icall)
  645. {
  646.     /* Accept connection */
  647.     ipc_answer_0(iid, EOK);
  648.    
  649.     bool cont = true;
  650.     while (cont) {
  651.         ipc_call_t call;
  652.         ipc_callid_t callid = async_get_call(&call);
  653.        
  654.         switch (IPC_GET_METHOD(call)) {
  655.         case IPC_M_PHONE_HUNGUP:
  656.             cont = false;
  657.             /* Exit thread */
  658.             continue;
  659.         case DEVMAP_DEVICE_GET_HANDLE:
  660.             devmap_get_handle(callid, &call);
  661.             break;
  662.         case DEVMAP_DEVICE_GET_NAME:
  663.             /* TODO */
  664.             devmap_get_name(callid, &call);
  665.             break;
  666.         default:
  667.             if (!(callid & IPC_CALLID_NOTIFICATION))
  668.                 ipc_answer_0(callid, ENOENT);
  669.         }
  670.     }
  671. }
  672.  
  673. /** Function for handling connections to devmap
  674.  *
  675.  */
  676. static void devmap_connection(ipc_callid_t iid, ipc_call_t *icall)
  677. {
  678.     /* Select interface */
  679.     switch ((ipcarg_t) (IPC_GET_ARG1(*icall))) {
  680.     case DEVMAP_DRIVER:
  681.         devmap_connection_driver(iid, icall);
  682.         break;
  683.     case DEVMAP_CLIENT:
  684.         devmap_connection_client(iid, icall);
  685.         break;
  686.     case DEVMAP_CONNECT_TO_DEVICE:
  687.         /* Connect client to selected device */
  688.         devmap_forward(iid, icall);
  689.         break;
  690.     default:
  691.         /* No such interface */
  692.         ipc_answer_0(iid, ENOENT);
  693.     }
  694. }
  695.  
  696. /**
  697.  *
  698.  */
  699. int main(int argc, char *argv[])
  700. {
  701.     printf(NAME ": HelenOS Device Mapper\n");
  702.    
  703.     if (devmap_init() != 0) {
  704.         printf(NAME ": Error while initializing service\n");
  705.         return -1;
  706.     }
  707.    
  708.     /* Set a handler of incomming connections */
  709.     async_set_client_connection(devmap_connection);
  710.    
  711.     /* Register device mapper at naming service */
  712.     ipcarg_t phonead;
  713.     if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAP, 0, 0, &phonead) != 0)
  714.         return -1;
  715.    
  716.     printf(NAME ": Accepting connections\n");
  717.     async_manager();
  718.    
  719.     /* Never reached */
  720.     return 0;
  721. }
  722.  
  723. /**
  724.  * @}
  725.  */
  726.