1 /*
2 * Cisco router simulation platform.
3 * Copyright (c) 2005 Christophe Fillot (cf@utc.fr)
4 *
5 * SB-1 I/O devices.
6 *
7 * XXX: just for tests!
8 */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <termios.h>
16 #include <fcntl.h>
17 #include <pthread.h>
18
19 #include "utils.h"
20 #include "ptask.h"
21 #include "cpu.h"
22 #include "vm.h"
23 #include "dynamips.h"
24 #include "memory.h"
25 #include "device.h"
26 #include "dev_c7200.h"
27
28 #define DEBUG_UNKNOWN 1
29
30 /* DUART Status Register */
31 #define DUART_SR_RX_RDY 0x01 /* Receiver ready */
32 #define DUART_SR_RX_FFUL 0x02 /* Receive FIFO full */
33 #define DUART_SR_TX_RDY 0x04 /* Transmitter ready */
34 #define DUART_SR_TX_EMT 0x08 /* Transmitter empty */
35
36 /* DUART Interrupt Status Register */
37 #define DUART_ISR_TXA 0x01 /* Channel A Transmitter Ready */
38 #define DUART_ISR_RXA 0x02 /* Channel A Receiver Ready */
39 #define DUART_ISR_TXB 0x10 /* Channel B Transmitter Ready */
40 #define DUART_ISR_RXB 0x20 /* Channel B Receiver Ready */
41
42 /* DUART Interrupt Mask Register */
43 #define DUART_IMR_TXA 0x01 /* Channel A Transmitter Ready */
44 #define DUART_IMR_RXA 0x02 /* Channel A Receiver Ready */
45 #define DUART_IMR_TXB 0x10 /* Channel B Transmitter Ready */
46 #define DUART_IMR_RXB 0x20 /* Channel B Receiver Ready */
47
48 /* SB-1 DUART channel */
49 struct sb1_duart_channel {
50 m_uint8_t mode;
51 m_uint8_t cmd;
52 };
53
54 /* SB-1 I/O private data */
55 struct sb1_io_data {
56 vm_obj_t vm_obj;
57 struct vdevice dev;
58
59 /* Virtual machine */
60 vm_instance_t *vm;
61
62 /* DUART info */
63 u_int duart_irq,duart_irq_seq;
64 m_uint8_t duart_isr,duart_imr;
65 struct sb1_duart_channel duart_chan[2];
66
67 /* Periodic task to trigger dummy DUART IRQ */
68 ptask_id_t duart_irq_tid;
69 };
70
71 /* Console port input */
tty_con_input(vtty_t * vtty)72 static void tty_con_input(vtty_t *vtty)
73 {
74 struct sb1_io_data *d = vtty->priv_data;
75
76 if (d->duart_imr & DUART_IMR_RXA) {
77 d->duart_isr |= DUART_ISR_RXA;
78 vm_set_irq(d->vm,d->duart_irq);
79 }
80 }
81
82 /* AUX port input */
tty_aux_input(vtty_t * vtty)83 static void tty_aux_input(vtty_t *vtty)
84 {
85 struct sb1_io_data *d = vtty->priv_data;
86
87 if (d->duart_imr & DUART_IMR_RXB) {
88 d->duart_isr |= DUART_ISR_RXB;
89 vm_set_irq(d->vm,d->duart_irq);
90 }
91 }
92
93 /* IRQ trickery for Console and AUX ports */
tty_trigger_dummy_irq(struct sb1_io_data * d,void * arg)94 static int tty_trigger_dummy_irq(struct sb1_io_data *d,void *arg)
95 {
96 u_int mask;
97
98 d->duart_irq_seq++;
99
100 if (d->duart_irq_seq == 2) {
101 mask = DUART_IMR_TXA|DUART_IMR_TXB;
102 if (d->duart_imr & mask) {
103 d->duart_isr |= DUART_ISR_TXA|DUART_ISR_TXB;
104 vm_set_irq(d->vm,d->duart_irq);
105 }
106
107 d->duart_irq_seq = 0;
108 }
109
110 return(0);
111 }
112
113 /*
114 * dev_sb1_io_access()
115 */
dev_sb1_io_access(cpu_gen_t * cpu,struct vdevice * dev,m_uint32_t offset,u_int op_size,u_int op_type,m_uint64_t * data)116 void *dev_sb1_io_access(cpu_gen_t *cpu,struct vdevice *dev,
117 m_uint32_t offset,u_int op_size,u_int op_type,
118 m_uint64_t *data)
119 {
120 struct sb1_io_data *d = dev->priv_data;
121 u_char odata;
122
123 if (op_type == MTS_READ)
124 *data = 0;
125
126 switch(offset) {
127 case 0x390: /* DUART Interrupt Status Register */
128 if (op_type == MTS_READ)
129 *data = d->duart_isr;
130 break;
131
132 case 0x320: /* DUART Channel A Only Interrupt Status Register */
133 if (op_type == MTS_READ)
134 *data = d->duart_isr & 0x0F;
135 break;
136
137 case 0x340: /* DUART Channel B Only Interrupt Status Register */
138 if (op_type == MTS_READ)
139 *data = (d->duart_isr >> 4) & 0x0F;
140 break;
141
142 case 0x3a0: /* DUART Interrupt Mask Register */
143 if (op_type == MTS_READ)
144 *data = d->duart_imr;
145 else
146 d->duart_imr = *data;
147 break;
148
149 case 0x330: /* DUART Channel A Only Interrupt Mask Register */
150 if (op_type == MTS_READ) {
151 *data = d->duart_imr & 0x0F;
152 } else {
153 d->duart_imr &= ~0x0F;
154 d->duart_imr |= *data & 0x0F;
155 }
156 break;
157
158 case 0x350: /* DUART Channel B Only Interrupt Mask Register */
159 if (op_type == MTS_READ) {
160 *data = (d->duart_imr >> 4) & 0x0F;
161 } else {
162 d->duart_imr &= ~0xF0;
163 d->duart_imr |= (*data & 0x0F) << 4;
164 }
165 break;
166
167 case 0x100: /* DUART Mode (Channel A) */
168 if (op_type == MTS_READ)
169 d->duart_chan[0].mode = *data;
170 else
171 *data = d->duart_chan[0].mode;
172 break;
173
174 case 0x200: /* DUART Mode (Channel B) */
175 if (op_type == MTS_READ)
176 d->duart_chan[1].mode = *data;
177 else
178 *data = d->duart_chan[1].mode;
179 break;
180
181 case 0x150: /* DUART Command Register (Channel A) */
182 if (op_type == MTS_READ)
183 d->duart_chan[0].cmd = *data;
184 else
185 *data = d->duart_chan[0].cmd;
186 break;
187
188 case 0x250: /* DUART Command Register (Channel B) */
189 if (op_type == MTS_READ)
190 d->duart_chan[1].cmd = *data;
191 else
192 *data = d->duart_chan[1].cmd;
193 break;
194
195 case 0x120: /* DUART Status Register (Channel A) */
196 if (op_type == MTS_READ) {
197 odata = 0;
198
199 if (vtty_is_char_avail(d->vm->vtty_con))
200 odata |= DUART_SR_RX_RDY;
201
202 odata |= DUART_SR_TX_RDY;
203
204 vm_clear_irq(d->vm,d->duart_irq);
205 *data = odata;
206 }
207 break;
208
209 case 0x220: /* DUART Status Register (Channel B) */
210 if (op_type == MTS_READ) {
211 odata = 0;
212
213 if (vtty_is_char_avail(d->vm->vtty_aux))
214 odata |= DUART_SR_RX_RDY;
215
216 odata |= DUART_SR_TX_RDY;
217
218 //vm_clear_irq(d->vm,d->duart_irq);
219 *data = odata;
220 }
221 break;
222
223 case 0x160: /* DUART Received Data Register (Channel A) */
224 if (op_type == MTS_READ) {
225 *data = vtty_get_char(d->vm->vtty_con);
226 d->duart_isr &= ~DUART_ISR_RXA;
227 }
228 break;
229
230 case 0x260: /* DUART Received Data Register (Channel B) */
231 if (op_type == MTS_READ) {
232 *data = vtty_get_char(d->vm->vtty_aux);
233 d->duart_isr &= ~DUART_ISR_RXB;
234 }
235 break;
236
237 case 0x170: /* DUART Transmit Data Register (Channel A) */
238 if (op_type == MTS_WRITE) {
239 vtty_put_char(d->vm->vtty_con,(char)*data);
240 d->duart_isr &= ~DUART_ISR_TXA;
241 }
242 break;
243
244 case 0x270: /* DUART Transmit Data Register (Channel B) */
245 if (op_type == MTS_WRITE) {
246 vtty_put_char(d->vm->vtty_aux,(char)*data);
247 d->duart_isr &= ~DUART_ISR_TXB;
248 }
249 break;
250
251 case 0x1a76: /* pcmcia status */
252 if (op_type == MTS_READ)
253 *data = 0xFF;
254 break;
255
256 #if DEBUG_UNKNOWN
257 default:
258 if (op_type == MTS_READ) {
259 cpu_log(cpu,"SB1_IO","read from addr 0x%x, pc=0x%llx\n",
260 offset,cpu_get_pc(cpu));
261 } else {
262 cpu_log(cpu,"SB1_IO","write to addr 0x%x, value=0x%llx, "
263 "pc=0x%llx\n",offset,*data,cpu_get_pc(cpu));
264 }
265 #endif
266 }
267
268 return NULL;
269 }
270
271 /* Shutdown the SB-1 I/O devices */
dev_sb1_io_shutdown(vm_instance_t * vm,struct sb1_io_data * d)272 void dev_sb1_io_shutdown(vm_instance_t *vm,struct sb1_io_data *d)
273 {
274 if (d != NULL) {
275 /* Remove the device */
276 dev_remove(vm,&d->dev);
277
278 /* Free the structure itself */
279 free(d);
280 }
281 }
282
283
284 /* Create SB-1 I/O devices */
dev_sb1_io_init(vm_instance_t * vm,u_int duart_irq)285 int dev_sb1_io_init(vm_instance_t *vm,u_int duart_irq)
286 {
287 struct sb1_io_data *d;
288
289 /* allocate private data structure */
290 if (!(d = malloc(sizeof(*d)))) {
291 fprintf(stderr,"SB1_IO: out of memory\n");
292 return(-1);
293 }
294
295 memset(d,0,sizeof(*d));
296 d->vm = vm;
297 d->duart_irq = duart_irq;
298
299 vm_object_init(&d->vm_obj);
300 d->vm_obj.name = "sb1_io";
301 d->vm_obj.data = d;
302 d->vm_obj.shutdown = (vm_shutdown_t)dev_sb1_io_shutdown;
303
304 /* Set device properties */
305 dev_init(&d->dev);
306 d->dev.name = "sb1_io";
307 d->dev.priv_data = d;
308 d->dev.phys_addr = 0x10060000ULL;
309 d->dev.phys_len = 0x10000;
310 d->dev.handler = dev_sb1_io_access;
311
312 /* Set console and AUX port notifying functions */
313 vm->vtty_con->priv_data = d;
314 vm->vtty_aux->priv_data = d;
315 vm->vtty_con->read_notifier = tty_con_input;
316 vm->vtty_aux->read_notifier = tty_aux_input;
317
318 /* Trigger periodically a dummy IRQ to flush buffers */
319 d->duart_irq_tid = ptask_add((ptask_callback)tty_trigger_dummy_irq,d,NULL);
320
321 /* Map this device to the VM */
322 vm_bind_device(vm,&d->dev);
323 vm_object_add(vm,&d->vm_obj);
324 return(0);
325 }
326