Subversion Repositories HelenOS

Rev

Rev 4667 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #include <unistd.h>
  2. #include <ddi.h>
  3. #include <libarch/ddi.h>
  4. #include <ipc/ipc.h>
  5. #include <ipc/services.h>
  6. #include <ipc/serial.h>
  7. #include <ipc/devmap.h>
  8. #include <ipc/ns.h>
  9. #include <bool.h>
  10. #include <errno.h>
  11. #include <async.h>
  12. #include <stdio.h>
  13. #include <futex.h>
  14. #include <assert.h>
  15. #include <adt/list.h>
  16. #include <string.h>
  17.  
  18. #include "isa.h"
  19. #include "serial.h"
  20.  
  21. #define NAME "serial"
  22.  
  23. #define REG_COUNT 7
  24.  
  25. #define MAX_NAME_LEN 8
  26.  
  27. struct serial_dev {
  28.     link_t link;
  29.     char name[MAX_NAME_LEN];
  30.     int handle;
  31.     bool client_connected;
  32.     ioport8_t *port;
  33.     void *phys_addr;
  34.     bridge_to_isa_t *parent;
  35. };
  36.  
  37. typedef struct serial_dev serial_dev_t;
  38.  
  39. static void * serial_phys_addresses[] = { (void *)0x3F8, (void *)0x2F8 };
  40. static int serial_phys_addr_cnt = sizeof(serial_phys_addresses)/sizeof(void *);
  41. // number, which should be assigned to a newly found serial device - increment first, then assign to the device
  42. static int serial_idx = 0;
  43.  
  44. static int serial_driver_phone = -1;
  45.  
  46. LIST_INITIALIZE(serial_devices_list);
  47.  
  48. static atomic_t serial_futex = FUTEX_INITIALIZER;
  49.  
  50.  
  51. static void serial_init_port(ioport8_t *port);
  52. static void serial_write_8(ioport8_t *port, uint8_t c);
  53. static bool is_transmit_empty(ioport8_t *port);
  54. static uint8_t serial_read_8(ioport8_t *port);
  55. static bool serial_received(ioport8_t *port);
  56. static void serial_probe(bridge_to_isa_t *parent);
  57. static ioport8_t * serial_probe_port(void *phys_addr);
  58. static int serial_device_register(int driver_phone, char *name, int *handle);
  59. static int serial_driver_register(char *name);
  60. static void serial_putchar(serial_dev_t *dev, ipc_callid_t rid, ipc_call_t *request);
  61. static void serial_getchar(serial_dev_t *dev, ipc_callid_t rid);
  62. static void serial_client_conn(ipc_callid_t iid, ipc_call_t *icall);
  63.  
  64. static isa_drv_ops_t serial_isa_ops = {
  65.     .probe = serial_probe  
  66. };
  67.  
  68. static isa_drv_t serial_isa_drv = {
  69.     .name = NAME,
  70.     .ops = &serial_isa_ops 
  71. };
  72.  
  73. int serial_init()
  74. {
  75.     // register driver by devmapper
  76.     serial_driver_phone = serial_driver_register(NAME);
  77.     if (serial_driver_phone < 0) {
  78.         printf(NAME ": Unable to register driver\n");
  79.         return false;
  80.     }
  81.        
  82.     // register this driver by generic isa bus driver  
  83.     isa_register_driver(&serial_isa_drv);
  84.     return 1;
  85. }
  86.  
  87. static bool serial_received(ioport8_t *port)
  88. {
  89.    return (pio_read_8(port + 5) & 1) != 0;
  90. }
  91.  
  92. static uint8_t serial_read_8(ioport8_t *port)
  93. {
  94.     while (!serial_received(port))
  95.         ;
  96.  
  97.     uint8_t c = pio_read_8(port);
  98.     return c;
  99. }
  100.  
  101. static bool is_transmit_empty(ioport8_t *port)
  102. {
  103.    return (pio_read_8(port + 5) & 0x20) != 0;
  104. }
  105.  
  106. static void serial_write_8(ioport8_t *port, uint8_t c)
  107. {
  108.     while (!is_transmit_empty(port))
  109.         ;
  110.    
  111.     pio_write_8(port, c);
  112. }
  113.  
  114. static void serial_init_port(ioport8_t *port)
  115. {  
  116.     pio_write_8(port + 1, 0x00);    // Disable all interrupts
  117.     pio_write_8(port + 3, 0x80);    // Enable DLAB (set baud rate divisor)
  118.     pio_write_8(port + 0, 0x60);    // Set divisor to 96 (lo byte) 1200 baud
  119.     pio_write_8(port + 1, 0x00);    //                   (hi byte)
  120.     pio_write_8(port + 3, 0x07);    // 8 bits, no parity, two stop bits
  121.     pio_write_8(port + 2, 0xC7);    // Enable FIFO, clear them, with 14-byte threshold
  122.     pio_write_8(port + 4, 0x0B);    // IRQs enabled, RTS/DSR set
  123. }
  124.  
  125. static serial_dev_t * serial_alloc_dev()
  126. {
  127.     serial_dev_t *dev = (serial_dev_t *)malloc(sizeof(serial_dev_t));
  128.     memset(dev, 0, sizeof(serial_dev_t));
  129.     return dev;
  130. }
  131.  
  132. static void serial_init_dev(serial_dev_t *dev, bridge_to_isa_t *parent, int idx)
  133. {
  134.     assert(dev != NULL);
  135.    
  136.     memset(dev, 0, sizeof(serial_dev_t));
  137.     dev->parent = parent;
  138.     dev->phys_addr = dev->parent->ops->absolutize(serial_phys_addresses[idx % serial_phys_addr_cnt]);
  139.     snprintf(dev->name, MAX_NAME_LEN, "com%d", idx + 1);
  140.     dev->client_connected = false; 
  141. }
  142.  
  143. static bool serial_probe_dev(serial_dev_t *dev)
  144. {
  145.     assert(dev != NULL);
  146.    
  147.     printf(NAME " driver: probing %s \n", dev->name);
  148.     return (dev->port = (ioport8_t *)serial_probe_port(dev->phys_addr)) != NULL;   
  149. }
  150.  
  151. static void serial_delete_dev(serial_dev_t *dev) {
  152.     free(dev);
  153. }
  154.  
  155. static void serial_probe(bridge_to_isa_t *parent)
  156. {
  157.     printf(NAME " driver: probe()\n"); 
  158.    
  159.     serial_dev_t *dev = serial_alloc_dev();
  160.    
  161.     int i;
  162.     for (i = 0; i < serial_phys_addr_cnt; i++) {       
  163.         serial_init_dev(dev, parent, serial_idx);
  164.         if (serial_probe_dev(dev)) {
  165.             printf(NAME " driver: initializing %s.\n", dev->name);
  166.             serial_init_port(dev->port);
  167.             if (EOK != serial_device_register(serial_driver_phone, dev->name, &(dev->handle))) {
  168.                 printf(NAME ": Unable to register device %s\n", dev->name);
  169.             }  
  170.             list_append(&(dev->link), &serial_devices_list);
  171.             dev = serial_alloc_dev();
  172.         } else {
  173.             printf(NAME " driver: %s is not present \n", dev->name);
  174.         }
  175.         serial_idx++;
  176.     }
  177.    
  178.     serial_delete_dev(dev);
  179. }
  180.  
  181. // returns virtual address of the serial port, if the serial port is present at this physical address, NULL otherwise  
  182. static ioport8_t * serial_probe_port(void *phys_addr)
  183. {
  184.     ioport8_t *port_addr = NULL;
  185.    
  186.     if (pio_enable(phys_addr, REG_COUNT, (void **)(&port_addr))) {  // Gain control over port's registers.
  187.         printf(NAME ": Error - cannot gain the port %lx.\n", phys_addr);
  188.         return NULL;
  189.     }
  190.    
  191.     uint8_t olddata;
  192.    
  193.     olddata = pio_read_8(port_addr + 4);
  194.     pio_write_8(port_addr + 4, 0x10);
  195.     if ((pio_read_8(port_addr + 6) & 0xf0)) {
  196.         return NULL;
  197.     }
  198.    
  199.     pio_write_8(port_addr + 4, 0x1f);
  200.     if ((pio_read_8(port_addr + 6) & 0xf0) != 0xf0) {
  201.         return 0;
  202.     }
  203.     pio_write_8(port_addr + 4, olddata);
  204.    
  205.     return port_addr;
  206. }
  207.  
  208.  
  209. static void serial_putchar(serial_dev_t *dev, ipc_callid_t rid, ipc_call_t *request)
  210. {
  211.     int c = IPC_GET_ARG1(*request);
  212.     serial_write_8(dev->port, (uint8_t)c); 
  213.     ipc_answer_0(rid, EOK);
  214. }
  215.  
  216. static void serial_getchar(serial_dev_t *dev, ipc_callid_t rid)
  217. {
  218.     uint8_t c = serial_read_8(dev->port);  
  219.     ipc_answer_1(rid, EOK, c);     
  220. }
  221.  
  222. static serial_dev_t * serial_handle_to_dev(int handle) {
  223.    
  224.     futex_down(&serial_futex); 
  225.    
  226.     link_t *item = serial_devices_list.next;
  227.     serial_dev_t *dev = NULL;
  228.    
  229.     while (item != &serial_devices_list) {
  230.         dev = list_get_instance(item, serial_dev_t, link);
  231.         if (dev->handle == handle) {
  232.             futex_up(&serial_futex);
  233.             return dev;
  234.         }
  235.         item = item->next;
  236.     }
  237.    
  238.     futex_up(&serial_futex);
  239.     return NULL;
  240. }
  241.  
  242.  
  243. /** Handle one connection to the driver.
  244.  *
  245.  * @param iid       Hash of the request that opened the connection.
  246.  * @param icall     Call data of the request that opened the connection.
  247.  */
  248. static void serial_client_conn(ipc_callid_t iid, ipc_call_t *icall)
  249. {  
  250.     /*
  251.      * Answer the first IPC_M_CONNECT_ME_TO call and remember the handle of the device to which the client connected.
  252.      */
  253.     int handle = IPC_GET_ARG1(*icall);
  254.     serial_dev_t *dev = serial_handle_to_dev(handle);
  255.    
  256.     if (dev == NULL) {
  257.         ipc_answer_0(iid, ENOENT);
  258.         return;
  259.     }
  260.     if (dev->client_connected) {
  261.         ipc_answer_0(iid, ELIMIT);
  262.         return;
  263.     }
  264.    
  265.     dev->client_connected = true;
  266.     ipc_answer_0(iid, EOK);
  267.    
  268.     while (1) {
  269.         ipc_callid_t callid;
  270.         ipc_call_t call;
  271.    
  272.         callid = async_get_call(&call);
  273.         switch  (IPC_GET_METHOD(call)) {
  274.         case IPC_M_PHONE_HUNGUP:
  275.             /*
  276.              * The other side has hung up.
  277.              * Answer the message and exit the fibril.
  278.              */
  279.             ipc_answer_0(callid, EOK);
  280.             dev->client_connected = false;
  281.             return;
  282.             break;
  283.         case SERIAL_GETCHAR:
  284.             serial_getchar(dev, callid);
  285.             break;
  286.         case SERIAL_PUTCHAR:
  287.             serial_putchar(dev, callid, &call);
  288.             break;
  289.         default:
  290.             ipc_answer_0(callid, ENOTSUP);
  291.             break;
  292.         }
  293.     }  
  294. }
  295.  
  296.  
  297. /**
  298.  *  Register the driver with the given name and return its newly created phone.
  299.  */
  300. static int serial_driver_register(char *name)
  301. {
  302.     ipcarg_t retval;
  303.     aid_t req;
  304.     ipc_call_t answer;
  305.     int phone;
  306.     ipcarg_t callback_phonehash;
  307.  
  308.     phone = ipc_connect_me_to(PHONE_NS, SERVICE_DEVMAP, DEVMAP_DRIVER, 0);
  309.  
  310.     while (phone < 0) {
  311.         usleep(10000);
  312.         phone = ipc_connect_me_to(PHONE_NS, SERVICE_DEVMAP,
  313.             DEVMAP_DRIVER, 0);
  314.     }
  315.    
  316.     req = async_send_2(phone, DEVMAP_DRIVER_REGISTER, 0, 0, &answer);
  317.  
  318.     retval = ipc_data_write_start(phone, (char *) name, str_length(name) + 1);
  319.  
  320.     if (retval != EOK) {
  321.         async_wait_for(req, NULL);
  322.         return -1;
  323.     }
  324.  
  325.     async_set_client_connection(serial_client_conn);  // set callback function which will serve client connections
  326.  
  327.     ipc_connect_to_me(phone, 0, 0, 0, &callback_phonehash);
  328.     async_wait_for(req, &retval);
  329.  
  330.     return phone;
  331. }
  332.  
  333. static int serial_device_register(int driver_phone, char *name, int *handle)
  334. {
  335.     ipcarg_t retval;
  336.     aid_t req;
  337.     ipc_call_t answer;
  338.  
  339.     req = async_send_2(driver_phone, DEVMAP_DEVICE_REGISTER, 0, 0, &answer);
  340.  
  341.     retval = ipc_data_write_start(driver_phone, (char *) name, str_length(name) + 1);
  342.  
  343.     if (retval != EOK) {
  344.         async_wait_for(req, NULL);
  345.         return retval;
  346.     }
  347.  
  348.     async_wait_for(req, &retval);
  349.  
  350.     if (handle != NULL)
  351.         *handle = -1;
  352.    
  353.     if (EOK == retval) {
  354.         if (NULL != handle)
  355.             *handle = (int) IPC_GET_ARG1(answer);
  356.     }
  357.    
  358.     return retval;
  359. }
  360.