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.


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