免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3019 | 回复: 1

linux串口驱动有一个init (*init)函数指针没有指向任何函数却被使用,求大侠帮忙 [复制链接]

论坛徽章:
0
发表于 2015-04-16 19:57 |显示全部楼层
我自己想学linux驱动,刚开始入手,有太多不明白之处,还想请教。

1. 驱动是exar提供的linux源码驱动,可以正常使用
2. *init指针函数在struct pci_serial_quirk结构体进行了声明
3. 定义了一个struct pci_serial_quirk结构类型的结构体pci_serial_quirks,但是结构体里面并没有给init和exit指针函数指定函数
4. init_one_xrpciserialcard函数里面的quirk指针实际是获取了pci_serial_quirks结构体中的其中一个枚举成员,且init_one_xrpciserialcard调用了该结构体成员init指针函数(quirk->init)

我的问题是:pci_serial_quirks结构体没有给init和exit指针函数指向函数,却能调用它呢?

以下是该驱动的linux源码,还望高手指点明津

/*****************************************************************************/
/*
*      xr1715x.c  -- EXAR multiport serial driver.
*
*      Copyright (C) 2010 Exar Corporation.
*
*      Based on Linux 2.6.31 Kernel's  drivers/serial/8250.c and /8250_pci.c
*
*      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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
*           Multiport Serial Driver for EXAR's PCI Family of UARTs (XR17c158/154/152)
*           for                        : LINUX 2.6.37        (Tested with openSuse 11.4)
*           date                        : Apr, 2011
*           version                : 4.7
*
*        Check Release Notes for information on what has changed in the new version.
*
*/

#include <linux/module.h>
#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>

#include <linux/serial_reg.h>
#include <linux/serial.h>
#include <linux/serialP.h>
#include <linux/serial_core.h>

#include <asm/io.h>
#include <asm/irq.h>
#include <asm/irq.h>
#include <asm/bitops.h>
#include <asm/byteorder.h>
#include <asm/serial.h>
#include <asm/io.h>
#include <asm/uaccess.h>

#define _INLINE_ inline

/*
* Definitions for PCI support.
*/
#define FL_BASE_MASK                0x0007
#define FL_BASE0                0x0000
#define FL_GET_BASE(x)                (x & FL_BASE_MASK)

#define NR_PORTS        128 // we can support more too

#define XR_MAJOR       30
#define XR_MINOR       0

/*
* The special register set for XR17c15x UARTs.
*/

#define XR_17C15X_EXTENDED_FCTR                8
#define XR_17C15X_EXTENDED_EFR                9
#define XR_17C15X_TXFIFO_CNT                10
#define XR_17C15X_RXFIFO_CNT                11
#define XR_17C15X_EXTENDED_RXTRG        11

#define XR_17C15X_FCTR_RTS_8DELAY        0x03
#define XR_17C15X_FCTR_TRGD                192

/* 17V15X TX/RX memory mapped buffer offsets */

#define UART_17C15X_RX_OFFSET                0x100
#define UART_17C15X_TX_OFFSET                 0x100

/*
* These are the EXTENDED definitions for the 17C15X's Interrupt
* Enable Register
*/
#define        XR_17C15X_IER_RTSDTR        0x40
#define XR_17C15X_IER_CTSDSR        0x80

#define PCI_NUM_BAR_RESOURCES        6

struct serial_private {
        struct pci_dev                *dev;
        unsigned int                nr;
        void                        *remapped_bar[PCI_NUM_BAR_RESOURCES];
        struct pci_serial_quirk        *quirk;
        int                        line[0];
};

struct pciserial_board {
        unsigned int flags;
        unsigned int num_ports;
        unsigned int base_baud;
        unsigned int uart_offset;
        unsigned int reg_shift;
        unsigned int first_offset;
};

/*
* init function returns:
*  > 0 - number of ports
*  = 0 - use board->num_ports
*  < 0 - error
*/
struct pci_serial_quirk {
        u32        vendor;
        u32        device;
        u32        subvendor;
        u32        subdevice;
        int        (*init)(struct pci_dev *dev);
        int        (*setup)(struct serial_private *, struct pciserial_board *,
                         struct uart_port *, int);
        void        (*exit)(struct pci_dev *dev);
};

/*
* This is the configuration table for all of the PCI serial boards
* which we support.  It is directly indexed by the xrpci_board_num_t enum
* value, which is encoded in the pci_device_id PCI probe table's
* driver_data member.
*
* The makeup of these names are:
*  pbn_bn{_bt}_n_baud
*
*  bn   = PCI BAR number
*  bt   = Index using PCI BARs
*  n    = number of serial ports
*  baud = baud rate
*
* Please note: in theory if n = 1, _bt infix should make no difference.
* ie, pbn_b0_1_115200 is the same as pbn_b0_bt_1_115200
*/
enum xrpci_board_num_t {
        xr_8port = 0,
        xr_4port,
        xr_2port
};

static struct pciserial_board xrpciserial_boards[] __devinitdata = {
        [xr_8port] = {
                .flags                = FL_BASE0,
                .num_ports        = 8,
                .base_baud        = 921600,
                .uart_offset        = 0x200,
                .reg_shift        = 0,
                .first_offset        = 0,
        },
       
        [xr_4port] = {
                .flags                = FL_BASE0,
                .num_ports        = 4,
                .base_baud        = 921600,
                .uart_offset        = 0x200,
                .reg_shift        = 0,
                .first_offset        = 0,
        },
        [xr_2port] = {
                .flags                = FL_BASE0,
                .num_ports        = 2,
                .base_baud        = 921600,
                .uart_offset        = 0x200,
                .reg_shift        = 0,
                .first_offset        = 0,
        },
};

