Subversion Repositories HelenOS

Rev

Rev 3080 | 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.  
  52. LIST_INITIALIZE(devices_list);
  53. LIST_INITIALIZE(drivers_list);
  54.  
  55. /* order of locking:
  56.  * drivers_list_futex
  57.  * devices_list_futex
  58.  * (devmap_driver_t *)->devices_futex
  59.  * create_handle_futex
  60.  **/
  61.  
  62. static atomic_t devices_list_futex = FUTEX_INITIALIZER;
  63. static atomic_t drivers_list_futex = FUTEX_INITIALIZER;
  64. static atomic_t create_handle_futex = FUTEX_INITIALIZER;
  65.  
  66.  
  67. static int devmap_create_handle(void)
  68. {
  69.     static int last_handle = 0;
  70.     int handle;
  71.  
  72.     /* TODO: allow reusing old handles after their unregistration
  73.         and implement some version of LRU algorithm */
  74.     /* FIXME: overflow */
  75.     futex_down(&create_handle_futex);  
  76.  
  77.     last_handle += 1;
  78.     handle = last_handle;
  79.  
  80.     futex_up(&create_handle_futex);
  81.  
  82.     return handle;
  83. }
  84.  
  85.  
  86. /** Initialize device mapper.
  87.  *
  88.  *
  89.  */
  90. static int devmap_init()
  91. {
  92.     /* TODO: */
  93.  
  94.     return EOK;
  95. }
  96.  
  97. /** Find device with given name.
  98.  *
  99.  */
  100. static devmap_device_t *devmap_device_find_name(const char *name)
  101. {
  102.     link_t *item;
  103.     devmap_device_t *device = NULL;
  104.  
  105.     item = devices_list.next;
  106.  
  107.     while (item != &devices_list) {
  108.  
  109.         device = list_get_instance(item, devmap_device_t, devices);
  110.         if (0 == strcmp(device->name, name)) {
  111.             break;
  112.         }
  113.         item = item->next;
  114.     }
  115.  
  116.     if (item == &devices_list)
  117.         return NULL;
  118.  
  119.     device = list_get_instance(item, devmap_device_t, devices);
  120.     return device;
  121. }
  122.  
  123. /** Find device with given handle.
  124.  * @todo: use hash table
  125.  */
  126. static devmap_device_t *devmap_device_find_handle(int handle)
  127. {
  128.     link_t *item;
  129.     devmap_device_t *device = NULL;
  130.    
  131.     futex_down(&devices_list_futex);
  132.  
  133.     item = (&devices_list)->next;
  134.  
  135.     while (item != &devices_list) {
  136.  
  137.         device = list_get_instance(item, devmap_device_t, devices);
  138.         if (device->handle == handle) {
  139.             break;
  140.         }
  141.         item = item->next;
  142.     }
  143.  
  144.     if (item == &devices_list) {
  145.         futex_up(&devices_list_futex);
  146.         return NULL;
  147.     }
  148.  
  149.     device = list_get_instance(item, devmap_device_t, devices);
  150.    
  151.     futex_up(&devices_list_futex);
  152.  
  153.     return device;
  154. }
  155.  
  156. /**
  157.  * Unregister device and free it. It's assumed that driver's device list is
  158.  * already locked.
  159.  */
  160. static int devmap_device_unregister_core(devmap_device_t *device)
  161. {
  162.  
  163.     list_remove(&(device->devices));
  164.     list_remove(&(device->driver_devices));
  165.  
  166.     free(device->name);
  167.     free(device);
  168.  
  169.  
  170.     return EOK;
  171. }
  172.  
  173. /**
  174.  * Read info about new driver and add it into linked list of registered
  175.  * drivers.
  176.  */
  177. static void devmap_driver_register(devmap_driver_t **odriver)
  178. {
  179.     size_t name_size;
  180.     ipc_callid_t callid;
  181.     ipc_call_t call;
  182.     devmap_driver_t *driver;
  183.     ipc_callid_t iid;
  184.     ipc_call_t icall;
  185.  
  186.     *odriver = NULL;
  187.    
  188.     iid = async_get_call(&icall);
  189.  
  190.     if (IPC_GET_METHOD(icall) != DEVMAP_DRIVER_REGISTER) {
  191.         ipc_answer_0(iid, EREFUSED);
  192.         return;
  193.     }
  194.  
  195.     if (NULL ==
  196.         (driver = (devmap_driver_t *)malloc(sizeof(devmap_driver_t)))) {
  197.         ipc_answer_0(iid, ENOMEM);
  198.         return;
  199.     }
  200.  
  201.     /*
  202.      * Get driver name
  203.      */
  204.     if (!ipc_data_write_receive(&callid, &name_size)) {
  205.         free(driver);
  206.         ipc_answer_0(callid, EREFUSED);
  207.         ipc_answer_0(iid, EREFUSED);
  208.         return;
  209.     }
  210.  
  211.     if (name_size > DEVMAP_NAME_MAXLEN) {
  212.         free(driver);
  213.         ipc_answer_0(callid, EINVAL);
  214.         ipc_answer_0(iid, EREFUSED);
  215.         return;
  216.     }
  217.  
  218.     /*
  219.      * Allocate buffer for device name.
  220.      */
  221.     if (NULL == (driver->name = (char *)malloc(name_size + 1))) {
  222.         free(driver);
  223.         ipc_answer_0(callid, ENOMEM);
  224.         ipc_answer_0(iid, EREFUSED);
  225.         return;
  226.     }  
  227.  
  228.     /*
  229.      * Send confirmation to sender and get data into buffer.
  230.      */
  231.     if (EOK != ipc_data_write_finalize(callid, driver->name, name_size)) {
  232.         free(driver->name);
  233.         free(driver);
  234.         ipc_answer_0(iid, EREFUSED);
  235.         return;
  236.     }
  237.  
  238.     driver->name[name_size] = 0;
  239.  
  240.     /* Initialize futex for list of devices owned by this driver */
  241.     futex_initialize(&(driver->devices_futex), 1);
  242.  
  243.     /*
  244.      * Initialize list of asociated devices
  245.      */
  246.     list_initialize(&(driver->devices));
  247.  
  248.     /*
  249.      * Create connection to the driver
  250.      */
  251.     callid = async_get_call(&call);
  252.  
  253.     if (IPC_M_CONNECT_TO_ME != IPC_GET_METHOD(call)) {
  254.         ipc_answer_0(callid, ENOTSUP);
  255.        
  256.         free(driver->name);
  257.         free(driver);
  258.         ipc_answer_0(iid, ENOTSUP);
  259.         return;
  260.     }
  261.  
  262.     driver->phone = IPC_GET_ARG5(call);
  263.    
  264.     ipc_answer_0(callid, EOK);
  265.    
  266.     list_initialize(&(driver->drivers));
  267.  
  268.     futex_down(&drivers_list_futex);   
  269.    
  270.     /* TODO:
  271.      * check that no driver with name equal to driver->name is registered
  272.      */
  273.  
  274.     /*
  275.      * Insert new driver into list of registered drivers
  276.      */
  277.     list_append(&(driver->drivers), &drivers_list);
  278.     futex_up(&drivers_list_futex); 
  279.    
  280.     ipc_answer_0(iid, EOK);
  281.  
  282.     *odriver = driver;
  283. }
  284.  
  285. /** Unregister device driver, unregister all its devices and free driver
  286.  * structure.
  287.  */
  288. static int devmap_driver_unregister(devmap_driver_t *driver)
  289. {
  290.     devmap_device_t *device;
  291.  
  292.     if (NULL == driver)
  293.         return EEXISTS;
  294.    
  295.     futex_down(&drivers_list_futex);   
  296.  
  297.     ipc_hangup(driver->phone);
  298.    
  299.     /* remove it from list of drivers */
  300.     list_remove(&(driver->drivers));
  301.  
  302.     /* unregister all its devices */
  303.    
  304.     futex_down(&devices_list_futex);   
  305.     futex_down(&(driver->devices_futex));
  306.  
  307.     while (!list_empty(&(driver->devices))) {
  308.         device = list_get_instance(driver->devices.next,
  309.             devmap_device_t, driver_devices);
  310.         devmap_device_unregister_core(device);
  311.     }
  312.    
  313.     futex_up(&(driver->devices_futex));
  314.     futex_up(&devices_list_futex); 
  315.     futex_up(&drivers_list_futex); 
  316.  
  317.     /* free name and driver */
  318.     if (NULL != driver->name) {
  319.         free(driver->name);
  320.     }
  321.  
  322.     free(driver);
  323.  
  324.     return EOK;
  325. }
  326.  
  327.  
  328. /** Register instance of device
  329.  *
  330.  */
  331. static void devmap_device_register(ipc_callid_t iid, ipc_call_t *icall,
  332.     devmap_driver_t *driver)
  333. {
  334.     ipc_callid_t callid;
  335.     size_t size;
  336.     devmap_device_t *device;
  337.  
  338.     if (NULL == driver) {
  339.         ipc_answer_0(iid, EREFUSED);
  340.         return;
  341.     }
  342.    
  343.     /* Create new device entry */
  344.     if (NULL ==
  345.         (device = (devmap_device_t *) malloc(sizeof(devmap_device_t)))) {
  346.         ipc_answer_0(iid, ENOMEM);
  347.         return;
  348.     }
  349.    
  350.     /* Get device name */
  351.     if (!ipc_data_write_receive(&callid, &size)) {
  352.         free(device);
  353.         ipc_answer_0(iid, EREFUSED);
  354.         return;
  355.     }
  356.  
  357.     if (size > DEVMAP_NAME_MAXLEN) {
  358.         free(device);
  359.         ipc_answer_0(callid, EINVAL);
  360.         ipc_answer_0(iid, EREFUSED);
  361.         return;
  362.     }
  363.    
  364.     /* +1 for terminating \0 */
  365.     device->name = (char *) malloc(size + 1);
  366.  
  367.     if (NULL == device->name) {
  368.         free(device);
  369.         ipc_answer_0(callid, ENOMEM);
  370.         ipc_answer_0(iid, EREFUSED);
  371.         return;
  372.     }
  373.    
  374.     ipc_data_write_finalize(callid, device->name, size);
  375.     device->name[size] = 0;
  376.  
  377.     list_initialize(&(device->devices));
  378.     list_initialize(&(device->driver_devices));
  379.  
  380.     futex_down(&devices_list_futex);   
  381.  
  382.     /* Check that device with such name is not already registered */
  383.     if (NULL != devmap_device_find_name(device->name)) {
  384.         printf(NAME ": Device '%s' already registered\n", device->name);
  385.         futex_up(&devices_list_futex); 
  386.         free(device->name);
  387.         free(device);
  388.         ipc_answer_0(iid, EEXISTS);
  389.         return;
  390.     }
  391.  
  392.     /* Get unique device handle */
  393.     device->handle = devmap_create_handle();
  394.  
  395.     device->driver = driver;
  396.    
  397.     /* Insert device into list of all devices  */
  398.     list_append(&device->devices, &devices_list);
  399.  
  400.     /* Insert device into list of devices that belog to one driver */
  401.     futex_down(&device->driver->devices_futex);
  402.    
  403.     list_append(&device->driver_devices, &device->driver->devices);
  404.    
  405.     futex_up(&device->driver->devices_futex);  
  406.     futex_up(&devices_list_futex); 
  407.  
  408.     ipc_answer_1(iid, EOK, device->handle);
  409. }
  410.  
  411. /**
  412.  *
  413.  */
  414. static int devmap_device_unregister(ipc_callid_t iid, ipc_call_t *icall,
  415.     devmap_driver_t *driver)
  416. {
  417.     /* TODO */
  418.  
  419.     return EOK;
  420. }
  421.  
  422. /** Connect client to the device.
  423.  * Find device driver owning requested device and forward
  424.  * the message to it.
  425.  */
  426. static void devmap_forward(ipc_callid_t callid, ipc_call_t *call)
  427. {
  428.     devmap_device_t *dev;
  429.     int handle;
  430.  
  431.     /*
  432.      * Get handle from request
  433.      */
  434.     handle = IPC_GET_ARG2(*call);
  435.     dev = devmap_device_find_handle(handle);
  436.  
  437.     if (NULL == dev) {
  438.         ipc_answer_0(callid, ENOENT);
  439.         return;
  440.     }
  441.  
  442.     ipc_forward_fast(callid, dev->driver->phone, (ipcarg_t)(dev->handle),
  443.         IPC_GET_ARG3(*call), 0, IPC_FF_NONE);
  444. }
  445.  
  446. /** Find handle for device instance identified by name.
  447.  * In answer will be send EOK and device handle in arg1 or a error
  448.  * code from errno.h.
  449.  */
  450. static void devmap_get_handle(ipc_callid_t iid, ipc_call_t *icall)
  451. {
  452.     char *name = NULL;
  453.     size_t name_size;
  454.     const devmap_device_t *dev;
  455.     ipc_callid_t callid;
  456.     ipcarg_t retval;
  457.    
  458.     /*
  459.      * Wait for incoming message with device name (but do not
  460.      * read the name itself until the buffer is allocated).
  461.      */
  462.     if (!ipc_data_write_receive(&callid, &name_size)) {
  463.         ipc_answer_0(callid, EREFUSED);
  464.         ipc_answer_0(iid, EREFUSED);
  465.         return;
  466.     }
  467.  
  468.     if (name_size > DEVMAP_NAME_MAXLEN) {
  469.         ipc_answer_0(callid, EINVAL);
  470.         ipc_answer_0(iid, EREFUSED);
  471.         return;
  472.     }
  473.  
  474.     /*
  475.      * Allocate buffer for device name.
  476.      */
  477.     if (NULL == (name = (char *)malloc(name_size))) {
  478.         ipc_answer_0(callid, ENOMEM);
  479.         ipc_answer_0(iid, EREFUSED);
  480.         return;
  481.     }  
  482.  
  483.     /*
  484.      * Send confirmation to sender and get data into buffer.
  485.      */
  486.     if (EOK != (retval = ipc_data_write_finalize(callid, name,
  487.         name_size))) {
  488.         ipc_answer_0(iid, EREFUSED);
  489.         return;
  490.     }
  491.  
  492.     /*
  493.      * Find device name in linked list of known devices.
  494.      */
  495.     dev = devmap_device_find_name(name);
  496.  
  497.     /*
  498.      * Device was not found.
  499.      */
  500.     if (NULL == dev) {
  501.         ipc_answer_0(iid, ENOENT);
  502.         return;
  503.     }
  504.  
  505.     ipc_answer_1(iid, EOK, dev->handle);
  506. }
  507.  
  508. /** Find name of device identified by id and send it to caller.
  509.  *
  510.  */
  511. static void devmap_get_name(ipc_callid_t iid, ipc_call_t *icall)
  512. {
  513.     const devmap_device_t *device;
  514.     size_t name_size;
  515.  
  516.     device = devmap_device_find_handle(IPC_GET_ARG1(*icall));
  517.  
  518.     /*
  519.      * Device not found.
  520.      */
  521.     if (NULL == device) {
  522.         ipc_answer_0(iid, ENOENT);
  523.         return;
  524.     }  
  525.  
  526.     ipc_answer_0(iid, EOK);
  527.  
  528.     name_size = strlen(device->name);
  529.  
  530.  
  531. /*  FIXME:
  532.     we have no channel from DEVMAP to client ->
  533.     sending must be initiated by client
  534.  
  535.     int rc = ipc_data_write_send(phone, device->name, name_size);
  536.     if (rc != EOK) {
  537.         async_wait_for(req, NULL);
  538.         return rc;
  539.     }
  540. */ 
  541.     /* TODO: send name in response */
  542. }
  543.  
  544. /** Handle connection with device driver.
  545.  *
  546.  */
  547. static void devmap_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
  548. {
  549.     ipc_callid_t callid;
  550.     ipc_call_t call;
  551.     bool cont = true;
  552.     devmap_driver_t *driver = NULL;
  553.  
  554.     ipc_answer_0(iid, EOK);
  555.  
  556.     devmap_driver_register(&driver);
  557.  
  558.     if (NULL == driver)
  559.         return;
  560.    
  561.     while (cont) {
  562.         callid = async_get_call(&call);
  563.  
  564.         switch (IPC_GET_METHOD(call)) {
  565.         case IPC_M_PHONE_HUNGUP:
  566.             cont = false;
  567.             continue; /* Exit thread */
  568.         case DEVMAP_DRIVER_UNREGISTER:
  569.             if (NULL == driver) {
  570.                 ipc_answer_0(callid, ENOENT);
  571.             } else {
  572.                 ipc_answer_0(callid, EOK);
  573.             }
  574.             break;
  575.         case DEVMAP_DEVICE_REGISTER:
  576.             /* Register one instance of device */
  577.             devmap_device_register(callid, &call, driver);
  578.             break;
  579.         case DEVMAP_DEVICE_UNREGISTER:
  580.             /* Remove instance of device identified by handler */
  581.             devmap_device_unregister(callid, &call, driver);
  582.             break;
  583.         case DEVMAP_DEVICE_GET_HANDLE:
  584.             devmap_get_handle(callid, &call);
  585.             break;
  586.         case DEVMAP_DEVICE_GET_NAME:
  587.             devmap_get_handle(callid, &call);
  588.             break;
  589.         default:
  590.             if (!(callid & IPC_CALLID_NOTIFICATION)) {
  591.                 ipc_answer_0(callid, ENOENT);
  592.             }
  593.         }
  594.     }
  595.    
  596.     if (NULL != driver) {
  597.         /*
  598.          * Unregister the device driver and all its devices.
  599.          */
  600.         devmap_driver_unregister(driver);
  601.         driver = NULL;
  602.     }
  603.    
  604. }
  605.  
  606. /** Handle connection with device client.
  607.  *
  608.  */
  609. static void devmap_connection_client(ipc_callid_t iid, ipc_call_t *icall)
  610. {
  611.     ipc_callid_t callid;
  612.     ipc_call_t call;
  613.     bool cont = true;
  614.  
  615.     ipc_answer_0(iid, EOK); /* Accept connection */
  616.  
  617.     while (cont) {
  618.         callid = async_get_call(&call);
  619.  
  620.         switch (IPC_GET_METHOD(call)) {
  621.         case IPC_M_PHONE_HUNGUP:
  622.             cont = false;
  623.             continue; /* Exit thread */
  624.  
  625.         case DEVMAP_DEVICE_GET_HANDLE:
  626.             devmap_get_handle(callid, &call);
  627.  
  628.             break;
  629.         case DEVMAP_DEVICE_GET_NAME:
  630.             /* TODO */
  631.             devmap_get_name(callid, &call);
  632.             break;
  633.         default:
  634.             if (!(callid & IPC_CALLID_NOTIFICATION)) {
  635.                 ipc_answer_0(callid, ENOENT);
  636.             }
  637.         }
  638.     }
  639. }
  640.  
  641. /** Function for handling connections to devmap
  642.  *
  643.  */
  644. static void devmap_connection(ipc_callid_t iid, ipc_call_t *icall)
  645. {
  646.     /* Select interface */
  647.     switch ((ipcarg_t) (IPC_GET_ARG1(*icall))) {
  648.     case DEVMAP_DRIVER:
  649.         devmap_connection_driver(iid, icall);
  650.         break;
  651.     case DEVMAP_CLIENT:
  652.         devmap_connection_client(iid, icall);
  653.         break;
  654.     case DEVMAP_CONNECT_TO_DEVICE:
  655.         /* Connect client to selected device */
  656.         devmap_forward(iid, icall);
  657.         break;
  658.     default:
  659.         ipc_answer_0(iid, ENOENT); /* No such interface */
  660.     }
  661.  
  662.     /* Cleanup */
  663. }
  664.  
  665. /**
  666.  *
  667.  */
  668. int main(int argc, char *argv[])
  669. {
  670.     printf(NAME ": HelenOS Device Mapper\n");
  671.    
  672.     ipcarg_t phonead;
  673.  
  674.     if (devmap_init() != 0) {
  675.         printf(NAME ": Error while initializing service\n");
  676.         return -1;
  677.     }
  678.    
  679.     /* Set a handler of incomming connections */
  680.     async_set_client_connection(devmap_connection);
  681.  
  682.     /* Register device mapper at naming service */
  683.     if (ipc_connect_to_me(PHONE_NS, SERVICE_DEVMAP, 0, 0, &phonead) != 0)
  684.         return -1;
  685.    
  686.     printf(NAME ": Accepting connections\n");
  687.     async_manager();
  688.     /* Never reached */
  689.     return 0;
  690. }
  691.  
  692. /**
  693.  * @}
  694.  */
  695.