Subversion Repositories HelenOS

Rev

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

Rev Author Line No. Line
4667 trochtova 1
#include <unistd.h>
2
#include <ddi.h>
3
#include <libarch/ddi.h>
4677 trochtova 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>
4667 trochtova 12
#include <stdio.h>
4677 trochtova 13
#include <futex.h>
14
#include <assert.h>
15
#include <adt/list.h>
16
#include <string.h>
4667 trochtova 17
 
18
#include "isa.h"
19
#include "serial.h"
20
 
21
#define NAME "serial"
22
 
23
#define REG_COUNT 7
24
 
4677 trochtova 25
#define MAX_NAME_LEN 8
4667 trochtova 26
 
4677 trochtova 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
 
4667 trochtova 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);
4677 trochtova 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);
4667 trochtova 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
{
4677 trochtova 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   
4667 trochtova 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
 
4677 trochtova 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
 
4667 trochtova 155
static void serial_probe(bridge_to_isa_t *parent)
156
{
157
    printf(NAME " driver: probe()\n"); 
4677 trochtova 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++;
4667 trochtova 176
    }
177
 
4677 trochtova 178
    serial_delete_dev(dev);
4667 trochtova 179
}
180
 
181
// returns virtual address of the serial port, if the serial port is present at this physical address, NULL otherwise  
4677 trochtova 182
static ioport8_t * serial_probe_port(void *phys_addr)
4667 trochtova 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
}
4677 trochtova 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
}