From 3105b8a08fd6f31e97f9ebc3236eb37937b51e33 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 24 Aug 2003 18:39:44 +0100 Subject: [SERIAL] Add new port numbers. This adds the new port numbers which are in use in MAC and PARISC trees (so other people know they're taken.) --- include/linux/serial_core.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index a7cb796c4deb..0bc15aeb153e 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -67,6 +67,15 @@ #define PORT_PC9861 45 #define PORT_PC9801_101 46 +/* DZ */ +#define PORT_DZ 47 + +/* Parisc type numbers. */ +#define PORT_MUX 48 + +/* Macintosh Zilog type numbers */ +#define PORT_MAC_ZILOG 50 /* m68k : not yet implemented */ +#define PORT_PMAC_ZILOG 51 #ifdef __KERNEL__ -- cgit v1.2.3 From d407e13e62177d2e6d0cbad38e3fd0b9d273ae0c Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 28 Aug 2003 00:07:18 +0100 Subject: [SERIAL] Rename core.o and 8250_cs.o core.ko is a bad name for a module - make it serial_core.ko 8250_cs.ko continues to cause people compatibility problems with older kernels, so rename that back to serial_cs.ko --- drivers/serial/8250_cs.c | 712 ------------- drivers/serial/Makefile | 4 +- drivers/serial/core.c | 2432 ------------------------------------------ drivers/serial/serial_core.c | 2432 ++++++++++++++++++++++++++++++++++++++++++ drivers/serial/serial_cs.c | 712 +++++++++++++ 5 files changed, 3146 insertions(+), 3146 deletions(-) delete mode 100644 drivers/serial/8250_cs.c delete mode 100644 drivers/serial/core.c create mode 100644 drivers/serial/serial_core.c create mode 100644 drivers/serial/serial_cs.c diff --git a/drivers/serial/8250_cs.c b/drivers/serial/8250_cs.c deleted file mode 100644 index a70c3ddfadfa..000000000000 --- a/drivers/serial/8250_cs.c +++ /dev/null @@ -1,712 +0,0 @@ -/*====================================================================== - - A driver for PCMCIA serial devices - - serial_cs.c 1.134 2002/05/04 05:48:53 - - The contents of this file are subject to the Mozilla Public - License Version 1.1 (the "License"); you may not use this file - except in compliance with the License. You may obtain a copy of - the License at http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS - IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - implied. See the License for the specific language governing - rights and limitations under the License. - - The initial developer of the original code is David A. Hinds - . Portions created by David A. Hinds - are Copyright (C) 1999 David A. Hinds. All Rights Reserved. - - Alternatively, the contents of this file may be used under the - terms of the GNU General Public License version 2 (the "GPL"), in which - case the provisions of the GPL are applicable instead of the - above. If you wish to allow the use of your version of this file - only under the terms of the GPL and not to allow others to use - your version of this file under the MPL, indicate your decision - by deleting the provisions above and replace them with the notice - and other provisions required by the GPL. If you do not delete - the provisions above, a recipient may use your version of this - file under either the MPL or the GPL. - -======================================================================*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#ifdef PCMCIA_DEBUG -static int pc_debug = PCMCIA_DEBUG; -MODULE_PARM(pc_debug, "i"); -#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) -static char *version = "serial_cs.c 1.134 2002/05/04 05:48:53 (David Hinds)"; -#else -#define DEBUG(n, args...) -#endif - -/*====================================================================*/ - -/* Parameters that can be set with 'insmod' */ - -/* Bit map of interrupts to choose from */ -static u_int irq_mask = 0xdeb8; -static int irq_list[4] = { -1 }; - -/* Enable the speaker? */ -static int do_sound = 1; -/* Skip strict UART tests? */ -static int buggy_uart; - -MODULE_PARM(irq_mask, "i"); -MODULE_PARM(irq_list, "1-4i"); -MODULE_PARM(do_sound, "i"); -MODULE_PARM(buggy_uart, "i"); - -/*====================================================================*/ - -/* Table of multi-port card ID's */ - -struct multi_id { - u_short manfid; - u_short prodid; - int multi; /* 1 = multifunction, > 1 = # ports */ -}; - -static struct multi_id multi_id[] = { - { MANFID_OMEGA, PRODID_OMEGA_QSP_100, 4 }, - { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232, 2 }, - { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D1, 2 }, - { MANFID_QUATECH, PRODID_QUATECH_QUAD_RS232, 4 }, - { MANFID_SOCKET, PRODID_SOCKET_DUAL_RS232, 2 }, - { MANFID_INTEL, PRODID_INTEL_DUAL_RS232, 2 }, - { MANFID_NATINST, PRODID_NATINST_QUAD_RS232, 4 } -}; -#define MULTI_COUNT (sizeof(multi_id)/sizeof(struct multi_id)) - -struct serial_info { - dev_link_t link; - int ndev; - int multi; - int slave; - int manfid; - dev_node_t node[4]; - int line[4]; -}; - -static void serial_config(dev_link_t * link); -static int serial_event(event_t event, int priority, - event_callback_args_t * args); - -static dev_info_t dev_info = "serial_cs"; - -static dev_link_t *serial_attach(void); -static void serial_detach(dev_link_t *); - -static dev_link_t *dev_list = NULL; - -/*====================================================================== - - After a card is removed, serial_remove() will unregister - the serial device(s), and release the PCMCIA configuration. - -======================================================================*/ - -static void serial_remove(dev_link_t *link) -{ - struct serial_info *info = link->priv; - int i; - - link->state &= ~DEV_PRESENT; - - DEBUG(0, "serial_release(0x%p)\n", link); - - /* - * Recheck to see if the device is still configured. - */ - if (info->link.state & DEV_CONFIG) { - for (i = 0; i < info->ndev; i++) - unregister_serial(info->line[i]); - - info->link.dev = NULL; - - if (!info->slave) { - CardServices(ReleaseConfiguration, info->link.handle); - CardServices(ReleaseIO, info->link.handle, &info->link.io); - CardServices(ReleaseIRQ, info->link.handle, &info->link.irq); - } - - info->link.state &= ~DEV_CONFIG; - } -} - -/*====================================================================== - - serial_attach() creates an "instance" of the driver, allocating - local data structures for one device. The device is registered - with Card Services. - -======================================================================*/ - -static dev_link_t *serial_attach(void) -{ - struct serial_info *info; - client_reg_t client_reg; - dev_link_t *link; - int i, ret; - - DEBUG(0, "serial_attach()\n"); - - /* Create new serial device */ - info = kmalloc(sizeof (*info), GFP_KERNEL); - if (!info) - return NULL; - memset(info, 0, sizeof (*info)); - link = &info->link; - link->priv = info; - - link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; - link->io.NumPorts1 = 8; - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; - link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; - if (irq_list[0] == -1) - link->irq.IRQInfo2 = irq_mask; - else - for (i = 0; i < 4; i++) - link->irq.IRQInfo2 |= 1 << irq_list[i]; - link->conf.Attributes = CONF_ENABLE_IRQ; - if (do_sound) { - link->conf.Attributes |= CONF_ENABLE_SPKR; - link->conf.Status = CCSR_AUDIO_ENA; - } - link->conf.IntType = INT_MEMORY_AND_IO; - - /* Register with Card Services */ - link->next = dev_list; - dev_list = link; - client_reg.dev_info = &dev_info; - client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; - client_reg.EventMask = - CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | - CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | - CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; - client_reg.event_handler = &serial_event; - client_reg.Version = 0x0210; - client_reg.event_callback_args.client_data = link; - ret = CardServices(RegisterClient, &link->handle, &client_reg); - if (ret != CS_SUCCESS) { - cs_error(link->handle, RegisterClient, ret); - serial_detach(link); - return NULL; - } - - return link; -} - -/*====================================================================== - - This deletes a driver "instance". The device is de-registered - with Card Services. If it has been released, all local data - structures are freed. Otherwise, the structures will be freed - when the device is released. - -======================================================================*/ - -static void serial_detach(dev_link_t * link) -{ - struct serial_info *info = link->priv; - dev_link_t **linkp; - int ret; - - DEBUG(0, "serial_detach(0x%p)\n", link); - - /* Locate device structure */ - for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) - if (*linkp == link) - break; - if (*linkp == NULL) - return; - - /* - * Ensure any outstanding scheduled tasks are completed. - */ - flush_scheduled_work(); - - /* - * Ensure that the ports have been released. - */ - serial_remove(link); - - if (link->handle) { - ret = CardServices(DeregisterClient, link->handle); - if (ret != CS_SUCCESS) - cs_error(link->handle, DeregisterClient, ret); - } - - /* Unlink device structure, free bits */ - *linkp = link->next; - kfree(info); -} - -/*====================================================================*/ - -static int setup_serial(struct serial_info * info, ioaddr_t port, int irq) -{ - struct serial_struct serial; - int line; - - memset(&serial, 0, sizeof (serial)); - serial.port = port; - serial.irq = irq; - serial.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ; - if (buggy_uart) - serial.flags |= UPF_BUGGY_UART; - line = register_serial(&serial); - if (line < 0) { - printk(KERN_NOTICE "serial_cs: register_serial() at 0x%04lx," - " irq %d failed\n", (u_long) serial.port, serial.irq); - return -EINVAL; - } - - info->line[info->ndev] = line; - sprintf(info->node[info->ndev].dev_name, "ttyS%d", line); - info->node[info->ndev].major = TTY_MAJOR; - info->node[info->ndev].minor = 0x40 + line; - if (info->ndev > 0) - info->node[info->ndev - 1].next = &info->node[info->ndev]; - info->ndev++; - - return 0; -} - -/*====================================================================*/ - -static int -get_tuple(int fn, client_handle_t handle, tuple_t * tuple, cisparse_t * parse) -{ - int i; - i = CardServices(fn, handle, tuple); - if (i != CS_SUCCESS) - return CS_NO_MORE_ITEMS; - i = CardServices(GetTupleData, handle, tuple); - if (i != CS_SUCCESS) - return i; - return CardServices(ParseTuple, handle, tuple, parse); -} - -#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c) -#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c) - -/*====================================================================*/ - -static int simple_config(dev_link_t * link) -{ - static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; - client_handle_t handle = link->handle; - struct serial_info *info = link->priv; - tuple_t tuple; - u_char buf[256]; - cisparse_t parse; - cistpl_cftable_entry_t *cf = &parse.cftable_entry; - config_info_t config; - int i, j, try; - - /* If the card is already configured, look up the port and irq */ - i = CardServices(GetConfigurationInfo, handle, &config); - if ((i == CS_SUCCESS) && (config.Attributes & CONF_VALID_CLIENT)) { - ioaddr_t port = 0; - if ((config.BasePort2 != 0) && (config.NumPorts2 == 8)) { - port = config.BasePort2; - info->slave = 1; - } else if ((info->manfid == MANFID_OSITECH) && - (config.NumPorts1 == 0x40)) { - port = config.BasePort1 + 0x28; - info->slave = 1; - } - if (info->slave) - return setup_serial(info, port, config.AssignedIRQ); - } - link->conf.Vcc = config.Vcc; - - /* First pass: look for a config entry that looks normal. */ - tuple.TupleData = (cisdata_t *) buf; - tuple.TupleOffset = 0; - tuple.TupleDataMax = 255; - tuple.Attributes = 0; - tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - /* Two tries: without IO aliases, then with aliases */ - for (try = 0; try < 2; try++) { - i = first_tuple(handle, &tuple, &parse); - while (i != CS_NO_MORE_ITEMS) { - if (i != CS_SUCCESS) - goto next_entry; - if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) - link->conf.Vpp1 = link->conf.Vpp2 = - cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; - if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && - (cf->io.win[0].base != 0)) { - link->conf.ConfigIndex = cf->index; - link->io.BasePort1 = cf->io.win[0].base; - link->io.IOAddrLines = (try == 0) ? - 16 : cf->io.flags & CISTPL_IO_LINES_MASK; - i = - CardServices(RequestIO, link->handle, - &link->io); - if (i == CS_SUCCESS) - goto found_port; - } - next_entry: - i = next_tuple(handle, &tuple, &parse); - } - } - - /* Second pass: try to find an entry that isn't picky about - its base address, then try to grab any standard serial port - address, and finally try to get any free port. */ - i = first_tuple(handle, &tuple, &parse); - while (i != CS_NO_MORE_ITEMS) { - if ((i == CS_SUCCESS) && (cf->io.nwin > 0) && - ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { - link->conf.ConfigIndex = cf->index; - for (j = 0; j < 5; j++) { - link->io.BasePort1 = base[j]; - link->io.IOAddrLines = base[j] ? 16 : 3; - i = CardServices(RequestIO, link->handle, - &link->io); - if (i == CS_SUCCESS) - goto found_port; - } - } - i = next_tuple(handle, &tuple, &parse); - } - - found_port: - if (i != CS_SUCCESS) { - printk(KERN_NOTICE - "serial_cs: no usable port range found, giving up\n"); - cs_error(link->handle, RequestIO, i); - return -1; - } - - i = CardServices(RequestIRQ, link->handle, &link->irq); - if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIRQ, i); - link->irq.AssignedIRQ = 0; - } - if (info->multi && (info->manfid == MANFID_3COM)) - link->conf.ConfigIndex &= ~(0x08); - i = CardServices(RequestConfiguration, link->handle, &link->conf); - if (i != CS_SUCCESS) { - cs_error(link->handle, RequestConfiguration, i); - return -1; - } - - return setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ); -} - -static int multi_config(dev_link_t * link) -{ - client_handle_t handle = link->handle; - struct serial_info *info = link->priv; - tuple_t tuple; - u_char buf[256]; - cisparse_t parse; - cistpl_cftable_entry_t *cf = &parse.cftable_entry; - config_info_t config; - int i, base2 = 0; - - i = CardServices(GetConfigurationInfo, handle, &config); - if (i != CS_SUCCESS) { - cs_error(handle, GetConfigurationInfo, i); - return -1; - } - link->conf.Vcc = config.Vcc; - - tuple.TupleData = (cisdata_t *) buf; - tuple.TupleOffset = 0; - tuple.TupleDataMax = 255; - tuple.Attributes = 0; - tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - - /* First, look for a generic full-sized window */ - link->io.NumPorts1 = info->multi * 8; - i = first_tuple(handle, &tuple, &parse); - while (i != CS_NO_MORE_ITEMS) { - /* The quad port cards have bad CIS's, so just look for a - window larger than 8 ports and assume it will be right */ - if ((i == CS_SUCCESS) && (cf->io.nwin == 1) && - (cf->io.win[0].len > 8)) { - link->conf.ConfigIndex = cf->index; - link->io.BasePort1 = cf->io.win[0].base; - link->io.IOAddrLines = - cf->io.flags & CISTPL_IO_LINES_MASK; - i = CardServices(RequestIO, link->handle, &link->io); - base2 = link->io.BasePort1 + 8; - if (i == CS_SUCCESS) - break; - } - i = next_tuple(handle, &tuple, &parse); - } - - /* If that didn't work, look for two windows */ - if (i != CS_SUCCESS) { - link->io.NumPorts1 = link->io.NumPorts2 = 8; - info->multi = 2; - i = first_tuple(handle, &tuple, &parse); - while (i != CS_NO_MORE_ITEMS) { - if ((i == CS_SUCCESS) && (cf->io.nwin == 2)) { - link->conf.ConfigIndex = cf->index; - link->io.BasePort1 = cf->io.win[0].base; - link->io.BasePort2 = cf->io.win[1].base; - link->io.IOAddrLines = - cf->io.flags & CISTPL_IO_LINES_MASK; - i = - CardServices(RequestIO, link->handle, - &link->io); - base2 = link->io.BasePort2; - if (i == CS_SUCCESS) - break; - } - i = next_tuple(handle, &tuple, &parse); - } - } - - if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIO, i); - return -1; - } - - i = CardServices(RequestIRQ, link->handle, &link->irq); - if (i != CS_SUCCESS) { - printk(KERN_NOTICE - "serial_cs: no usable port range found, giving up\n"); - cs_error(link->handle, RequestIRQ, i); - link->irq.AssignedIRQ = 0; - } - /* Socket Dual IO: this enables irq's for second port */ - if (info->multi && (info->manfid == MANFID_SOCKET)) { - link->conf.Present |= PRESENT_EXT_STATUS; - link->conf.ExtStatus = ESR_REQ_ATTN_ENA; - } - i = CardServices(RequestConfiguration, link->handle, &link->conf); - if (i != CS_SUCCESS) { - cs_error(link->handle, RequestConfiguration, i); - return -1; - } - - /* The Oxford Semiconductor OXCF950 cards are in fact single-port: - 8 registers are for the UART, the others are extra registers */ - if (info->manfid == MANFID_OXSEMI) { - if (cf->index == 1 || cf->index == 3) { - setup_serial(info, base2, link->irq.AssignedIRQ); - outb(12, link->io.BasePort1 + 1); - } else { - setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ); - outb(12, base2 + 1); - } - return 0; - } - - setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ); - /* The Nokia cards are not really multiport cards */ - if (info->manfid == MANFID_NOKIA) - return 0; - for (i = 0; i < info->multi - 1; i++) - setup_serial(info, base2 + (8 * i), link->irq.AssignedIRQ); - - return 0; -} - -/*====================================================================== - - serial_config() is scheduled to run after a CARD_INSERTION event - is received, to configure the PCMCIA socket, and to make the - serial device available to the system. - -======================================================================*/ - -#define CS_CHECK(fn, args...) \ -while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed - -void serial_config(dev_link_t * link) -{ - client_handle_t handle = link->handle; - struct serial_info *info = link->priv; - tuple_t tuple; - u_short buf[128]; - cisparse_t parse; - cistpl_cftable_entry_t *cf = &parse.cftable_entry; - int i, last_ret, last_fn; - - DEBUG(0, "serial_config(0x%p)\n", link); - - tuple.TupleData = (cisdata_t *) buf; - tuple.TupleOffset = 0; - tuple.TupleDataMax = 255; - tuple.Attributes = 0; - /* Get configuration register information */ - tuple.DesiredTuple = CISTPL_CONFIG; - last_ret = first_tuple(handle, &tuple, &parse); - if (last_ret != CS_SUCCESS) { - last_fn = ParseTuple; - goto cs_failed; - } - link->conf.ConfigBase = parse.config.base; - link->conf.Present = parse.config.rmask[0]; - - /* Configure card */ - link->state |= DEV_CONFIG; - - /* Is this a compliant multifunction card? */ - tuple.DesiredTuple = CISTPL_LONGLINK_MFC; - tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK; - info->multi = (first_tuple(handle, &tuple, &parse) == CS_SUCCESS); - - /* Is this a multiport card? */ - tuple.DesiredTuple = CISTPL_MANFID; - if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) { - info->manfid = le16_to_cpu(buf[0]); - for (i = 0; i < MULTI_COUNT; i++) - if ((info->manfid == multi_id[i].manfid) && - (le16_to_cpu(buf[1]) == multi_id[i].prodid)) - break; - if (i < MULTI_COUNT) - info->multi = multi_id[i].multi; - } - - /* Another check for dual-serial cards: look for either serial or - multifunction cards that ask for appropriate IO port ranges */ - tuple.DesiredTuple = CISTPL_FUNCID; - if ((info->multi == 0) && - ((first_tuple(handle, &tuple, &parse) != CS_SUCCESS) || - (parse.funcid.func == CISTPL_FUNCID_MULTI) || - (parse.funcid.func == CISTPL_FUNCID_SERIAL))) { - tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) { - if ((cf->io.nwin == 1) && (cf->io.win[0].len % 8 == 0)) - info->multi = cf->io.win[0].len >> 3; - if ((cf->io.nwin == 2) && (cf->io.win[0].len == 8) && - (cf->io.win[1].len == 8)) - info->multi = 2; - } - } - - if (info->multi > 1) - multi_config(link); - else - simple_config(link); - - if (info->ndev == 0) - goto failed; - - if (info->manfid == MANFID_IBM) { - conf_reg_t reg = { 0, CS_READ, 0x800, 0 }; - CS_CHECK(AccessConfigurationRegister, link->handle, ®); - reg.Action = CS_WRITE; - reg.Value = reg.Value | 1; - CS_CHECK(AccessConfigurationRegister, link->handle, ®); - } - - link->dev = &info->node[0]; - link->state &= ~DEV_CONFIG_PENDING; - return; - - cs_failed: - cs_error(link->handle, last_fn, last_ret); - failed: - serial_remove(link); - link->state &= ~DEV_CONFIG_PENDING; -} - -/*====================================================================== - - The card status event handler. Mostly, this schedules other - stuff to run after an event is received. A CARD_REMOVAL event - also sets some flags to discourage the serial drivers from - talking to the ports. - -======================================================================*/ - -static int -serial_event(event_t event, int priority, event_callback_args_t * args) -{ - dev_link_t *link = args->client_data; - struct serial_info *info = link->priv; - - DEBUG(1, "serial_event(0x%06x)\n", event); - - switch (event) { - case CS_EVENT_CARD_REMOVAL: - serial_remove(link); - break; - - case CS_EVENT_CARD_INSERTION: - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - serial_config(link); - break; - - case CS_EVENT_PM_SUSPEND: - link->state |= DEV_SUSPEND; - /* Fall through... */ - case CS_EVENT_RESET_PHYSICAL: - if ((link->state & DEV_CONFIG) && !info->slave) - CardServices(ReleaseConfiguration, link->handle); - break; - - case CS_EVENT_PM_RESUME: - link->state &= ~DEV_SUSPEND; - /* Fall through... */ - case CS_EVENT_CARD_RESET: - if (DEV_OK(link) && !info->slave) - CardServices(RequestConfiguration, link->handle, - &link->conf); - break; - } - return 0; -} - -static struct pcmcia_driver serial_cs_driver = { - .owner = THIS_MODULE, - .drv = { - .name = "serial_cs", - }, - .attach = serial_attach, - .detach = serial_detach, -}; - -static int __init init_serial_cs(void) -{ - return pcmcia_register_driver(&serial_cs_driver); -} - -static void __exit exit_serial_cs(void) -{ - pcmcia_unregister_driver(&serial_cs_driver); - - /* XXX: this really needs to move into generic code.. */ - while (dev_list != NULL) - serial_detach(dev_list); -} - -module_init(init_serial_cs); -module_exit(exit_serial_cs); - -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index c6e1fba4a2ad..b7857fb1590b 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -11,10 +11,10 @@ serial-8250-$(CONFIG_PNP) += 8250_pnp.o serial-8250-$(CONFIG_SERIAL_8250_HCDP) += 8250_hcdp.o serial-8250-$(CONFIG_SERIAL_8250_ACPI) += 8250_acpi.o -obj-$(CONFIG_SERIAL_CORE) += core.o +obj-$(CONFIG_SERIAL_CORE) += serial_core.o obj-$(CONFIG_SERIAL_21285) += 21285.o obj-$(CONFIG_SERIAL_8250) += 8250.o $(serial-8250-y) -obj-$(CONFIG_SERIAL_8250_CS) += 8250_cs.o +obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o obj-$(CONFIG_SERIAL_ANAKIN) += anakin.o obj-$(CONFIG_SERIAL_AMBA) += amba.o diff --git a/drivers/serial/core.c b/drivers/serial/core.c deleted file mode 100644 index a264d72ca695..000000000000 --- a/drivers/serial/core.c +++ /dev/null @@ -1,2432 +0,0 @@ -/* - * linux/drivers/char/core.c - * - * Driver core for serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Copyright 1999 ARM Limited - * Copyright (C) 2000-2001 Deep Blue Solutions Ltd. - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for serial_state and serial_icounter_struct */ - -#include -#include - -#undef DEBUG -#ifdef DEBUG -#define DPRINTK(x...) printk(x) -#else -#define DPRINTK(x...) do { } while (0) -#endif - -/* - * This is used to lock changes in serial line configuration. - */ -static DECLARE_MUTEX(port_sem); - -#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) - -#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->blocked_open : 0)) - -#ifdef CONFIG_SERIAL_CORE_CONSOLE -#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) -#else -#define uart_console(port) (0) -#endif - -static void uart_change_speed(struct uart_state *state, struct termios *old_termios); -static void uart_wait_until_sent(struct tty_struct *tty, int timeout); -static void uart_change_pm(struct uart_state *state, int pm_state); - -/* - * This routine is used by the interrupt handler to schedule processing in - * the software interrupt portion of the driver. - */ -void uart_write_wakeup(struct uart_port *port) -{ - struct uart_info *info = port->info; - tasklet_schedule(&info->tlet); -} - -static void uart_stop(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - port->ops->stop_tx(port, 1); - spin_unlock_irqrestore(&port->lock, flags); -} - -static void __uart_start(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; - - if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf && - !tty->stopped && !tty->hw_stopped) - port->ops->start_tx(port, 1); -} - -static void uart_start(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - __uart_start(tty); - spin_unlock_irqrestore(&port->lock, flags); -} - -static void uart_tasklet_action(unsigned long data) -{ - struct uart_state *state = (struct uart_state *)data; - struct tty_struct *tty; - - tty = state->info->tty; - if (tty) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - tty->ldisc.write_wakeup(tty); - wake_up_interruptible(&tty->write_wait); - } -} - -static inline void -uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) -{ - unsigned long flags; - unsigned int old; - - spin_lock_irqsave(&port->lock, flags); - old = port->mctrl; - port->mctrl = (old & ~clear) | set; - if (old != port->mctrl) - port->ops->set_mctrl(port, port->mctrl); - spin_unlock_irqrestore(&port->lock, flags); -} - -#define uart_set_mctrl(port,set) uart_update_mctrl(port,set,0) -#define uart_clear_mctrl(port,clear) uart_update_mctrl(port,0,clear) - -/* - * Startup the port. This will be called once per open. All calls - * will be serialised by the per-port semaphore. - */ -static int uart_startup(struct uart_state *state, int init_hw) -{ - struct uart_info *info = state->info; - struct uart_port *port = state->port; - unsigned long page; - int retval = 0; - - if (info->flags & UIF_INITIALIZED) - return 0; - - /* - * Set the TTY IO error marker - we will only clear this - * once we have successfully opened the port. Also set - * up the tty->alt_speed kludge - */ - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - - if (port->type == PORT_UNKNOWN) - return 0; - - /* - * Initialise and allocate the transmit and temporary - * buffer. - */ - if (!info->xmit.buf) { - page = get_zeroed_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - info->xmit.buf = (unsigned char *) page; - info->tmpbuf = info->xmit.buf + UART_XMIT_SIZE; - init_MUTEX(&info->tmpbuf_sem); - uart_circ_clear(&info->xmit); - } - - port->mctrl = 0; - - retval = port->ops->startup(port); - if (retval == 0) { - if (init_hw) { - /* - * Initialise the hardware port settings. - */ - uart_change_speed(state, NULL); - - /* - * Setup the RTS and DTR signals once the - * port is open and ready to respond. - */ - if (info->tty->termios->c_cflag & CBAUD) - uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); - } - - info->flags |= UIF_INITIALIZED; - - clear_bit(TTY_IO_ERROR, &info->tty->flags); - } - - if (retval && capable(CAP_SYS_ADMIN)) - retval = 0; - - return retval; -} - -/* - * This routine will shutdown a serial port; interrupts are disabled, and - * DTR is dropped if the hangup on close termio flag is on. Calls to - * uart_shutdown are serialised by the per-port semaphore. - */ -static void uart_shutdown(struct uart_state *state) -{ - struct uart_info *info = state->info; - struct uart_port *port = state->port; - - if (!(info->flags & UIF_INITIALIZED)) - return; - - /* - * Turn off DTR and RTS early. - */ - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) - uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); - - /* - * clear delta_msr_wait queue to avoid mem leaks: we may free - * the irq here so the queue might never be woken up. Note - * that we won't end up waiting on delta_msr_wait again since - * any outstanding file descriptors should be pointing at - * hung_up_tty_fops now. - */ - wake_up_interruptible(&info->delta_msr_wait); - - /* - * Free the IRQ and disable the port. - */ - port->ops->shutdown(port); - - /* - * Ensure that the IRQ handler isn't running on another CPU. - */ - synchronize_irq(port->irq); - - /* - * Free the transmit buffer page. - */ - if (info->xmit.buf) { - free_page((unsigned long)info->xmit.buf); - info->xmit.buf = NULL; - info->tmpbuf = NULL; - } - - /* - * kill off our tasklet - */ - tasklet_kill(&info->tlet); - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - - info->flags &= ~UIF_INITIALIZED; -} - -/** - * uart_update_timeout - update per-port FIFO timeout. - * @port: uart_port structure describing the port. - * @cflag: termios cflag value - * @quot: uart clock divisor quotient - * - * Set the port FIFO timeout value. The @cflag value should - * reflect the actual hardware settings. - */ -void -uart_update_timeout(struct uart_port *port, unsigned int cflag, - unsigned int baud) -{ - unsigned int bits; - - /* byte size and parity */ - switch (cflag & CSIZE) { - case CS5: - bits = 7; - break; - case CS6: - bits = 8; - break; - case CS7: - bits = 9; - break; - default: - bits = 10; - break; // CS8 - } - - if (cflag & CSTOPB) - bits++; - if (cflag & PARENB) - bits++; - - /* - * The total number of bits to be transmitted in the fifo. - */ - bits = bits * port->fifosize; - - /* - * Figure the timeout to send the above number of bits. - * Add .02 seconds of slop - */ - port->timeout = (HZ * bits) / baud + HZ/50; -} - -EXPORT_SYMBOL(uart_update_timeout); - -/** - * uart_get_baud_rate - return baud rate for a particular port - * @port: uart_port structure describing the port in question. - * @termios: desired termios settings. - * @old: old termios (or NULL) - * @min: minimum acceptable baud rate - * @max: maximum acceptable baud rate - * - * Decode the termios structure into a numeric baud rate, - * taking account of the magic 38400 baud rate (with spd_* - * flags), and mapping the %B0 rate to 9600 baud. - * - * If the new baud rate is invalid, try the old termios setting. - * If it's still invalid, we try 9600 baud. - * - * Update the @termios structure to reflect the baud rate - * we're actually going to be using. - */ -unsigned int -uart_get_baud_rate(struct uart_port *port, struct termios *termios, - struct termios *old, unsigned int min, unsigned int max) -{ - unsigned int try, baud, altbaud = 38400; - unsigned int flags = port->flags & UPF_SPD_MASK; - - if (flags == UPF_SPD_HI) - altbaud = 57600; - if (flags == UPF_SPD_VHI) - altbaud = 115200; - if (flags == UPF_SPD_SHI) - altbaud = 230400; - if (flags == UPF_SPD_WARP) - altbaud = 460800; - - for (try = 0; try < 2; try++) { - baud = tty_termios_baud_rate(termios); - - /* - * The spd_hi, spd_vhi, spd_shi, spd_warp kludge... - * Die! Die! Die! - */ - if (baud == 38400) - baud = altbaud; - - /* - * Special case: B0 rate. - */ - if (baud == 0) - baud = 9600; - - if (baud >= min && baud <= max) - return baud; - - /* - * Oops, the quotient was zero. Try again with - * the old baud rate if possible. - */ - termios->c_cflag &= ~CBAUD; - if (old) { - termios->c_cflag |= old->c_cflag & CBAUD; - old = NULL; - continue; - } - - /* - * As a last resort, if the quotient is zero, - * default to 9600 bps - */ - termios->c_cflag |= B9600; - } - - return 0; -} - -EXPORT_SYMBOL(uart_get_baud_rate); - -/** - * uart_get_divisor - return uart clock divisor - * @port: uart_port structure describing the port. - * @baud: desired baud rate - * - * Calculate the uart clock divisor for the port. - */ -unsigned int -uart_get_divisor(struct uart_port *port, unsigned int baud) -{ - unsigned int quot; - - /* - * Old custom speed handling. - */ - if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) - quot = port->custom_divisor; - else - quot = port->uartclk / (16 * baud); - - return quot; -} - -EXPORT_SYMBOL(uart_get_divisor); - -static void -uart_change_speed(struct uart_state *state, struct termios *old_termios) -{ - struct tty_struct *tty = state->info->tty; - struct uart_port *port = state->port; - struct termios *termios; - - /* - * If we have no tty, termios, or the port does not exist, - * then we can't set the parameters for this port. - */ - if (!tty || !tty->termios || port->type == PORT_UNKNOWN) - return; - - termios = tty->termios; - - /* - * Set flags based on termios cflag - */ - if (termios->c_cflag & CRTSCTS) - state->info->flags |= UIF_CTS_FLOW; - else - state->info->flags &= ~UIF_CTS_FLOW; - - if (termios->c_cflag & CLOCAL) - state->info->flags &= ~UIF_CHECK_CD; - else - state->info->flags |= UIF_CHECK_CD; - - port->ops->set_termios(port, termios, old_termios); -} - -static inline void -__uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c) -{ - unsigned long flags; - - if (!circ->buf) - return; - - spin_lock_irqsave(&port->lock, flags); - if (uart_circ_chars_free(circ) != 0) { - circ->buf[circ->head] = c; - circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); - } - spin_unlock_irqrestore(&port->lock, flags); -} - -static inline int -__uart_user_write(struct uart_port *port, struct circ_buf *circ, - const unsigned char *buf, int count) -{ - unsigned long flags; - int c, ret = 0; - - if (down_interruptible(&port->info->tmpbuf_sem)) - return -EINTR; - - while (1) { - int c1; - c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); - if (count < c) - c = count; - if (c <= 0) - break; - - c -= copy_from_user(port->info->tmpbuf, buf, c); - if (!c) { - if (!ret) - ret = -EFAULT; - break; - } - spin_lock_irqsave(&port->lock, flags); - c1 = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); - if (c1 < c) - c = c1; - memcpy(circ->buf + circ->head, port->info->tmpbuf, c); - circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); - spin_unlock_irqrestore(&port->lock, flags); - buf += c; - count -= c; - ret += c; - } - up(&port->info->tmpbuf_sem); - - return ret; -} - -static inline int -__uart_kern_write(struct uart_port *port, struct circ_buf *circ, - const unsigned char *buf, int count) -{ - unsigned long flags; - int c, ret = 0; - - spin_lock_irqsave(&port->lock, flags); - while (1) { - c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); - if (count < c) - c = count; - if (c <= 0) - break; - memcpy(circ->buf + circ->head, buf, c); - circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); - buf += c; - count -= c; - ret += c; - } - spin_unlock_irqrestore(&port->lock, flags); - - return ret; -} - -static void uart_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct uart_state *state = tty->driver_data; - - if (tty) - __uart_put_char(state->port, &state->info->xmit, ch); -} - -static void uart_flush_chars(struct tty_struct *tty) -{ - uart_start(tty); -} - -static int -uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf, - int count) -{ - struct uart_state *state = tty->driver_data; - int ret; - - if (!tty || !state->info->xmit.buf) - return 0; - - if (from_user) - ret = __uart_user_write(state->port, &state->info->xmit, buf, count); - else - ret = __uart_kern_write(state->port, &state->info->xmit, buf, count); - - uart_start(tty); - return ret; -} - -static int uart_write_room(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - - return uart_circ_chars_free(&state->info->xmit); -} - -static int uart_chars_in_buffer(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - - return uart_circ_chars_pending(&state->info->xmit); -} - -static void uart_flush_buffer(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; - unsigned long flags; - - DPRINTK("uart_flush_buffer(%d) called\n", tty->index); - - spin_lock_irqsave(&port->lock, flags); - uart_circ_clear(&state->info->xmit); - spin_unlock_irqrestore(&port->lock, flags); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); -} - -/* - * This function is used to send a high-priority XON/XOFF character to - * the device - */ -static void uart_send_xchar(struct tty_struct *tty, char ch) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; - unsigned long flags; - - if (port->ops->send_xchar) - port->ops->send_xchar(port, ch); - else { - port->x_char = ch; - if (ch) { - spin_lock_irqsave(&port->lock, flags); - port->ops->start_tx(port, 0); - spin_unlock_irqrestore(&port->lock, flags); - } - } -} - -static void uart_throttle(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - - if (I_IXOFF(tty)) - uart_send_xchar(tty, STOP_CHAR(tty)); - - if (tty->termios->c_cflag & CRTSCTS) - uart_clear_mctrl(state->port, TIOCM_RTS); -} - -static void uart_unthrottle(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; - - if (I_IXOFF(tty)) { - if (port->x_char) - port->x_char = 0; - else - uart_send_xchar(tty, START_CHAR(tty)); - } - - if (tty->termios->c_cflag & CRTSCTS) - uart_set_mctrl(port, TIOCM_RTS); -} - -static int uart_get_info(struct uart_state *state, struct serial_struct *retinfo) -{ - struct uart_port *port = state->port; - struct serial_struct tmp; - - memset(&tmp, 0, sizeof(tmp)); - tmp.type = port->type; - tmp.line = port->line; - tmp.port = port->iobase; - if (HIGH_BITS_OFFSET) - tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET; - tmp.irq = port->irq; - tmp.flags = port->flags; - tmp.xmit_fifo_size = port->fifosize; - tmp.baud_base = port->uartclk / 16; - tmp.close_delay = state->close_delay; - tmp.closing_wait = state->closing_wait; - tmp.custom_divisor = port->custom_divisor; - tmp.hub6 = port->hub6; - tmp.io_type = port->iotype; - tmp.iomem_reg_shift = port->regshift; - tmp.iomem_base = (void *)port->mapbase; - - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - return 0; -} - -static int -uart_set_info(struct uart_state *state, struct serial_struct *newinfo) -{ - struct serial_struct new_serial; - struct uart_port *port = state->port; - unsigned long new_port; - unsigned int change_irq, change_port, old_flags; - unsigned int old_custom_divisor; - int retval = 0; - - if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) - return -EFAULT; - - new_port = new_serial.port; - if (HIGH_BITS_OFFSET) - new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; - - new_serial.irq = irq_canonicalize(new_serial.irq); - - /* - * This semaphore protects state->count. It is also - * very useful to prevent opens. Also, take the - * port configuration semaphore to make sure that a - * module insertion/removal doesn't change anything - * under us. - */ - down(&state->sem); - - change_irq = new_serial.irq != port->irq; - - /* - * Since changing the 'type' of the port changes its resource - * allocations, we should treat type changes the same as - * IO port changes. - */ - change_port = new_port != port->iobase || - (unsigned long)new_serial.iomem_base != port->mapbase || - new_serial.hub6 != port->hub6 || - new_serial.io_type != port->iotype || - new_serial.iomem_reg_shift != port->regshift || - new_serial.type != port->type; - - old_flags = port->flags; - old_custom_divisor = port->custom_divisor; - - if (!capable(CAP_SYS_ADMIN)) { - retval = -EPERM; - if (change_irq || change_port || - (new_serial.baud_base != port->uartclk / 16) || - (new_serial.close_delay != state->close_delay) || - (new_serial.closing_wait != state->closing_wait) || - (new_serial.xmit_fifo_size != port->fifosize) || - (((new_serial.flags ^ old_flags) & ~UPF_USR_MASK) != 0)) - goto exit; - port->flags = ((port->flags & ~UPF_USR_MASK) | - (new_serial.flags & UPF_USR_MASK)); - port->custom_divisor = new_serial.custom_divisor; - goto check_and_exit; - } - - /* - * Ask the low level driver to verify the settings. - */ - if (port->ops->verify_port) - retval = port->ops->verify_port(port, &new_serial); - - if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || - (new_serial.baud_base < 9600)) - retval = -EINVAL; - - if (retval) - goto exit; - - if (change_port || change_irq) { - retval = -EBUSY; - - /* - * Make sure that we are the sole user of this port. - */ - if (uart_users(state) > 1) - goto exit; - - /* - * We need to shutdown the serial port at the old - * port/type/irq combination. - */ - uart_shutdown(state); - } - - if (change_port) { - unsigned long old_iobase, old_mapbase; - unsigned int old_type, old_iotype, old_hub6, old_shift; - - old_iobase = port->iobase; - old_mapbase = port->mapbase; - old_type = port->type; - old_hub6 = port->hub6; - old_iotype = port->iotype; - old_shift = port->regshift; - - /* - * Free and release old regions - */ - if (old_type != PORT_UNKNOWN) - port->ops->release_port(port); - - port->iobase = new_port; - port->type = new_serial.type; - port->hub6 = new_serial.hub6; - port->iotype = new_serial.io_type; - port->regshift = new_serial.iomem_reg_shift; - port->mapbase = (unsigned long)new_serial.iomem_base; - - /* - * Claim and map the new regions - */ - if (port->type != PORT_UNKNOWN) { - retval = port->ops->request_port(port); - } else { - /* Always success - Jean II */ - retval = 0; - } - - /* - * If we fail to request resources for the - * new port, try to restore the old settings. - */ - if (retval && old_type != PORT_UNKNOWN) { - port->iobase = old_iobase; - port->type = old_type; - port->hub6 = old_hub6; - port->iotype = old_iotype; - port->regshift = old_shift; - port->mapbase = old_mapbase; - retval = port->ops->request_port(port); - /* - * If we failed to restore the old settings, - * we fail like this. - */ - if (retval) - port->type = PORT_UNKNOWN; - - /* - * We failed anyway. - */ - retval = -EBUSY; - } - } - - port->irq = new_serial.irq; - port->uartclk = new_serial.baud_base * 16; - port->flags = (port->flags & ~UPF_CHANGE_MASK) | - (new_serial.flags & UPF_CHANGE_MASK); - port->custom_divisor = new_serial.custom_divisor; - state->close_delay = new_serial.close_delay * HZ / 100; - state->closing_wait = new_serial.closing_wait * HZ / 100; - port->fifosize = new_serial.xmit_fifo_size; - if (state->info->tty) - state->info->tty->low_latency = - (port->flags & UPF_LOW_LATENCY) ? 1 : 0; - - check_and_exit: - retval = 0; - if (port->type == PORT_UNKNOWN) - goto exit; - if (state->info->flags & UIF_INITIALIZED) { - if (((old_flags ^ port->flags) & UPF_SPD_MASK) || - old_custom_divisor != port->custom_divisor) { - /* If they're setting up a custom divisor or speed, - * instead of clearing it, then bitch about it. No - * need to rate-limit; it's CAP_SYS_ADMIN only. */ - if (port->flags & UPF_SPD_MASK) { - printk(KERN_NOTICE "%s sets custom speed on %s%d. This is deprecated.\n", - current->comm, state->info->tty->driver->name, - state->port->line); - } - uart_change_speed(state, NULL); - } - } else - retval = uart_startup(state, 1); - exit: - up(&state->sem); - return retval; -} - - -/* - * uart_get_lsr_info - get line status register info. - * Note: uart_ioctl protects us against hangups. - */ -static int uart_get_lsr_info(struct uart_state *state, unsigned int *value) -{ - struct uart_port *port = state->port; - unsigned int result; - - result = port->ops->tx_empty(port); - - /* - * If we're about to load something into the transmit - * register, we'll pretend the transmitter isn't empty to - * avoid a race condition (depending on when the transmit - * interrupt happens). - */ - if (port->x_char || - ((uart_circ_chars_pending(&state->info->xmit) > 0) && - !state->info->tty->stopped && !state->info->tty->hw_stopped)) - result &= ~TIOCSER_TEMT; - - return put_user(result, value); -} - -static int uart_tiocmget(struct tty_struct *tty, struct file *file) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; - int result = -EIO; - - down(&state->sem); - if ((!file || !tty_hung_up_p(file)) && - !(tty->flags & (1 << TTY_IO_ERROR))) { - result = port->mctrl; - result |= port->ops->get_mctrl(port); - } - up(&state->sem); - - return result; -} - -static int -uart_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; - int ret = -EIO; - - down(&state->sem); - if ((!file || !tty_hung_up_p(file)) && - !(tty->flags & (1 << TTY_IO_ERROR))) { - uart_update_mctrl(port, set, clear); - ret = 0; - } - up(&state->sem); - return ret; -} - -static void uart_break_ctl(struct tty_struct *tty, int break_state) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; - - BUG_ON(!kernel_locked()); - - down(&state->sem); - - if (port->type != PORT_UNKNOWN) - port->ops->break_ctl(port, break_state); - - up(&state->sem); -} - -static int uart_do_autoconfig(struct uart_state *state) -{ - struct uart_port *port = state->port; - int flags, ret; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - /* - * Take the per-port semaphore. This prevents count from - * changing, and hence any extra opens of the port while - * we're auto-configuring. - */ - if (down_interruptible(&state->sem)) - return -ERESTARTSYS; - - ret = -EBUSY; - if (uart_users(state) == 1) { - uart_shutdown(state); - - /* - * If we already have a port type configured, - * we must release its resources. - */ - if (port->type != PORT_UNKNOWN) - port->ops->release_port(port); - - flags = UART_CONFIG_TYPE; - if (port->flags & UPF_AUTO_IRQ) - flags |= UART_CONFIG_IRQ; - - /* - * This will claim the ports resources if - * a port is found. - */ - port->ops->config_port(port, flags); - - ret = uart_startup(state, 1); - } - up(&state->sem); - return ret; -} - -/* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was - */ -static int -uart_wait_modem_status(struct uart_state *state, unsigned long arg) -{ - struct uart_port *port = state->port; - DECLARE_WAITQUEUE(wait, current); - struct uart_icount cprev, cnow; - int ret; - - /* - * note the counters on entry - */ - spin_lock_irq(&port->lock); - memcpy(&cprev, &port->icount, sizeof(struct uart_icount)); - - /* - * Force modem status interrupts on - */ - port->ops->enable_ms(port); - spin_unlock_irq(&port->lock); - - add_wait_queue(&state->info->delta_msr_wait, &wait); - for (;;) { - spin_lock_irq(&port->lock); - memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); - spin_unlock_irq(&port->lock); - - set_current_state(TASK_INTERRUPTIBLE); - - if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { - ret = 0; - break; - } - - schedule(); - - /* see if a signal did it */ - if (signal_pending(current)) { - ret = -ERESTARTSYS; - break; - } - - cprev = cnow; - } - - current->state = TASK_RUNNING; - remove_wait_queue(&state->info->delta_msr_wait, &wait); - - return ret; -} - -/* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ -static int -uart_get_count(struct uart_state *state, struct serial_icounter_struct *icnt) -{ - struct serial_icounter_struct icount; - struct uart_icount cnow; - struct uart_port *port = state->port; - - spin_lock_irq(&port->lock); - memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); - spin_unlock_irq(&port->lock); - - icount.cts = cnow.cts; - icount.dsr = cnow.dsr; - icount.rng = cnow.rng; - icount.dcd = cnow.dcd; - icount.rx = cnow.rx; - icount.tx = cnow.tx; - icount.frame = cnow.frame; - icount.overrun = cnow.overrun; - icount.parity = cnow.parity; - icount.brk = cnow.brk; - icount.buf_overrun = cnow.buf_overrun; - - return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; -} - -/* - * Called via sys_ioctl under the BKL. We can use spin_lock_irq() here. - */ -static int -uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, - unsigned long arg) -{ - struct uart_state *state = tty->driver_data; - int ret = -ENOIOCTLCMD; - - BUG_ON(!kernel_locked()); - - /* - * These ioctls don't rely on the hardware to be present. - */ - switch (cmd) { - case TIOCGSERIAL: - ret = uart_get_info(state, (struct serial_struct *)arg); - break; - - case TIOCSSERIAL: - ret = uart_set_info(state, (struct serial_struct *)arg); - break; - - case TIOCSERCONFIG: - ret = uart_do_autoconfig(state); - break; - - case TIOCSERGWILD: /* obsolete */ - case TIOCSERSWILD: /* obsolete */ - ret = 0; - break; - } - - if (ret != -ENOIOCTLCMD) - goto out; - - if (tty->flags & (1 << TTY_IO_ERROR)) { - ret = -EIO; - goto out; - } - - /* - * The following should only be used when hardware is present. - */ - switch (cmd) { - case TIOCMIWAIT: - ret = uart_wait_modem_status(state, arg); - break; - - case TIOCGICOUNT: - ret = uart_get_count(state, (struct serial_icounter_struct *)arg); - break; - } - - if (ret != -ENOIOCTLCMD) - goto out; - - down(&state->sem); - - if (tty_hung_up_p(filp)) { - ret = -EIO; - goto out_up; - } - - /* - * All these rely on hardware being present and need to be - * protected against the tty being hung up. - */ - switch (cmd) { - case TIOCSERGETLSR: /* Get line status register */ - ret = uart_get_lsr_info(state, (unsigned int *)arg); - break; - - default: { - struct uart_port *port = state->port; - if (port->ops->ioctl) - ret = port->ops->ioctl(port, cmd, arg); - break; - } - } - out_up: - up(&state->sem); - out: - return ret; -} - -static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios) -{ - struct uart_state *state = tty->driver_data; - unsigned long flags; - unsigned int cflag = tty->termios->c_cflag; - - BUG_ON(!kernel_locked()); - - /* - * These are the bits that are used to setup various - * flags in the low level driver. - */ -#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) - - if ((cflag ^ old_termios->c_cflag) == 0 && - RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) - return; - - uart_change_speed(state, old_termios); - - /* Handle transition to B0 status */ - if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) - uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR); - - /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { - unsigned int mask = TIOCM_DTR; - if (!(cflag & CRTSCTS) || - !test_bit(TTY_THROTTLED, &tty->flags)) - mask |= TIOCM_RTS; - uart_set_mctrl(state->port, mask); - } - - /* Handle turning off CRTSCTS */ - if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { - spin_lock_irqsave(&state->port->lock, flags); - tty->hw_stopped = 0; - __uart_start(tty); - spin_unlock_irqrestore(&state->port->lock, flags); - } - -#if 0 - /* - * No need to wake up processes in open wait, since they - * sample the CLOCAL flag once, and don't recheck it. - * XXX It's not clear whether the current behavior is correct - * or not. Hence, this may change..... - */ - if (!(old_termios->c_cflag & CLOCAL) && - (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&state->info->open_wait); -#endif -} - -/* - * In 2.4.5, calls to this will be serialized via the BKL in - * linux/drivers/char/tty_io.c:tty_release() - * linux/drivers/char/tty_io.c:do_tty_handup() - */ -static void uart_close(struct tty_struct *tty, struct file *filp) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; - - BUG_ON(!kernel_locked()); - DPRINTK("uart_close(%d) called\n", port->line); - - down(&state->sem); - - if (tty_hung_up_p(filp)) - goto done; - - if ((tty->count == 1) && (state->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. state->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk("uart_close: bad serial port count; tty->count is 1, " - "state->count is %d\n", state->count); - state->count = 1; - } - if (--state->count < 0) { - printk("rs_close: bad serial port count for %s: %d\n", - tty->name, state->count); - state->count = 0; - } - if (state->count) - goto done; - - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters by - * setting tty->closing. - */ - tty->closing = 1; - - if (state->closing_wait != USF_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, state->closing_wait); - - /* - * At this point, we stop accepting input. To do this, we - * disable the receive line status interrupts. - */ - if (state->info->flags & UIF_INITIALIZED) { - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); - port->ops->stop_rx(port); - spin_unlock_irqrestore(&port->lock, flags); - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important if there is a transmit FIFO! - */ - uart_wait_until_sent(tty, port->timeout); - } - - uart_shutdown(state); - uart_flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); - tty->closing = 0; - state->info->tty = NULL; - - if (state->info->blocked_open) { - if (state->close_delay) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(state->close_delay); - set_current_state(TASK_RUNNING); - } - } else if (!uart_console(port)) { - uart_change_pm(state, 3); - } - - /* - * Wake up anyone trying to open this port. - */ - state->info->flags &= ~UIF_NORMAL_ACTIVE; - wake_up_interruptible(&state->info->open_wait); - - done: - up(&state->sem); -} - -static void uart_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; - unsigned long char_time, expire; - - BUG_ON(!kernel_locked()); - - if (port->type == PORT_UNKNOWN || port->fifosize == 0) - return; - - /* - * Set the check interval to be 1/5 of the estimated time to - * send a single character, and make it at least 1. The check - * interval should also be less than the timeout. - * - * Note: we have to use pretty tight timings here to satisfy - * the NIST-PCTS. - */ - char_time = (port->timeout - HZ/50) / port->fifosize; - char_time = char_time / 5; - if (char_time == 0) - char_time = 1; - if (timeout && timeout < char_time) - char_time = timeout; - - /* - * If the transmitter hasn't cleared in twice the approximate - * amount of time to send the entire FIFO, it probably won't - * ever clear. This assumes the UART isn't doing flow - * control, which is currently the case. Hence, if it ever - * takes longer than port->timeout, this is probably due to a - * UART bug of some kind. So, we clamp the timeout parameter at - * 2*port->timeout. - */ - if (timeout == 0 || timeout > 2 * port->timeout) - timeout = 2 * port->timeout; - - expire = jiffies + timeout; - - DPRINTK("uart_wait_until_sent(%d), jiffies=%lu, expire=%lu...\n", - port->line, jiffies, expire); - - /* - * Check whether the transmitter is empty every 'char_time'. - * 'timeout' / 'expire' give us the maximum amount of time - * we wait. - */ - while (!port->ops->tx_empty(port)) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(char_time); - if (signal_pending(current)) - break; - if (time_after(jiffies, expire)) - break; - } - set_current_state(TASK_RUNNING); /* might not be needed */ -} - -/* - * This is called with the BKL held in - * linux/drivers/char/tty_io.c:do_tty_hangup() - * We're called from the eventd thread, so we can sleep for - * a _short_ time only. - */ -static void uart_hangup(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - - BUG_ON(!kernel_locked()); - DPRINTK("uart_hangup(%d)\n", state->port->line); - - down(&state->sem); - if (state->info && state->info->flags & UIF_NORMAL_ACTIVE) { - uart_flush_buffer(tty); - uart_shutdown(state); - state->count = 0; - state->info->flags &= ~UIF_NORMAL_ACTIVE; - state->info->tty = NULL; - wake_up_interruptible(&state->info->open_wait); - wake_up_interruptible(&state->info->delta_msr_wait); - } - up(&state->sem); -} - -/* - * Copy across the serial console cflag setting into the termios settings - * for the initial open of the port. This allows continuity between the - * kernel settings, and the settings init adopts when it opens the port - * for the first time. - */ -static void uart_update_termios(struct uart_state *state) -{ - struct tty_struct *tty = state->info->tty; - struct uart_port *port = state->port; - - if (uart_console(port) && port->cons->cflag) { - tty->termios->c_cflag = port->cons->cflag; - port->cons->cflag = 0; - } - - /* - * If the device failed to grab its irq resources, - * or some other error occurred, don't try to talk - * to the port hardware. - */ - if (!(tty->flags & (1 << TTY_IO_ERROR))) { - /* - * Make termios settings take effect. - */ - uart_change_speed(state, NULL); - - /* - * And finally enable the RTS and DTR signals. - */ - if (tty->termios->c_cflag & CBAUD) - uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); - } -} - -/* - * Block the open until the port is ready. We must be called with - * the per-port semaphore held. - */ -static int -uart_block_til_ready(struct file *filp, struct uart_state *state) -{ - DECLARE_WAITQUEUE(wait, current); - struct uart_info *info = state->info; - struct uart_port *port = state->port; - - info->blocked_open++; - state->count--; - - add_wait_queue(&info->open_wait, &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - /* - * If we have been hung up, tell userspace/restart open. - */ - if (tty_hung_up_p(filp) || info->tty == NULL) - break; - - /* - * If the port has been closed, tell userspace/restart open. - */ - if (!(info->flags & UIF_INITIALIZED)) - break; - - /* - * If non-blocking mode is set, or CLOCAL mode is set, - * we don't want to wait for the modem status lines to - * indicate that the port is ready. - * - * Also, if the port is not enabled/configured, we want - * to allow the open to succeed here. Note that we will - * have set TTY_IO_ERROR for a non-existant port. - */ - if ((filp->f_flags & O_NONBLOCK) || - (info->tty->termios->c_cflag & CLOCAL) || - (info->tty->flags & (1 << TTY_IO_ERROR))) { - break; - } - - /* - * Set DTR to allow modem to know we're waiting. Do - * not set RTS here - we want to make sure we catch - * the data from the modem. - */ - if (info->tty->termios->c_cflag & CBAUD) - uart_set_mctrl(port, TIOCM_DTR); - - /* - * and wait for the carrier to indicate that the - * modem is ready for us. - */ - if (port->ops->get_mctrl(port) & TIOCM_CAR) - break; - - up(&state->sem); - schedule(); - down(&state->sem); - - if (signal_pending(current)) - break; - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); - - state->count++; - info->blocked_open--; - - if (signal_pending(current)) - return -ERESTARTSYS; - - if (!info->tty || tty_hung_up_p(filp)) - return -EAGAIN; - - return 0; -} - -static struct uart_state *uart_get(struct uart_driver *drv, int line) -{ - struct uart_state *state; - - down(&port_sem); - state = drv->state + line; - if (down_interruptible(&state->sem)) { - state = ERR_PTR(-ERESTARTSYS); - goto out; - } - - state->count++; - if (!state->port) { - state->count--; - up(&state->sem); - state = ERR_PTR(-ENXIO); - goto out; - } - - if (!state->info) { - state->info = kmalloc(sizeof(struct uart_info), GFP_KERNEL); - if (state->info) { - memset(state->info, 0, sizeof(struct uart_info)); - init_waitqueue_head(&state->info->open_wait); - init_waitqueue_head(&state->info->delta_msr_wait); - - /* - * Link the info into the other structures. - */ - state->port->info = state->info; - - tasklet_init(&state->info->tlet, uart_tasklet_action, - (unsigned long)state); - } else { - state->count--; - up(&state->sem); - state = ERR_PTR(-ENOMEM); - } - } - - out: - up(&port_sem); - return state; -} - -/* - * In 2.4.5, calls to uart_open are serialised by the BKL in - * linux/fs/devices.c:chrdev_open() - * Note that if this fails, then uart_close() _will_ be called. - * - * In time, we want to scrap the "opening nonpresent ports" - * behaviour and implement an alternative way for setserial - * to set base addresses/ports/types. This will allow us to - * get rid of a certain amount of extra tests. - */ -static int uart_open(struct tty_struct *tty, struct file *filp) -{ - struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state; - struct uart_state *state; - int retval, line = tty->index; - - BUG_ON(!kernel_locked()); - DPRINTK("uart_open(%d) called\n", line); - - /* - * tty->driver->num won't change, so we won't fail here with - * tty->driver_data set to something non-NULL (and therefore - * we won't get caught by uart_close()). - */ - retval = -ENODEV; - if (line >= tty->driver->num) - goto fail; - - /* - * We take the semaphore inside uart_get to guarantee that we won't - * be re-entered while allocating the info structure, or while we - * request any IRQs that the driver may need. This also has the nice - * side-effect that it delays the action of uart_hangup, so we can - * guarantee that info->tty will always contain something reasonable. - */ - state = uart_get(drv, line); - if (IS_ERR(state)) { - retval = PTR_ERR(state); - goto fail; - } - - /* - * Once we set tty->driver_data here, we are guaranteed that - * uart_close() will decrement the driver module use count. - * Any failures from here onwards should not touch the count. - */ - tty->driver_data = state; - tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; - tty->alt_speed = 0; - state->info->tty = tty; - - /* - * If the port is in the middle of closing, bail out now. - */ - if (tty_hung_up_p(filp)) { - retval = -EAGAIN; - state->count--; - up(&state->sem); - goto fail; - } - - /* - * Make sure the device is in D0 state. - */ - if (state->count == 1) - uart_change_pm(state, 0); - - /* - * Start up the serial port. - */ - retval = uart_startup(state, 0); - - /* - * If we succeeded, wait until the port is ready. - */ - if (retval == 0) - retval = uart_block_til_ready(filp, state); - up(&state->sem); - - /* - * If this is the first open to succeed, adjust things to suit. - */ - if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) { - state->info->flags |= UIF_NORMAL_ACTIVE; - - uart_update_termios(state); - } - - fail: - return retval; -} - -static const char *uart_type(struct uart_port *port) -{ - const char *str = NULL; - - if (port->ops->type) - str = port->ops->type(port); - - if (!str) - str = "unknown"; - - return str; -} - -#ifdef CONFIG_PROC_FS - -static int uart_line_info(char *buf, struct uart_driver *drv, int i) -{ - struct uart_state *state = drv->state + i; - struct uart_port *port = state->port; - char stat_buf[32]; - unsigned int status; - int ret; - - if (!port) - return 0; - - ret = sprintf(buf, "%d: uart:%s port:%08X irq:%d", - port->line, uart_type(port), - port->iobase, port->irq); - - if (port->type == PORT_UNKNOWN) { - strcat(buf, "\n"); - return ret + 1; - } - - if(capable(CAP_SYS_ADMIN)) - { - status = port->ops->get_mctrl(port); - - ret += sprintf(buf + ret, " tx:%d rx:%d", - port->icount.tx, port->icount.rx); - if (port->icount.frame) - ret += sprintf(buf + ret, " fe:%d", - port->icount.frame); - if (port->icount.parity) - ret += sprintf(buf + ret, " pe:%d", - port->icount.parity); - if (port->icount.brk) - ret += sprintf(buf + ret, " brk:%d", - port->icount.brk); - if (port->icount.overrun) - ret += sprintf(buf + ret, " oe:%d", - port->icount.overrun); - -#define INFOBIT(bit,str) \ - if (port->mctrl & (bit)) \ - strncat(stat_buf, (str), sizeof(stat_buf) - \ - strlen(stat_buf) - 2) -#define STATBIT(bit,str) \ - if (status & (bit)) \ - strncat(stat_buf, (str), sizeof(stat_buf) - \ - strlen(stat_buf) - 2) - - stat_buf[0] = '\0'; - stat_buf[1] = '\0'; - INFOBIT(TIOCM_RTS, "|RTS"); - STATBIT(TIOCM_CTS, "|CTS"); - INFOBIT(TIOCM_DTR, "|DTR"); - STATBIT(TIOCM_DSR, "|DSR"); - STATBIT(TIOCM_CAR, "|CD"); - STATBIT(TIOCM_RNG, "|RI"); - if (stat_buf[0]) - stat_buf[0] = ' '; - strcat(stat_buf, "\n"); - - ret += sprintf(buf + ret, stat_buf); - } -#undef STATBIT -#undef INFOBIT - return ret; -} - -static int uart_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - struct tty_driver *ttydrv = data; - struct uart_driver *drv = ttydrv->driver_state; - int i, len = 0, l; - off_t begin = 0; - - len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n", - "", "", ""); - for (i = 0; i < drv->nr && len < PAGE_SIZE - 96; i++) { - l = uart_line_info(page + len, drv, i); - len += l; - if (len + begin > off + count) - goto done; - if (len + begin < off) { - begin += len; - len = 0; - } - } - *eof = 1; - done: - if (off >= len + begin) - return 0; - *start = page + (off - begin); - return (count < begin + len - off) ? count : (begin + len - off); -} -#endif - -#ifdef CONFIG_SERIAL_CORE_CONSOLE -/* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ -struct uart_port * __init -uart_get_console(struct uart_port *ports, int nr, struct console *co) -{ - int idx = co->index; - - if (idx < 0 || idx >= nr || (ports[idx].iobase == 0 && - ports[idx].membase == NULL)) - for (idx = 0; idx < nr; idx++) - if (ports[idx].iobase != 0 || - ports[idx].membase != NULL) - break; - - co->index = idx; - - return ports + idx; -} - -/** - * uart_parse_options - Parse serial port baud/parity/bits/flow contro. - * @options: pointer to option string - * @baud: pointer to an 'int' variable for the baud rate. - * @parity: pointer to an 'int' variable for the parity. - * @bits: pointer to an 'int' variable for the number of data bits. - * @flow: pointer to an 'int' variable for the flow control character. - * - * uart_parse_options decodes a string containing the serial console - * options. The format of the string is , - * eg: 115200n8r - */ -void __init -uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow) -{ - char *s = options; - - *baud = simple_strtoul(s, NULL, 10); - while (*s >= '0' && *s <= '9') - s++; - if (*s) - *parity = *s++; - if (*s) - *bits = *s++ - '0'; - if (*s) - *flow = *s; -} - -struct baud_rates { - unsigned int rate; - unsigned int cflag; -}; - -static struct baud_rates baud_rates[] = { - { 921600, B921600 }, - { 460800, B460800 }, - { 230400, B230400 }, - { 115200, B115200 }, - { 57600, B57600 }, - { 38400, B38400 }, - { 19200, B19200 }, - { 9600, B9600 }, - { 4800, B4800 }, - { 2400, B2400 }, - { 1200, B1200 }, - { 0, B38400 } -}; - -/** - * uart_set_options - setup the serial console parameters - * @port: pointer to the serial ports uart_port structure - * @co: console pointer - * @baud: baud rate - * @parity: parity character - 'n' (none), 'o' (odd), 'e' (even) - * @bits: number of data bits - * @flow: flow control character - 'r' (rts) - */ -int __init -uart_set_options(struct uart_port *port, struct console *co, - int baud, int parity, int bits, int flow) -{ - struct termios termios; - int i; - - memset(&termios, 0, sizeof(struct termios)); - - termios.c_cflag = CREAD | HUPCL | CLOCAL; - - /* - * Construct a cflag setting. - */ - for (i = 0; baud_rates[i].rate; i++) - if (baud_rates[i].rate <= baud) - break; - - termios.c_cflag |= baud_rates[i].cflag; - - if (bits == 7) - termios.c_cflag |= CS7; - else - termios.c_cflag |= CS8; - - switch (parity) { - case 'o': case 'O': - termios.c_cflag |= PARODD; - /*fall through*/ - case 'e': case 'E': - termios.c_cflag |= PARENB; - break; - } - - if (flow == 'r') - termios.c_cflag |= CRTSCTS; - - port->ops->set_termios(port, &termios, NULL); - co->cflag = termios.c_cflag; - - return 0; -} -#endif /* CONFIG_SERIAL_CORE_CONSOLE */ - -static void uart_change_pm(struct uart_state *state, int pm_state) -{ - struct uart_port *port = state->port; - if (port->ops->pm) - port->ops->pm(port, pm_state, state->pm_state); - state->pm_state = pm_state; -} - -int uart_suspend_port(struct uart_driver *drv, struct uart_port *port, u32 level) -{ - struct uart_state *state = drv->state + port->line; - - down(&state->sem); - - switch (level) { - case SUSPEND_SAVE_STATE: - if (state->info && state->info->flags & UIF_INITIALIZED) { - struct uart_ops *ops = port->ops; - - spin_lock_irq(&port->lock); - ops->stop_tx(port, 0); - ops->set_mctrl(port, 0); - ops->stop_rx(port); - spin_unlock_irq(&port->lock); - - /* - * Wait for the transmitter to empty. - */ - while (!ops->tx_empty(port)) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(10*HZ/1000); - } - set_current_state(TASK_RUNNING); - - ops->shutdown(port); - } - break; - - case SUSPEND_POWER_DOWN: - /* - * Disable the console device before suspending. - */ - if (uart_console(port)) - port->cons->flags &= ~CON_ENABLED; - - uart_change_pm(state, 3); - break; - } - - up(&state->sem); - - return 0; -} - -int uart_resume_port(struct uart_driver *drv, struct uart_port *port, u32 level) -{ - struct uart_state *state = drv->state + port->line; - - down(&state->sem); - - switch (level) { - case RESUME_POWER_ON: - uart_change_pm(state, 0); - - /* - * Re-enable the console device after suspending. - */ - if (uart_console(port)) { - uart_change_speed(state, NULL); - port->cons->flags |= CON_ENABLED; - } - break; - - case RESUME_RESTORE_STATE: - if (state->info && state->info->flags & UIF_INITIALIZED) { - struct uart_ops *ops = port->ops; - - ops->set_mctrl(port, 0); - ops->startup(port); - uart_change_speed(state, NULL); - spin_lock_irq(&port->lock); - ops->set_mctrl(port, port->mctrl); - ops->start_tx(port, 0); - spin_unlock_irq(&port->lock); - } - break; - } - - up(&state->sem); - - return 0; -} - -static inline void -uart_report_port(struct uart_driver *drv, struct uart_port *port) -{ - printk("%s%d", drv->dev_name, port->line); - printk(" at "); - switch (port->iotype) { - case UPIO_PORT: - printk("I/O 0x%x", port->iobase); - break; - case UPIO_HUB6: - printk("I/O 0x%x offset 0x%x", port->iobase, port->hub6); - break; - case UPIO_MEM: - printk("MMIO 0x%lx", port->mapbase); - break; - } - printk(" (irq = %d) is a %s\n", port->irq, uart_type(port)); -} - -static void -uart_configure_port(struct uart_driver *drv, struct uart_state *state, - struct uart_port *port) -{ - unsigned int flags; - - /* - * If there isn't a port here, don't do anything further. - */ - if (!port->iobase && !port->mapbase && !port->membase) - return; - - /* - * Now do the auto configuration stuff. Note that config_port - * is expected to claim the resources and map the port for us. - */ - flags = UART_CONFIG_TYPE; - if (port->flags & UPF_AUTO_IRQ) - flags |= UART_CONFIG_IRQ; - if (port->flags & UPF_BOOT_AUTOCONF) { - port->type = PORT_UNKNOWN; - port->ops->config_port(port, flags); - } - - if (port->type != PORT_UNKNOWN) { - unsigned long flags; - - uart_report_port(drv, port); - - /* - * Ensure that the modem control lines are de-activated. - * We probably don't need a spinlock around this, but - */ - spin_lock_irqsave(&port->lock, flags); - port->ops->set_mctrl(port, 0); - spin_unlock_irqrestore(&port->lock, flags); - - /* - * Power down all ports by default, except the - * console if we have one. - */ - if (!uart_console(port)) - uart_change_pm(state, 3); - } -} - -/* - * This reverses the effects of uart_configure_port, hanging up the - * port before removal. - */ -static void -uart_unconfigure_port(struct uart_driver *drv, struct uart_state *state) -{ - struct uart_port *port = state->port; - struct uart_info *info = state->info; - - if (info && info->tty) - tty_vhangup(info->tty); - - down(&state->sem); - - state->info = NULL; - - /* - * Free the port IO and memory resources, if any. - */ - if (port->type != PORT_UNKNOWN) - port->ops->release_port(port); - - /* - * Indicate that there isn't a port here anymore. - */ - port->type = PORT_UNKNOWN; - - /* - * Kill the tasklet, and free resources. - */ - if (info) { - tasklet_kill(&info->tlet); - kfree(info); - } - - up(&state->sem); -} - -static struct tty_operations uart_ops = { - .open = uart_open, - .close = uart_close, - .write = uart_write, - .put_char = uart_put_char, - .flush_chars = uart_flush_chars, - .write_room = uart_write_room, - .chars_in_buffer= uart_chars_in_buffer, - .flush_buffer = uart_flush_buffer, - .ioctl = uart_ioctl, - .throttle = uart_throttle, - .unthrottle = uart_unthrottle, - .send_xchar = uart_send_xchar, - .set_termios = uart_set_termios, - .stop = uart_stop, - .start = uart_start, - .hangup = uart_hangup, - .break_ctl = uart_break_ctl, - .wait_until_sent= uart_wait_until_sent, -#ifdef CONFIG_PROC_FS - .read_proc = uart_read_proc, -#endif - .tiocmget = uart_tiocmget, - .tiocmset = uart_tiocmset, -}; - -/** - * uart_register_driver - register a driver with the uart core layer - * @drv: low level driver structure - * - * Register a uart driver with the core driver. We in turn register - * with the tty layer, and initialise the core driver per-port state. - * - * We have a proc file in /proc/tty/driver which is named after the - * normal driver. - * - * drv->port should be NULL, and the per-port structures should be - * registered using uart_add_one_port after this call has succeeded. - */ -int uart_register_driver(struct uart_driver *drv) -{ - struct tty_driver *normal = NULL; - int i, retval; - - BUG_ON(drv->state); - - /* - * Maybe we should be using a slab cache for this, especially if - * we have a large number of ports to handle. - */ - drv->state = kmalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL); - retval = -ENOMEM; - if (!drv->state) - goto out; - - memset(drv->state, 0, sizeof(struct uart_state) * drv->nr); - - normal = alloc_tty_driver(drv->nr); - if (!normal) - goto out; - - drv->tty_driver = normal; - - normal->owner = drv->owner; - normal->driver_name = drv->driver_name; - normal->devfs_name = drv->devfs_name; - normal->name = drv->dev_name; - normal->major = drv->major; - normal->minor_start = drv->minor; - normal->type = TTY_DRIVER_TYPE_SERIAL; - normal->subtype = SERIAL_TYPE_NORMAL; - normal->init_termios = tty_std_termios; - normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; - normal->driver_state = drv; - tty_set_operations(normal, &uart_ops); - - /* - * Initialise the UART state(s). - */ - for (i = 0; i < drv->nr; i++) { - struct uart_state *state = drv->state + i; - - state->close_delay = 5 * HZ / 10; - state->closing_wait = 30 * HZ; - - init_MUTEX(&state->sem); - } - - retval = tty_register_driver(normal); - out: - if (retval < 0) { - put_tty_driver(normal); - kfree(drv->state); - } - return retval; -} - -/** - * uart_unregister_driver - remove a driver from the uart core layer - * @drv: low level driver structure - * - * Remove all references to a driver from the core driver. The low - * level driver must have removed all its ports via the - * uart_remove_one_port() if it registered them with uart_add_one_port(). - * (ie, drv->port == NULL) - */ -void uart_unregister_driver(struct uart_driver *drv) -{ - struct tty_driver *p = drv->tty_driver; - tty_unregister_driver(p); - put_tty_driver(p); - kfree(drv->state); - drv->tty_driver = NULL; -} - -struct tty_driver *uart_console_device(struct console *co, int *index) -{ - struct uart_driver *p = co->data; - *index = co->index; - return p->tty_driver; -} - -/** - * uart_add_one_port - attach a driver-defined port structure - * @drv: pointer to the uart low level driver structure for this port - * @port: uart port structure to use for this port. - * - * This allows the driver to register its own uart_port structure - * with the core driver. The main purpose is to allow the low - * level uart drivers to expand uart_port, rather than having yet - * more levels of structures. - */ -int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) -{ - struct uart_state *state; - int ret = 0; - - BUG_ON(in_interrupt()); - - if (port->line >= drv->nr) - return -EINVAL; - - state = drv->state + port->line; - - down(&port_sem); - if (state->port) { - ret = -EINVAL; - goto out; - } - - state->port = port; - - spin_lock_init(&port->lock); - port->cons = drv->cons; - port->info = state->info; - - uart_configure_port(drv, state, port); - - /* - * Register the port whether it's detected or not. This allows - * setserial to be used to alter this ports parameters. - */ - tty_register_device(drv->tty_driver, port->line, NULL); - - out: - up(&port_sem); - - return ret; -} - -/** - * uart_remove_one_port - detach a driver defined port structure - * @drv: pointer to the uart low level driver structure for this port - * @port: uart port structure for this port - * - * This unhooks (and hangs up) the specified port structure from the - * core driver. No further calls will be made to the low-level code - * for this port. - */ -int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) -{ - struct uart_state *state = drv->state + port->line; - - BUG_ON(in_interrupt()); - - if (state->port != port) - printk(KERN_ALERT "Removing wrong port: %p != %p\n", - state->port, port); - - down(&port_sem); - - /* - * Remove the devices from devfs - */ - tty_unregister_device(drv->tty_driver, port->line); - - uart_unconfigure_port(drv, state); - state->port = NULL; - up(&port_sem); - - return 0; -} - -/* - * Are the two ports equivalent? - */ -static int uart_match_port(struct uart_port *port1, struct uart_port *port2) -{ - if (port1->iotype != port2->iotype) - return 0; - - switch (port1->iotype) { - case UPIO_PORT: - return (port1->iobase == port2->iobase); - case UPIO_HUB6: - return (port1->iobase == port2->iobase) && - (port1->hub6 == port2->hub6); - case UPIO_MEM: - return (port1->membase == port2->membase); - } - return 0; -} - -/* - * Try to find an unused uart_state slot for a port. - */ -static struct uart_state * -uart_find_match_or_unused(struct uart_driver *drv, struct uart_port *port) -{ - int i; - - /* - * First, find a port entry which matches. Note: if we do - * find a matching entry, and it has a non-zero use count, - * then we can't register the port. - */ - for (i = 0; i < drv->nr; i++) - if (uart_match_port(drv->state[i].port, port)) - return &drv->state[i]; - - /* - * We didn't find a matching entry, so look for the first - * free entry. We look for one which hasn't been previously - * used (indicated by zero iobase). - */ - for (i = 0; i < drv->nr; i++) - if (drv->state[i].port->type == PORT_UNKNOWN && - drv->state[i].port->iobase == 0 && - drv->state[i].count == 0) - return &drv->state[i]; - - /* - * That also failed. Last resort is to find any currently - * entry which doesn't have a real port associated with it. - */ - for (i = 0; i < drv->nr; i++) - if (drv->state[i].port->type == PORT_UNKNOWN && - drv->state[i].count == 0) - return &drv->state[i]; - - return NULL; -} - -/** - * uart_register_port: register uart settings with a port - * @drv: pointer to the uart low level driver structure for this port - * @port: uart port structure describing the port - * - * Register UART settings with the specified low level driver. Detect - * the type of the port if UPF_BOOT_AUTOCONF is set, and detect the - * IRQ if UPF_AUTO_IRQ is set. - * - * We try to pick the same port for the same IO base address, so that - * when a modem is plugged in, unplugged and plugged back in, it gets - * allocated the same port. - * - * Returns negative error, or positive line number. - */ -int uart_register_port(struct uart_driver *drv, struct uart_port *port) -{ - struct uart_state *state; - int ret; - - down(&port_sem); - - state = uart_find_match_or_unused(drv, port); - - if (state) { - /* - * Ok, we've found a line that we can use. - * - * If we find a port that matches this one, and it appears - * to be in-use (even if it doesn't have a type) we shouldn't - * alter it underneath itself - the port may be open and - * trying to do useful work. - */ - if (uart_users(state) != 0) { - ret = -EBUSY; - goto out; - } - - /* - * If the port is already initialised, don't touch it. - */ - if (state->port->type == PORT_UNKNOWN) { - state->port->iobase = port->iobase; - state->port->membase = port->membase; - state->port->irq = port->irq; - state->port->uartclk = port->uartclk; - state->port->fifosize = port->fifosize; - state->port->regshift = port->regshift; - state->port->iotype = port->iotype; - state->port->flags = port->flags; - state->port->line = state - drv->state; - state->port->mapbase = port->mapbase; - - uart_configure_port(drv, state, state->port); - } - - ret = state->port->line; - } else - ret = -ENOSPC; - out: - up(&port_sem); - return ret; -} - -/** - * uart_unregister_port - de-allocate a port - * @drv: pointer to the uart low level driver structure for this port - * @line: line index previously returned from uart_register_port() - * - * Hang up the specified line associated with the low level driver, - * and mark the port as unused. - */ -void uart_unregister_port(struct uart_driver *drv, int line) -{ - struct uart_state *state; - - if (line < 0 || line >= drv->nr) { - printk(KERN_ERR "Attempt to unregister "); - printk("%s%d", drv->dev_name, line); - printk("\n"); - return; - } - - state = drv->state + line; - - down(&port_sem); - uart_unconfigure_port(drv, state); - up(&port_sem); -} - -EXPORT_SYMBOL(uart_write_wakeup); -EXPORT_SYMBOL(uart_register_driver); -EXPORT_SYMBOL(uart_unregister_driver); -EXPORT_SYMBOL(uart_suspend_port); -EXPORT_SYMBOL(uart_resume_port); -EXPORT_SYMBOL(uart_register_port); -EXPORT_SYMBOL(uart_unregister_port); -EXPORT_SYMBOL(uart_add_one_port); -EXPORT_SYMBOL(uart_remove_one_port); - -MODULE_DESCRIPTION("Serial driver core"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c new file mode 100644 index 000000000000..a264d72ca695 --- /dev/null +++ b/drivers/serial/serial_core.c @@ -0,0 +1,2432 @@ +/* + * linux/drivers/char/core.c + * + * Driver core for serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright 1999 ARM Limited + * Copyright (C) 2000-2001 Deep Blue Solutions Ltd. + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for serial_state and serial_icounter_struct */ + +#include +#include + +#undef DEBUG +#ifdef DEBUG +#define DPRINTK(x...) printk(x) +#else +#define DPRINTK(x...) do { } while (0) +#endif + +/* + * This is used to lock changes in serial line configuration. + */ +static DECLARE_MUTEX(port_sem); + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->blocked_open : 0)) + +#ifdef CONFIG_SERIAL_CORE_CONSOLE +#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) +#else +#define uart_console(port) (0) +#endif + +static void uart_change_speed(struct uart_state *state, struct termios *old_termios); +static void uart_wait_until_sent(struct tty_struct *tty, int timeout); +static void uart_change_pm(struct uart_state *state, int pm_state); + +/* + * This routine is used by the interrupt handler to schedule processing in + * the software interrupt portion of the driver. + */ +void uart_write_wakeup(struct uart_port *port) +{ + struct uart_info *info = port->info; + tasklet_schedule(&info->tlet); +} + +static void uart_stop(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + port->ops->stop_tx(port, 1); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void __uart_start(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + + if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf && + !tty->stopped && !tty->hw_stopped) + port->ops->start_tx(port, 1); +} + +static void uart_start(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + __uart_start(tty); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void uart_tasklet_action(unsigned long data) +{ + struct uart_state *state = (struct uart_state *)data; + struct tty_struct *tty; + + tty = state->info->tty; + if (tty) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + tty->ldisc.write_wakeup(tty); + wake_up_interruptible(&tty->write_wait); + } +} + +static inline void +uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) +{ + unsigned long flags; + unsigned int old; + + spin_lock_irqsave(&port->lock, flags); + old = port->mctrl; + port->mctrl = (old & ~clear) | set; + if (old != port->mctrl) + port->ops->set_mctrl(port, port->mctrl); + spin_unlock_irqrestore(&port->lock, flags); +} + +#define uart_set_mctrl(port,set) uart_update_mctrl(port,set,0) +#define uart_clear_mctrl(port,clear) uart_update_mctrl(port,0,clear) + +/* + * Startup the port. This will be called once per open. All calls + * will be serialised by the per-port semaphore. + */ +static int uart_startup(struct uart_state *state, int init_hw) +{ + struct uart_info *info = state->info; + struct uart_port *port = state->port; + unsigned long page; + int retval = 0; + + if (info->flags & UIF_INITIALIZED) + return 0; + + /* + * Set the TTY IO error marker - we will only clear this + * once we have successfully opened the port. Also set + * up the tty->alt_speed kludge + */ + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + if (port->type == PORT_UNKNOWN) + return 0; + + /* + * Initialise and allocate the transmit and temporary + * buffer. + */ + if (!info->xmit.buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + info->xmit.buf = (unsigned char *) page; + info->tmpbuf = info->xmit.buf + UART_XMIT_SIZE; + init_MUTEX(&info->tmpbuf_sem); + uart_circ_clear(&info->xmit); + } + + port->mctrl = 0; + + retval = port->ops->startup(port); + if (retval == 0) { + if (init_hw) { + /* + * Initialise the hardware port settings. + */ + uart_change_speed(state, NULL); + + /* + * Setup the RTS and DTR signals once the + * port is open and ready to respond. + */ + if (info->tty->termios->c_cflag & CBAUD) + uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); + } + + info->flags |= UIF_INITIALIZED; + + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + + if (retval && capable(CAP_SYS_ADMIN)) + retval = 0; + + return retval; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. Calls to + * uart_shutdown are serialised by the per-port semaphore. + */ +static void uart_shutdown(struct uart_state *state) +{ + struct uart_info *info = state->info; + struct uart_port *port = state->port; + + if (!(info->flags & UIF_INITIALIZED)) + return; + + /* + * Turn off DTR and RTS early. + */ + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free + * the irq here so the queue might never be woken up. Note + * that we won't end up waiting on delta_msr_wait again since + * any outstanding file descriptors should be pointing at + * hung_up_tty_fops now. + */ + wake_up_interruptible(&info->delta_msr_wait); + + /* + * Free the IRQ and disable the port. + */ + port->ops->shutdown(port); + + /* + * Ensure that the IRQ handler isn't running on another CPU. + */ + synchronize_irq(port->irq); + + /* + * Free the transmit buffer page. + */ + if (info->xmit.buf) { + free_page((unsigned long)info->xmit.buf); + info->xmit.buf = NULL; + info->tmpbuf = NULL; + } + + /* + * kill off our tasklet + */ + tasklet_kill(&info->tlet); + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~UIF_INITIALIZED; +} + +/** + * uart_update_timeout - update per-port FIFO timeout. + * @port: uart_port structure describing the port. + * @cflag: termios cflag value + * @quot: uart clock divisor quotient + * + * Set the port FIFO timeout value. The @cflag value should + * reflect the actual hardware settings. + */ +void +uart_update_timeout(struct uart_port *port, unsigned int cflag, + unsigned int baud) +{ + unsigned int bits; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: + bits = 7; + break; + case CS6: + bits = 8; + break; + case CS7: + bits = 9; + break; + default: + bits = 10; + break; // CS8 + } + + if (cflag & CSTOPB) + bits++; + if (cflag & PARENB) + bits++; + + /* + * The total number of bits to be transmitted in the fifo. + */ + bits = bits * port->fifosize; + + /* + * Figure the timeout to send the above number of bits. + * Add .02 seconds of slop + */ + port->timeout = (HZ * bits) / baud + HZ/50; +} + +EXPORT_SYMBOL(uart_update_timeout); + +/** + * uart_get_baud_rate - return baud rate for a particular port + * @port: uart_port structure describing the port in question. + * @termios: desired termios settings. + * @old: old termios (or NULL) + * @min: minimum acceptable baud rate + * @max: maximum acceptable baud rate + * + * Decode the termios structure into a numeric baud rate, + * taking account of the magic 38400 baud rate (with spd_* + * flags), and mapping the %B0 rate to 9600 baud. + * + * If the new baud rate is invalid, try the old termios setting. + * If it's still invalid, we try 9600 baud. + * + * Update the @termios structure to reflect the baud rate + * we're actually going to be using. + */ +unsigned int +uart_get_baud_rate(struct uart_port *port, struct termios *termios, + struct termios *old, unsigned int min, unsigned int max) +{ + unsigned int try, baud, altbaud = 38400; + unsigned int flags = port->flags & UPF_SPD_MASK; + + if (flags == UPF_SPD_HI) + altbaud = 57600; + if (flags == UPF_SPD_VHI) + altbaud = 115200; + if (flags == UPF_SPD_SHI) + altbaud = 230400; + if (flags == UPF_SPD_WARP) + altbaud = 460800; + + for (try = 0; try < 2; try++) { + baud = tty_termios_baud_rate(termios); + + /* + * The spd_hi, spd_vhi, spd_shi, spd_warp kludge... + * Die! Die! Die! + */ + if (baud == 38400) + baud = altbaud; + + /* + * Special case: B0 rate. + */ + if (baud == 0) + baud = 9600; + + if (baud >= min && baud <= max) + return baud; + + /* + * Oops, the quotient was zero. Try again with + * the old baud rate if possible. + */ + termios->c_cflag &= ~CBAUD; + if (old) { + termios->c_cflag |= old->c_cflag & CBAUD; + old = NULL; + continue; + } + + /* + * As a last resort, if the quotient is zero, + * default to 9600 bps + */ + termios->c_cflag |= B9600; + } + + return 0; +} + +EXPORT_SYMBOL(uart_get_baud_rate); + +/** + * uart_get_divisor - return uart clock divisor + * @port: uart_port structure describing the port. + * @baud: desired baud rate + * + * Calculate the uart clock divisor for the port. + */ +unsigned int +uart_get_divisor(struct uart_port *port, unsigned int baud) +{ + unsigned int quot; + + /* + * Old custom speed handling. + */ + if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) + quot = port->custom_divisor; + else + quot = port->uartclk / (16 * baud); + + return quot; +} + +EXPORT_SYMBOL(uart_get_divisor); + +static void +uart_change_speed(struct uart_state *state, struct termios *old_termios) +{ + struct tty_struct *tty = state->info->tty; + struct uart_port *port = state->port; + struct termios *termios; + + /* + * If we have no tty, termios, or the port does not exist, + * then we can't set the parameters for this port. + */ + if (!tty || !tty->termios || port->type == PORT_UNKNOWN) + return; + + termios = tty->termios; + + /* + * Set flags based on termios cflag + */ + if (termios->c_cflag & CRTSCTS) + state->info->flags |= UIF_CTS_FLOW; + else + state->info->flags &= ~UIF_CTS_FLOW; + + if (termios->c_cflag & CLOCAL) + state->info->flags &= ~UIF_CHECK_CD; + else + state->info->flags |= UIF_CHECK_CD; + + port->ops->set_termios(port, termios, old_termios); +} + +static inline void +__uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c) +{ + unsigned long flags; + + if (!circ->buf) + return; + + spin_lock_irqsave(&port->lock, flags); + if (uart_circ_chars_free(circ) != 0) { + circ->buf[circ->head] = c; + circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); + } + spin_unlock_irqrestore(&port->lock, flags); +} + +static inline int +__uart_user_write(struct uart_port *port, struct circ_buf *circ, + const unsigned char *buf, int count) +{ + unsigned long flags; + int c, ret = 0; + + if (down_interruptible(&port->info->tmpbuf_sem)) + return -EINTR; + + while (1) { + int c1; + c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + + c -= copy_from_user(port->info->tmpbuf, buf, c); + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + spin_lock_irqsave(&port->lock, flags); + c1 = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(circ->buf + circ->head, port->info->tmpbuf, c); + circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); + spin_unlock_irqrestore(&port->lock, flags); + buf += c; + count -= c; + ret += c; + } + up(&port->info->tmpbuf_sem); + + return ret; +} + +static inline int +__uart_kern_write(struct uart_port *port, struct circ_buf *circ, + const unsigned char *buf, int count) +{ + unsigned long flags; + int c, ret = 0; + + spin_lock_irqsave(&port->lock, flags); + while (1) { + c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + memcpy(circ->buf + circ->head, buf, c); + circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); + buf += c; + count -= c; + ret += c; + } + spin_unlock_irqrestore(&port->lock, flags); + + return ret; +} + +static void uart_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct uart_state *state = tty->driver_data; + + if (tty) + __uart_put_char(state->port, &state->info->xmit, ch); +} + +static void uart_flush_chars(struct tty_struct *tty) +{ + uart_start(tty); +} + +static int +uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf, + int count) +{ + struct uart_state *state = tty->driver_data; + int ret; + + if (!tty || !state->info->xmit.buf) + return 0; + + if (from_user) + ret = __uart_user_write(state->port, &state->info->xmit, buf, count); + else + ret = __uart_kern_write(state->port, &state->info->xmit, buf, count); + + uart_start(tty); + return ret; +} + +static int uart_write_room(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + + return uart_circ_chars_free(&state->info->xmit); +} + +static int uart_chars_in_buffer(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + + return uart_circ_chars_pending(&state->info->xmit); +} + +static void uart_flush_buffer(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + unsigned long flags; + + DPRINTK("uart_flush_buffer(%d) called\n", tty->index); + + spin_lock_irqsave(&port->lock, flags); + uart_circ_clear(&state->info->xmit); + spin_unlock_irqrestore(&port->lock, flags); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void uart_send_xchar(struct tty_struct *tty, char ch) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + unsigned long flags; + + if (port->ops->send_xchar) + port->ops->send_xchar(port, ch); + else { + port->x_char = ch; + if (ch) { + spin_lock_irqsave(&port->lock, flags); + port->ops->start_tx(port, 0); + spin_unlock_irqrestore(&port->lock, flags); + } + } +} + +static void uart_throttle(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + + if (I_IXOFF(tty)) + uart_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) + uart_clear_mctrl(state->port, TIOCM_RTS); +} + +static void uart_unthrottle(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + + if (I_IXOFF(tty)) { + if (port->x_char) + port->x_char = 0; + else + uart_send_xchar(tty, START_CHAR(tty)); + } + + if (tty->termios->c_cflag & CRTSCTS) + uart_set_mctrl(port, TIOCM_RTS); +} + +static int uart_get_info(struct uart_state *state, struct serial_struct *retinfo) +{ + struct uart_port *port = state->port; + struct serial_struct tmp; + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = port->type; + tmp.line = port->line; + tmp.port = port->iobase; + if (HIGH_BITS_OFFSET) + tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET; + tmp.irq = port->irq; + tmp.flags = port->flags; + tmp.xmit_fifo_size = port->fifosize; + tmp.baud_base = port->uartclk / 16; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait; + tmp.custom_divisor = port->custom_divisor; + tmp.hub6 = port->hub6; + tmp.io_type = port->iotype; + tmp.iomem_reg_shift = port->regshift; + tmp.iomem_base = (void *)port->mapbase; + + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int +uart_set_info(struct uart_state *state, struct serial_struct *newinfo) +{ + struct serial_struct new_serial; + struct uart_port *port = state->port; + unsigned long new_port; + unsigned int change_irq, change_port, old_flags; + unsigned int old_custom_divisor; + int retval = 0; + + if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) + return -EFAULT; + + new_port = new_serial.port; + if (HIGH_BITS_OFFSET) + new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + + new_serial.irq = irq_canonicalize(new_serial.irq); + + /* + * This semaphore protects state->count. It is also + * very useful to prevent opens. Also, take the + * port configuration semaphore to make sure that a + * module insertion/removal doesn't change anything + * under us. + */ + down(&state->sem); + + change_irq = new_serial.irq != port->irq; + + /* + * Since changing the 'type' of the port changes its resource + * allocations, we should treat type changes the same as + * IO port changes. + */ + change_port = new_port != port->iobase || + (unsigned long)new_serial.iomem_base != port->mapbase || + new_serial.hub6 != port->hub6 || + new_serial.io_type != port->iotype || + new_serial.iomem_reg_shift != port->regshift || + new_serial.type != port->type; + + old_flags = port->flags; + old_custom_divisor = port->custom_divisor; + + if (!capable(CAP_SYS_ADMIN)) { + retval = -EPERM; + if (change_irq || change_port || + (new_serial.baud_base != port->uartclk / 16) || + (new_serial.close_delay != state->close_delay) || + (new_serial.closing_wait != state->closing_wait) || + (new_serial.xmit_fifo_size != port->fifosize) || + (((new_serial.flags ^ old_flags) & ~UPF_USR_MASK) != 0)) + goto exit; + port->flags = ((port->flags & ~UPF_USR_MASK) | + (new_serial.flags & UPF_USR_MASK)); + port->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + /* + * Ask the low level driver to verify the settings. + */ + if (port->ops->verify_port) + retval = port->ops->verify_port(port, &new_serial); + + if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || + (new_serial.baud_base < 9600)) + retval = -EINVAL; + + if (retval) + goto exit; + + if (change_port || change_irq) { + retval = -EBUSY; + + /* + * Make sure that we are the sole user of this port. + */ + if (uart_users(state) > 1) + goto exit; + + /* + * We need to shutdown the serial port at the old + * port/type/irq combination. + */ + uart_shutdown(state); + } + + if (change_port) { + unsigned long old_iobase, old_mapbase; + unsigned int old_type, old_iotype, old_hub6, old_shift; + + old_iobase = port->iobase; + old_mapbase = port->mapbase; + old_type = port->type; + old_hub6 = port->hub6; + old_iotype = port->iotype; + old_shift = port->regshift; + + /* + * Free and release old regions + */ + if (old_type != PORT_UNKNOWN) + port->ops->release_port(port); + + port->iobase = new_port; + port->type = new_serial.type; + port->hub6 = new_serial.hub6; + port->iotype = new_serial.io_type; + port->regshift = new_serial.iomem_reg_shift; + port->mapbase = (unsigned long)new_serial.iomem_base; + + /* + * Claim and map the new regions + */ + if (port->type != PORT_UNKNOWN) { + retval = port->ops->request_port(port); + } else { + /* Always success - Jean II */ + retval = 0; + } + + /* + * If we fail to request resources for the + * new port, try to restore the old settings. + */ + if (retval && old_type != PORT_UNKNOWN) { + port->iobase = old_iobase; + port->type = old_type; + port->hub6 = old_hub6; + port->iotype = old_iotype; + port->regshift = old_shift; + port->mapbase = old_mapbase; + retval = port->ops->request_port(port); + /* + * If we failed to restore the old settings, + * we fail like this. + */ + if (retval) + port->type = PORT_UNKNOWN; + + /* + * We failed anyway. + */ + retval = -EBUSY; + } + } + + port->irq = new_serial.irq; + port->uartclk = new_serial.baud_base * 16; + port->flags = (port->flags & ~UPF_CHANGE_MASK) | + (new_serial.flags & UPF_CHANGE_MASK); + port->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay * HZ / 100; + state->closing_wait = new_serial.closing_wait * HZ / 100; + port->fifosize = new_serial.xmit_fifo_size; + if (state->info->tty) + state->info->tty->low_latency = + (port->flags & UPF_LOW_LATENCY) ? 1 : 0; + + check_and_exit: + retval = 0; + if (port->type == PORT_UNKNOWN) + goto exit; + if (state->info->flags & UIF_INITIALIZED) { + if (((old_flags ^ port->flags) & UPF_SPD_MASK) || + old_custom_divisor != port->custom_divisor) { + /* If they're setting up a custom divisor or speed, + * instead of clearing it, then bitch about it. No + * need to rate-limit; it's CAP_SYS_ADMIN only. */ + if (port->flags & UPF_SPD_MASK) { + printk(KERN_NOTICE "%s sets custom speed on %s%d. This is deprecated.\n", + current->comm, state->info->tty->driver->name, + state->port->line); + } + uart_change_speed(state, NULL); + } + } else + retval = uart_startup(state, 1); + exit: + up(&state->sem); + return retval; +} + + +/* + * uart_get_lsr_info - get line status register info. + * Note: uart_ioctl protects us against hangups. + */ +static int uart_get_lsr_info(struct uart_state *state, unsigned int *value) +{ + struct uart_port *port = state->port; + unsigned int result; + + result = port->ops->tx_empty(port); + + /* + * If we're about to load something into the transmit + * register, we'll pretend the transmitter isn't empty to + * avoid a race condition (depending on when the transmit + * interrupt happens). + */ + if (port->x_char || + ((uart_circ_chars_pending(&state->info->xmit) > 0) && + !state->info->tty->stopped && !state->info->tty->hw_stopped)) + result &= ~TIOCSER_TEMT; + + return put_user(result, value); +} + +static int uart_tiocmget(struct tty_struct *tty, struct file *file) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + int result = -EIO; + + down(&state->sem); + if ((!file || !tty_hung_up_p(file)) && + !(tty->flags & (1 << TTY_IO_ERROR))) { + result = port->mctrl; + result |= port->ops->get_mctrl(port); + } + up(&state->sem); + + return result; +} + +static int +uart_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + int ret = -EIO; + + down(&state->sem); + if ((!file || !tty_hung_up_p(file)) && + !(tty->flags & (1 << TTY_IO_ERROR))) { + uart_update_mctrl(port, set, clear); + ret = 0; + } + up(&state->sem); + return ret; +} + +static void uart_break_ctl(struct tty_struct *tty, int break_state) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + + BUG_ON(!kernel_locked()); + + down(&state->sem); + + if (port->type != PORT_UNKNOWN) + port->ops->break_ctl(port, break_state); + + up(&state->sem); +} + +static int uart_do_autoconfig(struct uart_state *state) +{ + struct uart_port *port = state->port; + int flags, ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + /* + * Take the per-port semaphore. This prevents count from + * changing, and hence any extra opens of the port while + * we're auto-configuring. + */ + if (down_interruptible(&state->sem)) + return -ERESTARTSYS; + + ret = -EBUSY; + if (uart_users(state) == 1) { + uart_shutdown(state); + + /* + * If we already have a port type configured, + * we must release its resources. + */ + if (port->type != PORT_UNKNOWN) + port->ops->release_port(port); + + flags = UART_CONFIG_TYPE; + if (port->flags & UPF_AUTO_IRQ) + flags |= UART_CONFIG_IRQ; + + /* + * This will claim the ports resources if + * a port is found. + */ + port->ops->config_port(port, flags); + + ret = uart_startup(state, 1); + } + up(&state->sem); + return ret; +} + +/* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ +static int +uart_wait_modem_status(struct uart_state *state, unsigned long arg) +{ + struct uart_port *port = state->port; + DECLARE_WAITQUEUE(wait, current); + struct uart_icount cprev, cnow; + int ret; + + /* + * note the counters on entry + */ + spin_lock_irq(&port->lock); + memcpy(&cprev, &port->icount, sizeof(struct uart_icount)); + + /* + * Force modem status interrupts on + */ + port->ops->enable_ms(port); + spin_unlock_irq(&port->lock); + + add_wait_queue(&state->info->delta_msr_wait, &wait); + for (;;) { + spin_lock_irq(&port->lock); + memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&port->lock); + + set_current_state(TASK_INTERRUPTIBLE); + + if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { + ret = 0; + break; + } + + schedule(); + + /* see if a signal did it */ + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + cprev = cnow; + } + + current->state = TASK_RUNNING; + remove_wait_queue(&state->info->delta_msr_wait, &wait); + + return ret; +} + +/* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ +static int +uart_get_count(struct uart_state *state, struct serial_icounter_struct *icnt) +{ + struct serial_icounter_struct icount; + struct uart_icount cnow; + struct uart_port *port = state->port; + + spin_lock_irq(&port->lock); + memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&port->lock); + + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; +} + +/* + * Called via sys_ioctl under the BKL. We can use spin_lock_irq() here. + */ +static int +uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct uart_state *state = tty->driver_data; + int ret = -ENOIOCTLCMD; + + BUG_ON(!kernel_locked()); + + /* + * These ioctls don't rely on the hardware to be present. + */ + switch (cmd) { + case TIOCGSERIAL: + ret = uart_get_info(state, (struct serial_struct *)arg); + break; + + case TIOCSSERIAL: + ret = uart_set_info(state, (struct serial_struct *)arg); + break; + + case TIOCSERCONFIG: + ret = uart_do_autoconfig(state); + break; + + case TIOCSERGWILD: /* obsolete */ + case TIOCSERSWILD: /* obsolete */ + ret = 0; + break; + } + + if (ret != -ENOIOCTLCMD) + goto out; + + if (tty->flags & (1 << TTY_IO_ERROR)) { + ret = -EIO; + goto out; + } + + /* + * The following should only be used when hardware is present. + */ + switch (cmd) { + case TIOCMIWAIT: + ret = uart_wait_modem_status(state, arg); + break; + + case TIOCGICOUNT: + ret = uart_get_count(state, (struct serial_icounter_struct *)arg); + break; + } + + if (ret != -ENOIOCTLCMD) + goto out; + + down(&state->sem); + + if (tty_hung_up_p(filp)) { + ret = -EIO; + goto out_up; + } + + /* + * All these rely on hardware being present and need to be + * protected against the tty being hung up. + */ + switch (cmd) { + case TIOCSERGETLSR: /* Get line status register */ + ret = uart_get_lsr_info(state, (unsigned int *)arg); + break; + + default: { + struct uart_port *port = state->port; + if (port->ops->ioctl) + ret = port->ops->ioctl(port, cmd, arg); + break; + } + } + out_up: + up(&state->sem); + out: + return ret; +} + +static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct uart_state *state = tty->driver_data; + unsigned long flags; + unsigned int cflag = tty->termios->c_cflag; + + BUG_ON(!kernel_locked()); + + /* + * These are the bits that are used to setup various + * flags in the low level driver. + */ +#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + + if ((cflag ^ old_termios->c_cflag) == 0 && + RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) + return; + + uart_change_speed(state, old_termios); + + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) + uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR); + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { + unsigned int mask = TIOCM_DTR; + if (!(cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) + mask |= TIOCM_RTS; + uart_set_mctrl(state->port, mask); + } + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { + spin_lock_irqsave(&state->port->lock, flags); + tty->hw_stopped = 0; + __uart_start(tty); + spin_unlock_irqrestore(&state->port->lock, flags); + } + +#if 0 + /* + * No need to wake up processes in open wait, since they + * sample the CLOCAL flag once, and don't recheck it. + * XXX It's not clear whether the current behavior is correct + * or not. Hence, this may change..... + */ + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&state->info->open_wait); +#endif +} + +/* + * In 2.4.5, calls to this will be serialized via the BKL in + * linux/drivers/char/tty_io.c:tty_release() + * linux/drivers/char/tty_io.c:do_tty_handup() + */ +static void uart_close(struct tty_struct *tty, struct file *filp) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + + BUG_ON(!kernel_locked()); + DPRINTK("uart_close(%d) called\n", port->line); + + down(&state->sem); + + if (tty_hung_up_p(filp)) + goto done; + + if ((tty->count == 1) && (state->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. state->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("uart_close: bad serial port count; tty->count is 1, " + "state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("rs_close: bad serial port count for %s: %d\n", + tty->name, state->count); + state->count = 0; + } + if (state->count) + goto done; + + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters by + * setting tty->closing. + */ + tty->closing = 1; + + if (state->closing_wait != USF_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, state->closing_wait); + + /* + * At this point, we stop accepting input. To do this, we + * disable the receive line status interrupts. + */ + if (state->info->flags & UIF_INITIALIZED) { + unsigned long flags; + spin_lock_irqsave(&port->lock, flags); + port->ops->stop_rx(port); + spin_unlock_irqrestore(&port->lock, flags); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + uart_wait_until_sent(tty, port->timeout); + } + + uart_shutdown(state); + uart_flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + state->info->tty = NULL; + + if (state->info->blocked_open) { + if (state->close_delay) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(state->close_delay); + set_current_state(TASK_RUNNING); + } + } else if (!uart_console(port)) { + uart_change_pm(state, 3); + } + + /* + * Wake up anyone trying to open this port. + */ + state->info->flags &= ~UIF_NORMAL_ACTIVE; + wake_up_interruptible(&state->info->open_wait); + + done: + up(&state->sem); +} + +static void uart_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + unsigned long char_time, expire; + + BUG_ON(!kernel_locked()); + + if (port->type == PORT_UNKNOWN || port->fifosize == 0) + return; + + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check + * interval should also be less than the timeout. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + char_time = (port->timeout - HZ/50) / port->fifosize; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout && timeout < char_time) + char_time = timeout; + + /* + * If the transmitter hasn't cleared in twice the approximate + * amount of time to send the entire FIFO, it probably won't + * ever clear. This assumes the UART isn't doing flow + * control, which is currently the case. Hence, if it ever + * takes longer than port->timeout, this is probably due to a + * UART bug of some kind. So, we clamp the timeout parameter at + * 2*port->timeout. + */ + if (timeout == 0 || timeout > 2 * port->timeout) + timeout = 2 * port->timeout; + + expire = jiffies + timeout; + + DPRINTK("uart_wait_until_sent(%d), jiffies=%lu, expire=%lu...\n", + port->line, jiffies, expire); + + /* + * Check whether the transmitter is empty every 'char_time'. + * 'timeout' / 'expire' give us the maximum amount of time + * we wait. + */ + while (!port->ops->tx_empty(port)) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (time_after(jiffies, expire)) + break; + } + set_current_state(TASK_RUNNING); /* might not be needed */ +} + +/* + * This is called with the BKL held in + * linux/drivers/char/tty_io.c:do_tty_hangup() + * We're called from the eventd thread, so we can sleep for + * a _short_ time only. + */ +static void uart_hangup(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + + BUG_ON(!kernel_locked()); + DPRINTK("uart_hangup(%d)\n", state->port->line); + + down(&state->sem); + if (state->info && state->info->flags & UIF_NORMAL_ACTIVE) { + uart_flush_buffer(tty); + uart_shutdown(state); + state->count = 0; + state->info->flags &= ~UIF_NORMAL_ACTIVE; + state->info->tty = NULL; + wake_up_interruptible(&state->info->open_wait); + wake_up_interruptible(&state->info->delta_msr_wait); + } + up(&state->sem); +} + +/* + * Copy across the serial console cflag setting into the termios settings + * for the initial open of the port. This allows continuity between the + * kernel settings, and the settings init adopts when it opens the port + * for the first time. + */ +static void uart_update_termios(struct uart_state *state) +{ + struct tty_struct *tty = state->info->tty; + struct uart_port *port = state->port; + + if (uart_console(port) && port->cons->cflag) { + tty->termios->c_cflag = port->cons->cflag; + port->cons->cflag = 0; + } + + /* + * If the device failed to grab its irq resources, + * or some other error occurred, don't try to talk + * to the port hardware. + */ + if (!(tty->flags & (1 << TTY_IO_ERROR))) { + /* + * Make termios settings take effect. + */ + uart_change_speed(state, NULL); + + /* + * And finally enable the RTS and DTR signals. + */ + if (tty->termios->c_cflag & CBAUD) + uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); + } +} + +/* + * Block the open until the port is ready. We must be called with + * the per-port semaphore held. + */ +static int +uart_block_til_ready(struct file *filp, struct uart_state *state) +{ + DECLARE_WAITQUEUE(wait, current); + struct uart_info *info = state->info; + struct uart_port *port = state->port; + + info->blocked_open++; + state->count--; + + add_wait_queue(&info->open_wait, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + + /* + * If we have been hung up, tell userspace/restart open. + */ + if (tty_hung_up_p(filp) || info->tty == NULL) + break; + + /* + * If the port has been closed, tell userspace/restart open. + */ + if (!(info->flags & UIF_INITIALIZED)) + break; + + /* + * If non-blocking mode is set, or CLOCAL mode is set, + * we don't want to wait for the modem status lines to + * indicate that the port is ready. + * + * Also, if the port is not enabled/configured, we want + * to allow the open to succeed here. Note that we will + * have set TTY_IO_ERROR for a non-existant port. + */ + if ((filp->f_flags & O_NONBLOCK) || + (info->tty->termios->c_cflag & CLOCAL) || + (info->tty->flags & (1 << TTY_IO_ERROR))) { + break; + } + + /* + * Set DTR to allow modem to know we're waiting. Do + * not set RTS here - we want to make sure we catch + * the data from the modem. + */ + if (info->tty->termios->c_cflag & CBAUD) + uart_set_mctrl(port, TIOCM_DTR); + + /* + * and wait for the carrier to indicate that the + * modem is ready for us. + */ + if (port->ops->get_mctrl(port) & TIOCM_CAR) + break; + + up(&state->sem); + schedule(); + down(&state->sem); + + if (signal_pending(current)) + break; + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + + state->count++; + info->blocked_open--; + + if (signal_pending(current)) + return -ERESTARTSYS; + + if (!info->tty || tty_hung_up_p(filp)) + return -EAGAIN; + + return 0; +} + +static struct uart_state *uart_get(struct uart_driver *drv, int line) +{ + struct uart_state *state; + + down(&port_sem); + state = drv->state + line; + if (down_interruptible(&state->sem)) { + state = ERR_PTR(-ERESTARTSYS); + goto out; + } + + state->count++; + if (!state->port) { + state->count--; + up(&state->sem); + state = ERR_PTR(-ENXIO); + goto out; + } + + if (!state->info) { + state->info = kmalloc(sizeof(struct uart_info), GFP_KERNEL); + if (state->info) { + memset(state->info, 0, sizeof(struct uart_info)); + init_waitqueue_head(&state->info->open_wait); + init_waitqueue_head(&state->info->delta_msr_wait); + + /* + * Link the info into the other structures. + */ + state->port->info = state->info; + + tasklet_init(&state->info->tlet, uart_tasklet_action, + (unsigned long)state); + } else { + state->count--; + up(&state->sem); + state = ERR_PTR(-ENOMEM); + } + } + + out: + up(&port_sem); + return state; +} + +/* + * In 2.4.5, calls to uart_open are serialised by the BKL in + * linux/fs/devices.c:chrdev_open() + * Note that if this fails, then uart_close() _will_ be called. + * + * In time, we want to scrap the "opening nonpresent ports" + * behaviour and implement an alternative way for setserial + * to set base addresses/ports/types. This will allow us to + * get rid of a certain amount of extra tests. + */ +static int uart_open(struct tty_struct *tty, struct file *filp) +{ + struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state; + struct uart_state *state; + int retval, line = tty->index; + + BUG_ON(!kernel_locked()); + DPRINTK("uart_open(%d) called\n", line); + + /* + * tty->driver->num won't change, so we won't fail here with + * tty->driver_data set to something non-NULL (and therefore + * we won't get caught by uart_close()). + */ + retval = -ENODEV; + if (line >= tty->driver->num) + goto fail; + + /* + * We take the semaphore inside uart_get to guarantee that we won't + * be re-entered while allocating the info structure, or while we + * request any IRQs that the driver may need. This also has the nice + * side-effect that it delays the action of uart_hangup, so we can + * guarantee that info->tty will always contain something reasonable. + */ + state = uart_get(drv, line); + if (IS_ERR(state)) { + retval = PTR_ERR(state); + goto fail; + } + + /* + * Once we set tty->driver_data here, we are guaranteed that + * uart_close() will decrement the driver module use count. + * Any failures from here onwards should not touch the count. + */ + tty->driver_data = state; + tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; + tty->alt_speed = 0; + state->info->tty = tty; + + /* + * If the port is in the middle of closing, bail out now. + */ + if (tty_hung_up_p(filp)) { + retval = -EAGAIN; + state->count--; + up(&state->sem); + goto fail; + } + + /* + * Make sure the device is in D0 state. + */ + if (state->count == 1) + uart_change_pm(state, 0); + + /* + * Start up the serial port. + */ + retval = uart_startup(state, 0); + + /* + * If we succeeded, wait until the port is ready. + */ + if (retval == 0) + retval = uart_block_til_ready(filp, state); + up(&state->sem); + + /* + * If this is the first open to succeed, adjust things to suit. + */ + if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) { + state->info->flags |= UIF_NORMAL_ACTIVE; + + uart_update_termios(state); + } + + fail: + return retval; +} + +static const char *uart_type(struct uart_port *port) +{ + const char *str = NULL; + + if (port->ops->type) + str = port->ops->type(port); + + if (!str) + str = "unknown"; + + return str; +} + +#ifdef CONFIG_PROC_FS + +static int uart_line_info(char *buf, struct uart_driver *drv, int i) +{ + struct uart_state *state = drv->state + i; + struct uart_port *port = state->port; + char stat_buf[32]; + unsigned int status; + int ret; + + if (!port) + return 0; + + ret = sprintf(buf, "%d: uart:%s port:%08X irq:%d", + port->line, uart_type(port), + port->iobase, port->irq); + + if (port->type == PORT_UNKNOWN) { + strcat(buf, "\n"); + return ret + 1; + } + + if(capable(CAP_SYS_ADMIN)) + { + status = port->ops->get_mctrl(port); + + ret += sprintf(buf + ret, " tx:%d rx:%d", + port->icount.tx, port->icount.rx); + if (port->icount.frame) + ret += sprintf(buf + ret, " fe:%d", + port->icount.frame); + if (port->icount.parity) + ret += sprintf(buf + ret, " pe:%d", + port->icount.parity); + if (port->icount.brk) + ret += sprintf(buf + ret, " brk:%d", + port->icount.brk); + if (port->icount.overrun) + ret += sprintf(buf + ret, " oe:%d", + port->icount.overrun); + +#define INFOBIT(bit,str) \ + if (port->mctrl & (bit)) \ + strncat(stat_buf, (str), sizeof(stat_buf) - \ + strlen(stat_buf) - 2) +#define STATBIT(bit,str) \ + if (status & (bit)) \ + strncat(stat_buf, (str), sizeof(stat_buf) - \ + strlen(stat_buf) - 2) + + stat_buf[0] = '\0'; + stat_buf[1] = '\0'; + INFOBIT(TIOCM_RTS, "|RTS"); + STATBIT(TIOCM_CTS, "|CTS"); + INFOBIT(TIOCM_DTR, "|DTR"); + STATBIT(TIOCM_DSR, "|DSR"); + STATBIT(TIOCM_CAR, "|CD"); + STATBIT(TIOCM_RNG, "|RI"); + if (stat_buf[0]) + stat_buf[0] = ' '; + strcat(stat_buf, "\n"); + + ret += sprintf(buf + ret, stat_buf); + } +#undef STATBIT +#undef INFOBIT + return ret; +} + +static int uart_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct tty_driver *ttydrv = data; + struct uart_driver *drv = ttydrv->driver_state; + int i, len = 0, l; + off_t begin = 0; + + len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n", + "", "", ""); + for (i = 0; i < drv->nr && len < PAGE_SIZE - 96; i++) { + l = uart_line_info(page + len, drv, i); + len += l; + if (len + begin > off + count) + goto done; + if (len + begin < off) { + begin += len; + len = 0; + } + } + *eof = 1; + done: + if (off >= len + begin) + return 0; + *start = page + (off - begin); + return (count < begin + len - off) ? count : (begin + len - off); +} +#endif + +#ifdef CONFIG_SERIAL_CORE_CONSOLE +/* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ +struct uart_port * __init +uart_get_console(struct uart_port *ports, int nr, struct console *co) +{ + int idx = co->index; + + if (idx < 0 || idx >= nr || (ports[idx].iobase == 0 && + ports[idx].membase == NULL)) + for (idx = 0; idx < nr; idx++) + if (ports[idx].iobase != 0 || + ports[idx].membase != NULL) + break; + + co->index = idx; + + return ports + idx; +} + +/** + * uart_parse_options - Parse serial port baud/parity/bits/flow contro. + * @options: pointer to option string + * @baud: pointer to an 'int' variable for the baud rate. + * @parity: pointer to an 'int' variable for the parity. + * @bits: pointer to an 'int' variable for the number of data bits. + * @flow: pointer to an 'int' variable for the flow control character. + * + * uart_parse_options decodes a string containing the serial console + * options. The format of the string is , + * eg: 115200n8r + */ +void __init +uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow) +{ + char *s = options; + + *baud = simple_strtoul(s, NULL, 10); + while (*s >= '0' && *s <= '9') + s++; + if (*s) + *parity = *s++; + if (*s) + *bits = *s++ - '0'; + if (*s) + *flow = *s; +} + +struct baud_rates { + unsigned int rate; + unsigned int cflag; +}; + +static struct baud_rates baud_rates[] = { + { 921600, B921600 }, + { 460800, B460800 }, + { 230400, B230400 }, + { 115200, B115200 }, + { 57600, B57600 }, + { 38400, B38400 }, + { 19200, B19200 }, + { 9600, B9600 }, + { 4800, B4800 }, + { 2400, B2400 }, + { 1200, B1200 }, + { 0, B38400 } +}; + +/** + * uart_set_options - setup the serial console parameters + * @port: pointer to the serial ports uart_port structure + * @co: console pointer + * @baud: baud rate + * @parity: parity character - 'n' (none), 'o' (odd), 'e' (even) + * @bits: number of data bits + * @flow: flow control character - 'r' (rts) + */ +int __init +uart_set_options(struct uart_port *port, struct console *co, + int baud, int parity, int bits, int flow) +{ + struct termios termios; + int i; + + memset(&termios, 0, sizeof(struct termios)); + + termios.c_cflag = CREAD | HUPCL | CLOCAL; + + /* + * Construct a cflag setting. + */ + for (i = 0; baud_rates[i].rate; i++) + if (baud_rates[i].rate <= baud) + break; + + termios.c_cflag |= baud_rates[i].cflag; + + if (bits == 7) + termios.c_cflag |= CS7; + else + termios.c_cflag |= CS8; + + switch (parity) { + case 'o': case 'O': + termios.c_cflag |= PARODD; + /*fall through*/ + case 'e': case 'E': + termios.c_cflag |= PARENB; + break; + } + + if (flow == 'r') + termios.c_cflag |= CRTSCTS; + + port->ops->set_termios(port, &termios, NULL); + co->cflag = termios.c_cflag; + + return 0; +} +#endif /* CONFIG_SERIAL_CORE_CONSOLE */ + +static void uart_change_pm(struct uart_state *state, int pm_state) +{ + struct uart_port *port = state->port; + if (port->ops->pm) + port->ops->pm(port, pm_state, state->pm_state); + state->pm_state = pm_state; +} + +int uart_suspend_port(struct uart_driver *drv, struct uart_port *port, u32 level) +{ + struct uart_state *state = drv->state + port->line; + + down(&state->sem); + + switch (level) { + case SUSPEND_SAVE_STATE: + if (state->info && state->info->flags & UIF_INITIALIZED) { + struct uart_ops *ops = port->ops; + + spin_lock_irq(&port->lock); + ops->stop_tx(port, 0); + ops->set_mctrl(port, 0); + ops->stop_rx(port); + spin_unlock_irq(&port->lock); + + /* + * Wait for the transmitter to empty. + */ + while (!ops->tx_empty(port)) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(10*HZ/1000); + } + set_current_state(TASK_RUNNING); + + ops->shutdown(port); + } + break; + + case SUSPEND_POWER_DOWN: + /* + * Disable the console device before suspending. + */ + if (uart_console(port)) + port->cons->flags &= ~CON_ENABLED; + + uart_change_pm(state, 3); + break; + } + + up(&state->sem); + + return 0; +} + +int uart_resume_port(struct uart_driver *drv, struct uart_port *port, u32 level) +{ + struct uart_state *state = drv->state + port->line; + + down(&state->sem); + + switch (level) { + case RESUME_POWER_ON: + uart_change_pm(state, 0); + + /* + * Re-enable the console device after suspending. + */ + if (uart_console(port)) { + uart_change_speed(state, NULL); + port->cons->flags |= CON_ENABLED; + } + break; + + case RESUME_RESTORE_STATE: + if (state->info && state->info->flags & UIF_INITIALIZED) { + struct uart_ops *ops = port->ops; + + ops->set_mctrl(port, 0); + ops->startup(port); + uart_change_speed(state, NULL); + spin_lock_irq(&port->lock); + ops->set_mctrl(port, port->mctrl); + ops->start_tx(port, 0); + spin_unlock_irq(&port->lock); + } + break; + } + + up(&state->sem); + + return 0; +} + +static inline void +uart_report_port(struct uart_driver *drv, struct uart_port *port) +{ + printk("%s%d", drv->dev_name, port->line); + printk(" at "); + switch (port->iotype) { + case UPIO_PORT: + printk("I/O 0x%x", port->iobase); + break; + case UPIO_HUB6: + printk("I/O 0x%x offset 0x%x", port->iobase, port->hub6); + break; + case UPIO_MEM: + printk("MMIO 0x%lx", port->mapbase); + break; + } + printk(" (irq = %d) is a %s\n", port->irq, uart_type(port)); +} + +static void +uart_configure_port(struct uart_driver *drv, struct uart_state *state, + struct uart_port *port) +{ + unsigned int flags; + + /* + * If there isn't a port here, don't do anything further. + */ + if (!port->iobase && !port->mapbase && !port->membase) + return; + + /* + * Now do the auto configuration stuff. Note that config_port + * is expected to claim the resources and map the port for us. + */ + flags = UART_CONFIG_TYPE; + if (port->flags & UPF_AUTO_IRQ) + flags |= UART_CONFIG_IRQ; + if (port->flags & UPF_BOOT_AUTOCONF) { + port->type = PORT_UNKNOWN; + port->ops->config_port(port, flags); + } + + if (port->type != PORT_UNKNOWN) { + unsigned long flags; + + uart_report_port(drv, port); + + /* + * Ensure that the modem control lines are de-activated. + * We probably don't need a spinlock around this, but + */ + spin_lock_irqsave(&port->lock, flags); + port->ops->set_mctrl(port, 0); + spin_unlock_irqrestore(&port->lock, flags); + + /* + * Power down all ports by default, except the + * console if we have one. + */ + if (!uart_console(port)) + uart_change_pm(state, 3); + } +} + +/* + * This reverses the effects of uart_configure_port, hanging up the + * port before removal. + */ +static void +uart_unconfigure_port(struct uart_driver *drv, struct uart_state *state) +{ + struct uart_port *port = state->port; + struct uart_info *info = state->info; + + if (info && info->tty) + tty_vhangup(info->tty); + + down(&state->sem); + + state->info = NULL; + + /* + * Free the port IO and memory resources, if any. + */ + if (port->type != PORT_UNKNOWN) + port->ops->release_port(port); + + /* + * Indicate that there isn't a port here anymore. + */ + port->type = PORT_UNKNOWN; + + /* + * Kill the tasklet, and free resources. + */ + if (info) { + tasklet_kill(&info->tlet); + kfree(info); + } + + up(&state->sem); +} + +static struct tty_operations uart_ops = { + .open = uart_open, + .close = uart_close, + .write = uart_write, + .put_char = uart_put_char, + .flush_chars = uart_flush_chars, + .write_room = uart_write_room, + .chars_in_buffer= uart_chars_in_buffer, + .flush_buffer = uart_flush_buffer, + .ioctl = uart_ioctl, + .throttle = uart_throttle, + .unthrottle = uart_unthrottle, + .send_xchar = uart_send_xchar, + .set_termios = uart_set_termios, + .stop = uart_stop, + .start = uart_start, + .hangup = uart_hangup, + .break_ctl = uart_break_ctl, + .wait_until_sent= uart_wait_until_sent, +#ifdef CONFIG_PROC_FS + .read_proc = uart_read_proc, +#endif + .tiocmget = uart_tiocmget, + .tiocmset = uart_tiocmset, +}; + +/** + * uart_register_driver - register a driver with the uart core layer + * @drv: low level driver structure + * + * Register a uart driver with the core driver. We in turn register + * with the tty layer, and initialise the core driver per-port state. + * + * We have a proc file in /proc/tty/driver which is named after the + * normal driver. + * + * drv->port should be NULL, and the per-port structures should be + * registered using uart_add_one_port after this call has succeeded. + */ +int uart_register_driver(struct uart_driver *drv) +{ + struct tty_driver *normal = NULL; + int i, retval; + + BUG_ON(drv->state); + + /* + * Maybe we should be using a slab cache for this, especially if + * we have a large number of ports to handle. + */ + drv->state = kmalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL); + retval = -ENOMEM; + if (!drv->state) + goto out; + + memset(drv->state, 0, sizeof(struct uart_state) * drv->nr); + + normal = alloc_tty_driver(drv->nr); + if (!normal) + goto out; + + drv->tty_driver = normal; + + normal->owner = drv->owner; + normal->driver_name = drv->driver_name; + normal->devfs_name = drv->devfs_name; + normal->name = drv->dev_name; + normal->major = drv->major; + normal->minor_start = drv->minor; + normal->type = TTY_DRIVER_TYPE_SERIAL; + normal->subtype = SERIAL_TYPE_NORMAL; + normal->init_termios = tty_std_termios; + normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + normal->driver_state = drv; + tty_set_operations(normal, &uart_ops); + + /* + * Initialise the UART state(s). + */ + for (i = 0; i < drv->nr; i++) { + struct uart_state *state = drv->state + i; + + state->close_delay = 5 * HZ / 10; + state->closing_wait = 30 * HZ; + + init_MUTEX(&state->sem); + } + + retval = tty_register_driver(normal); + out: + if (retval < 0) { + put_tty_driver(normal); + kfree(drv->state); + } + return retval; +} + +/** + * uart_unregister_driver - remove a driver from the uart core layer + * @drv: low level driver structure + * + * Remove all references to a driver from the core driver. The low + * level driver must have removed all its ports via the + * uart_remove_one_port() if it registered them with uart_add_one_port(). + * (ie, drv->port == NULL) + */ +void uart_unregister_driver(struct uart_driver *drv) +{ + struct tty_driver *p = drv->tty_driver; + tty_unregister_driver(p); + put_tty_driver(p); + kfree(drv->state); + drv->tty_driver = NULL; +} + +struct tty_driver *uart_console_device(struct console *co, int *index) +{ + struct uart_driver *p = co->data; + *index = co->index; + return p->tty_driver; +} + +/** + * uart_add_one_port - attach a driver-defined port structure + * @drv: pointer to the uart low level driver structure for this port + * @port: uart port structure to use for this port. + * + * This allows the driver to register its own uart_port structure + * with the core driver. The main purpose is to allow the low + * level uart drivers to expand uart_port, rather than having yet + * more levels of structures. + */ +int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) +{ + struct uart_state *state; + int ret = 0; + + BUG_ON(in_interrupt()); + + if (port->line >= drv->nr) + return -EINVAL; + + state = drv->state + port->line; + + down(&port_sem); + if (state->port) { + ret = -EINVAL; + goto out; + } + + state->port = port; + + spin_lock_init(&port->lock); + port->cons = drv->cons; + port->info = state->info; + + uart_configure_port(drv, state, port); + + /* + * Register the port whether it's detected or not. This allows + * setserial to be used to alter this ports parameters. + */ + tty_register_device(drv->tty_driver, port->line, NULL); + + out: + up(&port_sem); + + return ret; +} + +/** + * uart_remove_one_port - detach a driver defined port structure + * @drv: pointer to the uart low level driver structure for this port + * @port: uart port structure for this port + * + * This unhooks (and hangs up) the specified port structure from the + * core driver. No further calls will be made to the low-level code + * for this port. + */ +int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) +{ + struct uart_state *state = drv->state + port->line; + + BUG_ON(in_interrupt()); + + if (state->port != port) + printk(KERN_ALERT "Removing wrong port: %p != %p\n", + state->port, port); + + down(&port_sem); + + /* + * Remove the devices from devfs + */ + tty_unregister_device(drv->tty_driver, port->line); + + uart_unconfigure_port(drv, state); + state->port = NULL; + up(&port_sem); + + return 0; +} + +/* + * Are the two ports equivalent? + */ +static int uart_match_port(struct uart_port *port1, struct uart_port *port2) +{ + if (port1->iotype != port2->iotype) + return 0; + + switch (port1->iotype) { + case UPIO_PORT: + return (port1->iobase == port2->iobase); + case UPIO_HUB6: + return (port1->iobase == port2->iobase) && + (port1->hub6 == port2->hub6); + case UPIO_MEM: + return (port1->membase == port2->membase); + } + return 0; +} + +/* + * Try to find an unused uart_state slot for a port. + */ +static struct uart_state * +uart_find_match_or_unused(struct uart_driver *drv, struct uart_port *port) +{ + int i; + + /* + * First, find a port entry which matches. Note: if we do + * find a matching entry, and it has a non-zero use count, + * then we can't register the port. + */ + for (i = 0; i < drv->nr; i++) + if (uart_match_port(drv->state[i].port, port)) + return &drv->state[i]; + + /* + * We didn't find a matching entry, so look for the first + * free entry. We look for one which hasn't been previously + * used (indicated by zero iobase). + */ + for (i = 0; i < drv->nr; i++) + if (drv->state[i].port->type == PORT_UNKNOWN && + drv->state[i].port->iobase == 0 && + drv->state[i].count == 0) + return &drv->state[i]; + + /* + * That also failed. Last resort is to find any currently + * entry which doesn't have a real port associated with it. + */ + for (i = 0; i < drv->nr; i++) + if (drv->state[i].port->type == PORT_UNKNOWN && + drv->state[i].count == 0) + return &drv->state[i]; + + return NULL; +} + +/** + * uart_register_port: register uart settings with a port + * @drv: pointer to the uart low level driver structure for this port + * @port: uart port structure describing the port + * + * Register UART settings with the specified low level driver. Detect + * the type of the port if UPF_BOOT_AUTOCONF is set, and detect the + * IRQ if UPF_AUTO_IRQ is set. + * + * We try to pick the same port for the same IO base address, so that + * when a modem is plugged in, unplugged and plugged back in, it gets + * allocated the same port. + * + * Returns negative error, or positive line number. + */ +int uart_register_port(struct uart_driver *drv, struct uart_port *port) +{ + struct uart_state *state; + int ret; + + down(&port_sem); + + state = uart_find_match_or_unused(drv, port); + + if (state) { + /* + * Ok, we've found a line that we can use. + * + * If we find a port that matches this one, and it appears + * to be in-use (even if it doesn't have a type) we shouldn't + * alter it underneath itself - the port may be open and + * trying to do useful work. + */ + if (uart_users(state) != 0) { + ret = -EBUSY; + goto out; + } + + /* + * If the port is already initialised, don't touch it. + */ + if (state->port->type == PORT_UNKNOWN) { + state->port->iobase = port->iobase; + state->port->membase = port->membase; + state->port->irq = port->irq; + state->port->uartclk = port->uartclk; + state->port->fifosize = port->fifosize; + state->port->regshift = port->regshift; + state->port->iotype = port->iotype; + state->port->flags = port->flags; + state->port->line = state - drv->state; + state->port->mapbase = port->mapbase; + + uart_configure_port(drv, state, state->port); + } + + ret = state->port->line; + } else + ret = -ENOSPC; + out: + up(&port_sem); + return ret; +} + +/** + * uart_unregister_port - de-allocate a port + * @drv: pointer to the uart low level driver structure for this port + * @line: line index previously returned from uart_register_port() + * + * Hang up the specified line associated with the low level driver, + * and mark the port as unused. + */ +void uart_unregister_port(struct uart_driver *drv, int line) +{ + struct uart_state *state; + + if (line < 0 || line >= drv->nr) { + printk(KERN_ERR "Attempt to unregister "); + printk("%s%d", drv->dev_name, line); + printk("\n"); + return; + } + + state = drv->state + line; + + down(&port_sem); + uart_unconfigure_port(drv, state); + up(&port_sem); +} + +EXPORT_SYMBOL(uart_write_wakeup); +EXPORT_SYMBOL(uart_register_driver); +EXPORT_SYMBOL(uart_unregister_driver); +EXPORT_SYMBOL(uart_suspend_port); +EXPORT_SYMBOL(uart_resume_port); +EXPORT_SYMBOL(uart_register_port); +EXPORT_SYMBOL(uart_unregister_port); +EXPORT_SYMBOL(uart_add_one_port); +EXPORT_SYMBOL(uart_remove_one_port); + +MODULE_DESCRIPTION("Serial driver core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c new file mode 100644 index 000000000000..a70c3ddfadfa --- /dev/null +++ b/drivers/serial/serial_cs.c @@ -0,0 +1,712 @@ +/*====================================================================== + + A driver for PCMCIA serial devices + + serial_cs.c 1.134 2002/05/04 05:48:53 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is David A. Hinds + . Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU General Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = "serial_cs.c 1.134 2002/05/04 05:48:53 (David Hinds)"; +#else +#define DEBUG(n, args...) +#endif + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* Bit map of interrupts to choose from */ +static u_int irq_mask = 0xdeb8; +static int irq_list[4] = { -1 }; + +/* Enable the speaker? */ +static int do_sound = 1; +/* Skip strict UART tests? */ +static int buggy_uart; + +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); +MODULE_PARM(do_sound, "i"); +MODULE_PARM(buggy_uart, "i"); + +/*====================================================================*/ + +/* Table of multi-port card ID's */ + +struct multi_id { + u_short manfid; + u_short prodid; + int multi; /* 1 = multifunction, > 1 = # ports */ +}; + +static struct multi_id multi_id[] = { + { MANFID_OMEGA, PRODID_OMEGA_QSP_100, 4 }, + { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232, 2 }, + { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D1, 2 }, + { MANFID_QUATECH, PRODID_QUATECH_QUAD_RS232, 4 }, + { MANFID_SOCKET, PRODID_SOCKET_DUAL_RS232, 2 }, + { MANFID_INTEL, PRODID_INTEL_DUAL_RS232, 2 }, + { MANFID_NATINST, PRODID_NATINST_QUAD_RS232, 4 } +}; +#define MULTI_COUNT (sizeof(multi_id)/sizeof(struct multi_id)) + +struct serial_info { + dev_link_t link; + int ndev; + int multi; + int slave; + int manfid; + dev_node_t node[4]; + int line[4]; +}; + +static void serial_config(dev_link_t * link); +static int serial_event(event_t event, int priority, + event_callback_args_t * args); + +static dev_info_t dev_info = "serial_cs"; + +static dev_link_t *serial_attach(void); +static void serial_detach(dev_link_t *); + +static dev_link_t *dev_list = NULL; + +/*====================================================================== + + After a card is removed, serial_remove() will unregister + the serial device(s), and release the PCMCIA configuration. + +======================================================================*/ + +static void serial_remove(dev_link_t *link) +{ + struct serial_info *info = link->priv; + int i; + + link->state &= ~DEV_PRESENT; + + DEBUG(0, "serial_release(0x%p)\n", link); + + /* + * Recheck to see if the device is still configured. + */ + if (info->link.state & DEV_CONFIG) { + for (i = 0; i < info->ndev; i++) + unregister_serial(info->line[i]); + + info->link.dev = NULL; + + if (!info->slave) { + CardServices(ReleaseConfiguration, info->link.handle); + CardServices(ReleaseIO, info->link.handle, &info->link.io); + CardServices(ReleaseIRQ, info->link.handle, &info->link.irq); + } + + info->link.state &= ~DEV_CONFIG; + } +} + +/*====================================================================== + + serial_attach() creates an "instance" of the driver, allocating + local data structures for one device. The device is registered + with Card Services. + +======================================================================*/ + +static dev_link_t *serial_attach(void) +{ + struct serial_info *info; + client_reg_t client_reg; + dev_link_t *link; + int i, ret; + + DEBUG(0, "serial_attach()\n"); + + /* Create new serial device */ + info = kmalloc(sizeof (*info), GFP_KERNEL); + if (!info) + return NULL; + memset(info, 0, sizeof (*info)); + link = &info->link; + link->priv = info; + + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.NumPorts1 = 8; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->conf.Attributes = CONF_ENABLE_IRQ; + if (do_sound) { + link->conf.Attributes |= CONF_ENABLE_SPKR; + link->conf.Status = CCSR_AUDIO_ENA; + } + link->conf.IntType = INT_MEMORY_AND_IO; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &serial_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != CS_SUCCESS) { + cs_error(link->handle, RegisterClient, ret); + serial_detach(link); + return NULL; + } + + return link; +} + +/*====================================================================== + + This deletes a driver "instance". The device is de-registered + with Card Services. If it has been released, all local data + structures are freed. Otherwise, the structures will be freed + when the device is released. + +======================================================================*/ + +static void serial_detach(dev_link_t * link) +{ + struct serial_info *info = link->priv; + dev_link_t **linkp; + int ret; + + DEBUG(0, "serial_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) + break; + if (*linkp == NULL) + return; + + /* + * Ensure any outstanding scheduled tasks are completed. + */ + flush_scheduled_work(); + + /* + * Ensure that the ports have been released. + */ + serial_remove(link); + + if (link->handle) { + ret = CardServices(DeregisterClient, link->handle); + if (ret != CS_SUCCESS) + cs_error(link->handle, DeregisterClient, ret); + } + + /* Unlink device structure, free bits */ + *linkp = link->next; + kfree(info); +} + +/*====================================================================*/ + +static int setup_serial(struct serial_info * info, ioaddr_t port, int irq) +{ + struct serial_struct serial; + int line; + + memset(&serial, 0, sizeof (serial)); + serial.port = port; + serial.irq = irq; + serial.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ; + if (buggy_uart) + serial.flags |= UPF_BUGGY_UART; + line = register_serial(&serial); + if (line < 0) { + printk(KERN_NOTICE "serial_cs: register_serial() at 0x%04lx," + " irq %d failed\n", (u_long) serial.port, serial.irq); + return -EINVAL; + } + + info->line[info->ndev] = line; + sprintf(info->node[info->ndev].dev_name, "ttyS%d", line); + info->node[info->ndev].major = TTY_MAJOR; + info->node[info->ndev].minor = 0x40 + line; + if (info->ndev > 0) + info->node[info->ndev - 1].next = &info->node[info->ndev]; + info->ndev++; + + return 0; +} + +/*====================================================================*/ + +static int +get_tuple(int fn, client_handle_t handle, tuple_t * tuple, cisparse_t * parse) +{ + int i; + i = CardServices(fn, handle, tuple); + if (i != CS_SUCCESS) + return CS_NO_MORE_ITEMS; + i = CardServices(GetTupleData, handle, tuple); + if (i != CS_SUCCESS) + return i; + return CardServices(ParseTuple, handle, tuple, parse); +} + +#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c) +#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c) + +/*====================================================================*/ + +static int simple_config(dev_link_t * link) +{ + static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; + client_handle_t handle = link->handle; + struct serial_info *info = link->priv; + tuple_t tuple; + u_char buf[256]; + cisparse_t parse; + cistpl_cftable_entry_t *cf = &parse.cftable_entry; + config_info_t config; + int i, j, try; + + /* If the card is already configured, look up the port and irq */ + i = CardServices(GetConfigurationInfo, handle, &config); + if ((i == CS_SUCCESS) && (config.Attributes & CONF_VALID_CLIENT)) { + ioaddr_t port = 0; + if ((config.BasePort2 != 0) && (config.NumPorts2 == 8)) { + port = config.BasePort2; + info->slave = 1; + } else if ((info->manfid == MANFID_OSITECH) && + (config.NumPorts1 == 0x40)) { + port = config.BasePort1 + 0x28; + info->slave = 1; + } + if (info->slave) + return setup_serial(info, port, config.AssignedIRQ); + } + link->conf.Vcc = config.Vcc; + + /* First pass: look for a config entry that looks normal. */ + tuple.TupleData = (cisdata_t *) buf; + tuple.TupleOffset = 0; + tuple.TupleDataMax = 255; + tuple.Attributes = 0; + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + /* Two tries: without IO aliases, then with aliases */ + for (try = 0; try < 2; try++) { + i = first_tuple(handle, &tuple, &parse); + while (i != CS_NO_MORE_ITEMS) { + if (i != CS_SUCCESS) + goto next_entry; + if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) + link->conf.Vpp1 = link->conf.Vpp2 = + cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; + if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && + (cf->io.win[0].base != 0)) { + link->conf.ConfigIndex = cf->index; + link->io.BasePort1 = cf->io.win[0].base; + link->io.IOAddrLines = (try == 0) ? + 16 : cf->io.flags & CISTPL_IO_LINES_MASK; + i = + CardServices(RequestIO, link->handle, + &link->io); + if (i == CS_SUCCESS) + goto found_port; + } + next_entry: + i = next_tuple(handle, &tuple, &parse); + } + } + + /* Second pass: try to find an entry that isn't picky about + its base address, then try to grab any standard serial port + address, and finally try to get any free port. */ + i = first_tuple(handle, &tuple, &parse); + while (i != CS_NO_MORE_ITEMS) { + if ((i == CS_SUCCESS) && (cf->io.nwin > 0) && + ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { + link->conf.ConfigIndex = cf->index; + for (j = 0; j < 5; j++) { + link->io.BasePort1 = base[j]; + link->io.IOAddrLines = base[j] ? 16 : 3; + i = CardServices(RequestIO, link->handle, + &link->io); + if (i == CS_SUCCESS) + goto found_port; + } + } + i = next_tuple(handle, &tuple, &parse); + } + + found_port: + if (i != CS_SUCCESS) { + printk(KERN_NOTICE + "serial_cs: no usable port range found, giving up\n"); + cs_error(link->handle, RequestIO, i); + return -1; + } + + i = CardServices(RequestIRQ, link->handle, &link->irq); + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestIRQ, i); + link->irq.AssignedIRQ = 0; + } + if (info->multi && (info->manfid == MANFID_3COM)) + link->conf.ConfigIndex &= ~(0x08); + i = CardServices(RequestConfiguration, link->handle, &link->conf); + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestConfiguration, i); + return -1; + } + + return setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ); +} + +static int multi_config(dev_link_t * link) +{ + client_handle_t handle = link->handle; + struct serial_info *info = link->priv; + tuple_t tuple; + u_char buf[256]; + cisparse_t parse; + cistpl_cftable_entry_t *cf = &parse.cftable_entry; + config_info_t config; + int i, base2 = 0; + + i = CardServices(GetConfigurationInfo, handle, &config); + if (i != CS_SUCCESS) { + cs_error(handle, GetConfigurationInfo, i); + return -1; + } + link->conf.Vcc = config.Vcc; + + tuple.TupleData = (cisdata_t *) buf; + tuple.TupleOffset = 0; + tuple.TupleDataMax = 255; + tuple.Attributes = 0; + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + + /* First, look for a generic full-sized window */ + link->io.NumPorts1 = info->multi * 8; + i = first_tuple(handle, &tuple, &parse); + while (i != CS_NO_MORE_ITEMS) { + /* The quad port cards have bad CIS's, so just look for a + window larger than 8 ports and assume it will be right */ + if ((i == CS_SUCCESS) && (cf->io.nwin == 1) && + (cf->io.win[0].len > 8)) { + link->conf.ConfigIndex = cf->index; + link->io.BasePort1 = cf->io.win[0].base; + link->io.IOAddrLines = + cf->io.flags & CISTPL_IO_LINES_MASK; + i = CardServices(RequestIO, link->handle, &link->io); + base2 = link->io.BasePort1 + 8; + if (i == CS_SUCCESS) + break; + } + i = next_tuple(handle, &tuple, &parse); + } + + /* If that didn't work, look for two windows */ + if (i != CS_SUCCESS) { + link->io.NumPorts1 = link->io.NumPorts2 = 8; + info->multi = 2; + i = first_tuple(handle, &tuple, &parse); + while (i != CS_NO_MORE_ITEMS) { + if ((i == CS_SUCCESS) && (cf->io.nwin == 2)) { + link->conf.ConfigIndex = cf->index; + link->io.BasePort1 = cf->io.win[0].base; + link->io.BasePort2 = cf->io.win[1].base; + link->io.IOAddrLines = + cf->io.flags & CISTPL_IO_LINES_MASK; + i = + CardServices(RequestIO, link->handle, + &link->io); + base2 = link->io.BasePort2; + if (i == CS_SUCCESS) + break; + } + i = next_tuple(handle, &tuple, &parse); + } + } + + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestIO, i); + return -1; + } + + i = CardServices(RequestIRQ, link->handle, &link->irq); + if (i != CS_SUCCESS) { + printk(KERN_NOTICE + "serial_cs: no usable port range found, giving up\n"); + cs_error(link->handle, RequestIRQ, i); + link->irq.AssignedIRQ = 0; + } + /* Socket Dual IO: this enables irq's for second port */ + if (info->multi && (info->manfid == MANFID_SOCKET)) { + link->conf.Present |= PRESENT_EXT_STATUS; + link->conf.ExtStatus = ESR_REQ_ATTN_ENA; + } + i = CardServices(RequestConfiguration, link->handle, &link->conf); + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestConfiguration, i); + return -1; + } + + /* The Oxford Semiconductor OXCF950 cards are in fact single-port: + 8 registers are for the UART, the others are extra registers */ + if (info->manfid == MANFID_OXSEMI) { + if (cf->index == 1 || cf->index == 3) { + setup_serial(info, base2, link->irq.AssignedIRQ); + outb(12, link->io.BasePort1 + 1); + } else { + setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ); + outb(12, base2 + 1); + } + return 0; + } + + setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ); + /* The Nokia cards are not really multiport cards */ + if (info->manfid == MANFID_NOKIA) + return 0; + for (i = 0; i < info->multi - 1; i++) + setup_serial(info, base2 + (8 * i), link->irq.AssignedIRQ); + + return 0; +} + +/*====================================================================== + + serial_config() is scheduled to run after a CARD_INSERTION event + is received, to configure the PCMCIA socket, and to make the + serial device available to the system. + +======================================================================*/ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed + +void serial_config(dev_link_t * link) +{ + client_handle_t handle = link->handle; + struct serial_info *info = link->priv; + tuple_t tuple; + u_short buf[128]; + cisparse_t parse; + cistpl_cftable_entry_t *cf = &parse.cftable_entry; + int i, last_ret, last_fn; + + DEBUG(0, "serial_config(0x%p)\n", link); + + tuple.TupleData = (cisdata_t *) buf; + tuple.TupleOffset = 0; + tuple.TupleDataMax = 255; + tuple.Attributes = 0; + /* Get configuration register information */ + tuple.DesiredTuple = CISTPL_CONFIG; + last_ret = first_tuple(handle, &tuple, &parse); + if (last_ret != CS_SUCCESS) { + last_fn = ParseTuple; + goto cs_failed; + } + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + /* Configure card */ + link->state |= DEV_CONFIG; + + /* Is this a compliant multifunction card? */ + tuple.DesiredTuple = CISTPL_LONGLINK_MFC; + tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK; + info->multi = (first_tuple(handle, &tuple, &parse) == CS_SUCCESS); + + /* Is this a multiport card? */ + tuple.DesiredTuple = CISTPL_MANFID; + if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) { + info->manfid = le16_to_cpu(buf[0]); + for (i = 0; i < MULTI_COUNT; i++) + if ((info->manfid == multi_id[i].manfid) && + (le16_to_cpu(buf[1]) == multi_id[i].prodid)) + break; + if (i < MULTI_COUNT) + info->multi = multi_id[i].multi; + } + + /* Another check for dual-serial cards: look for either serial or + multifunction cards that ask for appropriate IO port ranges */ + tuple.DesiredTuple = CISTPL_FUNCID; + if ((info->multi == 0) && + ((first_tuple(handle, &tuple, &parse) != CS_SUCCESS) || + (parse.funcid.func == CISTPL_FUNCID_MULTI) || + (parse.funcid.func == CISTPL_FUNCID_SERIAL))) { + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) { + if ((cf->io.nwin == 1) && (cf->io.win[0].len % 8 == 0)) + info->multi = cf->io.win[0].len >> 3; + if ((cf->io.nwin == 2) && (cf->io.win[0].len == 8) && + (cf->io.win[1].len == 8)) + info->multi = 2; + } + } + + if (info->multi > 1) + multi_config(link); + else + simple_config(link); + + if (info->ndev == 0) + goto failed; + + if (info->manfid == MANFID_IBM) { + conf_reg_t reg = { 0, CS_READ, 0x800, 0 }; + CS_CHECK(AccessConfigurationRegister, link->handle, ®); + reg.Action = CS_WRITE; + reg.Value = reg.Value | 1; + CS_CHECK(AccessConfigurationRegister, link->handle, ®); + } + + link->dev = &info->node[0]; + link->state &= ~DEV_CONFIG_PENDING; + return; + + cs_failed: + cs_error(link->handle, last_fn, last_ret); + failed: + serial_remove(link); + link->state &= ~DEV_CONFIG_PENDING; +} + +/*====================================================================== + + The card status event handler. Mostly, this schedules other + stuff to run after an event is received. A CARD_REMOVAL event + also sets some flags to discourage the serial drivers from + talking to the ports. + +======================================================================*/ + +static int +serial_event(event_t event, int priority, event_callback_args_t * args) +{ + dev_link_t *link = args->client_data; + struct serial_info *info = link->priv; + + DEBUG(1, "serial_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + serial_remove(link); + break; + + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + serial_config(link); + break; + + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if ((link->state & DEV_CONFIG) && !info->slave) + CardServices(ReleaseConfiguration, link->handle); + break; + + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (DEV_OK(link) && !info->slave) + CardServices(RequestConfiguration, link->handle, + &link->conf); + break; + } + return 0; +} + +static struct pcmcia_driver serial_cs_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "serial_cs", + }, + .attach = serial_attach, + .detach = serial_detach, +}; + +static int __init init_serial_cs(void) +{ + return pcmcia_register_driver(&serial_cs_driver); +} + +static void __exit exit_serial_cs(void) +{ + pcmcia_unregister_driver(&serial_cs_driver); + + /* XXX: this really needs to move into generic code.. */ + while (dev_list != NULL) + serial_detach(dev_list); +} + +module_init(init_serial_cs); +module_exit(exit_serial_cs); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From d5a45662a10e72d0efdfc6014b9a06de5e146345 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 9 Sep 2003 15:15:57 +0100 Subject: [SERIAL] Drop "level" argument from serial PM calls. Since the driver model has transitioned away from using multi-level device suspend/resume, we also drop the multi-level support from the serial layer. Update the 8250 and sa1100 drivers for this change. --- drivers/serial/8250.c | 8 ++-- drivers/serial/8250.h | 4 +- drivers/serial/8250_pci.c | 22 ++--------- drivers/serial/sa1100.c | 8 ++-- drivers/serial/serial_core.c | 94 +++++++++++++++++++------------------------- include/linux/serial_core.h | 4 +- 6 files changed, 56 insertions(+), 84 deletions(-) diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index ba16a21a7a05..5a722a5bd8fc 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -2101,9 +2101,9 @@ void serial8250_get_irq_map(unsigned int *map) * * Suspend one serial port. */ -void serial8250_suspend_port(int line, u32 level) +void serial8250_suspend_port(int line) { - uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port, level); + uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port); } /** @@ -2112,9 +2112,9 @@ void serial8250_suspend_port(int line, u32 level) * * Resume one serial port. */ -void serial8250_resume_port(int line, u32 level) +void serial8250_resume_port(int line) { - uart_resume_port(&serial8250_reg, &serial8250_ports[line].port, level); + uart_resume_port(&serial8250_reg, &serial8250_ports[line].port); } static int __init serial8250_init(void) diff --git a/drivers/serial/8250.h b/drivers/serial/8250.h index cc206f9476ac..91ced9bdb3be 100644 --- a/drivers/serial/8250.h +++ b/drivers/serial/8250.h @@ -27,8 +27,8 @@ struct serial8250_probe { int serial8250_register_probe(struct serial8250_probe *probe); void serial8250_unregister_probe(struct serial8250_probe *probe); void serial8250_get_irq_map(unsigned int *map); -void serial8250_suspend_port(int line, u32 level); -void serial8250_resume_port(int line, u32 level); +void serial8250_suspend_port(int line); +void serial8250_resume_port(int line); struct old_serial_port { unsigned int uart; diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index dc90cc477844..778cd1eea5ba 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -1600,19 +1600,6 @@ static void __devexit pciserial_remove_one(struct pci_dev *dev) } } -static int pciserial_save_state_one(struct pci_dev *dev, u32 state) -{ - struct serial_private *priv = pci_get_drvdata(dev); - - if (priv) { - int i; - - for (i = 0; i < priv->nr; i++) - serial8250_suspend_port(priv->line[i], SUSPEND_SAVE_STATE); - } - return 0; -} - static int pciserial_suspend_one(struct pci_dev *dev, u32 state) { struct serial_private *priv = pci_get_drvdata(dev); @@ -1621,7 +1608,7 @@ static int pciserial_suspend_one(struct pci_dev *dev, u32 state) int i; for (i = 0; i < priv->nr; i++) - serial8250_suspend_port(priv->line[i], SUSPEND_POWER_DOWN); + serial8250_suspend_port(priv->line[i]); } return 0; } @@ -1639,10 +1626,8 @@ static int pciserial_resume_one(struct pci_dev *dev) if (priv->quirk->init) priv->quirk->init(dev); - for (i = 0; i < priv->nr; i++) { - serial8250_resume_port(priv->line[i], RESUME_POWER_ON); - serial8250_resume_port(priv->line[i], RESUME_RESTORE_STATE); - } + for (i = 0; i < priv->nr; i++) + serial8250_resume_port(priv->line[i]); } return 0; } @@ -2040,7 +2025,6 @@ static struct pci_driver serial_pci_driver = { .name = "serial", .probe = pciserial_init_one, .remove = __devexit_p(pciserial_remove_one), - .save_state = pciserial_save_state_one, .suspend = pciserial_suspend_one, .resume = pciserial_resume_one, .id_table = serial_pci_tbl, diff --git a/drivers/serial/sa1100.c b/drivers/serial/sa1100.c index 8ded3cf74f28..51b191307bad 100644 --- a/drivers/serial/sa1100.c +++ b/drivers/serial/sa1100.c @@ -862,8 +862,8 @@ static int sa1100_serial_suspend(struct device *_dev, u32 state, u32 level) { struct sa1100_port *sport = dev_get_drvdata(_dev); - if (sport) - uart_suspend_port(&sa1100_reg, &sport->port, level); + if (sport && level == SUSPEND_DISABLE) + uart_suspend_port(&sa1100_reg, &sport->port); return 0; } @@ -872,8 +872,8 @@ static int sa1100_serial_resume(struct device *_dev, u32 level) { struct sa1100_port *sport = dev_get_drvdata(_dev); - if (sport) - uart_resume_port(&sa1100_reg, &sport->port, level); + if (sport && level == RESUME_ENABLE) + uart_resume_port(&sa1100_reg, &sport->port); return 0; } diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index a264d72ca695..064b9631201f 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -1875,84 +1875,72 @@ static void uart_change_pm(struct uart_state *state, int pm_state) state->pm_state = pm_state; } -int uart_suspend_port(struct uart_driver *drv, struct uart_port *port, u32 level) +int uart_suspend_port(struct uart_driver *drv, struct uart_port *port) { struct uart_state *state = drv->state + port->line; down(&state->sem); - switch (level) { - case SUSPEND_SAVE_STATE: - if (state->info && state->info->flags & UIF_INITIALIZED) { - struct uart_ops *ops = port->ops; + if (state->info && state->info->flags & UIF_INITIALIZED) { + struct uart_ops *ops = port->ops; - spin_lock_irq(&port->lock); - ops->stop_tx(port, 0); - ops->set_mctrl(port, 0); - ops->stop_rx(port); - spin_unlock_irq(&port->lock); - - /* - * Wait for the transmitter to empty. - */ - while (!ops->tx_empty(port)) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(10*HZ/1000); - } - set_current_state(TASK_RUNNING); - - ops->shutdown(port); - } - break; + spin_lock_irq(&port->lock); + ops->stop_tx(port, 0); + ops->set_mctrl(port, 0); + ops->stop_rx(port); + spin_unlock_irq(&port->lock); - case SUSPEND_POWER_DOWN: /* - * Disable the console device before suspending. + * Wait for the transmitter to empty. */ - if (uart_console(port)) - port->cons->flags &= ~CON_ENABLED; + while (!ops->tx_empty(port)) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(10*HZ/1000); + } + set_current_state(TASK_RUNNING); - uart_change_pm(state, 3); - break; + ops->shutdown(port); } + /* + * Disable the console device before suspending. + */ + if (uart_console(port)) + port->cons->flags &= ~CON_ENABLED; + + uart_change_pm(state, 3); + up(&state->sem); return 0; } -int uart_resume_port(struct uart_driver *drv, struct uart_port *port, u32 level) +int uart_resume_port(struct uart_driver *drv, struct uart_port *port) { struct uart_state *state = drv->state + port->line; down(&state->sem); - switch (level) { - case RESUME_POWER_ON: - uart_change_pm(state, 0); + uart_change_pm(state, 0); - /* - * Re-enable the console device after suspending. - */ - if (uart_console(port)) { - uart_change_speed(state, NULL); - port->cons->flags |= CON_ENABLED; - } - break; + /* + * Re-enable the console device after suspending. + */ + if (uart_console(port)) { + uart_change_speed(state, NULL); + port->cons->flags |= CON_ENABLED; + } - case RESUME_RESTORE_STATE: - if (state->info && state->info->flags & UIF_INITIALIZED) { - struct uart_ops *ops = port->ops; + if (state->info && state->info->flags & UIF_INITIALIZED) { + struct uart_ops *ops = port->ops; - ops->set_mctrl(port, 0); - ops->startup(port); - uart_change_speed(state, NULL); - spin_lock_irq(&port->lock); - ops->set_mctrl(port, port->mctrl); - ops->start_tx(port, 0); - spin_unlock_irq(&port->lock); - } - break; + ops->set_mctrl(port, 0); + ops->startup(port); + uart_change_speed(state, NULL); + spin_lock_irq(&port->lock); + ops->set_mctrl(port, port->mctrl); + ops->start_tx(port, 0); + spin_unlock_irq(&port->lock); } up(&state->sem); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 0bc15aeb153e..8e09c20be413 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -325,8 +325,8 @@ int uart_remove_one_port(struct uart_driver *reg, struct uart_port *port); /* * Power Management */ -int uart_suspend_port(struct uart_driver *reg, struct uart_port *port, u32 level); -int uart_resume_port(struct uart_driver *reg, struct uart_port *port, u32 level); +int uart_suspend_port(struct uart_driver *reg, struct uart_port *port); +int uart_resume_port(struct uart_driver *reg, struct uart_port *port); #define uart_circ_empty(circ) ((circ)->head == (circ)->tail) #define uart_circ_clear(circ) ((circ)->head = (circ)->tail = 0) -- cgit v1.2.3 From c56308f3019761dc4a315eb690599eec2caedeee Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 9 Sep 2003 15:26:47 +0100 Subject: [SERIAL] Convert serial config deps to select statements The dependencies for CONFIG_SERIAL_CORE / CONFIG_SERIAL_CORE_CONSOLE were becoming very messy. This cset converts the dependencies to use "select" statements instead. --- drivers/serial/Kconfig | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 66cc7dfe5c56..333aa2f78f15 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -10,6 +10,7 @@ menu "Serial drivers" # The new 8250/16550 serial drivers config SERIAL_8250 tristate "8250/16550 and compatible serial support" + select SERIAL_CORE ---help--- This selects whether you want to include the driver for the standard serial ports. The standard answer is Y. People who might say N @@ -41,6 +42,7 @@ config SERIAL_8250 config SERIAL_8250_CONSOLE bool "Console on 8250/16550 and compatible serial port" depends on SERIAL_8250=y + select SERIAL_CORE_CONSOLE ---help--- If you say Y here, it will be possible to use a serial port as the system console (the system console is the device which receives all @@ -167,12 +169,14 @@ config SERIAL_8250_ACORN config SERIAL_ANAKIN bool "Anakin serial port support" depends on ARM && ARCH_ANAKIN + select SERIAL_CORE help ::: To be written ::: config SERIAL_ANAKIN_CONSOLE bool "Console on Anakin serial port" depends on SERIAL_ANAKIN + select SERIAL_CORE_CONSOLE help Even if you say Y here, the currently visible virtual console (/dev/tty0) will still be used as the system console by default, but @@ -190,7 +194,8 @@ config ANAKIN_DEFAULT_BAUDRATE config SERIAL_AMBA tristate "ARM AMBA serial port support" - depends on ARM && ARCH_INTEGRATOR + depends on ARM_AMBA + select SERIAL_CORE help This selects the ARM(R) AMBA(R) PrimeCell UART. If you have an Integrator platform, say Y or M here. @@ -200,6 +205,7 @@ config SERIAL_AMBA config SERIAL_AMBA_CONSOLE bool "Support for console on AMBA serial port" depends on SERIAL_AMBA=y + select SERIAL_CORE_CONSOLE ---help--- Say Y here if you wish to use an AMBA PrimeCell UART as the system console (the system console is the device which receives all kernel @@ -214,18 +220,20 @@ config SERIAL_AMBA_CONSOLE config SERIAL_INTEGRATOR bool - depends on SERIAL_AMBA=y + depends on SERIAL_AMBA && ARCH_INTEGRATOR default y config SERIAL_CLPS711X tristate "CLPS711X serial port support" depends on ARM && ARCH_CLPS711X + select SERIAL_CORE help ::: To be written ::: config SERIAL_CLPS711X_CONSOLE bool "Support for console on CLPS711X serial port" depends on SERIAL_CLPS711X=y + select SERIAL_CORE_CONSOLE help Even if you say Y here, the currently visible virtual console (/dev/tty0) will still be used as the system console by default, but @@ -243,6 +251,7 @@ config SERIAL_CLPS711X_OLD_NAME config SERIAL_21285 tristate "DC21285 serial port support" depends on ARM && FOOTBRIDGE + select SERIAL_CORE help If you have a machine based on a 21285 (Footbridge) StrongARM(R)/ PCI bridge you can enable its onboard serial port by enabling this @@ -258,6 +267,7 @@ config SERIAL_21285_OLD config SERIAL_21285_CONSOLE bool "Console on DC21285 serial port" depends on SERIAL_21285=y + select SERIAL_CORE_CONSOLE help If you have enabled the serial port on the 21285 footbridge you can make it the console by answering Y to this option. @@ -272,6 +282,7 @@ config SERIAL_21285_CONSOLE config SERIAL_UART00 bool "Excalibur serial port (uart00) support" depends on ARM && ARCH_CAMELOT + select SERIAL_CORE help Say Y here if you want to use the hard logic uart on Excalibur. This driver also supports soft logic implentations of this uart core. @@ -279,6 +290,7 @@ config SERIAL_UART00 config SERIAL_UART00_CONSOLE bool "Support for console on Excalibur serial port" depends on SERIAL_UART00 + select SERIAL_CORE_CONSOLE help Say Y here if you want to support a serial console on an Excalibur hard logic uart or uart00 IP core. @@ -293,6 +305,7 @@ config SERIAL_UART00_CONSOLE config SERIAL_SA1100 bool "SA1100 serial port support" depends on ARM && ARCH_SA1100 + select SERIAL_CORE help If you have a machine based on a SA1100/SA1110 StrongARM(R) CPU you can enable its onboard serial port by enabling this option. @@ -302,6 +315,7 @@ config SERIAL_SA1100 config SERIAL_SA1100_CONSOLE bool "Console on SA1100 serial port" depends on SERIAL_SA1100 + select SERIAL_CORE_CONSOLE help If you have enabled the serial port on the SA1100/SA1110 StrongARM CPU you can make it the console by answering Y to this option. @@ -316,6 +330,8 @@ config SERIAL_SA1100_CONSOLE config SERIAL_SUNCORE bool depends on SPARC32 || SPARC64 + select SERIAL_CORE + select SERIAL_CORE_CONSOLE default y config SERIAL_SUNZILOG @@ -352,6 +368,7 @@ config SERIAL_SUNSU_CONSOLE config SERIAL_MUX tristate "Serial MUX support" depends on PARISC + select SERIAL_CORE default y ---help--- Saying Y here will enable the hardware MUX serial driver for @@ -369,6 +386,7 @@ config SERIAL_MUX config SERIAL_MUX_CONSOLE bool "Support for console on serial MUX" depends on SERIAL_MUX + select SERIAL_CORE_CONSOLE default y config PDC_CONSOLE @@ -406,6 +424,7 @@ config SERIAL_SUNSAB_CONSOLE config V850E_UART bool "NEC V850E on-chip UART support" depends on V850E_MA1 || V850E_ME2 || V850E_TEG || V850E2_ANNA || V850E_AS85EP1 + select SERIAL_CORE default y config V850E_UARTB @@ -416,10 +435,12 @@ config V850E_UARTB config V850E_UART_CONSOLE bool "Use NEC V850E on-chip UART for console" depends on V850E_UART + select SERIAL_CORE_CONSOLE config SERIAL98 tristate "PC-9800 8251-based primary serial port support" depends on X86_PC9800 + select SERIAL_CORE help If you want to use standard primary serial ports on PC-9800, say Y. Otherwise, say N. @@ -427,16 +448,13 @@ config SERIAL98 config SERIAL98_CONSOLE bool "Support for console on PC-9800 standard serial port" depends on SERIAL98=y + select SERIAL_CORE_CONSOLE config SERIAL_CORE tristate - default m if SERIAL_AMBA!=y && SERIAL_CLPS711X!=y && SERIAL_21285!=y && !SERIAL_SA1100 && !SERIAL_ANAKIN && !SERIAL_UART00 && SERIAL_8250!=y && SERIAL_MUX!=y && !SERIAL_ROCKETPORT && !SERIAL_SUNCORE && !V850E_UART && SERIAL_PMACZILOG!=y && (SERIAL_AMBA=m || SERIAL_CLPS711X=m || SERIAL_21285=m || SERIAL_8250=m || SERIAL_MUX=m || SERIAL98=m || SERIAL_PMACZILOG=m) - default y if SERIAL_AMBA=y || SERIAL_CLPS711X=y || SERIAL_21285=y || SERIAL_SA1100 || SERIAL_ANAKIN || SERIAL_UART00 || SERIAL_8250=y || SERIAL_MUX=y || SERIAL_ROCKETPORT || SERIAL_SUNCORE || V850E_UART || SERIAL98=y || SERIAL_PMACZILOG=y config SERIAL_CORE_CONSOLE bool - depends on SERIAL_AMBA_CONSOLE || SERIAL_CLPS711X_CONSOLE || SERIAL_21285_CONSOLE || SERIAL_SA1100_CONSOLE || SERIAL_ANAKIN_CONSOLE || SERIAL_UART00_CONSOLE || SERIAL_8250_CONSOLE || SERIAL_MUX_CONSOLE || SERIAL_SUNZILOG_CONSOLE || SERIAL_SUNSU_CONSOLE || SERIAL_SUNSAB_CONSOLE || V850E_UART_CONSOLE || SERIAL98_CONSOLE || SERIAL_PMACZILOG_CONSOLE - default y config SERIAL_68328 bool "68328 serial support" @@ -476,6 +494,7 @@ config SERIAL_68360 config SERIAL_PMACZILOG tristate "PowerMac z85c30 ESCC support" depends on PPC_OF + select SERIAL_CORE help This driver supports the Zilog z85C30 serial ports found on PowerMac machines. @@ -484,6 +503,7 @@ config SERIAL_PMACZILOG config SERIAL_PMACZILOG_CONSOLE bool "Console on PowerMac z85c30 serial port" depends on SERIAL_PMACZILOG=y + select SERIAL_CORE_CONSOLE help If you would like to be able to use the z85c30 serial port on your PowerMac as the console, you can do so by answering -- cgit v1.2.3 From cca9cd91ee049e2e051cf79887e6ace5d308ecba Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 9 Sep 2003 15:33:12 +0100 Subject: [SERIAL] Fix another missing irqreturn_t (clps711x.c) --- drivers/serial/clps711x.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/serial/clps711x.c b/drivers/serial/clps711x.c index a32dae486b77..1848756ca2bf 100644 --- a/drivers/serial/clps711x.c +++ b/drivers/serial/clps711x.c @@ -104,7 +104,7 @@ static void clps711xuart_enable_ms(struct uart_port *port) { } -static void clps711xuart_int_rx(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id, struct pt_regs *regs) { struct uart_port *port = dev_id; struct tty_struct *tty = port->info->tty; @@ -139,7 +139,7 @@ static void clps711xuart_int_rx(int irq, void *dev_id, struct pt_regs *regs) } out: tty_flip_buffer_push(tty); - return; + return IRQ_HANDLED; handle_error: if (ch & UARTDR_PARERR) @@ -180,7 +180,7 @@ static void clps711xuart_int_rx(int irq, void *dev_id, struct pt_regs *regs) goto error_return; } -static void clps711xuart_int_tx(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id, struct pt_regs *regs) { struct uart_port *port = dev_id; struct circ_buf *xmit = &port->info->xmit; @@ -190,11 +190,11 @@ static void clps711xuart_int_tx(int irq, void *dev_id, struct pt_regs *regs) clps_writel(port->x_char, UARTDR(port)); port->icount.tx++; port->x_char = 0; - return; + return IRQ_HANDLED; } if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { clps711xuart_stop_tx(port, 0); - return; + return IRQ_HANDLED; } count = port->fifosize >> 1; @@ -211,6 +211,8 @@ static void clps711xuart_int_tx(int irq, void *dev_id, struct pt_regs *regs) if (uart_circ_empty(xmit)) clps711xuart_stop_tx(port, 0); + + return IRQ_HANDLED; } static unsigned int clps711xuart_tx_empty(struct uart_port *port) -- cgit v1.2.3 From 3cd30bf04e97ab33123b7c8ccd3dbc1eee531e58 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 9 Sep 2003 17:23:36 +0100 Subject: [SERIAL] Introduce per-port capabilities. This allows us to maintain quirks or capabilities on a per-port basis, so we can handle buggy clones more effectively. --- drivers/serial/8250.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 5a722a5bd8fc..c60101ffc3ca 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -122,6 +122,7 @@ struct uart_8250_port { struct uart_port port; struct timer_list timer; /* "no irq" timer */ struct list_head list; /* ports on this IRQ */ + unsigned int capabilities; /* port capabilities */ unsigned short rev; unsigned char acr; unsigned char ier; @@ -683,6 +684,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) serial_outp(up, UART_LCR, save_lcr); up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; + up->capabilities = uart_config[up->port.type].flags; if (up->port.type == PORT_UNKNOWN) goto out; @@ -1190,6 +1192,8 @@ static int serial8250_startup(struct uart_port *port) unsigned long flags; int retval; + up->capabilities = uart_config[up->port.type].flags; + if (up->port.type == PORT_16C950) { /* Wake up and initialize UART */ up->acr = 0; @@ -1215,7 +1219,7 @@ static int serial8250_startup(struct uart_port *port) * Clear the FIFO buffers and disable them. * (they will be reeanbled in set_termios()) */ - if (uart_config[up->port.type].flags & UART_CLEAR_FIFO) { + if (up->capabilities & UART_CLEAR_FIFO) { serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); @@ -1428,7 +1432,7 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios, up->rev == 0x5201) quot ++; - if (uart_config[up->port.type].flags & UART_USE_FIFO) { + if (up->capabilities & UART_USE_FIFO) { if (baud < 2400) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; #ifdef CONFIG_SERIAL_8250_RSA @@ -1489,13 +1493,13 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios, serial_out(up, UART_IER, up->ier); - if (uart_config[up->port.type].flags & UART_STARTECH) { + if (up->capabilities & UART_STARTECH) { serial_outp(up, UART_LCR, 0xBF); serial_outp(up, UART_EFR, termios->c_cflag & CRTSCTS ? UART_EFR_CTS :0); } - if (uart_config[up->port.type].flags & UART_NATSEMI) { + if (up->capabilities & UART_NATSEMI) { /* Switch to bank 2 not bank 1, to avoid resetting EXCR2 */ serial_outp(up, UART_LCR, 0xe0); } else { @@ -1524,7 +1528,7 @@ serial8250_pm(struct uart_port *port, unsigned int state, struct uart_8250_port *up = (struct uart_8250_port *)port; if (state) { /* sleep */ - if (uart_config[up->port.type].flags & UART_STARTECH) { + if (up->capabilities & UART_STARTECH) { /* Arrange to enter sleep mode */ serial_outp(up, UART_LCR, 0xBF); serial_outp(up, UART_EFR, UART_EFR_ECB); @@ -1543,7 +1547,7 @@ serial8250_pm(struct uart_port *port, unsigned int state, up->pm(port, state, oldstate); } else { /* wake */ - if (uart_config[up->port.type].flags & UART_STARTECH) { + if (up->capabilities & UART_STARTECH) { /* Wake up UART */ serial_outp(up, UART_LCR, 0xBF); serial_outp(up, UART_EFR, UART_EFR_ECB); -- cgit v1.2.3