]>
Commit | Line | Data |
---|---|---|
47d2d65e | 1 | /* |
2 | * GRUB -- GRand Unified Bootloader | |
75eb7d11 | 3 | * Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc. |
47d2d65e | 4 | * |
5a79f472 | 5 | * GRUB is free software: you can redistribute it and/or modify |
47d2d65e | 6 | * it under the terms of the GNU General Public License as published by |
5a79f472 | 7 | * the Free Software Foundation, either version 3 of the License, or |
47d2d65e | 8 | * (at your option) any later version. |
9 | * | |
5a79f472 | 10 | * GRUB is distributed in the hope that it will be useful, |
47d2d65e | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
5a79f472 | 16 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. |
47d2d65e | 17 | */ |
18 | ||
ffa9860a | 19 | #include <grub/serial.h> |
47d2d65e | 20 | #include <grub/term.h> |
21 | #include <grub/types.h> | |
22 | #include <grub/dl.h> | |
23 | #include <grub/misc.h> | |
47d2d65e | 24 | #include <grub/terminfo.h> |
a9c7fd1c | 25 | #if !defined (GRUB_MACHINE_EMU) && (defined(__mips__) || defined (__i386__) || defined (__x86_64__)) |
5c58b791 | 26 | #include <grub/cpu/io.h> |
a9c7fd1c | 27 | #endif |
b1b797cb | 28 | #include <grub/extcmd.h> |
77a79592 | 29 | #include <grub/i18n.h> |
75eb7d11 | 30 | #include <grub/list.h> |
54da1feb | 31 | #ifdef GRUB_MACHINE_MIPS_LOONGSON |
74eea126 VS |
32 | #include <grub/machine/kernel.h> |
33 | #endif | |
47d2d65e | 34 | |
e745cf0c VS |
35 | GRUB_MOD_LICENSE ("GPLv3+"); |
36 | ||
75eb7d11 | 37 | #define FOR_SERIAL_PORTS(var) FOR_LIST_ELEMENTS((var), (grub_serial_ports)) |
47d2d65e | 38 | |
47d2d65e | 39 | /* Argument options. */ |
40 | static const struct grub_arg_option options[] = | |
41 | { | |
29c44ad1 | 42 | {"unit", 'u', 0, N_("Set the serial unit."), 0, ARG_TYPE_INT}, |
43 | {"port", 'p', 0, N_("Set the serial port address."), 0, ARG_TYPE_STRING}, | |
44 | {"speed", 's', 0, N_("Set the serial port speed."), 0, ARG_TYPE_INT}, | |
45 | {"word", 'w', 0, N_("Set the serial port word length."), 0, ARG_TYPE_INT}, | |
46 | {"parity", 'r', 0, N_("Set the serial port parity."), 0, ARG_TYPE_STRING}, | |
47 | {"stop", 't', 0, N_("Set the serial port stop bits."), 0, ARG_TYPE_INT}, | |
47d2d65e | 48 | {0, 0, 0, 0, 0, 0} |
49 | }; | |
50 | ||
7d4e39d6 | 51 | static struct grub_serial_port *grub_serial_ports; |
47d2d65e | 52 | |
75eb7d11 | 53 | struct grub_serial_output_state |
47d2d65e | 54 | { |
75eb7d11 VS |
55 | struct grub_terminfo_output_state tinfo; |
56 | struct grub_serial_port *port; | |
57 | }; | |
47d2d65e | 58 | |
75eb7d11 | 59 | struct grub_serial_input_state |
47d2d65e | 60 | { |
75eb7d11 VS |
61 | struct grub_terminfo_input_state tinfo; |
62 | struct grub_serial_port *port; | |
63 | }; | |
47d2d65e | 64 | |
75eb7d11 VS |
65 | static void |
66 | serial_put (grub_term_output_t term, const int c) | |
47d2d65e | 67 | { |
75eb7d11 VS |
68 | struct grub_serial_output_state *data = term->data; |
69 | data->port->driver->put (data->port, c); | |
47d2d65e | 70 | } |
71 | ||
75eb7d11 VS |
72 | static int |
73 | serial_fetch (grub_term_input_t term) | |
47d2d65e | 74 | { |
75eb7d11 VS |
75 | struct grub_serial_input_state *data = term->data; |
76 | return data->port->driver->fetch (data->port); | |
47d2d65e | 77 | } |
78 | ||
7d4e39d6 | 79 | static const struct grub_serial_input_state grub_serial_terminfo_input_template = |
bf873374 | 80 | { |
75eb7d11 VS |
81 | .tinfo = |
82 | { | |
83 | .readkey = serial_fetch | |
84 | } | |
bf873374 | 85 | }; |
47d2d65e | 86 | |
7d4e39d6 | 87 | static const struct grub_serial_output_state grub_serial_terminfo_output_template = |
bf873374 | 88 | { |
75eb7d11 VS |
89 | .tinfo = |
90 | { | |
a9cc5438 VS |
91 | .put = serial_put, |
92 | .width = 80, | |
93 | .height = 24 | |
75eb7d11 | 94 | } |
bf873374 | 95 | }; |
47d2d65e | 96 | |
7d4e39d6 | 97 | static struct grub_serial_input_state grub_serial_terminfo_input; |
9c693bd6 | 98 | |
7d4e39d6 | 99 | static struct grub_serial_output_state grub_serial_terminfo_output; |
9c693bd6 | 100 | |
7d4e39d6 | 101 | static int registered = 0; |
75eb7d11 | 102 | |
651c29b7 | 103 | static struct grub_term_input grub_serial_term_input = |
47d2d65e | 104 | { |
105 | .name = "serial", | |
bf873374 | 106 | .init = grub_terminfo_input_init, |
bf873374 VS |
107 | .getkey = grub_terminfo_getkey, |
108 | .data = &grub_serial_terminfo_input | |
651c29b7 | 109 | }; |
110 | ||
111 | static struct grub_term_output grub_serial_term_output = | |
112 | { | |
113 | .name = "serial", | |
b3f8d28a | 114 | .init = grub_terminfo_output_init, |
bf873374 | 115 | .putchar = grub_terminfo_putchar, |
a9cc5438 | 116 | .getwh = grub_terminfo_getwh, |
bf873374 VS |
117 | .getxy = grub_terminfo_getxy, |
118 | .gotoxy = grub_terminfo_gotoxy, | |
119 | .cls = grub_terminfo_cls, | |
120 | .setcolorstate = grub_terminfo_setcolorstate, | |
bf873374 | 121 | .setcursor = grub_terminfo_setcursor, |
d6e0e85b | 122 | .flags = GRUB_TERM_CODE_TYPE_ASCII, |
3c151d94 | 123 | .data = &grub_serial_terminfo_output, |
47d2d65e | 124 | }; |
125 | ||
126 | \f | |
127 | ||
33f784e8 | 128 | struct grub_serial_port * |
bf3a3857 | 129 | grub_serial_find (const char *name) |
75eb7d11 VS |
130 | { |
131 | struct grub_serial_port *port; | |
132 | ||
133 | FOR_SERIAL_PORTS (port) | |
134 | if (grub_strcmp (port->name, name) == 0) | |
135 | break; | |
136 | ||
a9c7fd1c | 137 | #if (defined(__mips__) || defined (__i386__) || defined (__x86_64__)) && !defined(GRUB_MACHINE_EMU) |
75eb7d11 | 138 | if (!port && grub_memcmp (name, "port", sizeof ("port") - 1) == 0 |
0eb8ffb1 | 139 | && grub_isxdigit (name [sizeof ("port") - 1])) |
75eb7d11 VS |
140 | { |
141 | name = grub_serial_ns8250_add_port (grub_strtoul (&name[sizeof ("port") - 1], | |
142 | 0, 16)); | |
143 | if (!name) | |
144 | return NULL; | |
145 | ||
146 | FOR_SERIAL_PORTS (port) | |
147 | if (grub_strcmp (port->name, name) == 0) | |
148 | break; | |
149 | } | |
44e7b8cb | 150 | #endif |
75eb7d11 VS |
151 | |
152 | return port; | |
153 | } | |
154 | ||
47d2d65e | 155 | static grub_err_t |
928bad47 | 156 | grub_cmd_serial (grub_extcmd_context_t ctxt, int argc, char **args) |
47d2d65e | 157 | { |
928bad47 | 158 | struct grub_arg_list *state = ctxt->state; |
75eb7d11 | 159 | char pname[40]; |
bf3a3857 | 160 | const char *name = NULL; |
75eb7d11 | 161 | struct grub_serial_port *port; |
8c8e2699 | 162 | struct grub_serial_config config; |
75eb7d11 | 163 | grub_err_t err; |
47d2d65e | 164 | |
165 | if (state[0].set) | |
166 | { | |
75eb7d11 VS |
167 | grub_snprintf (pname, sizeof (pname), "com%ld", |
168 | grub_strtoul (state[0].arg, 0, 0)); | |
169 | name = pname; | |
47d2d65e | 170 | } |
b39f9d20 | 171 | |
47d2d65e | 172 | if (state[1].set) |
47d2d65e | 173 | { |
75eb7d11 VS |
174 | grub_snprintf (pname, sizeof (pname), "port%lx", |
175 | grub_strtoul (state[1].arg, 0, 0)); | |
176 | name = pname; | |
47d2d65e | 177 | } |
b39f9d20 | 178 | |
75eb7d11 VS |
179 | if (argc >= 1) |
180 | name = args[0]; | |
181 | ||
182 | if (!name) | |
183 | name = "com0"; | |
184 | ||
185 | port = grub_serial_find (name); | |
186 | if (!port) | |
d61386e2 VS |
187 | return grub_error (GRUB_ERR_BAD_ARGUMENT, |
188 | N_("serial port `%s' isn't found"), | |
189 | name); | |
75eb7d11 | 190 | |
8c8e2699 | 191 | config = port->config; |
75eb7d11 VS |
192 | |
193 | if (state[2].set) | |
8c8e2699 | 194 | config.speed = grub_strtoul (state[2].arg, 0, 0); |
75eb7d11 | 195 | |
47d2d65e | 196 | if (state[3].set) |
fd5b6637 | 197 | config.word_len = grub_strtoul (state[3].arg, 0, 0); |
b39f9d20 | 198 | |
47d2d65e | 199 | if (state[4].set) |
200 | { | |
201 | if (! grub_strcmp (state[4].arg, "no")) | |
1da07b14 | 202 | config.parity = GRUB_SERIAL_PARITY_NONE; |
47d2d65e | 203 | else if (! grub_strcmp (state[4].arg, "odd")) |
1da07b14 | 204 | config.parity = GRUB_SERIAL_PARITY_ODD; |
47d2d65e | 205 | else if (! grub_strcmp (state[4].arg, "even")) |
1da07b14 | 206 | config.parity = GRUB_SERIAL_PARITY_EVEN; |
47d2d65e | 207 | else |
d61386e2 VS |
208 | return grub_error (GRUB_ERR_BAD_ARGUMENT, |
209 | N_("unsupported serial port parity")); | |
47d2d65e | 210 | } |
b39f9d20 | 211 | |
47d2d65e | 212 | if (state[5].set) |
213 | { | |
214 | if (! grub_strcmp (state[5].arg, "1")) | |
1da07b14 | 215 | config.stop_bits = GRUB_SERIAL_STOP_BITS_1; |
47d2d65e | 216 | else if (! grub_strcmp (state[5].arg, "2")) |
1da07b14 | 217 | config.stop_bits = GRUB_SERIAL_STOP_BITS_2; |
08dafeea VS |
218 | else if (! grub_strcmp (state[5].arg, "1.5")) |
219 | config.stop_bits = GRUB_SERIAL_STOP_BITS_1_5; | |
47d2d65e | 220 | else |
d61386e2 VS |
221 | return grub_error (GRUB_ERR_BAD_ARGUMENT, |
222 | N_("unsupported serial port stop bits number")); | |
47d2d65e | 223 | } |
224 | ||
225 | /* Initialize with new settings. */ | |
8c8e2699 | 226 | err = port->driver->configure (port, &config); |
75eb7d11 VS |
227 | if (err) |
228 | return err; | |
a9c7fd1c VS |
229 | #if !defined (GRUB_MACHINE_EMU) && (defined(__mips__) || defined (__i386__) || defined (__x86_64__)) |
230 | ||
24494d47 VS |
231 | /* Compatibility kludge. */ |
232 | if (port->driver == &grub_ns8250_driver) | |
75eb7d11 | 233 | { |
24494d47 VS |
234 | if (!registered) |
235 | { | |
9c693bd6 VS |
236 | grub_terminfo_output_register (&grub_serial_term_output, "vt100"); |
237 | ||
24494d47 VS |
238 | grub_term_register_input ("serial", &grub_serial_term_input); |
239 | grub_term_register_output ("serial", &grub_serial_term_output); | |
240 | } | |
241 | grub_serial_terminfo_output.port = port; | |
242 | grub_serial_terminfo_input.port = port; | |
243 | registered = 1; | |
75eb7d11 | 244 | } |
44e7b8cb | 245 | #endif |
75eb7d11 VS |
246 | return GRUB_ERR_NONE; |
247 | } | |
248 | ||
14a2562c VS |
249 | #ifdef GRUB_MACHINE_MIPS_LOONGSON |
250 | const char loongson_defserial[][6] = | |
251 | { | |
252 | [GRUB_ARCH_MACHINE_YEELOONG] = "com0", | |
253 | [GRUB_ARCH_MACHINE_FULOONG2F] = "com2", | |
254 | [GRUB_ARCH_MACHINE_FULOONG2E] = "com1" | |
255 | }; | |
256 | #endif | |
257 | ||
75eb7d11 VS |
258 | grub_err_t |
259 | grub_serial_register (struct grub_serial_port *port) | |
260 | { | |
261 | struct grub_term_input *in; | |
262 | struct grub_term_output *out; | |
263 | struct grub_serial_input_state *indata; | |
264 | struct grub_serial_output_state *outdata; | |
b39f9d20 | 265 | |
75eb7d11 VS |
266 | in = grub_malloc (sizeof (*in)); |
267 | if (!in) | |
268 | return grub_errno; | |
269 | ||
270 | indata = grub_malloc (sizeof (*indata)); | |
271 | if (!indata) | |
47d2d65e | 272 | { |
75eb7d11 VS |
273 | grub_free (in); |
274 | return grub_errno; | |
47d2d65e | 275 | } |
75eb7d11 VS |
276 | |
277 | grub_memcpy (in, &grub_serial_term_input, sizeof (*in)); | |
278 | in->data = indata; | |
279 | in->name = grub_xasprintf ("serial_%s", port->name); | |
280 | grub_memcpy (indata, &grub_serial_terminfo_input, sizeof (*indata)); | |
281 | ||
282 | if (!in->name) | |
283 | { | |
284 | grub_free (in); | |
285 | grub_free (indata); | |
286 | return grub_errno; | |
287 | } | |
288 | ||
289 | out = grub_malloc (sizeof (*out)); | |
290 | if (!out) | |
291 | { | |
292 | grub_free (in); | |
293 | grub_free (indata); | |
294 | grub_free ((char *) in->name); | |
295 | return grub_errno; | |
296 | } | |
297 | ||
298 | outdata = grub_malloc (sizeof (*outdata)); | |
299 | if (!outdata) | |
47d2d65e | 300 | { |
75eb7d11 VS |
301 | grub_free (in); |
302 | grub_free (indata); | |
303 | grub_free ((char *) in->name); | |
304 | grub_free (out); | |
305 | return grub_errno; | |
47d2d65e | 306 | } |
b39f9d20 | 307 | |
75eb7d11 VS |
308 | grub_memcpy (out, &grub_serial_term_output, sizeof (*out)); |
309 | out->data = outdata; | |
310 | out->name = in->name; | |
311 | grub_memcpy (outdata, &grub_serial_terminfo_output, sizeof (*outdata)); | |
312 | ||
313 | grub_list_push (GRUB_AS_LIST_P (&grub_serial_ports), GRUB_AS_LIST (port)); | |
314 | ((struct grub_serial_input_state *) in->data)->port = port; | |
315 | ((struct grub_serial_output_state *) out->data)->port = port; | |
24494d47 VS |
316 | port->term_in = in; |
317 | port->term_out = out; | |
75eb7d11 | 318 | grub_terminfo_output_register (out, "vt100"); |
54da1feb | 319 | #ifdef GRUB_MACHINE_MIPS_LOONGSON |
14a2562c | 320 | if (grub_strcmp (port->name, loongson_defserial[grub_arch_machine]) == 0) |
24494d47 VS |
321 | { |
322 | grub_term_register_input_active ("serial_*", in); | |
323 | grub_term_register_output_active ("serial_*", out); | |
324 | } | |
d7345994 VS |
325 | else |
326 | { | |
327 | grub_term_register_input_inactive ("serial_*", in); | |
328 | grub_term_register_output_inactive ("serial_*", out); | |
329 | } | |
74eea126 VS |
330 | #else |
331 | grub_term_register_input ("serial_*", in); | |
332 | grub_term_register_output ("serial_*", out); | |
333 | #endif | |
75eb7d11 VS |
334 | |
335 | return GRUB_ERR_NONE; | |
336 | } | |
337 | ||
338 | void | |
339 | grub_serial_unregister (struct grub_serial_port *port) | |
340 | { | |
24494d47 VS |
341 | if (port->driver->fini) |
342 | port->driver->fini (port); | |
343 | ||
344 | if (port->term_in) | |
345 | grub_term_unregister_input (port->term_in); | |
346 | if (port->term_out) | |
347 | grub_term_unregister_output (port->term_out); | |
348 | ||
87edb894 | 349 | grub_list_remove (GRUB_AS_LIST (port)); |
24494d47 VS |
350 | } |
351 | ||
352 | void | |
353 | grub_serial_unregister_driver (struct grub_serial_driver *driver) | |
354 | { | |
355 | struct grub_serial_port *port, *next; | |
356 | for (port = grub_serial_ports; port; port = next) | |
357 | { | |
358 | next = port->next; | |
359 | if (port->driver == driver) | |
360 | grub_serial_unregister (port); | |
361 | } | |
47d2d65e | 362 | } |
363 | ||
b1b797cb | 364 | static grub_extcmd_t cmd; |
365 | ||
036985b8 VS |
366 | #if defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS) |
367 | void grub_serial_init (void) | |
368 | #else | |
6d099807 | 369 | GRUB_MOD_INIT(serial) |
036985b8 | 370 | #endif |
47d2d65e | 371 | { |
ed80f7d5 | 372 | cmd = grub_register_extcmd ("serial", grub_cmd_serial, 0, |
d8b5cd40 VS |
373 | N_("[OPTIONS...]"), |
374 | N_("Configure serial port."), options); | |
9c693bd6 VS |
375 | grub_memcpy (&grub_serial_terminfo_output, |
376 | &grub_serial_terminfo_output_template, | |
377 | sizeof (grub_serial_terminfo_output)); | |
378 | ||
379 | grub_memcpy (&grub_serial_terminfo_input, | |
380 | &grub_serial_terminfo_input_template, | |
381 | sizeof (grub_serial_terminfo_input)); | |
a9c7fd1c VS |
382 | |
383 | #if !defined (GRUB_MACHINE_EMU) && (defined(__mips__) || defined (__i386__) || defined (__x86_64__)) | |
75eb7d11 | 384 | grub_ns8250_init (); |
44e7b8cb | 385 | #endif |
a9c7fd1c VS |
386 | #ifdef GRUB_MACHINE_IEEE1275 |
387 | grub_ofserial_init (); | |
388 | #endif | |
389 | #ifdef GRUB_MACHINE_EFI | |
390 | grub_efiserial_init (); | |
391 | #endif | |
47d2d65e | 392 | } |
393 | ||
036985b8 VS |
394 | #if defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS) |
395 | void grub_serial_fini (void) | |
396 | #else | |
6d099807 | 397 | GRUB_MOD_FINI(serial) |
036985b8 | 398 | #endif |
47d2d65e | 399 | { |
75eb7d11 VS |
400 | while (grub_serial_ports) |
401 | grub_serial_unregister (grub_serial_ports); | |
24494d47 VS |
402 | if (registered) |
403 | { | |
404 | grub_term_unregister_input (&grub_serial_term_input); | |
405 | grub_term_unregister_output (&grub_serial_term_output); | |
406 | } | |
b1b797cb | 407 | grub_unregister_extcmd (cmd); |
47d2d65e | 408 | } |