123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- /*
- * GRUB -- GRand Unified Bootloader
- * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2012 Free Software Foundation, Inc.
- *
- * GRUB is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GRUB is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <grub/serial.h>
- #include <grub/types.h>
- #include <grub/dl.h>
- #include <grub/misc.h>
- #include <grub/mm.h>
- #include <grub/time.h>
- #include <grub/i18n.h>
- GRUB_MOD_LICENSE ("GPLv3+");
- struct grub_escc_descriptor
- {
- volatile grub_uint8_t *escc_ctrl;
- volatile grub_uint8_t *escc_data;
- };
- static void
- do_real_config (struct grub_serial_port *port)
- {
- grub_uint8_t bitsspec;
- grub_uint8_t parity_stop_spec;
- if (port->configured)
- return;
- /* Make sure the port is waiting for address now. */
- (void) *port->escc_desc->escc_ctrl;
- switch (port->config.speed)
- {
- case 57600:
- *port->escc_desc->escc_ctrl = 13;
- *port->escc_desc->escc_ctrl = 0;
- *port->escc_desc->escc_ctrl = 12;
- *port->escc_desc->escc_ctrl = 0;
- *port->escc_desc->escc_ctrl = 14;
- *port->escc_desc->escc_ctrl = 1;
- *port->escc_desc->escc_ctrl = 11;
- *port->escc_desc->escc_ctrl = 0x50;
- break;
- case 38400:
- *port->escc_desc->escc_ctrl = 13;
- *port->escc_desc->escc_ctrl = 0;
- *port->escc_desc->escc_ctrl = 12;
- *port->escc_desc->escc_ctrl = 1;
- *port->escc_desc->escc_ctrl = 14;
- *port->escc_desc->escc_ctrl = 1;
- *port->escc_desc->escc_ctrl = 11;
- *port->escc_desc->escc_ctrl = 0x50;
- break;
- }
- parity_stop_spec = 0;
- switch (port->config.parity)
- {
- case GRUB_SERIAL_PARITY_NONE:
- parity_stop_spec |= 0;
- break;
- case GRUB_SERIAL_PARITY_ODD:
- parity_stop_spec |= 1;
- break;
- case GRUB_SERIAL_PARITY_EVEN:
- parity_stop_spec |= 3;
- break;
- }
- switch (port->config.stop_bits)
- {
- case GRUB_SERIAL_STOP_BITS_1:
- parity_stop_spec |= 0x4;
- break;
- case GRUB_SERIAL_STOP_BITS_1_5:
- parity_stop_spec |= 0x8;
- break;
- case GRUB_SERIAL_STOP_BITS_2:
- parity_stop_spec |= 0xc;
- break;
- }
- *port->escc_desc->escc_ctrl = 4;
- *port->escc_desc->escc_ctrl = 0x40 | parity_stop_spec;
- bitsspec = port->config.word_len - 5;
- bitsspec = ((bitsspec >> 1) | (bitsspec << 1)) & 3;
- *port->escc_desc->escc_ctrl = 3;
- *port->escc_desc->escc_ctrl = (bitsspec << 6) | 0x1;
- port->configured = 1;
- return;
- }
- /* Fetch a key. */
- static int
- serial_hw_fetch (struct grub_serial_port *port)
- {
- do_real_config (port);
- *port->escc_desc->escc_ctrl = 0;
- if (*port->escc_desc->escc_ctrl & 1)
- return *port->escc_desc->escc_data;
- return -1;
- }
- /* Put a character. */
- static void
- serial_hw_put (struct grub_serial_port *port, const int c)
- {
- grub_uint64_t endtime;
- do_real_config (port);
- if (port->broken > 5)
- endtime = grub_get_time_ms ();
- else if (port->broken > 1)
- endtime = grub_get_time_ms () + 50;
- else
- endtime = grub_get_time_ms () + 200;
- /* Wait until the transmitter holding register is empty. */
- while (1)
- {
- *port->escc_desc->escc_ctrl = 0;
- if (*port->escc_desc->escc_ctrl & 4)
- break;
- if (grub_get_time_ms () > endtime)
- {
- port->broken++;
- /* There is something wrong. But what can I do? */
- return;
- }
- }
- if (port->broken)
- port->broken--;
- *port->escc_desc->escc_data = c;
- }
- /* Initialize a serial device. PORT is the port number for a serial device.
- SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600,
- 19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used
- for the device. Likewise, PARITY is the type of the parity and
- STOP_BIT_LEN is the length of the stop bit. The possible values for
- WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as
- macros. */
- static grub_err_t
- serial_hw_configure (struct grub_serial_port *port __attribute__ ((unused)),
- struct grub_serial_config *config __attribute__ ((unused)))
- {
- if (config->speed != 38400 && config->speed != 57600)
- return grub_error (GRUB_ERR_BAD_ARGUMENT,
- N_("unsupported serial port speed"));
- if (config->parity != GRUB_SERIAL_PARITY_NONE
- && config->parity != GRUB_SERIAL_PARITY_ODD
- && config->parity != GRUB_SERIAL_PARITY_EVEN)
- return grub_error (GRUB_ERR_BAD_ARGUMENT,
- N_("unsupported serial port parity"));
- if (config->stop_bits != GRUB_SERIAL_STOP_BITS_1
- && config->stop_bits != GRUB_SERIAL_STOP_BITS_1_5
- && config->stop_bits != GRUB_SERIAL_STOP_BITS_2)
- return grub_error (GRUB_ERR_BAD_ARGUMENT,
- N_("unsupported serial port stop bits number"));
- if (config->word_len < 5 || config->word_len > 8)
- return grub_error (GRUB_ERR_BAD_ARGUMENT,
- N_("unsupported serial port word length"));
- port->config = *config;
- port->configured = 0;
- /* FIXME: should check if the serial terminal was found. */
- return GRUB_ERR_NONE;
- }
- struct grub_serial_driver grub_escc_driver =
- {
- .configure = serial_hw_configure,
- .fetch = serial_hw_fetch,
- .put = serial_hw_put
- };
- static struct grub_escc_descriptor escc_descs[2];
- static char *macio = 0;
- static void
- add_device (grub_addr_t addr, int channel)
- {
- struct grub_serial_port *port;
- grub_err_t err;
- struct grub_serial_config config =
- {
- .speed = 38400,
- .word_len = 8,
- .parity = GRUB_SERIAL_PARITY_NONE,
- .stop_bits = GRUB_SERIAL_STOP_BITS_1
- };
- escc_descs[channel].escc_ctrl
- = (volatile grub_uint8_t *) (grub_addr_t) addr;
- escc_descs[channel].escc_data = escc_descs[channel].escc_ctrl + 16;
- port = grub_zalloc (sizeof (*port));
- if (!port)
- {
- grub_errno = 0;
- return;
- }
- port->name = grub_xasprintf ("escc-ch-%c", channel + 'a');
- if (!port->name)
- {
- grub_errno = 0;
- return;
- }
- port->escc_desc = &escc_descs[channel];
- port->driver = &grub_escc_driver;
- err = port->driver->configure (port, &config);
- if (err)
- grub_print_error ();
- grub_serial_register (port);
- }
- static int
- find_macio (struct grub_ieee1275_devalias *alias)
- {
- if (grub_strcmp (alias->type, "mac-io") != 0)
- return 0;
- macio = grub_strdup (alias->path);
- return 1;
- }
- GRUB_MOD_INIT (escc)
- {
- grub_uint32_t macio_addr[4];
- grub_uint32_t escc_addr[2];
- grub_ieee1275_phandle_t dev;
- struct grub_ieee1275_devalias alias;
- char *escc = 0;
- grub_ieee1275_devices_iterate (find_macio);
- if (!macio)
- return;
- FOR_IEEE1275_DEVCHILDREN(macio, alias)
- if (grub_strcmp (alias.type, "escc") == 0)
- {
- escc = grub_strdup (alias.path);
- break;
- }
- grub_ieee1275_devalias_free (&alias);
- if (!escc)
- {
- grub_free (macio);
- return;
- }
- if (grub_ieee1275_finddevice (macio, &dev))
- {
- grub_free (macio);
- grub_free (escc);
- return;
- }
- if (grub_ieee1275_get_integer_property (dev, "assigned-addresses",
- macio_addr, sizeof (macio_addr), 0))
- {
- grub_free (macio);
- grub_free (escc);
- return;
- }
- if (grub_ieee1275_finddevice (escc, &dev))
- {
- grub_free (macio);
- grub_free (escc);
- return;
- }
- if (grub_ieee1275_get_integer_property (dev, "reg",
- escc_addr, sizeof (escc_addr), 0))
- {
- grub_free (macio);
- grub_free (escc);
- return;
- }
- add_device (macio_addr[2] + escc_addr[0] + 32, 0);
- add_device (macio_addr[2] + escc_addr[0], 1);
- grub_free (macio);
- grub_free (escc);
- }
- GRUB_MOD_FINI (escc)
- {
- }
|