/*
* Configuration:
*   share_irqs - whether we pass SA_SHIRQ to request_irq().  This option
*                is unsafe when used on edge-triggered interrupts.
*/
#define SERIALEXAR_SHARE_IRQS 1
unsigned int share_irqs = SERIALEXAR_SHARE_IRQS;

/*
* Debugging.
*/
#if 0
#define DEBUG_AUTOCONF(fmt...)        printk(fmt)
#else
#define DEBUG_AUTOCONF(fmt...)        do { } while (0)
#endif

#if 0
#define DEBUG_INTR(fmt...)        printk(fmt)
#else
#define DEBUG_INTR(fmt...)        do { } while (0)
#endif

#define PASS_LIMIT        256

/*
* We default to IRQ0 for the "no irq" hack.   Some
* machine types want others as well - they're free
* to redefine this in their header file.
*/
#define is_real_interrupt(irq)        ((irq) != 0)


struct uart_xr_port {
        struct uart_port        port;
        struct timer_list        timer;                /* "no irq" timer */
        struct list_head        list;                /* ports on this IRQ */
        unsigned short                capabilities;        /* port capabilities */
        unsigned short                bugs;                /* port bugs */
        unsigned int                tx_loadsz;        /* transmit fifo load size */
        unsigned char                acr;
        unsigned char                ier;
        unsigned char                lcr;
        unsigned char                mcr;
        unsigned char                mcr_mask;        /* mask of user bits */
        unsigned char                mcr_force;        /* mask of forced bits */
        unsigned char                lsr_break_flag;

        /*
         * We provide a per-port pm hook.
         */
        void                        (*pm)(struct uart_port *port,
        unsigned int                 state, unsigned int old);
};

struct irq_info {
        spinlock_t                lock;
        struct list_head        *head;
};

static struct irq_info irq_lists[NR_IRQS];

/*
* Here we define the default xmit fifo size used for each type of UART.
*/
#define PORT_MAX_XR 1
#define XRPCI_TYPE 1 // the second entry that is [1] in the array
static const struct serial_uart_config uart_config[PORT_MAX_XR+1] = {
        { "Unknown",        1,        0 },
        { "XR17c15x",                64,        0 },
};

static int
setup_port(struct serial_private *priv, struct uart_port *port,
           int bar, int offset, int regshift)
{
        struct pci_dev *dev = priv->dev;
        unsigned long base, len;

        if (bar >= PCI_NUM_BAR_RESOURCES)
                return -EINVAL;

        base = pci_resource_start(dev, bar);

        if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) {
                len =  pci_resource_len(dev, bar);
                if (!priv->remapped_bar[bar])
                        priv->remapped_bar[bar] = ioremap(base, len);
                if (!priv->remapped_bar[bar])
                        return -ENOMEM;

                port->iotype = UPIO_MEM;
                port->iobase = 0;
                port->mapbase = base + offset;
                port->membase = priv->remapped_bar[bar] + offset;
                port->regshift = regshift;
        } else {
                //Exar's got to be memory mapped. some hardware reading error?
                return -EINVAL;
        }
        return 0;
}


static int
pci_default_setup(struct serial_private *priv, struct pciserial_board *board,
                  struct uart_port *port, int idx)
{
        unsigned int bar, offset = board->first_offset;

        bar = FL_GET_BASE(board->flags);
        offset += idx * board->uart_offset;
                       
        return setup_port(priv, port, bar, offset, board->reg_shift);
}

/*
* Master list of serial port init/setup/exit quirks.
* This does not describe the general nature of the port.
* (ie, baud base, number and location of ports, etc)
*
* This list is ordered alphabetically by vendor then device.
* Specific entries must come before more generic entries.
*/
static struct pci_serial_quirk pci_serial_quirks[] = {       
        {
                .vendor                = 0x13a8,
                .device                = 0x158,
                .subvendor        = PCI_ANY_ID,
                .subdevice        = PCI_ANY_ID,
                .setup                = pci_default_setup,       
        },
       
        {
                .vendor                = 0x13a8,
                .device                = 0x154,
                .subvendor        = PCI_ANY_ID,
                .subdevice        = PCI_ANY_ID,
                .setup                = pci_default_setup,       
        },

        {
                .vendor                = 0x13a8,
                .device                = 0x152,
                .subvendor        = PCI_ANY_ID,
                .subdevice        = PCI_ANY_ID,
                .setup                = pci_default_setup,       
        }       
};

static inline int quirk_id_matches(u32 quirk_id, u32 dev_id)
{
        return quirk_id == PCI_ANY_ID || quirk_id == dev_id;
}

static struct pci_serial_quirk *find_quirk(struct pci_dev *dev)
{
        struct pci_serial_quirk *quirk;

        for (quirk = pci_serial_quirks; ; quirk++)
                if (quirk_id_matches(quirk->vendor, dev->vendor) &&
                    quirk_id_matches(quirk->device, dev->device))                    
                         break;
        return quirk;
}

static _INLINE_ unsigned int serial_in(struct uart_xr_port *up, int offset)
{
        offset <<= up->port.regshift;

        switch (up->port.iotype) {
        case SERIAL_IO_HUB6:
                outb(up->port.hub6 - 1 + offset, up->port.iobase);
                return inb(up->port.iobase + 1);

        case SERIAL_IO_MEM:
                return readb(up->port.membase + offset);

        default:
                return inb(up->port.iobase + offset);
        }
}

