/[dynamips]/trunk/dev_ns16552.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/dev_ns16552.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 12 - (show annotations)
Sat Oct 6 16:45:40 2007 UTC (11 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 6716 byte(s)
make working copy

1 /*
2 * Cisco 3600 simulation platform.
3 * Copyright (c) 2006 Christophe Fillot (cf@utc.fr)
4 *
5 * NS16552 DUART.
6 */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <sys/types.h>
13
14 #include <termios.h>
15 #include <fcntl.h>
16 #include <pthread.h>
17
18 #include "ptask.h"
19 #include "cpu.h"
20 #include "vm.h"
21 #include "dynamips.h"
22 #include "memory.h"
23 #include "device.h"
24 #include "dev_vtty.h"
25
26 /* Debugging flags */
27 #define DEBUG_UNKNOWN 1
28 #define DEBUG_ACCESS 0
29
30 /* Interrupt Enable Register (IER) */
31 #define IER_ERXRDY 0x1
32 #define IER_ETXRDY 0x2
33
34 /* Interrupt Identification Register */
35 #define IIR_NPENDING 0x01 /* 0: irq pending, 1: no irq pending */
36 #define IIR_TXRDY 0x02
37 #define IIR_RXRDY 0x04
38
39 /* Line Status Register (LSR) */
40 #define LSR_RXRDY 0x01
41 #define LSR_TXRDY 0x20
42 #define LSR_TXEMPTY 0x40
43
44 /* UART channel */
45 struct ns16552_channel {
46 u_int ier,output;
47 vtty_t *vtty;
48 };
49
50 /* NS16552 structure */
51 struct ns16552_data {
52 vm_obj_t vm_obj;
53 struct vdevice dev;
54 vm_instance_t *vm;
55 u_int irq;
56
57 /* Register offset divisor */
58 u_int reg_div;
59
60 /* Periodic task to trigger DUART IRQ */
61 ptask_id_t tid;
62
63 struct ns16552_channel channel[2];
64 u_int duart_irq_seq;
65 };
66
67 /* Console port input */
68 static void tty_con_input(vtty_t *vtty)
69 {
70 struct ns16552_data *d = vtty->priv_data;
71
72 if (d->channel[0].ier & IER_ERXRDY)
73 vm_set_irq(d->vm,d->irq);
74 }
75
76 /* AUX port input */
77 static void tty_aux_input(vtty_t *vtty)
78 {
79 struct ns16552_data *d = vtty->priv_data;
80
81 if (d->channel[1].ier & IER_ERXRDY)
82 vm_set_irq(d->vm,d->irq);
83 }
84
85 /* IRQ trickery for Console and AUX ports */
86 static int tty_trigger_dummy_irq(struct ns16552_data *d,void *arg)
87 {
88 d->duart_irq_seq++;
89
90 if (d->duart_irq_seq == 2) {
91 if (d->channel[0].ier & IER_ETXRDY) {
92 d->channel[0].output = TRUE;
93 vm_set_irq(d->vm,d->irq);
94 }
95
96 #if 0
97 if (d->channel[1].ier & IER_ETXRDY) {
98 d->channel[1].output = TRUE;
99 vm_set_irq(d->vm,d->irq);
100 }
101 #endif
102
103 d->duart_irq_seq = 0;
104 }
105
106 return(0);
107 }
108
109 /*
110 * dev_ns16552_access()
111 */
112 void *dev_ns16552_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset,
113 u_int op_size,u_int op_type,m_uint64_t *data)
114 {
115 struct ns16552_data *d = dev->priv_data;
116 int channel = 0;
117 u_char odata;
118
119 if (op_type == MTS_READ)
120 *data = 0;
121
122 #if DEBUG_ACCESS
123 if (op_type == MTS_READ) {
124 cpu_log(cpu,"NS16552","read from 0x%x, pc=0x%llx\n",
125 offset,cpu_get_pc(cpu));
126 } else {
127 cpu_log(cpu,"NS16552","write to 0x%x, value=0x%llx, pc=0x%llx\n",
128 offset,*data,cpu_get_pc(cpu));
129 }
130 #endif
131
132 offset >>= d->reg_div;
133
134 if (offset >= 0x08)
135 channel = 1;
136
137 switch(offset) {
138 /* Receiver Buffer Reg. (RBR) / Transmitting Holding Reg. (THR) */
139 case 0x00:
140 case 0x08:
141 if (op_type == MTS_WRITE) {
142 vtty_put_char(d->channel[channel].vtty,(char)*data);
143
144 if (d->channel[channel].ier & IER_ETXRDY)
145 vm_set_irq(d->vm,d->irq);
146
147 d->channel[channel].output = TRUE;
148 } else {
149 *data = vtty_get_char(d->channel[channel].vtty);
150 }
151 break;
152
153 /* Interrupt Enable Register (IER) */
154 case 0x01:
155 case 0x09:
156 if (op_type == MTS_READ) {
157 *data = d->channel[channel].ier;
158 } else {
159 d->channel[channel].ier = *data & 0xFF;
160
161 if ((*data & 0x02) == 0) { /* transmit holding register */
162 d->channel[channel].vtty->managed_flush = TRUE;
163 vtty_flush(d->channel[channel].vtty);
164 }
165 }
166 break;
167
168 /* Interrupt Ident Register (IIR) */
169 case 0x02:
170 vm_clear_irq(d->vm,d->irq);
171 case 0x0A:
172 if (op_type == MTS_READ) {
173 odata = IIR_NPENDING;
174
175 if (vtty_is_char_avail(d->channel[channel].vtty)) {
176 odata = IIR_RXRDY;
177 } else {
178 if (d->channel[channel].output) {
179 odata = IIR_TXRDY;
180 d->channel[channel].output = 0;
181 }
182 }
183
184 *data = odata;
185 }
186 break;
187
188 /* Line Status Register (LSR) */
189 case 0x05:
190 case 0x0D:
191 if (op_type == MTS_READ) {
192 odata = 0;
193
194 if (vtty_is_char_avail(d->channel[channel].vtty))
195 odata |= LSR_RXRDY;
196
197 odata |= LSR_TXRDY|LSR_TXEMPTY;
198 *data = odata;
199 }
200 break;
201
202 #if DEBUG_UNKNOWN
203 default:
204 if (op_type == MTS_READ) {
205 cpu_log(cpu,"NS16552","read from addr 0x%x, pc=0x%llx (size=%u)\n",
206 offset,cpu_get_pc(cpu),op_size);
207 } else {
208 cpu_log(cpu,
209 "NS16552","write to addr 0x%x, value=0x%llx, "
210 "pc=0x%llx (size=%u)\n",
211 offset,*data,cpu_get_pc(cpu),op_size);
212 }
213 #endif
214 }
215
216 return NULL;
217 }
218
219 /* Shutdown a NS16552 device */
220 void dev_ns16552_shutdown(vm_instance_t *vm,struct ns16552_data *d)
221 {
222 if (d != NULL) {
223 d->channel[0].vtty->read_notifier = NULL;
224 d->channel[1].vtty->read_notifier = NULL;
225
226 /* Remove the periodic task */
227 ptask_remove(d->tid);
228
229 /* Remove the device */
230 dev_remove(vm,&d->dev);
231
232 /* Free the structure itself */
233 free(d);
234 }
235 }
236
237 /* Create a NS16552 device */
238 int dev_ns16552_init(vm_instance_t *vm,m_uint64_t paddr,m_uint32_t len,
239 u_int reg_div,u_int irq,vtty_t *vtty_A,vtty_t *vtty_B)
240 {
241 struct ns16552_data *d;
242
243 /* Allocate private data structure */
244 if (!(d = malloc(sizeof(*d)))) {
245 fprintf(stderr,"NS16552: out of memory\n");
246 return(-1);
247 }
248
249 memset(d,0,sizeof(*d));
250 d->vm = vm;
251 d->irq = irq;
252 d->reg_div = reg_div;
253 d->channel[0].vtty = vtty_A;
254 d->channel[1].vtty = vtty_B;
255
256 vm_object_init(&d->vm_obj);
257 d->vm_obj.name = "ns16552";
258 d->vm_obj.data = d;
259 d->vm_obj.shutdown = (vm_shutdown_t)dev_ns16552_shutdown;
260
261 /* Set device properties */
262 dev_init(&d->dev);
263 d->dev.name = "ns16552";
264 d->dev.phys_addr = paddr;
265 d->dev.phys_len = len;
266 d->dev.handler = dev_ns16552_access;
267 d->dev.priv_data = d;
268
269 vtty_A->priv_data = d;
270 vtty_B->priv_data = d;
271 vtty_A->read_notifier = tty_con_input;
272 vtty_B->read_notifier = tty_aux_input;
273
274 /* Trigger periodically a dummy IRQ to flush buffers */
275 d->tid = ptask_add((ptask_callback)tty_trigger_dummy_irq,d,NULL);
276
277 /* Map this device to the VM */
278 vm_bind_device(vm,&d->dev);
279 vm_object_add(vm,&d->vm_obj);
280 return(0);
281 }

  ViewVC Help
Powered by ViewVC 1.1.26