Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


778050a1523ac2003dd3f66f5285a689c5ab43ad
[palacios.git] / palacios / src / devices / serial.c
1 /*
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu> 
11  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Jack Lange <jarusl@cs.northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20 #include <palacios/vmm.h>
21 #include <palacios/vmm_dev_mgr.h>
22
23 #define COM1_DATA_PORT           0x3f8
24 #define COM1_IRQ_ENABLE_PORT     0x3f9
25 #define COM1_DIV_LATCH_LSB_PORT  0x3f8
26 #define COM1_DIV_LATCH_MSB_PORT  0x3f9
27 #define COM1_IIR_PORT            0x3fa
28 #define COM1_FIFO_CTRL_PORT      0x3fa
29 #define COM1_LINE_CTRL_PORT      0x3fb
30 #define COM1_MODEM_CTRL_PORT     0x3fc
31 #define COM1_LINE_STATUS_PORT    0x3fd
32 #define COM1_MODEM_STATUS_PORT   0x3fe
33 #define COM1_SCRATCH_PORT        0x3ff
34
35 #define COM2_DATA_PORT           0x2f8
36 #define COM2_IRQ_ENABLE_PORT     0x2f9
37 #define COM2_DIV_LATCH_LSB_PORT  0x2f8
38 #define COM2_DIV_LATCH_MSB_PORT  0x2f9
39 #define COM2_IIR_PORT            0x2fa
40 #define COM2_FIFO_CTRL_PORT      0x2fa
41 #define COM2_LINE_CTRL_PORT      0x2fb
42 #define COM2_MODEM_CTRL_PORT     0x2fc
43 #define COM2_LINE_STATUS_PORT    0x2fd
44 #define COM2_MODEM_STATUS_PORT   0x2fe
45 #define COM2_SCRATCH_PORT        0x2ff
46
47 #define COM3_DATA_PORT           0x3e8
48 #define COM3_IRQ_ENABLE_PORT     0x3e9
49 #define COM3_DIV_LATCH_LSB_PORT  0x3e8
50 #define COM3_DIV_LATCH_MSB_PORT  0x3e9
51 #define COM3_IIR_PORT            0x3ea
52 #define COM3_FIFO_CTRL_PORT      0x3ea
53 #define COM3_LINE_CTRL_PORT      0x3eb
54 #define COM3_MODEM_CTRL_PORT     0x3ec
55 #define COM3_LINE_STATUS_PORT    0x3ed
56 #define COM3_MODEM_STATUS_PORT   0x3ee
57 #define COM3_SCRATCH_PORT        0x3ef
58
59 #define COM4_DATA_PORT           0x2e8
60 #define COM4_IRQ_ENABLE_PORT     0x2e9
61 #define COM4_DIV_LATCH_LSB_PORT  0x2e8
62 #define COM4_DIV_LATCH_MSB_PORT  0x2e9
63 #define COM4_IIR_PORT            0x2ea
64 #define COM4_FIFO_CTRL_PORT      0x2ea
65 #define COM4_LINE_CTRL_PORT      0x2eb
66 #define COM4_MODEM_CTRL_PORT     0x2ec
67 #define COM4_LINE_STATUS_PORT    0x2ed
68 #define COM4_MODEM_STATUS_PORT   0x2ee
69 #define COM4_SCRATCH_PORT        0x2ef
70
71
72
73 struct irq_enable_reg {
74     uint_t erbfi   : 1;  // Enable Receiver Buffer full interrupt
75     uint_t etbei   : 1;  // Enable Transmit buffer empty interrupt
76     uint_t elsi    : 1;  // Enable Line Status Interrupt
77     uint_t edssi   : 1;  // Enable Delta Status signals interrupt
78     uint_t rsvd    : 4;   // MBZ
79 };
80
81
82
83 // Interrupt IDs (in priority order, highest is first)
84 #define STATUS_IRQ_LSR_OE_SET   0x3
85 #define STATUS_IRQ_LSR_PE_SET   0x3
86 #define STATUS_IRQ_LSR_FE_SET   0x3
87 #define STATUS_IRQ_LSR_BI_SET   0x3
88 #define RX_IRQ_DR               0x2
89 #define RX_IRQ_TRIGGER_LEVEL    0x2
90 #define FIFO_IRQ                0x6
91 #define TX_IRQ_THRE             0x1
92 #define MODEL_IRQ_DELTA_SET     0x0
93
94 struct irq_id_reg {
95     uint_t pending : 1; // Interrupt pending (0=interrupt pending)
96     uint_t iid     : 3; // Interrupt Identification
97     uint_t rsvd    : 2; // MBZ
98     uint_t fifo_en : 2; // FIFO enable
99 };
100
101 struct fifo_ctrl_reg {
102     uint_t enable  : 1; // enable fifo
103     uint_t rfres   : 1; // RX FIFO reset
104     uint_t xfres   : 1; // TX FIFO reset
105     uint_t dma_sel : 1; // DMA mode select
106     uint_t rsvd    : 2; // MBZ
107     uint_t rx_trigger: 2; // RX FIFO trigger level select
108 };
109
110 struct line_ctrl_reg {
111     uint_t word_len       : 2;  // word length select
112     uint_t stop_bits      : 1;  // Stop Bit select
113     uint_t parity_enable  : 1;  // Enable parity 
114     uint_t even_sel       : 1;  // Even Parity Select
115     uint_t stick_parity   : 1;  // Stick Parity Select
116     uint_t sbr            : 1;  // Set Break 
117     uint_t dlab           : 1;  // Divisor latch access bit
118 };
119
120
121 struct modem_ctrl_reg { 
122     uint_t dtr      : 1;
123     uint_t rts      : 1;
124     uint_t out1     : 1;
125     uint_t out2     : 1;
126     uint_t loop     : 1;  // loopback mode
127     uint_t rsvd     : 3;  // MBZ
128 };
129
130
131 struct line_status_reg {
132     uint_t rbf      : 1;  // Receiver Buffer Full
133     uint_t oe       : 1;  // Overrun error
134     uint_t pe       : 1;  // Parity Error
135     uint_t fe       : 1;  // Framing Error
136     uint_t brk      : 1;  // broken line detected
137     uint_t thre     : 1;  // Transmitter holding register empty
138     uint_t temt     : 1;  // Transmitter Empty
139     uint_t fifo_err : 1;  // at least one error is pending in the RX FIFO chain
140 };
141
142
143 struct modem_status_reg {
144     uint_t dcts     : 1;  // Delta Clear To Send
145     uint_t ddsr     : 1;  // Delta Data Set Ready
146     uint_t teri     : 1;  // Trailing Edge Ring Indicator
147     uint_t ddcd     : 1;  // Delta Data Carrier Detect
148     uint_t cts      : 1;  // Clear to Send
149     uint_t dsr      : 1;  // Data Set Ready
150     uint_t ri       : 1;  // Ring Indicator
151     uint_t dcd      : 1;  // Data Carrier Detect
152 };
153
154
155 #define SERIAL_BUF_LEN 256
156
157 struct serial_buffer {
158     uint_t head; // most recent data
159     uint_t tail; // oldest char
160     char buffer[SERIAL_BUF_LEN];
161 };
162
163 static int queue_data(struct serial_buffer * buf, char data) {
164     uint_t next_loc = (buf->head + 1) % SERIAL_BUF_LEN;
165
166     if (next_loc == buf->tail) {
167         return -1;
168     }
169
170     buf->buffer[next_loc] = data;
171     buf->head = next_loc;
172
173     return 0;
174 }
175
176 static int dequeue_data(struct serial_buffer * buf, char * data) {
177     uint_t next_tail = (buf->tail + 1) % SERIAL_BUF_LEN;
178
179     if (buf->head == buf->tail) {
180         return -1;
181     }
182
183     *data = buf->buffer[buf->tail];
184     buf->tail = next_tail;
185
186     return 0;
187 }
188
189
190 struct serial_port {
191     char     ier;
192     char     iir;
193     char     fcr;
194     char     lcr;
195     char     mcr;
196     char     lsr;
197     char     msr;
198
199     struct serial_buffer tx_buffer;
200     struct serial_buffer rx_buffer;
201 };
202
203
204 struct serial_state {
205     struct serial_port com1;
206     struct serial_port com2;
207     struct serial_port com3;
208     struct serial_port com4;
209 };
210
211
212 static int write_data_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
213     struct serial_state * state = (struct serial_state *)dev->private_data;
214     char * val = (char *)src;
215     PrintDebug("Write to Data Port 0x%x (val=%x)\n", port, *(char*)src);
216
217     if (length != 1) {
218         PrintDebug("Invalid length(%d) in write to 0x%x\n", length, port);
219         return -1;
220     }
221
222     switch (port) {
223         case COM1_DATA_PORT:
224             queue_data(&(state->com1.tx_buffer), *val);
225             break;
226         case COM2_DATA_PORT:
227             queue_data(&(state->com2.tx_buffer), *val);
228             break;
229         case COM3_DATA_PORT:
230             queue_data(&(state->com3.tx_buffer), *val);
231             break;
232         case COM4_DATA_PORT:
233             queue_data(&(state->com4.tx_buffer), *val);
234             break;
235         default:
236             return -1;
237     }
238   
239
240     return length;
241 }
242
243
244
245 static int read_data_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
246     struct serial_state * state = (struct serial_state *)dev->private_data;
247     char * val = (char *)dst;
248     PrintDebug("Read from Data Port 0x%x\n", port);
249
250     if (length != 1) {
251         PrintDebug("Invalid length(%d) in write to 0x%x\n", length, port);
252         return -1;
253     }
254
255     switch (port) {
256         case COM1_DATA_PORT:
257             dequeue_data(&(state->com1.tx_buffer), val);
258             break;
259         case COM2_DATA_PORT:
260             dequeue_data(&(state->com2.tx_buffer), val);
261             break;
262         case COM3_DATA_PORT:
263             dequeue_data(&(state->com3.tx_buffer), val);
264             break;
265         case COM4_DATA_PORT:
266             dequeue_data(&(state->com4.tx_buffer), val);
267             break;
268         default:
269             return -1;
270     }
271   
272
273     return length;
274 }
275
276
277
278 static int handle_ier_write(struct serial_port * com, struct irq_enable_reg * ier) {
279   
280
281     return -1;
282 }
283
284
285 static int write_ctrl_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
286     struct serial_state * state = (struct serial_state *)dev->private_data;
287     char * val = (char *)src;
288     PrintDebug("Write to Control Port (val=%x)\n", *(char *)src);
289
290     if (length != 1) {
291         PrintDebug("Invalid Write length to control port %d\n", port);
292         return -1;
293     }
294
295     switch (port) {
296         case COM1_IRQ_ENABLE_PORT:
297             if (handle_ier_write(&(state->com1), (struct irq_enable_reg *)val) == -1) {
298                 return -1;
299             }
300             break;
301         case COM2_IRQ_ENABLE_PORT:
302             if (handle_ier_write(&(state->com2), (struct irq_enable_reg *)val) == -1) {
303                 return -1;
304             }
305             break;
306         case COM3_IRQ_ENABLE_PORT:
307             if (handle_ier_write(&(state->com3), (struct irq_enable_reg *)val) == -1) {
308                 return -1;
309             }
310             break;
311         case COM4_IRQ_ENABLE_PORT:
312             if (handle_ier_write(&(state->com4), (struct irq_enable_reg *)val) == -1) {
313                 return -1;
314             }
315             break;
316
317         case COM1_FIFO_CTRL_PORT:
318         case COM2_FIFO_CTRL_PORT:
319         case COM3_FIFO_CTRL_PORT:
320         case COM4_FIFO_CTRL_PORT:
321
322         case COM1_LINE_CTRL_PORT:
323         case COM2_LINE_CTRL_PORT:
324         case COM3_LINE_CTRL_PORT:
325         case COM4_LINE_CTRL_PORT:
326
327         case COM1_MODEM_CTRL_PORT:
328         case COM2_MODEM_CTRL_PORT:
329         case COM3_MODEM_CTRL_PORT:
330         case COM4_MODEM_CTRL_PORT:
331     
332
333
334         default:
335             return -1;
336     }
337
338
339     return -1;
340 }
341
342
343
344
345 static int read_ctrl_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
346     struct serial_state * state = (struct serial_state *)dev->private_data;
347     char * val = (char *)dst;
348     PrintDebug("Read from Control Port\n");
349
350     if (length != 1) {
351         PrintDebug("Invalid Read length to control port\n");
352         return -1;
353     }
354
355     switch (port) {
356         case COM1_IRQ_ENABLE_PORT:
357             *val = state->com1.ier;
358             break;
359         case COM2_IRQ_ENABLE_PORT:
360             *val = state->com2.ier;
361             break;
362         case COM3_IRQ_ENABLE_PORT:
363             *val = state->com3.ier;
364             break;
365         case COM4_IRQ_ENABLE_PORT:
366             *val = state->com4.ier;
367             break;
368
369         case COM1_FIFO_CTRL_PORT:
370             *val = state->com1.fcr;
371             break;
372         case COM2_FIFO_CTRL_PORT:
373             *val = state->com2.fcr;
374             break;
375         case COM3_FIFO_CTRL_PORT:
376             *val = state->com3.fcr;
377             break;
378         case COM4_FIFO_CTRL_PORT:
379             *val = state->com4.fcr;
380             break;
381
382         case COM1_LINE_CTRL_PORT:
383             *val = state->com1.lcr;
384             break;
385         case COM2_LINE_CTRL_PORT:
386             *val = state->com2.lcr;
387             break;
388         case COM3_LINE_CTRL_PORT:
389             *val = state->com3.lcr;
390             break;
391         case COM4_LINE_CTRL_PORT:
392             *val = state->com4.lcr;
393             break;
394
395         case COM1_MODEM_CTRL_PORT:
396             *val = state->com1.mcr;
397             break;
398         case COM2_MODEM_CTRL_PORT:
399             *val = state->com2.mcr;
400             break;
401         case COM3_MODEM_CTRL_PORT:
402             *val = state->com3.mcr;
403             break;
404         case COM4_MODEM_CTRL_PORT:
405             *val = state->com4.mcr;
406             break;
407
408         default:
409             return -1;
410     }
411
412     return length;
413 }
414
415
416 static int write_status_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
417     PrintDebug("Write to Status Port 0x%x (val=%x)\n", port, *(char *)src);
418
419     return -1;
420 }
421
422 static int read_status_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
423     struct serial_state * state = (struct serial_state *)dev->private_data;
424     char * val = (char *)dst;
425     PrintDebug("Read from Status Port 0x%x\n", port);
426   
427     if (length != 1) {
428         PrintDebug("Invalid Read length to control port\n");
429         return -1;
430     }
431   
432     switch (port) {
433         case COM1_LINE_STATUS_PORT:
434             *val = state->com1.lsr;
435             break;
436         case COM2_LINE_STATUS_PORT:
437             *val = state->com2.lsr;
438             break;
439         case COM3_LINE_STATUS_PORT:
440             *val = state->com3.lsr;
441             break;
442         case COM4_LINE_STATUS_PORT:
443             *val = state->com4.lsr;
444             break;
445     
446         case COM1_MODEM_STATUS_PORT:
447             *val = state->com1.msr;
448             break;
449         case COM2_MODEM_STATUS_PORT:
450             *val = state->com2.msr;
451             break;
452         case COM3_MODEM_STATUS_PORT:
453             *val = state->com3.msr;
454             break;
455         case COM4_MODEM_STATUS_PORT:
456             *val = state->com4.msr;
457             break;
458
459         default:
460             return -1;
461     }
462
463
464
465     return length;
466 }
467
468
469
470
471
472 static int init_serial_port(struct serial_port * com) {
473     //struct irq_enable_reg * ier = (struct irq_enable_reg *)&(com->ier);
474     //struct irq_id_reg * iir = (struct irq_id_reg *)&(com->iir);
475     //struct fifo_ctrl_reg * fcr = (struct fifo_ctrl_reg *)&(com->fcr);
476     //struct line_ctrl_reg * lcr = (struct line_ctrl_reg *)&(com->lcr);
477     //struct modem_ctrl_reg * mcr = (struct modem_ctrl_reg *)&(com->mcr);
478     //struct line_status_reg * lsr = (struct line_status_reg *)&(com->lsr);
479     //struct modem_status_reg * msr = (struct modem_status_reg *)&(com->msr);
480
481     com->ier = 0x00;
482     com->iir = 0x01;
483     com->fcr = 0x00;
484     com->lcr = 0x00;
485     com->mcr = 0x00;
486     com->lsr = 0x60;
487     com->msr = 0x00;
488
489     return 0;
490 }
491
492
493
494 static int serial_free(struct vm_device * dev) {
495
496
497     v3_dev_unhook_io(dev, COM1_DATA_PORT);
498     v3_dev_unhook_io(dev, COM1_IRQ_ENABLE_PORT);
499     v3_dev_unhook_io(dev, COM1_FIFO_CTRL_PORT);
500     v3_dev_unhook_io(dev, COM1_LINE_CTRL_PORT);
501     v3_dev_unhook_io(dev, COM1_MODEM_CTRL_PORT);
502     v3_dev_unhook_io(dev, COM1_LINE_STATUS_PORT);
503     v3_dev_unhook_io(dev, COM1_MODEM_STATUS_PORT);
504     v3_dev_unhook_io(dev, COM1_SCRATCH_PORT);
505
506     v3_dev_unhook_io(dev, COM2_DATA_PORT);
507     v3_dev_unhook_io(dev, COM2_IRQ_ENABLE_PORT);
508     v3_dev_unhook_io(dev, COM2_FIFO_CTRL_PORT);
509     v3_dev_unhook_io(dev, COM2_LINE_CTRL_PORT);
510     v3_dev_unhook_io(dev, COM2_MODEM_CTRL_PORT);
511     v3_dev_unhook_io(dev, COM2_LINE_STATUS_PORT);
512     v3_dev_unhook_io(dev, COM2_MODEM_STATUS_PORT);
513     v3_dev_unhook_io(dev, COM2_SCRATCH_PORT);
514
515     v3_dev_unhook_io(dev, COM3_DATA_PORT);
516     v3_dev_unhook_io(dev, COM3_IRQ_ENABLE_PORT);
517     v3_dev_unhook_io(dev, COM3_FIFO_CTRL_PORT);
518     v3_dev_unhook_io(dev, COM3_LINE_CTRL_PORT);
519     v3_dev_unhook_io(dev, COM3_MODEM_CTRL_PORT);
520     v3_dev_unhook_io(dev, COM3_LINE_STATUS_PORT);
521     v3_dev_unhook_io(dev, COM3_MODEM_STATUS_PORT);
522     v3_dev_unhook_io(dev, COM3_SCRATCH_PORT);
523
524     v3_dev_unhook_io(dev, COM4_DATA_PORT);
525     v3_dev_unhook_io(dev, COM4_IRQ_ENABLE_PORT);
526     v3_dev_unhook_io(dev, COM4_FIFO_CTRL_PORT);
527     v3_dev_unhook_io(dev, COM4_LINE_CTRL_PORT);
528     v3_dev_unhook_io(dev, COM4_MODEM_CTRL_PORT);
529     v3_dev_unhook_io(dev, COM4_LINE_STATUS_PORT);
530     v3_dev_unhook_io(dev, COM4_MODEM_STATUS_PORT);
531     v3_dev_unhook_io(dev, COM4_SCRATCH_PORT);
532
533     return 0;
534 }
535
536
537
538 static struct v3_device_ops dev_ops = {
539     .free = serial_free,
540     .reset = NULL,
541     .start = NULL,
542     .stop = NULL,
543 };
544
545
546
547
548 static int serial_init(struct guest_info * vm, void * cfg_data) {
549     struct serial_state * state = NULL;
550
551     state = (struct serial_state *)V3_Malloc(sizeof(struct serial_state));
552
553     V3_ASSERT(state != NULL);
554
555     struct vm_device * dev = v3_allocate_device("SERIAL", &dev_ops, state);
556
557
558     if (v3_attach_device(vm, dev) == -1) {
559         PrintError("Could not attach device %s\n", "SERIAL");
560         return -1;
561     }
562
563
564
565     init_serial_port(&(state->com1));
566     init_serial_port(&(state->com2));
567     init_serial_port(&(state->com3));
568     init_serial_port(&(state->com4));
569
570     v3_dev_hook_io(dev, COM1_DATA_PORT, &read_data_port, &write_data_port);
571     v3_dev_hook_io(dev, COM1_IRQ_ENABLE_PORT, &read_ctrl_port, &write_ctrl_port);
572     v3_dev_hook_io(dev, COM1_FIFO_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
573     v3_dev_hook_io(dev, COM1_LINE_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
574     v3_dev_hook_io(dev, COM1_MODEM_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
575     v3_dev_hook_io(dev, COM1_LINE_STATUS_PORT, &read_status_port, &write_status_port);
576     v3_dev_hook_io(dev, COM1_MODEM_STATUS_PORT, &read_status_port, &write_status_port);
577     v3_dev_hook_io(dev, COM1_SCRATCH_PORT, &read_ctrl_port, &write_ctrl_port);
578
579     v3_dev_hook_io(dev, COM2_DATA_PORT, &read_data_port, &write_data_port);
580     v3_dev_hook_io(dev, COM2_IRQ_ENABLE_PORT, &read_ctrl_port, &write_ctrl_port);
581     v3_dev_hook_io(dev, COM2_FIFO_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
582     v3_dev_hook_io(dev, COM2_LINE_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
583     v3_dev_hook_io(dev, COM2_MODEM_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
584     v3_dev_hook_io(dev, COM2_LINE_STATUS_PORT, &read_status_port, &write_status_port);
585     v3_dev_hook_io(dev, COM2_MODEM_STATUS_PORT, &read_status_port, &write_status_port);
586     v3_dev_hook_io(dev, COM2_SCRATCH_PORT, &read_ctrl_port, &write_ctrl_port);
587
588     v3_dev_hook_io(dev, COM3_DATA_PORT, &read_data_port, &write_data_port);
589     v3_dev_hook_io(dev, COM3_IRQ_ENABLE_PORT, &read_ctrl_port, &write_ctrl_port);
590     v3_dev_hook_io(dev, COM3_FIFO_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
591     v3_dev_hook_io(dev, COM3_LINE_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
592     v3_dev_hook_io(dev, COM3_MODEM_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
593     v3_dev_hook_io(dev, COM3_LINE_STATUS_PORT, &read_status_port, &write_status_port);
594     v3_dev_hook_io(dev, COM3_MODEM_STATUS_PORT, &read_status_port, &write_status_port);
595     v3_dev_hook_io(dev, COM3_SCRATCH_PORT, &read_ctrl_port, &write_ctrl_port);
596
597     v3_dev_hook_io(dev, COM4_DATA_PORT, &read_data_port, &write_data_port);
598     v3_dev_hook_io(dev, COM4_IRQ_ENABLE_PORT, &read_ctrl_port, &write_ctrl_port);
599     v3_dev_hook_io(dev, COM4_FIFO_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
600     v3_dev_hook_io(dev, COM4_LINE_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
601     v3_dev_hook_io(dev, COM4_MODEM_CTRL_PORT, &read_ctrl_port, &write_ctrl_port);
602     v3_dev_hook_io(dev, COM4_LINE_STATUS_PORT, &read_status_port, &write_status_port);
603     v3_dev_hook_io(dev, COM4_MODEM_STATUS_PORT, &read_status_port, &write_status_port);
604     v3_dev_hook_io(dev, COM4_SCRATCH_PORT, &read_ctrl_port, &write_ctrl_port);
605
606     return 0;
607 }
608
609
610 device_register("SERIAL", serial_init)