static _INLINE_ void
serial_out(struct uart_xr_port *up, int offset, int value)
{
        offset <<= up->port.regshift;

        switch (up->port.iotype) {
        case SERIAL_IO_HUB6:
                outb(up->port.hub6 - 1 + offset, up->port.iobase);
                outb(value, up->port.iobase + 1);
                break;

        case SERIAL_IO_MEM:
                writeb(value, up->port.membase + offset);
                break;

        default:
                outb(value, up->port.iobase + offset);
        }
}

/*
* We used to support using pause I/O for certain machines.  We
* haven't supported this for a while, but just in case it's badly
* needed for certain old 386 machines, I've left these #define's
* in....
*/
#define serial_inp(up, offset)                serial_in(up, offset)
#define serial_outp(up, offset, value)        serial_out(up, offset, value)

static void serialxr_stop_tx(struct uart_port *port)
{
        struct uart_xr_port *up = (struct uart_xr_port *)port;

        if (up->ier & UART_IER_THRI) {
                up->ier &= ~UART_IER_THRI;
                serial_out(up, UART_IER, up->ier);
        }
}

static void serialxr_start_tx(struct uart_port *port)
{
        struct uart_xr_port *up = (struct uart_xr_port *)port;

        if (!(up->ier & UART_IER_THRI)) {
                up->ier |= UART_IER_THRI;
                serial_out(up, UART_IER, up->ier);
        }
}

static void serialxr_stop_rx(struct uart_port *port)
{
        struct uart_xr_port *up = (struct uart_xr_port *)port;

        up->ier &= ~UART_IER_RLSI;
        up->port.read_status_mask &= ~UART_LSR_DR;
        serial_out(up, UART_IER, up->ier);
}

static void serialxr_enable_ms(struct uart_port *port)
{
        struct uart_xr_port *up = (struct uart_xr_port *)port;

        up->ier |= UART_IER_MSI;
        serial_out(up, UART_IER, up->ier);
}

static _INLINE_ void
receive_chars(struct uart_xr_port *up, int *status)
{
        struct tty_struct *tty = up->port.state->port.tty;
        unsigned char ch, lsr = *status;
        int max_count = 256;
        char flag;

        do {
                ch = serial_inp(up, UART_RX);
                flag = TTY_NORMAL;
                up->port.icount.rx++;

                if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE |
                                    UART_LSR_FE | UART_LSR_OE))) {
                        /*
                         * For statistics only
                         */
                        if (lsr & UART_LSR_BI) {
                                lsr &= ~(UART_LSR_FE | UART_LSR_PE);
                                up->port.icount.brk++;
                                /*
                                 * We do the SysRQ and SAK checking
                                 * here because otherwise the break
                                 * may get masked by ignore_status_mask
                                 * or read_status_mask.
                                 */
                                if (uart_handle_break(&up->port))
                                        goto ignore_char;
                        } else if (lsr & UART_LSR_PE)
                                up->port.icount.parity++;
                        else if (lsr & UART_LSR_FE)
                                up->port.icount.frame++;
                        if (lsr & UART_LSR_OE)
                                up->port.icount.overrun++;

                        /*
                         * Mask off conditions which should be ignored.
                         */
                        lsr &= up->port.read_status_mask;

                        if (lsr & UART_LSR_BI) {
                                DEBUG_INTR("handling break....";
                                flag = TTY_BREAK;
                        } else if (lsr & UART_LSR_PE)
                                flag = TTY_PARITY;
                        else if (lsr & UART_LSR_FE)
                                flag = TTY_FRAME;
                }
                if (uart_handle_sysrq_char(&up->port, ch))
                        goto ignore_char;

                uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);

        ignore_char:
                lsr = serial_inp(up, UART_LSR);
        } while ((lsr & UART_LSR_DR) && (max_count-- > 0));
        spin_unlock(&up->port.lock);
        tty_flip_buffer_push(tty);
        spin_lock(&up->port.lock);
        *status = lsr;
}

static _INLINE_ void transmit_chars(struct uart_xr_port *up)
{
        struct circ_buf *xmit = &up->port.state->xmit;
        int count, bytes_in_fifo;
        int tmp;

        if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
                serialxr_stop_tx(&up->port);
                return;
        }
               
        bytes_in_fifo = serial_inp(up, XR_17C15X_TXFIFO_CNT);
        // read the fifo count untill we get the same value twice
        while (bytes_in_fifo != serial_inp(up, XR_17C15X_TXFIFO_CNT))
                bytes_in_fifo = serial_inp(up, XR_17C15X_TXFIFO_CNT);
       
        // how much buffer is availabe now to write?       
        count = up->port.fifosize - bytes_in_fifo;
        if (uart_circ_chars_pending(xmit) < count)
                count = uart_circ_chars_pending(xmit);
       
        do
        {       
                // if the count is more than (tail to end of the buffer), transmit only the rest here.
                // tail+tmp&(UART_XMIT_SIZE-1) will reset the tail to the starting of the circular buffer
                if( ((xmit->tail + count) & (UART_XMIT_SIZE-1)) < xmit->tail)
                {                       
                        tmp = UART_XMIT_SIZE - xmit->tail;
                        memcpy(up->port.membase + UART_17C15X_TX_OFFSET, &(xmit->buf[xmit->tail]), tmp);
                        xmit->tail += tmp;
                        xmit->tail &= (UART_XMIT_SIZE-1);
                        up->port.icount.tx += tmp;

                        count        -= tmp;
                }
                else
                {               
                        memcpy(up->port.membase + UART_17C15X_TX_OFFSET, &(xmit->buf[xmit->tail]), count);       
                        xmit->tail += count;
                        xmit->tail &= UART_XMIT_SIZE - 1;
       
                        up->port.icount.tx += count;
                        count = 0;
                }
       
        }while (count > 0);


        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
                uart_write_wakeup(&up->port);

        DEBUG_INTR("THRE...";

        if (uart_circ_empty(xmit))
                serialxr_stop_tx(&up->port);
}

