nckernel  0.1
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
serial.c
Go to the documentation of this file.
1 #include <slibc.h>
2 #include <sys/types.h>
3 #include <sys/io.h>
4 #include <stdio.h>
5 #include <errno.h>
6 #include <stdarg.h>
7 #include <stdlib.h>
8 #include <stddef.h>
9 #include <assert.h>
10 
11 #include <list.h>
12 
13 #include <object.h>
14 #include <vfs.h>
15 #include <device.h>
16 #include <serial.h>
17 #include <interrupt.h>
18 #include <isr.h>
19 
20 #include <debug.h>
21 
22 static unsigned int COM[] = { 0x3F8, 0x2F8, 0x3E8, 0x2E8 };
23 static LIST_HEAD(s_serial);
24 
25 struct context {
26  int setup;
27  int parity;
28  int stop;
29  int data;
30  int baudrate;
31  int validate;
32 };
33 
35  DATA = 0x00,
36  INTR_ENABLE = 0x01,
37  BAUD_LO = 0x00,
38  BAUD_HI = 0x01,
39  FIFO = 0x02,
40  LINE_CTRL = 0x03,
41  MODEM_CTRL = 0x04,
42  LINE_STATUS = 0x05,
43  MODEM_STATUS = 0x06,
44  SCRATCH = 0x07,
45 };
46 
47 #define DATA5 0x00
48 #define DATA6 0x01
49 #define DATA7 0x02
50 #define DATA8 0x03
51 
52 #define STOPBITS1 0x00
53 #define STOPBITS2 0x01
54 
55 #define NO_PARITY 0x00
56 #define ODD_PARITY 0x01
57 #define EVEN_PARITY 0x02
58 
59 #define SERIAL_MAX_SPEED 115200
60 
61 enum IER {
62  RX = 0x01,
63  TX = 0x02,
64  RX_STAT = 0x04, /*< Enable Receiver Line Status Interrupt */
65  MODEM_STAT = 0x08,
66  SLEEP_MODE = 0x10,
67  LOW_POWER = 0x20,
68 };
69 
70 static int serial_isr(int sub_irq, void *info, void *data)
71 {
72  //printf("%s\n", __func__);
73  return 0;
74 }
75 
76 static inline void apply_setup(struct ninfo *ninfo, struct nctx *ctx)
77 {
78  struct ninfo_dev *dev;
79  struct context *priv;
80  unsigned char value;
81  int divisor;
82  int id;
83 
84  dev = ninfo->priv;
85  priv = ctx->priv;
86 
87  id = MINOR(dev->device);
88 
90  outb(COM[id] + 1, 0x00);
91 
92  divisor = SERIAL_MAX_SPEED / priv->baudrate;
93  outb(COM[id] + 3, 0x80);
95  outb(COM[id] + 0, (unsigned char)divisor);
96  outb(COM[id] + 1, (unsigned char)(divisor>>4));
97 
98  value = priv->data & 0x03;
99  value |= ((priv->stop & 0x1) << 2);
100  value |= ((priv->parity & 0x07) << 3);
101  outb(COM[id] + 3, value);
102 
104  outb(COM[id] + 4, RX | TX | MODEM_STAT);
105 }
106 
107 int serial_open(struct ninfo *ninfo, struct nctx *ctx)
108 {
109  int id;
110  struct ninfo_dev *dev;
111  struct context *priv;
112  int ret;
113  int irq;
114 
115  dev = ninfo->priv;
116 
117  id = MINOR(dev->device);
118  if (id > (sizeof(COM) / sizeof(COM[0]))) {
119  return -ENODEV;
120  }
121 
122  priv = malloc(sizeof(*priv));
123  if (!priv) {
124  return -ENOMEM;
125  }
126 
127  ctx->priv = priv;
128  ctx->offset = 0;
129 
131  irq = (id % 2) ? IRQ_NR_SERIAL_ODD : IRQ_NR_SERIAL_EVEN;
132  ret = register_irq(irq, NORMAL_PRIORITY, serial_isr, NULL);
133  if (ret < 0) {
134  free(priv);
135  return ret;
136  }
137 
138  return 0;
139 }
140 
141 int serial_read(struct ninfo *ninfo, struct nctx *ctx, void *buf, size_t size)
142 {
143  struct context *priv;
144 
145  priv = ctx->priv;
146  if (priv->setup) {
147  return -EBUSY;
148  }
149 
150  /* READ: inb(COM[ctx->id]) */
151  return 0;
152 }
153 
154 int serial_write(struct ninfo *ninfo, struct nctx *ctx,
155  const void *buf, size_t size)
156 {
157  struct context *priv;
158  priv = ctx->priv;
159 
160  if (priv->setup) {
161  return -EBUSY;
162  }
163 
164  /* WRITE: outb(COM[ctx->id], buffer[0]) */
165  return 0;
166 }
167 
168 int serial_ioctl(struct ninfo *ninfo, struct nctx *ctx, int request, va_list va)
169 {
170  struct context *priv;
171 
172  priv = ctx->priv;
173 
174  switch (request) {
176  if (!priv->setup) {
177  priv->setup = 1;
178  priv->validate = 0;
179  }
180  break;
181  case SERIAL_CMD_BAUDRATE:
182  priv->baudrate = va_arg(va, int);
183  priv->validate |= 0x01;
184  break;
185  case SERIAL_CMD_PARITY:
186  priv->parity = va_arg(va, int);
187  priv->validate |= 0x02;
188  break;
189  case SERIAL_CMD_STOP:
190  priv->stop = va_arg(va, int);
191  priv->validate |= 0x04;
192  break;
193  case SERIAL_CMD_DATA:
194  priv->data = va_arg(va, int);
195  priv->validate |= 0x08;
196  break;
198  if (priv->setup) {
199  if (priv->validate == 0x0F) {
200  apply_setup(ninfo, ctx);
201  }
202 
203  priv->setup = 0;
204  }
205  break;
206  default:
207  return -EINVAL;
208  }
209 
210  return 0;
211 }
212 
213 int serial_close(struct ninfo *ninfo, struct nctx *ctx)
214 {
215  struct ninfo_dev *dev;
216  int irq;
217  int ret;
218 
219  dev = ninfo->priv;
220  //priv = ctx->priv;
221 
222  irq = (MINOR(dev->device) % 2) ? IRQ_NR_SERIAL_ODD : IRQ_NR_SERIAL_EVEN;
223 
227  ret = unregister_irq(irq, serial_isr, NULL);
228 
229  free(ctx->priv);
230  ctx->priv = NULL;
231  return ret;
232 }
233 
234 static struct ninfo_ops serial_ops = {
235  .open = serial_open,
236  .read = serial_read,
237  .write = serial_write,
238  .ioctl = serial_ioctl,
239  .close = serial_close,
240 };
241 
242 int serial_init(void)
243 {
244  struct ninfo *parent;
245  struct ninfo *ninfo;
246 
247  parent = vfs_get_ninfo(NULL, NULL, "/dev");
248  if (!parent) {
249  return -EFAULT;
250  }
251 
252  ninfo = vfs_new_dev_ninfo(NULL, parent,
253  "com1", DEVICE_COM1, &serial_ops);
254  if (!ninfo) {
255  return -EFAULT;
256  }
257 
258  ninfo = vfs_new_dev_ninfo(NULL, parent,
259  "com2", DEVICE_COM2, &serial_ops);
260  if (!ninfo) {
261  goto out;
262  }
263 
264  ninfo = vfs_new_dev_ninfo(NULL, parent,
265  "com3", DEVICE_COM3, &serial_ops);
266  if (!ninfo) {
267  goto out;
268  }
269 
270  ninfo = vfs_new_dev_ninfo(NULL, parent,
271  "com4", DEVICE_COM4, &serial_ops);
272  if (!ninfo) {
273  goto out;
274  }
275 
276 out:
277  return 0;
278 }
279 
280 /* End of a file */