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