static _INLINE_ void check_modem_status(struct uart_xr_port *up)
{
        int status;

        status = serial_in(up, UART_MSR);

        if ((status & UART_MSR_ANY_DELTA) == 0)
                return;

        if (status & UART_MSR_TERI)
                up->port.icount.rng++;
        if (status & UART_MSR_DDSR)
                up->port.icount.dsr++;
        if (status & UART_MSR_DDCD)
                uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
        if (status & UART_MSR_DCTS)
                uart_handle_cts_change(&up->port, status & UART_MSR_CTS);

        wake_up_interruptible(&up->port.state->port.delta_msr_wait);
}

/*
* This handles the interrupt from one port.
*/
static inline void
serialxr_handle_port(struct uart_xr_port *up)
{
        unsigned int status = serial_inp(up, UART_LSR);

        DEBUG_INTR("status = %x...", status);

        if (status & UART_LSR_DR)
                receive_chars(up, &status);
        check_modem_status(up);
        if (status & UART_LSR_THRE)
                transmit_chars(up);
}

/*
* This is the serial driver's interrupt routine.
*
* Arjan thinks the old way was overly complex, so it got simplified.
* Alan disagrees, saying that need the complexity to handle the weird
* nature of ISA shared interrupts.  (This is a special exception.)
*
* In order to handle ISA shared interrupts properly, we need to check
* that all ports have been serviced, and therefore the ISA interrupt
* line has been de-asserted.
*
* This means we need to loop through all ports. checking that they
* don't have an interrupt pending.
*/
static irqreturn_t serialxr_interrupt(int irq, void *dev_id)
{
        struct irq_info *i = dev_id;
        struct list_head *l, *end = NULL;
        int pass_counter = 0;

        DEBUG_INTR("serialxr_interrupt(%d)...", irq);

        spin_lock(&i->lock);

        l = i->head;
        do {
                struct uart_xr_port *up;
                unsigned int iir;

                up = list_entry(l, struct uart_xr_port, list);

                iir = serial_in(up, UART_IIR);
                if (!(iir & UART_IIR_NO_INT)) {
                        spin_lock(&up->port.lock);
                        serialxr_handle_port(up);
                        spin_unlock(&up->port.lock);

                        end = NULL;
                } else if (end == NULL)
                        end = l;

                l = l->next;

                if (l == i->head && pass_counter++ > PASS_LIMIT) {
                        /* If we hit this, we're dead. */
                        printk(KERN_ERR "serialxr: too much work for "
                                "irq%d\n", irq);
                        break;
                }
        } while (l != end);

        spin_unlock(&i->lock);

        DEBUG_INTR("end.\n";
        /* FIXME! Was it really ours? */
        return IRQ_HANDLED;
}

/*
* To support ISA shared interrupts, we need to have one interrupt
* handler that ensures that the IRQ line has been deasserted
* before returning.  Failing to do this will result in the IRQ
* line being stuck active, and, since ISA irqs are edge triggered,
* no more IRQs will be seen.
*/
static void serial_do_unlink(struct irq_info *i, struct uart_xr_port *up)
{
        spin_lock_irq(&i->lock);

        if (!list_empty(i->head)) {
                if (i->head == &up->list)
                        i->head = i->head->next;
                list_del(&up->list);
        } else {
                BUG_ON(i->head != &up->list);
                i->head = NULL;
        }

        spin_unlock_irq(&i->lock);
}

static int serial_link_irq_chain(struct uart_xr_port *up)
{
        struct irq_info *i = irq_lists + up->port.irq;
        int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;

        spin_lock_irq(&i->lock);

        if (i->head) {
                list_add(&up->list, i->head);
                spin_unlock_irq(&i->lock);

                ret = 0;
        } else {
                INIT_LIST_HEAD(&up->list);
                i->head = &up->list;
                spin_unlock_irq(&i->lock);

                ret = request_irq(up->port.irq, serialxr_interrupt,
                                  irq_flags, "xrserial", i);
                if (ret < 0)
                        serial_do_unlink(i, up);
        }

        return ret;
}

static void serial_unlink_irq_chain(struct uart_xr_port *up)
{
        struct irq_info *i = irq_lists + up->port.irq;

        BUG_ON(i->head == NULL);

        if (list_empty(i->head))
                free_irq(up->port.irq, i);

        serial_do_unlink(i, up);
}

/*
* This function is used to handle ports that do not have an
* interrupt.  This doesn't work very well for 16450's, but gives
* barely passable results for a 16550A.  (Although at the expense
* of much CPU overhead).
*/
static void serialxr_timeout(unsigned long data)
{
        struct uart_xr_port *up = (struct uart_xr_port *)data;
        unsigned int timeout;
        unsigned int iir;
       
        iir = serial_in(up, UART_IIR);
        if (!(iir & UART_IIR_NO_INT)) {
                spin_lock(&up->port.lock);
                serialxr_handle_port(up);
                spin_unlock(&up->port.lock);
        }

        timeout = up->port.timeout;
        timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
        mod_timer(&up->timer, jiffies + timeout);
}

static unsigned int serialxr_tx_empty(struct uart_port *port)
{
        struct uart_xr_port *up = (struct uart_xr_port *)port;
        unsigned long flags;
        unsigned int ret;

        spin_lock_irqsave(&up->port.lock, flags);
        ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
        spin_unlock_irqrestore(&up->port.lock, flags);

        return ret;
}

static unsigned int serialxr_get_mctrl(struct uart_port *port)
{
        struct uart_xr_port *up = (struct uart_xr_port *)port;       
        unsigned char status;
        unsigned int ret;

        status = serial_in(up, UART_MSR);

        ret = 0;
        if (status & UART_MSR_DCD)
                ret |= TIOCM_CAR;
        if (status & UART_MSR_RI)
                ret |= TIOCM_RNG;
        if (status & UART_MSR_DSR)
                ret |= TIOCM_DSR;
        if (status & UART_MSR_CTS)
                ret |= TIOCM_CTS;
        return ret;
}

static void serialxr_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
        struct uart_xr_port *up = (struct uart_xr_port *)port;
        unsigned char mcr = 0;

        if (mctrl & TIOCM_RTS)
                mcr |= UART_MCR_RTS;
        if (mctrl & TIOCM_DTR)
                mcr |= UART_MCR_DTR;
        if (mctrl & TIOCM_OUT1)
                mcr |= UART_MCR_OUT1;
        if (mctrl & TIOCM_OUT2)
                mcr |= UART_MCR_OUT2;
        if (mctrl & TIOCM_LOOP)
                mcr |= UART_MCR_LOOP;

        mcr = (mcr & up->mcr_mask) | up->mcr_force;

        serial_out(up, UART_MCR, mcr);
}

