This source file includes following definitions.
- send_break
- send_intr
- receive_intr
- line_status_intr
- modem_status_intr
- check_tty
- do_rs_write
- com1_IRQ
- com2_IRQ
- com3_IRQ
- com4_IRQ
- com1_timer
- com2_timer
- com3_timer
- com4_timer
- com1_timeout
- com2_timeout
- com3_timeout
- com4_timeout
- init
- serial_close
- startup
- change_speed
- serial_open
- get_serial_info
- set_serial_info
- rs_init
- rs_write
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 #include <linux/errno.h>
17 #include <linux/signal.h>
18 #include <linux/sched.h>
19 #include <linux/timer.h>
20 #include <linux/tty.h>
21
22 #include <asm/system.h>
23 #include <asm/io.h>
24 #include <asm/segment.h>
25
26 #define WAKEUP_CHARS (3*TTY_BUF_SIZE/4)
27
28 struct serial_struct serial_table[NR_SERIALS] = {
29 { PORT_UNKNOWN, 0, 0x3F8, 4, NULL},
30 { PORT_UNKNOWN, 1, 0x2F8, 3, NULL},
31 { PORT_UNKNOWN, 2, 0x3E8, 4, NULL},
32 { PORT_UNKNOWN, 3, 0x2E8, 3, NULL},
33 };
34
35 void send_break(unsigned int line)
36 {
37 unsigned short port;
38 struct serial_struct * info;
39
40 if (line >= NR_SERIALS)
41 return;
42 info = serial_table + line;
43 if (!(port = info->port))
44 return;
45 port += 3;
46 current->state = TASK_INTERRUPTIBLE;
47 current->timeout = jiffies + 25;
48 outb_p(inb_p(port) | 0x40,port);
49 schedule();
50 outb_p(inb_p(port) & 0xbf,port);
51 }
52
53
54
55
56
57
58
59
60
61
62 static void send_intr(struct serial_struct * info)
63 {
64 unsigned short port = info->port;
65 unsigned int timer = SER1_TIMEOUT + info->line;
66 struct tty_queue * queue = info->tty->write_q;
67 int c, i = 0;
68
69 if (info->tty->stopped) return;
70
71 timer_active &= ~(1 << timer);
72 while (inb_p(info->port+5) & 0x20) {
73 if (queue->tail == queue->head)
74 goto end_send;
75 c = queue->buf[queue->tail];
76 queue->tail++;
77 queue->tail &= TTY_BUF_SIZE-1;
78 outb(c,port);
79 if ((info->type != PORT_16550A) || (++i >= 14) || info->tty->stopped)
80 break;
81 }
82 timer_table[timer].expires = jiffies + 10;
83 timer_active |= 1 << timer;
84 end_send:
85 if (LEFT(queue) > WAKEUP_CHARS)
86 wake_up(&queue->proc_list);
87 }
88
89 static void receive_intr(struct serial_struct * info)
90 {
91 unsigned short port = info->port;
92 struct tty_queue * queue = info->tty->read_q;
93 int head = queue->head;
94 int maxhead = (queue->tail-1) & (TTY_BUF_SIZE-1);
95
96 timer_active &= ~((1<<SER1_TIMER)<<info->line);
97 do {
98 queue->buf[head] = inb(port);
99 if (head != maxhead) {
100 head++;
101 head &= TTY_BUF_SIZE-1;
102 }
103 } while (inb(port+5) & 1);
104 queue->head = head;
105 timer_active |= (1<<SER1_TIMER)<<info->line;
106 }
107
108 static void line_status_intr(struct serial_struct * info)
109 {
110 unsigned char status = inb(info->port+5);
111
112
113 }
114
115 static void modem_status_intr(struct serial_struct * info)
116 {
117 unsigned char status = inb(info->port+6);
118
119 if (!(info->tty->termios.c_cflag & CLOCAL)) {
120 if ((status & 0x88) == 0x08 && info->tty->pgrp > 0)
121 kill_pg(info->tty->pgrp,SIGHUP,1);
122
123 if (info->tty->termios.c_cflag & CRTSCTS)
124 info->tty->stopped = !(status & 0x10);
125
126 if (!info->tty->stopped)
127 send_intr(info);
128 }
129 }
130
131 static void (*jmp_table[4])(struct serial_struct *) = {
132 modem_status_intr,
133 send_intr,
134 receive_intr,
135 line_status_intr
136 };
137
138 static void check_tty(struct serial_struct * info)
139 {
140 unsigned char ident;
141
142 if (!info || !info->tty || !info->port)
143 return;
144 while (1) {
145 ident = inb(info->port+2) & 7;
146 if (ident & 1)
147 return;
148 ident >>= 1;
149 if (ident > 3)
150 return;
151 jmp_table[ident](info);
152 }
153 }
154
155
156
157
158
159 static inline void do_rs_write(struct serial_struct * info)
160 {
161 if (!info->tty || !info->port)
162 return;
163 if (!info->tty->write_q || EMPTY(info->tty->write_q))
164 return;
165 cli();
166 send_intr(info);
167 sti();
168 }
169
170
171
172
173 static void com1_IRQ(int unused)
174 {
175 check_tty(serial_table+0);
176 }
177
178 static void com2_IRQ(int unused)
179 {
180 check_tty(serial_table+1);
181 }
182
183 static void com3_IRQ(int unused)
184 {
185 check_tty(serial_table+2);
186 }
187
188 static void com4_IRQ(int unused)
189 {
190 check_tty(serial_table+3);
191 }
192
193
194
195
196 static void com1_timer(void)
197 {
198 TTY_READ_FLUSH(tty_table+64);
199 }
200
201 static void com2_timer(void)
202 {
203 TTY_READ_FLUSH(tty_table+65);
204 }
205
206 static void com3_timer(void)
207 {
208 TTY_READ_FLUSH(tty_table+66);
209 }
210
211 static void com4_timer(void)
212 {
213 TTY_READ_FLUSH(tty_table+67);
214 }
215
216
217
218
219 static void com1_timeout(void)
220 {
221 do_rs_write(serial_table);
222 }
223
224 static void com2_timeout(void)
225 {
226 do_rs_write(serial_table + 1);
227 }
228
229 static void com3_timeout(void)
230 {
231 do_rs_write(serial_table + 2);
232 }
233
234 static void com4_timeout(void)
235 {
236 do_rs_write(serial_table + 3);
237 }
238
239 static void init(struct serial_struct * info)
240 {
241 unsigned char status1, status2, scratch;
242 unsigned short port = info->port;
243
244 if (inb(port+5) == 0xff) {
245 info->type = PORT_UNKNOWN;
246 return;
247 }
248
249 scratch = inb(port+7);
250 outb_p(0xa5, port+7);
251 status1 = inb(port+7);
252 outb_p(0x5a, port+7);
253 status2 = inb(port+7);
254 if (status1 == 0xa5 && status2 == 0x5a) {
255 outb_p(scratch, port+7);
256 outb_p(0x01, port+2);
257 scratch = inb(port+2) >> 6;
258 switch (scratch) {
259 case 0:
260 info->type = PORT_16450;
261 break;
262 case 1:
263 info->type = PORT_UNKNOWN;
264 break;
265 case 2:
266 info->type = PORT_16550;
267 outb_p(0x00, port+2);
268 break;
269 case 3:
270 info->type = PORT_16550A;
271 outb_p(0xc7, port+2);
272 break;
273 }
274 } else
275 info->type = PORT_8250;
276 outb_p(0x80,port+3);
277 outb_p(0x30,port);
278 outb_p(0x00,port+1);
279 outb_p(0x03,port+3);
280 outb_p(0x00,port+4);
281 outb_p(0x00,port+1);
282 (void)inb(port);
283 }
284
285 void serial_close(unsigned line, struct file * filp)
286 {
287 struct serial_struct * info;
288 int irq;
289
290 if (line >= NR_SERIALS)
291 return;
292 info = serial_table + line;
293 if (!info->port)
294 return;
295 outb(0x00,info->port+4);
296 irq = info->irq;
297 if (irq == 2)
298 irq = 9;
299 free_irq(irq);
300 }
301
302 static void startup(unsigned short port)
303 {
304 int i;
305
306 outb_p(0x03,port+3);
307 outb_p(0x0b,port+4);
308 outb_p(0x0f,port+1);
309 inb_p(port+2);
310 inb_p(port+6);
311 inb_p(port+2);
312 inb_p(port+5);
313 for (i = 0; i < 16 ; i++) {
314 inb_p(port+0);
315 if (!(inb_p(port+5) & 1))
316 break;
317 }
318 inb_p(port+2);
319 inb_p(port+5);
320 }
321
322 void change_speed(unsigned int line)
323 {
324 struct serial_struct * info;
325 unsigned short port,quot;
326 unsigned cflag,cval;
327 static unsigned short quotient[] = {
328 0, 2304, 1536, 1047, 857,
329 768, 576, 384, 192, 96,
330 64, 48, 24, 12, 6, 3
331 };
332
333 if (line >= NR_SERIALS)
334 return;
335 info = serial_table + line;
336 cflag = info->tty->termios.c_cflag;
337 if (!(port = info->port))
338 return;
339 quot = quotient[cflag & CBAUD];
340 if (!quot)
341 outb(0x00,port+4);
342 else if (!inb(port+4))
343 startup(port);
344
345 cval = cflag & (CSIZE | CSTOPB);
346 cval >>= 4;
347 if (cflag & PARENB)
348 cval |= 8;
349 if (!(cflag & PARODD))
350 cval |= 16;
351 cli();
352 outb_p(cval | 0x80,port+3);
353 outb_p(quot & 0xff,port);
354 outb_p(quot >> 8,port+1);
355 outb(cval,port+3);
356 sti();
357 }
358
359 static void (*serial_handler[NR_SERIALS])(int) = {
360 com1_IRQ,com2_IRQ,com3_IRQ,com4_IRQ
361 };
362
363
364
365
366
367 int serial_open(unsigned line, struct file * filp)
368 {
369 struct serial_struct * info;
370 int irq,retval;
371 unsigned short port;
372 struct sigaction sa;
373
374 sa.sa_handler = serial_handler[line];
375 sa.sa_flags = SA_INTERRUPT;
376 sa.sa_mask = 0;
377 sa.sa_restorer = NULL;
378 if (line >= NR_SERIALS)
379 return -ENODEV;
380 info = serial_table + line;
381 if (!(port = info->port))
382 return -ENODEV;
383 irq = info->irq;
384 if (irq == 2)
385 irq = 9;
386 if (retval = irqaction(irq,&sa))
387 return retval;
388 startup(port);
389 return 0;
390 }
391
392 int get_serial_info(unsigned int line, struct serial_struct * info)
393 {
394 if (line >= NR_SERIALS)
395 return -ENODEV;
396 if (!info)
397 return -EFAULT;
398 memcpy_tofs(info,serial_table+line,sizeof(*info));
399 return 0;
400 }
401
402 int set_serial_info(unsigned int line, struct serial_struct * info)
403 {
404 struct serial_struct tmp;
405 unsigned new_port;
406 unsigned irq,new_irq;
407 int retval;
408 void (*handler)(int) = serial_handler[line];
409
410 if (!suser())
411 return -EPERM;
412 if (line >= NR_SERIALS)
413 return -ENODEV;
414 if (!info)
415 return -EFAULT;
416 memcpy_fromfs(&tmp,info,sizeof(tmp));
417 info = serial_table + line;
418 if (!(new_port = tmp.port))
419 new_port = info->port;
420 if (!(new_irq = tmp.irq))
421 new_irq = info->irq;
422 if (new_irq > 15 || new_port > 0xffff)
423 return -EINVAL;
424 if (new_irq == 2)
425 new_irq = 9;
426 irq = info->irq;
427 if (irq == 2)
428 irq = 9;
429 if (irq != new_irq) {
430 retval = request_irq(new_irq,handler);
431 if (retval)
432 return retval;
433 info->irq = new_irq;
434 free_irq(irq);
435 }
436 cli();
437 if (new_port != info->port) {
438 outb(0x00,info->port+4);
439 info->port = new_port;
440 init(info);
441 startup(new_port);
442 }
443 sti();
444 return 0;
445 }
446
447 long rs_init(long kmem_start)
448 {
449 int i;
450 struct serial_struct * info;
451
452
453 timer_table[SER1_TIMER].fn = com1_timer;
454 timer_table[SER1_TIMER].expires = 0;
455 timer_table[SER2_TIMER].fn = com2_timer;
456 timer_table[SER2_TIMER].expires = 0;
457 timer_table[SER3_TIMER].fn = com3_timer;
458 timer_table[SER3_TIMER].expires = 0;
459 timer_table[SER4_TIMER].fn = com4_timer;
460 timer_table[SER4_TIMER].expires = 0;
461
462 timer_table[SER1_TIMEOUT].fn = com1_timeout;
463 timer_table[SER1_TIMEOUT].expires = 0;
464 timer_table[SER2_TIMEOUT].fn = com2_timeout;
465 timer_table[SER2_TIMEOUT].expires = 0;
466 timer_table[SER3_TIMEOUT].fn = com3_timeout;
467 timer_table[SER3_TIMEOUT].expires = 0;
468 timer_table[SER4_TIMEOUT].fn = com4_timeout;
469 timer_table[SER4_TIMEOUT].expires = 0;
470 for (i = 0, info = serial_table; i < NR_SERIALS; i++,info++) {
471 info->tty = (tty_table+64) + i;
472 init(info);
473 if (info->type == PORT_UNKNOWN)
474 continue;
475 printk("serial port at 0x%04x (irq = %d)",info->port,info->irq);
476 switch (info->type) {
477 case PORT_8250:
478 printk(" is a 8250\n");
479 break;
480 case PORT_16450:
481 printk(" is a 16450\n");
482 break;
483 case PORT_16550:
484 printk(" is a 16550\n");
485 break;
486 case PORT_16550A:
487 printk(" is a 16550A\n");
488 break;
489 default:
490 printk("\n");
491 break;
492 }
493 }
494 return kmem_start;
495 }
496
497
498
499
500
501
502
503
504 void rs_write(struct tty_struct * tty)
505 {
506 int line = tty - tty_table - 64;
507
508 do_rs_write(serial_table+line);
509 }