static void serialxr_break_ctl(struct uart_port *port, int break_state)
{
        struct uart_xr_port *up = (struct uart_xr_port *)port;
        unsigned long flags;

        spin_lock_irqsave(&up->port.lock, flags);
        if (break_state == -1)
                up->lcr |= UART_LCR_SBC;
        else
                up->lcr &= ~UART_LCR_SBC;
        serial_out(up, UART_LCR, up->lcr);
        spin_unlock_irqrestore(&up->port.lock, flags);
}

static int serialxr_startup(struct uart_port *port)
{
        struct uart_xr_port *up = (struct uart_xr_port *)port;
        unsigned long flags;
        int retval;

        up->capabilities = uart_config[up->port.type].flags;

        serial_out(up, XR_17C15X_EXTENDED_EFR, UART_EFR_ECB);
        serial_out(up, UART_IER, 0);

        /* Set the RX trigger level for 32 bytes, with a Hysteresis level of 8.  */
        /* These are some default values, the OEMs can change these values
                * according to their best case scenarios */

        serial_out(up, XR_17C15X_EXTENDED_RXTRG, 32);
        serial_out(up, XR_17C15X_EXTENDED_FCTR, XR_17C15X_FCTR_TRGD | XR_17C15X_FCTR_RTS_8DELAY);

        serial_outp(up, UART_LCR, 0);

        /* Wake up and initialize UART */
        serial_out(up, XR_17C15X_EXTENDED_EFR, UART_EFR_ECB);
        serial_out(up, UART_IER, 0);
        serial_out(up, UART_LCR, 0);

        /*
         * Clear the FIFO buffers and disable them.
         * (they will be reeanbled in set_termios())
         */
        serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
                        UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
        serial_outp(up, UART_FCR, 0);
       
        /*
         * Clear the interrupt registers.
         */
        (void) serial_inp(up, UART_LSR);
        (void) serial_inp(up, UART_RX);
        (void) serial_inp(up, UART_IIR);
        (void) serial_inp(up, UART_MSR);

        /*
         * If the "interrupt" for this port doesn't correspond with any
         * hardware interrupt, we use a timer-based system.  The original
         * driver used to do this with IRQ0.
         */
        if (!is_real_interrupt(up->port.irq)) {
                unsigned int timeout = up->port.timeout;

                timeout = timeout > 6 ? (timeout / 2 - 2) : 1;

                up->timer.data = (unsigned long)up;
                mod_timer(&up->timer, jiffies + timeout);
        } else {
                retval = serial_link_irq_chain(up);
                if (retval)
                        return retval;
        }

        /*
         * Now, initialize the UART
         */
        serial_outp(up, UART_LCR, UART_LCR_WLEN;

        spin_lock_irqsave(&up->port.lock, flags);

        /*
        * Most PC uarts need OUT2 raised to enable interrupts.
        */
        if (is_real_interrupt(up->port.irq))
                up->port.mctrl |= TIOCM_OUT2;

        serialxr_set_mctrl(&up->port, up->port.mctrl);
        spin_unlock_irqrestore(&up->port.lock, flags);

        /*
         * Finally, enable interrupts.  Note: Modem status interrupts
         * are set via set_termios(), which will be occurring imminently
         * anyway, so we don't enable them here.
         */
        up->ier = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
        // extra for XR17c15x
        up->ier |= XR_17C15X_IER_RTSDTR | XR_17C15X_IER_CTSDSR;
        serial_outp(up, UART_IER, up->ier);

        /*
         * And clear the interrupt registers again for luck.
         */
        (void) serial_inp(up, UART_LSR);
        (void) serial_inp(up, UART_RX);
        (void) serial_inp(up, UART_IIR);
        (void) serial_inp(up, UART_MSR);

        return 0;
}

static void serialxr_shutdown(struct uart_port *port)
{
        struct uart_xr_port *up = (struct uart_xr_port *)port;
        unsigned long flags;
       
        /*
         * Disable interrupts from this port
         */
        up->ier = 0;
        serial_outp(up, UART_IER, 0);

        spin_lock_irqsave(&up->port.lock, flags);
       
        up->port.mctrl &= ~TIOCM_OUT2;

        serialxr_set_mctrl(&up->port, up->port.mctrl);
        spin_unlock_irqrestore(&up->port.lock, flags);

        /*
         * Disable break condition and FIFOs
         */
        serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC);
        serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
                                  UART_FCR_CLEAR_RCVR |
                                  UART_FCR_CLEAR_XMIT);
        serial_outp(up, UART_FCR, 0);

        /*
         * Read data port to reset things, and then unlink from
         * the IRQ chain.
         */
        (void) serial_in(up, UART_RX);

        if (!is_real_interrupt(up->port.irq))
                del_timer_sync(&up->timer);
        else
                serial_unlink_irq_chain(up);
}

static unsigned int serialxr_get_divisor(struct uart_port *port, unsigned int baud)
{
        unsigned int quot;

        quot = uart_get_divisor(port, baud);

        return quot;
}

static void
serialxr_set_termios(struct uart_port *port, struct ktermios *termios,
                       struct ktermios *old)
{
        struct uart_xr_port *up = (struct uart_xr_port *)port;
        unsigned char cval;
        unsigned long flags;
        unsigned int baud, quot;

        switch (termios->c_cflag & CSIZE) {
        case CS5:
                cval = 0x00;
                break;
        case CS6:
                cval = 0x01;
                break;
        case CS7:
                cval = 0x02;
                break;
        default:
        case CS8:
                cval = 0x03;
                break;
        }

        if (termios->c_cflag & CSTOPB)
                cval |= 0x04;
        if (termios->c_cflag & PARENB)
                cval |= UART_LCR_PARITY;
        if (!(termios->c_cflag & PARODD))
                cval |= UART_LCR_EPAR;
#ifdef CMSPAR
        if (termios->c_cflag & CMSPAR)
                cval |= UART_LCR_SPAR;
#endif

        /*
         * Ask the core to calculate the divisor for us.
         */
        baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
        quot = serialxr_get_divisor(port, baud);
       
        /*
         * Ok, we're now changing the port state.  Do it with
         * interrupts disabled.
         */
        spin_lock_irqsave(&up->port.lock, flags);

        /*
         * Update the per-port timeout.
         */
        uart_update_timeout(port, termios->c_cflag, baud);

        up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
        if (termios->c_iflag & INPCK)
                up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
        if (termios->c_iflag & (BRKINT | PARMRK))
                up->port.read_status_mask |= UART_LSR_BI;

        /*
         * Characteres to ignore
         */
        up->port.ignore_status_mask = 0;
        if (termios->c_iflag & IGNPAR)
                up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
        if (termios->c_iflag & IGNBRK) {
                up->port.ignore_status_mask |= UART_LSR_BI;
                /*
                 * If we're ignoring parity and break indicators,
                 * ignore overruns too (for real raw support).
                 */
                if (termios->c_iflag & IGNPAR)
                        up->port.ignore_status_mask |= UART_LSR_OE;
        }

        /*
         * ignore all characters if CREAD is not set
         */
        if ((termios->c_cflag & CREAD) == 0)
                up->port.ignore_status_mask |= UART_LSR_DR;

        /*
         * CTS flow control flag and modem status interrupts
         */
        up->ier &= ~UART_IER_MSI;
        if (UART_ENABLE_MS(&up->port, termios->c_cflag))
                up->ier |= UART_IER_MSI;

        serial_out(up, UART_IER, up->ier);

        serial_outp(up, XR_17C15X_EXTENDED_EFR, termios->c_cflag & CRTSCTS ? UART_EFR_CTS :0);
       
        serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
       
        serial_outp(up, UART_DLL, quot & 0xff);                /* LS of divisor */
        serial_outp(up, UART_DLM, quot >> ;                /* MS of divisor */
        serial_outp(up, UART_LCR, cval);                /* reset DLAB */
        up->lcr = cval;                                        /* Save LCR */
       
        serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);/* set fcr */
       
        spin_unlock_irqrestore(&up->port.lock, flags);
}

/*
*      EXAR ioctls
*/
//#define         FIOQSIZE                0x5460
#define                EXAR_READ_REG              (FIOQSIZE + 1)
#define         EXAR_WRITE_REG             (FIOQSIZE + 2)

struct xrioctl_rw_reg {
        unsigned char reg;
        unsigned char regvalue;
};
/*
* This function is used to handle Exar Device specific ioctl calls
* The user level application should have defined the above ioctl
* commands with the above values to access these ioctls and the
* input parameters for these ioctls should be struct xrioctl_rw_reg
* The Ioctl functioning is pretty much self explanatory here in the code,
* and the register values should be between 0 to XR_17C15X_EXTENDED_RXTRG
*/

static int
serialxr_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
{
        struct uart_xr_port *up = (struct uart_xr_port *)port;
        int ret = -ENOIOCTLCMD;
        struct xrioctl_rw_reg ioctlrwarg;

        switch (cmd)
        {
                case EXAR_READ_REG:
                if (copy_from_user(&ioctlrwarg, (void *)arg, sizeof(ioctlrwarg)))
                        return -EFAULT;
                ioctlrwarg.regvalue = serial_inp(up, ioctlrwarg.reg);
                if (copy_to_user((void *)arg, &ioctlrwarg, sizeof(ioctlrwarg)))
                        return -EFAULT;
                ret = 0;
                break;
               
                case EXAR_WRITE_REG:
                if (copy_from_user(&ioctlrwarg, (void *)arg, sizeof(ioctlrwarg)))
                        return -EFAULT;
                serial_outp(up, ioctlrwarg.reg, ioctlrwarg.regvalue);
                ret = 0;
                break;
        }
       
        return ret;
}
             
static void
serialxr_pm(struct uart_port *port, unsigned int state,
              unsigned int oldstate)
{
        struct uart_xr_port *up = (struct uart_xr_port *)port;
        if (state) {
                /* sleep */
                serial_outp(up, XR_17C15X_EXTENDED_EFR, UART_EFR_ECB);
                serial_outp(up, UART_IER, UART_IERX_SLEEP);
                serial_outp(up, XR_17C15X_EXTENDED_EFR, 0);
                               
                if (up->pm)
                        up->pm(port, state, oldstate);
        } else {
                /* wake */
               
                /* Wake up UART */
                serial_outp(up, XR_17C15X_EXTENDED_EFR, UART_EFR_ECB);
                serial_outp(up, UART_IER, 0);
                serial_outp(up, XR_17C15X_EXTENDED_EFR, 0);

                if (up->pm)
                        up->pm(port, state, oldstate);
        }
}

static void serialxr_release_port(struct uart_port *port)
{       
}

static int serialxr_request_port(struct uart_port *port)
{
        return 0;
}

static void serialxr_config_port(struct uart_port *port, int flags)
{
        struct uart_xr_port *up = (struct uart_xr_port *)port;       
       
        if (flags & UART_CONFIG_TYPE)
        {       
                up->port.type = XRPCI_TYPE;
                up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size;
                up->capabilities = uart_config[up->port.type].flags;       
        }
}

static const char *
serialxr_type(struct uart_port *port)
{
        int type = port->type;
       
        if (type >= ARRAY_SIZE(uart_config))
                type = 0;
        return uart_config[type].name;
}

static struct uart_ops serialxr_pops = {
        .tx_empty        = serialxr_tx_empty,
        .set_mctrl        = serialxr_set_mctrl,
        .get_mctrl        = serialxr_get_mctrl,
        .stop_tx        = serialxr_stop_tx,
        .start_tx        = serialxr_start_tx,
        .stop_rx        = serialxr_stop_rx,
        .enable_ms        = serialxr_enable_ms,
        .break_ctl        = serialxr_break_ctl,
        .startup        = serialxr_startup,
        .shutdown        = serialxr_shutdown,
        .set_termios        = serialxr_set_termios,
        .pm                = serialxr_pm,
        .type                = serialxr_type,
        .release_port        = serialxr_release_port,
        .request_port        = serialxr_request_port,
        .config_port        = serialxr_config_port,
        .ioctl                = serialxr_ioctl,
};

static DEFINE_MUTEX(serial_mutex);

static struct uart_xr_port serialxr_ports[NR_PORTS];

#define SERIALXR_CONSOLE        NULL

static struct uart_driver xr_uart_driver = {
        .owner                        = THIS_MODULE,
        .driver_name                = "xrserial",
        .dev_name                = "ttyXR",
        .major                        = XR_MAJOR,
        .minor                        = XR_MINOR,
        .nr                        = NR_PORTS,
        .cons                        = SERIALXR_CONSOLE,
};

static struct uart_xr_port *serialxr_find_match_or_unused(struct uart_port *port)
{
        int i;

        /*
         * First, find a port entry which matches.
         */
        for (i = 0; i < NR_PORTS; i++)
                if (uart_match_port(&serialxr_ports[i].port, port))
                        return &serialxr_ports[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 < NR_PORTS; i++)
                if (serialxr_ports[i].port.type == PORT_UNKNOWN &&
                    serialxr_ports[i].port.iobase == 0)
                {
                        port->line = i;
                        return &serialxr_ports[i];
                }

        /*
         * That also failed.  Last resort is to find any entry which
         * doesn't have a real port associated with it.
         */
        for (i = 0; i < NR_PORTS; i++)
                if (serialxr_ports[i].port.type == PORT_UNKNOWN)
                        return &serialxr_ports[i];

        return NULL;
}


/*
*        serialxr_register_port - register a serial port
*        @port: serial port template
*
*        Configure the serial port specified by the request. If the
*        port exists and is in use, it is hung up and unregistered
*        first.
*
*        The port is then probed and if necessary the IRQ is autodetected
*        If this fails an error is returned.
*
*        On success the port is ready to use and the line number is returned.
*/
int serialxr_register_port(struct uart_port *port)
{
        struct uart_xr_port *uart;
        int ret = -ENOSPC;

        if (port->uartclk == 0)
                return -EINVAL;

        mutex_lock(&serial_mutex);
        uart = serialxr_find_match_or_unused(port);
        if (uart) {
                uart->port.iobase   = port->iobase;
                uart->port.membase  = port->membase;
                uart->port.irq      = port->irq;
                uart->port.uartclk  = port->uartclk;
                uart->port.fifosize = port->fifosize;
                uart->port.regshift = port->regshift;
                uart->port.iotype   = port->iotype;
                uart->port.flags    = port->flags | UPF_BOOT_AUTOCONF;
                uart->port.mapbase  = port->mapbase;
                if (port->dev)
                        uart->port.dev = port->dev;

                uart->port.line = port->line;
                spin_lock_init(&uart->port.lock);

                init_timer(&uart->timer);
                uart->timer.function = serialxr_timeout;

                /*
                 * ALPHA_KLUDGE_MCR needs to be killed.
                 */
                uart->mcr_mask = ~ALPHA_KLUDGE_MCR;
                uart->mcr_force = ALPHA_KLUDGE_MCR;

                uart->port.ops = &serialxr_pops;               
               
                ret = uart_add_one_port(&xr_uart_driver, &uart->port);
                if (ret == 0)
                        ret = uart->port.line;
        }
        mutex_unlock(&serial_mutex);

        return ret;
}

/*
* Probe one serial board.  Unfortunately, there is no rhyme nor reason
* to the arrangement of serial ports on a PCI card.
*/
static int __devinit
init_one_xrpciserialcard(struct pci_dev *dev, const struct pci_device_id *ent)
{
        struct serial_private *priv;
        struct pciserial_board *board;
        struct pci_serial_quirk *quirk;
        struct uart_port serial_port;
        int rc, nr_ports, i;
                       
        if (ent->driver_data >= ARRAY_SIZE(xrpciserial_boards)) {
                printk(KERN_INFO "pci_init_one: invalid driver_data: %ld\n",
                        ent->driver_data);
                return -EINVAL;
        }

        board = &xrpciserial_boards[ent->driver_data];

        rc = pci_enable_device(dev);
        if (rc)
                return rc;
       
        nr_ports = board->num_ports;

        /*
         * Find an init and setup quirks.
         */
        quirk = find_quirk(dev);

        /*
         * Run the new-style initialization function.
         * The initialization function returns:
         *  <0  - error
         *   0  - use board->num_ports
         *  >0  - number of ports
         */
        if (quirk->init) {
                rc = quirk->init(dev);
                if (rc < 0)
                        goto disable;
                if (rc)
                        nr_ports = rc;
        }

        priv = kmalloc(sizeof(struct serial_private) +
                       sizeof(unsigned int) * nr_ports,
                       GFP_KERNEL);
        if (!priv) {
                rc = -ENOMEM;
                goto deinit;
        }

        memset(priv, 0, sizeof(struct serial_private) +
                        sizeof(unsigned int) * nr_ports);

        priv->dev = dev;
        priv->quirk = quirk;

        memset(&serial_port, 0, sizeof(struct uart_port));
        serial_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
        serial_port.uartclk = board->base_baud * 16;
        serial_port.irq = dev->irq;
        serial_port.dev = &dev->dev;
        for (i = 0; i < nr_ports; i++) {
                if (quirk->setup(priv, board, &serial_port, i))
                        break;

                priv->line[i] = serialxr_register_port(&serial_port);
                if (priv->line[i] < 0) {
                        printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]);
                        break;
                }
        }

        priv->nr = i;

        if (!IS_ERR(priv)) {
                pci_set_drvdata(dev, priv);
                return 0;
        }

deinit:
        if (quirk->exit)
                quirk->exit(dev);
disable:
        pci_disable_device(dev);
        return rc;
}

/*
*        serialxr_unregister_port - remove a serial port at runtime
*        @line: serial line number
*
*        Remove one serial port.  This may not be called from interrupt
*        context.  We hand the port back to the our control.
*/
void serialxr_unregister_port(int line)
{
        struct uart_xr_port *uart = &serialxr_ports[line];

        mutex_lock(&serial_mutex);       
        uart_remove_one_port(&xr_uart_driver, &uart->port);
        uart->port.dev = NULL;       
        mutex_unlock(&serial_mutex);
}

void pciserial_remove_ports(struct serial_private *priv)
{
        struct pci_serial_quirk *quirk;
        int i;

        for (i = 0; i < priv->nr; i++)
                serialxr_unregister_port(priv->line[i]);

        for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) {
                if (priv->remapped_bar[i])
                        iounmap(priv->remapped_bar[i]);
                priv->remapped_bar[i] = NULL;
        }

        /*
         * Find the exit quirks.
         */
        quirk = find_quirk(priv->dev);
        if (quirk->exit)
                quirk->exit(priv->dev);

        kfree(priv);
}

static void __devexit remove_one_xrpciserialcard(struct pci_dev *dev)
{
        struct serial_private *priv = pci_get_drvdata(dev);

        pci_set_drvdata(dev, NULL);

        pciserial_remove_ports(priv);

        pci_disable_device(dev);
}


static struct pci_device_id xrserial_pci_tbl[] = {
        {        0x13a8, 0x158,
                PCI_ANY_ID, PCI_ANY_ID,
                0, 0, xr_8port },
        {        0x13a8, 0x154,
                PCI_ANY_ID, PCI_ANY_ID,
                0, 0, xr_4port },
        {        0x13a8, 0x152,
                PCI_ANY_ID, PCI_ANY_ID,
                0, 0, xr_2port },
        { 0, }
};

static struct pci_driver xrserial_pci_driver = {
        .name                = "xrserial",
        .probe                = init_one_xrpciserialcard,
        .remove                = __devexit_p(remove_one_xrpciserialcard),
        .id_table        = xrserial_pci_tbl,
};


static int __init serialxr_init(void)
{
        int ret, i;

        printk(KERN_INFO "Exar PCI 17c15x serial driver Revision: 4.7\n";

        for (i = 0; i < NR_IRQS; i++)
                spin_lock_init(&irq_lists[i].lock);

        ret = uart_register_driver(&xr_uart_driver);
        if (ret)
                return ret;

        ret = pci_register_driver(&xrserial_pci_driver);

        if (ret < 0)
                uart_unregister_driver(&xr_uart_driver);

        return ret;
}

static void __exit serialxr_exit(void)
{
        pci_unregister_driver(&xrserial_pci_driver);
        uart_unregister_driver(&xr_uart_driver);
}

module_init(serialxr_init);
module_exit(serialxr_exit);

MODULE_LICENSE("GPL";
MODULE_DESCRIPTION("Exar PCI specific serial driver for XR17c15x- Revision: 4.7";

论坛徽章:
0
发表于 2015-04-18 12:41 |显示全部楼层
回复 1# pezhang
没人回复啊,自己顶一下,求指教。

   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP