zoukankan      html  css  js  c++  java
  • tiny4412 串口驱动分析五 --- LDD3上TTY驱动程序源码

    关于tty这部分请参考:

    《Linux设备驱动开发详解 第二版》第14章 Linux终端设备驱动

    《精通Linux设备驱动程序开发》第6章 串行设备驱动程序

    《Linux设备驱动程序 第三版》第18章 TTY驱动程序

     

    下面是一些串口相关的文档:

     http://pan.baidu.com/s/1mg20Umc

    Makefile:

    # Comment/uncomment the following line to disable/enable debugging
    #DEBUG = y
    
    
    # Add your debugging flag (or not) to CFLAGS
    ifeq ($(DEBUG),y)
      DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
    else
      DEBFLAGS = -O2
    endif
    
    EXTRA_CFLAGS += $(DEBFLAGS)
    EXTRA_CFLAGS += -I..
    
    ifneq ($(KERNELRELEASE),)
    # call from kernel build system
    
    obj-m    := tiny_tty.o tiny_serial.o
    
    else
    
    #KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    KERNELDIR ?= /root/Tiny4412_android_4_1_2/linux-3.0.31
    PWD       := $(shell pwd)
    
    default:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    
    endif
    
    clean:
        rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
    
    depend .depend dep:
        $(CC) $(EXTRA_CFLAGS) -M *.c > .depend
    
    
    ifeq (.depend,$(wildcard .depend))
    include .depend
    endif

    tiny_tty.c

    /*
     * Tiny TTY driver
     *
     * Copyright (C) 2002-2004 Greg Kroah-Hartman (greg@kroah.com)
     *
     *    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, version 2 of the License.
     *
     * This driver shows how to create a minimal tty driver.  It does not rely on
     * any backing hardware, but creates a timer that emulates data being received
     * from some kind of hardware.
     */
    
    #include <linux/kernel.h>
    #include <linux/errno.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/slab.h>
    #include <linux/wait.h>
    #include <linux/tty.h>
    #include <linux/tty_driver.h>
    #include <linux/tty_flip.h>
    #include <linux/serial.h>
    #include <linux/sched.h>
    #include <linux/seq_file.h>
    #include <asm/uaccess.h>
    
    
    #define DRIVER_VERSION "v2.0"
    #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
    #define DRIVER_DESC "Tiny TTY driver"
    
    /* Module information */
    MODULE_AUTHOR( DRIVER_AUTHOR );
    MODULE_DESCRIPTION( DRIVER_DESC );
    MODULE_LICENSE("GPL");
    
    #define DELAY_TIME        HZ * 2    /* 2 seconds per character */
    #define TINY_DATA_CHARACTER    't'
    
    #define TINY_TTY_MAJOR        240    /* experimental range */
    #define TINY_TTY_MINORS        4    /* only have 4 devices */
    
    struct tiny_serial {
        struct tty_struct    *tty;        /* pointer to the tty for this device */
        int            open_count;    /* number of times this port has been opened */
        struct semaphore    sem;        /* locks this structure */
        struct timer_list    *timer;
    
        /* for tiocmget and tiocmset functions */
        int            msr;        /* MSR shadow */
        int            mcr;        /* MCR shadow */
    
        /* for ioctl fun */
        struct serial_struct    serial;
        wait_queue_head_t    wait;
        struct async_icount    icount;
    };
    
    static struct tiny_serial *tiny_table[TINY_TTY_MINORS];    /* initially all NULL */
    
    
    static void tiny_timer(unsigned long timer_data)
    {
        struct tiny_serial *tiny = (struct tiny_serial *)timer_data;
        struct tty_struct *tty;
        int i;
        char data[1] = {TINY_DATA_CHARACTER};
        int data_size = 1;
    
        if (!tiny)
            return;
    
        tty = tiny->tty;
    
        /* send the data to the tty layer for users to read.  This doesn't
         * actually push the data through unless tty->low_latency is set */
        for (i = 0; i < data_size; ++i) {
            if (!tty_buffer_request_room(tty, 1))
                tty_flip_buffer_push(tty);
            tty_insert_flip_char(tty, data[i], TTY_NORMAL);
        }
        tty_flip_buffer_push(tty);
    
        /* resubmit the timer again */
        tiny->timer->expires = jiffies + DELAY_TIME;
        add_timer(tiny->timer);
    }
    
    static int tiny_open(struct tty_struct *tty, struct file *file)
    {
        struct tiny_serial *tiny;
        struct timer_list *timer;
        int index;
    
        /* initialize the pointer in case something fails */
        tty->driver_data = NULL;
    
        /* get the serial object associated with this tty pointer */
        index = tty->index;
        tiny = tiny_table[index];
        if (tiny == NULL) {
            /* first time accessing this device, let's create it */
            tiny = kmalloc(sizeof(*tiny), GFP_KERNEL);
            if (!tiny)
                return -ENOMEM;
    
            sema_init(&tiny->sem, 1);
            tiny->open_count = 0;
            tiny->timer = NULL;
    
            tiny_table[index] = tiny;
        }
    
        down(&tiny->sem);
    
        /* save our structure within the tty structure */
        tty->driver_data = tiny;
        tiny->tty = tty;
    
        ++tiny->open_count;
        if (tiny->open_count == 1) {
            /* this is the first time this port is opened */
            /* do any hardware initialization needed here */
    
            /* create our timer and submit it */
            if (!tiny->timer) {
                timer = kmalloc(sizeof(*timer), GFP_KERNEL);
                if (!timer) {
                    up(&tiny->sem);
                    return -ENOMEM;
                }
                tiny->timer = timer;
            }
            init_timer(tiny->timer);
            tiny->timer->data = (unsigned long )tiny;
            tiny->timer->expires = jiffies + DELAY_TIME;
            tiny->timer->function = tiny_timer;
            add_timer(tiny->timer);
        }
    
        up(&tiny->sem);
        return 0;
    }
    
    static void do_close(struct tiny_serial *tiny)
    {
        down(&tiny->sem);
    
        if (!tiny->open_count) {
            /* port was never opened */
            goto exit;
        }
    
        --tiny->open_count;
        if (tiny->open_count <= 0) {
            /* The port is being closed by the last user. */
            /* Do any hardware specific stuff here */
    
            /* shut down our timer */
            del_timer(tiny->timer);
        }
    exit:
        up(&tiny->sem);
    }
    
    static void tiny_close(struct tty_struct *tty, struct file *file)
    {
        struct tiny_serial *tiny = tty->driver_data;
    
        if (tiny)
            do_close(tiny);
    }    
    
    static int tiny_write(struct tty_struct *tty, 
                  const unsigned char *buffer, int count)
    {
        struct tiny_serial *tiny = tty->driver_data;
        int i;
        int retval = -EINVAL;
    
        if (!tiny)
            return -ENODEV;
    
        down(&tiny->sem);
    
        if (!tiny->open_count)
            /* port was not opened */
            goto exit;
    
        /* fake sending the data out a hardware port by
         * writing it to the kernel debug log.
         */
        printk(KERN_DEBUG "%s - ", __FUNCTION__);
        for (i = 0; i < count; ++i)
            printk("%02x ", buffer[i]);
        printk("
    ");
            
    exit:
        up(&tiny->sem);
        return retval;
    }
    
    static int tiny_write_room(struct tty_struct *tty) 
    {
        struct tiny_serial *tiny = tty->driver_data;
        int room = -EINVAL;
    
        if (!tiny)
            return -ENODEV;
    
        down(&tiny->sem);
        
        if (!tiny->open_count) {
            /* port was not opened */
            goto exit;
        }
    
        /* calculate how much room is left in the device */
        room = 255;
    
    exit:
        up(&tiny->sem);
        return room;
    }
    
    #define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
    
    static void tiny_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
    {
        unsigned int cflag;
    
        cflag = tty->termios->c_cflag;
    
        /* check that they really want us to change something */
        if (old_termios) {
            if ((cflag == old_termios->c_cflag) &&
                (RELEVANT_IFLAG(tty->termios->c_iflag) == 
                 RELEVANT_IFLAG(old_termios->c_iflag))) {
                printk(KERN_DEBUG " - nothing to change...
    ");
                return;
            }
        }
    
        /* get the byte size */
        switch (cflag & CSIZE) {
            case CS5:
                printk(KERN_DEBUG " - data bits = 5
    ");
                break;
            case CS6:
                printk(KERN_DEBUG " - data bits = 6
    ");
                break;
            case CS7:
                printk(KERN_DEBUG " - data bits = 7
    ");
                break;
            default:
            case CS8:
                printk(KERN_DEBUG " - data bits = 8
    ");
                break;
        }
        
        /* determine the parity */
        if (cflag & PARENB)
            if (cflag & PARODD)
                printk(KERN_DEBUG " - parity = odd
    ");
            else
                printk(KERN_DEBUG " - parity = even
    ");
        else
            printk(KERN_DEBUG " - parity = none
    ");
    
        /* figure out the stop bits requested */
        if (cflag & CSTOPB)
            printk(KERN_DEBUG " - stop bits = 2
    ");
        else
            printk(KERN_DEBUG " - stop bits = 1
    ");
    
        /* figure out the hardware flow control settings */
        if (cflag & CRTSCTS)
            printk(KERN_DEBUG " - RTS/CTS is enabled
    ");
        else
            printk(KERN_DEBUG " - RTS/CTS is disabled
    ");
        
        /* determine software flow control */
        /* if we are implementing XON/XOFF, set the start and 
         * stop character in the device */
        if (I_IXOFF(tty) || I_IXON(tty)) {
            unsigned char stop_char  = STOP_CHAR(tty);
            unsigned char start_char = START_CHAR(tty);
    
            /* if we are implementing INBOUND XON/XOFF */
            if (I_IXOFF(tty))
                printk(KERN_DEBUG " - INBOUND XON/XOFF is enabled, "
                    "XON = %2x, XOFF = %2x", start_char, stop_char);
            else
                printk(KERN_DEBUG" - INBOUND XON/XOFF is disabled");
    
            /* if we are implementing OUTBOUND XON/XOFF */
            if (I_IXON(tty))
                printk(KERN_DEBUG" - OUTBOUND XON/XOFF is enabled, "
                    "XON = %2x, XOFF = %2x", start_char, stop_char);
            else
                printk(KERN_DEBUG" - OUTBOUND XON/XOFF is disabled");
        }
    
        /* get the baud rate wanted */
        printk(KERN_DEBUG " - baud rate = %d", tty_get_baud_rate(tty));
    }
    
    /* Our fake UART values */
    #define MCR_DTR        0x01
    #define MCR_RTS        0x02
    #define MCR_LOOP    0x04
    #define MSR_CTS        0x08
    #define MSR_CD        0x10
    #define MSR_RI        0x20
    #define MSR_DSR        0x40
    
    static int tiny_tiocmget(struct tty_struct *tty)
    {
        struct tiny_serial *tiny = tty->driver_data;
    
        unsigned int result = 0;
        unsigned int msr = tiny->msr;
        unsigned int mcr = tiny->mcr;
    
        result = ((mcr & MCR_DTR)  ? TIOCM_DTR  : 0) |    /* DTR is set */
                 ((mcr & MCR_RTS)  ? TIOCM_RTS  : 0) |    /* RTS is set */
                 ((mcr & MCR_LOOP) ? TIOCM_LOOP : 0) |    /* LOOP is set */
                 ((msr & MSR_CTS)  ? TIOCM_CTS  : 0) |    /* CTS is set */
                 ((msr & MSR_CD)   ? TIOCM_CAR  : 0) |    /* Carrier detect is set*/
                 ((msr & MSR_RI)   ? TIOCM_RI   : 0) |    /* Ring Indicator is set */
                 ((msr & MSR_DSR)  ? TIOCM_DSR  : 0);    /* DSR is set */
    
        return result;
    }
    
    static int tiny_tiocmset(struct tty_struct *tty, unsigned int set,
                 unsigned int clear)
    {
        struct tiny_serial *tiny = tty->driver_data;
        unsigned int mcr = tiny->mcr;
    
        if (set & TIOCM_RTS)
            mcr |= MCR_RTS;
        if (set & TIOCM_DTR)
            mcr |= MCR_RTS;
    
        if (clear & TIOCM_RTS)
            mcr &= ~MCR_RTS;
        if (clear & TIOCM_DTR)
            mcr &= ~MCR_RTS;
    
        /* set the new MCR value in the device */
        tiny->mcr = mcr;
        return 0;
    }
    
    static int tiny_proc_show(struct seq_file *m, void *v)
    {
        struct tiny_serial *tiny;
        int i;
    
        seq_printf(m, "tinyserinfo:1.0 driver:%s
    ", DRIVER_VERSION);
        for (i = 0; i < TINY_TTY_MINORS; ++i) {
            tiny = tiny_table[i];
            if (tiny == NULL)
                continue;
    
            seq_printf(m, "%d
    ", i);
        }
    
        return 0;
    }
    
    #define tiny_ioctl tiny_ioctl_tiocgserial
    static int tiny_ioctl(struct tty_struct *tty, unsigned int cmd,
                  unsigned long arg)
    {
        struct tiny_serial *tiny = tty->driver_data;
    
        if (cmd == TIOCGSERIAL) {
            struct serial_struct tmp;
    
            if (!arg)
                return -EFAULT;
    
            memset(&tmp, 0, sizeof(tmp));
    
            tmp.type        = tiny->serial.type;
            tmp.line        = tiny->serial.line;
            tmp.port        = tiny->serial.port;
            tmp.irq            = tiny->serial.irq;
            tmp.flags        = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
            tmp.xmit_fifo_size    = tiny->serial.xmit_fifo_size;
            tmp.baud_base        = tiny->serial.baud_base;
            tmp.close_delay        = 5*HZ;
            tmp.closing_wait    = 30*HZ;
            tmp.custom_divisor    = tiny->serial.custom_divisor;
            tmp.hub6        = tiny->serial.hub6;
            tmp.io_type        = tiny->serial.io_type;
    
            if (copy_to_user((void __user *)arg, &tmp, sizeof(struct serial_struct)))
                return -EFAULT;
            return 0;
        }
        return -ENOIOCTLCMD;
    }
    #undef tiny_ioctl
    
    #define tiny_ioctl tiny_ioctl_tiocmiwait
    static int tiny_ioctl(struct tty_struct *tty, unsigned int cmd,
                  unsigned long arg)
    {
        struct tiny_serial *tiny = tty->driver_data;
    
        if (cmd == TIOCMIWAIT) {
            DECLARE_WAITQUEUE(wait, current);
            struct async_icount cnow;
            struct async_icount cprev;
    
            cprev = tiny->icount;
            while (1) {
                add_wait_queue(&tiny->wait, &wait);
                set_current_state(TASK_INTERRUPTIBLE);
                schedule();
                remove_wait_queue(&tiny->wait, &wait);
    
                /* see if a signal woke us up */
                if (signal_pending(current))
                    return -ERESTARTSYS;
    
                cnow = tiny->icount;
                if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
                    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
                    return -EIO; /* no change => error */
                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)) ) {
                    return 0;
                }
                cprev = cnow;
            }
    
        }
        return -ENOIOCTLCMD;
    }
    #undef tiny_ioctl
    
    #define tiny_ioctl tiny_ioctl_tiocgicount
    static int tiny_ioctl(struct tty_struct *tty, unsigned int cmd,
                  unsigned long arg)
    {
        struct tiny_serial *tiny = tty->driver_data;
    
        if (cmd == TIOCGICOUNT) {
            struct async_icount cnow = tiny->icount;
            struct serial_icounter_struct icount;
    
            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;
    
            if (copy_to_user((void __user *)arg, &icount, sizeof(icount)))
                return -EFAULT;
            return 0;
        }
        return -ENOIOCTLCMD;
    }
    #undef tiny_ioctl
    
    /* the real tiny_ioctl function.  The above is done to get the small functions in the book */
    static int tiny_ioctl(struct tty_struct *tty, unsigned int cmd,
                  unsigned long arg)
    {
        switch (cmd) {
        case TIOCGSERIAL:
            return tiny_ioctl_tiocgserial(tty, cmd, arg);
        case TIOCMIWAIT:
            return tiny_ioctl_tiocmiwait(tty, cmd, arg);
        case TIOCGICOUNT:
            return tiny_ioctl_tiocgicount(tty, cmd, arg);
        }
    
        return -ENOIOCTLCMD;
    }
    
    static int tiny_proc_open(struct inode *inode, struct file *file)
    {
            return single_open(file, tiny_proc_show, NULL);
    }
    
    
    static const struct file_operations serial_proc_fops = {
            .owner          = THIS_MODULE,
            .open           = tiny_proc_open,
        .read           = seq_read,
            .llseek         = seq_lseek,
            .release        = single_release,
    };
    
    static struct tty_operations serial_ops = {
        .open = tiny_open,
        .close = tiny_close,
        .write = tiny_write,
        .write_room = tiny_write_room,
        .set_termios = tiny_set_termios,
        .proc_fops    = &serial_proc_fops,
        .tiocmget = tiny_tiocmget,
        .tiocmset = tiny_tiocmset,
        .ioctl = tiny_ioctl,
    };
    
    static struct tty_driver *tiny_tty_driver;
    
    static int __init tiny_init(void)
    {
        int retval;
        int i;
    
        /* allocate the tty driver */
        tiny_tty_driver = alloc_tty_driver(TINY_TTY_MINORS);
        if (!tiny_tty_driver)
            return -ENOMEM;
    
        /* initialize the tty driver */
        tiny_tty_driver->owner = THIS_MODULE;
        tiny_tty_driver->driver_name = "tiny_tty";
        tiny_tty_driver->name = "ttty";
        tiny_tty_driver->major = TINY_TTY_MAJOR,
        tiny_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
        tiny_tty_driver->subtype = SERIAL_TYPE_NORMAL,
        tiny_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV,
        tiny_tty_driver->init_termios = tty_std_termios;
        tiny_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
        tty_set_operations(tiny_tty_driver, &serial_ops);
    
        /* register the tty driver */
        retval = tty_register_driver(tiny_tty_driver);
        if (retval) {
            printk(KERN_ERR "failed to register tiny tty driver");
            put_tty_driver(tiny_tty_driver);
            return retval;
        }
    
        for (i = 0; i < TINY_TTY_MINORS; ++i)
            tty_register_device(tiny_tty_driver, i, NULL);
    
        printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION);
        return retval;
    }
    
    static void __exit tiny_exit(void)
    {
        struct tiny_serial *tiny;
        int i;
    
        for (i = 0; i < TINY_TTY_MINORS; ++i)
            tty_unregister_device(tiny_tty_driver, i);
        tty_unregister_driver(tiny_tty_driver);
    
        /* shut down all of the timers and free the memory */
        for (i = 0; i < TINY_TTY_MINORS; ++i) {
            tiny = tiny_table[i];
            if (tiny) {
                /* close the port */
                while (tiny->open_count)
                    do_close(tiny);
    
                /* shut down our timer and free the memory */
                del_timer(tiny->timer);
                kfree(tiny->timer);
                kfree(tiny);
                tiny_table[i] = NULL;
            }
        }
    }
    
    module_init(tiny_init);
    module_exit(tiny_exit);

    tiny_serial.c

    /*
     * Tiny Serial driver
     *
     * Copyright (C) 2002-2004 Greg Kroah-Hartman (greg@kroah.com)
     *
     *    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, version 2 of the License.
     *
     * This driver shows how to create a minimal serial driver.  It does not rely on
     * any backing hardware, but creates a timer that emulates data being received
     * from some kind of hardware.
     */
    
    #include <linux/kernel.h>
    #include <linux/errno.h>
    #include <linux/init.h>
    #include <linux/slab.h>
    #include <linux/tty.h>
    #include <linux/tty_flip.h>
    #include <linux/serial.h>
    #include <linux/serial_core.h>
    #include <linux/module.h>
    
    
    #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
    #define DRIVER_DESC "Tiny serial driver"
    
    /* Module information */
    MODULE_AUTHOR( DRIVER_AUTHOR );
    MODULE_DESCRIPTION( DRIVER_DESC );
    MODULE_LICENSE("GPL");
    
    #define DELAY_TIME        HZ * 2    /* 2 seconds per character */
    #define TINY_DATA_CHARACTER    't'
    
    #define TINY_SERIAL_MAJOR    240    /* experimental range */
    #define TINY_SERIAL_MINORS    1    /* only have one minor */
    #define UART_NR            1    /* only use one port */
    
    #define TINY_SERIAL_NAME    "ttytiny"
    
    #define MY_NAME            TINY_SERIAL_NAME
    
    static struct timer_list *timer;
    
    static void tiny_stop_tx(struct uart_port *port)
    {
    }
    
    static void tiny_stop_rx(struct uart_port *port)
    {
    }
    
    static void tiny_enable_ms(struct uart_port *port)
    {
    }
    
    static void tiny_tx_chars(struct uart_port *port)
    {
        struct circ_buf *xmit = &port->state->xmit;
        int count;
    
        if (port->x_char) {
            pr_debug("wrote %2x", port->x_char);
            port->icount.tx++;
            port->x_char = 0;
            return;
        }
        if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
            tiny_stop_tx(port);
            return;
        }
    
        count = port->fifosize >> 1;
        do {
            pr_debug("wrote %2x", xmit->buf[xmit->tail]);
            xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
            port->icount.tx++;
            if (uart_circ_empty(xmit))
                break;
        } while (--count > 0);
    
        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
            uart_write_wakeup(port);
    
        if (uart_circ_empty(xmit))
            tiny_stop_tx(port);
    }
    
    static void tiny_start_tx(struct uart_port *port)
    {
    }
    
    static void tiny_timer(unsigned long data)
    {
        struct uart_port *port;
        struct tty_struct *tty;
    
    
        port = (struct uart_port *)data;
        if (!port)
            return;
        if (!port->state)
            return;
        tty = port->state->port.tty;
        if (!tty)
            return;
    
        /* add one character to the tty port */
        /* this doesn't actually push the data through unless tty->low_latency is set */
        tty_insert_flip_char(tty, TINY_DATA_CHARACTER, 0);
    
        tty_flip_buffer_push(tty);
    
        /* resubmit the timer again */
        timer->expires = jiffies + DELAY_TIME;
        add_timer(timer);
    
        /* see if we have any data to transmit */
        tiny_tx_chars(port);
    }
    
    static unsigned int tiny_tx_empty(struct uart_port *port)
    {
        return 0;
    }
    
    static unsigned int tiny_get_mctrl(struct uart_port *port)
    {
        return 0;
    }
    
    static void tiny_set_mctrl(struct uart_port *port, unsigned int mctrl)
    {
    }
    
    static void tiny_break_ctl(struct uart_port *port, int break_state)
    {
    }
    
    static void tiny_set_termios(struct uart_port *port,
                     struct ktermios *new, struct ktermios *old)
    {
        int baud, quot, cflag = new->c_cflag;
        /* get the byte size */
        switch (cflag & CSIZE) {
        case CS5:
            printk(KERN_DEBUG " - data bits = 5
    ");
            break;
        case CS6:
            printk(KERN_DEBUG " - data bits = 6
    ");
            break;
        case CS7:
            printk(KERN_DEBUG " - data bits = 7
    ");
            break;
        default: // CS8
            printk(KERN_DEBUG " - data bits = 8
    ");
            break;
        }
    
        /* determine the parity */
        if (cflag & PARENB)
            if (cflag & PARODD)
                pr_debug(" - parity = odd
    ");
            else
                pr_debug(" - parity = even
    ");
        else
            pr_debug(" - parity = none
    ");
    
        /* figure out the stop bits requested */
        if (cflag & CSTOPB)
            pr_debug(" - stop bits = 2
    ");
        else
            pr_debug(" - stop bits = 1
    ");
    
        /* figure out the flow control settings */
        if (cflag & CRTSCTS)
            pr_debug(" - RTS/CTS is enabled
    ");
        else
            pr_debug(" - RTS/CTS is disabled
    ");
    
        /* Set baud rate */
            baud = uart_get_baud_rate(port, new, old, 0, port->uartclk/16);
            quot = uart_get_divisor(port, baud);
        
        //UART_PUT_DIV_LO(port, (quot & 0xff));
        //UART_PUT_DIV_HI(port, ((quot & 0xf00) >> 8));
    }
    
    static int tiny_startup(struct uart_port *port)
    {
        /* this is the first time this port is opened */
        /* do any hardware initialization needed here */
    
        /* create our timer and submit it */
        if (!timer) {
            timer = kmalloc(sizeof(*timer), GFP_KERNEL);
            if (!timer)
                return -ENOMEM;
        }
        init_timer(timer);
        timer->data = (unsigned long)port;
        timer->expires = jiffies + DELAY_TIME;
        timer->function = tiny_timer;
        add_timer(timer);
        return 0;
    }
    
    static void tiny_shutdown(struct uart_port *port)
    {
        /* The port is being closed by the last user. */
        /* Do any hardware specific stuff here */
    
        /* shut down our timer */
        del_timer(timer);
    }
    
    static const char *tiny_type(struct uart_port *port)
    {
        return "tinytty";
    }
    
    static void tiny_release_port(struct uart_port *port)
    {
    
    }
    
    static int tiny_request_port(struct uart_port *port)
    {
        return 0;
    }
    
    static void tiny_config_port(struct uart_port *port, int flags)
    {
    }
    
    static int tiny_verify_port(struct uart_port *port, struct serial_struct *ser)
    {
        return 0;
    }
    
    static struct uart_ops tiny_ops = {
        .tx_empty    = tiny_tx_empty,
        .set_mctrl    = tiny_set_mctrl,
        .get_mctrl    = tiny_get_mctrl,
        .stop_tx    = tiny_stop_tx,
        .start_tx    = tiny_start_tx,
        .stop_rx    = tiny_stop_rx,
        .enable_ms    = tiny_enable_ms,
        .break_ctl    = tiny_break_ctl,
        .startup    = tiny_startup,
        .shutdown    = tiny_shutdown,
        .set_termios    = tiny_set_termios,
        .type        = tiny_type,
        .release_port    = tiny_release_port,
        .request_port    = tiny_request_port,
        .config_port    = tiny_config_port,
        .verify_port    = tiny_verify_port,
    };
    
    static struct uart_port tiny_port = {
        .ops        = &tiny_ops,
    };
    
    static struct uart_driver tiny_reg = {
        .owner        = THIS_MODULE,
        .driver_name    = TINY_SERIAL_NAME,
        .dev_name    = TINY_SERIAL_NAME,
        .major        = TINY_SERIAL_MAJOR,
        .minor        = TINY_SERIAL_MINORS,
        .nr        = UART_NR,
    };
    
    static int __init tiny_init(void)
    {
        int result;
    
        printk(KERN_INFO "Tiny serial driver loaded
    ");
    
        result = uart_register_driver(&tiny_reg);
        if (result)
            return result;
    
        result = uart_add_one_port(&tiny_reg, &tiny_port);
        if (result)
            uart_unregister_driver(&tiny_reg);
    
        return result;
    }
    
    module_init(tiny_init);
  • 相关阅读:
    【Luogu P5515】[MtOI2019]灵梦的计算器
    简易的命令行入门教程
    8个接私活的网站,只要你有码,那“我”就有钱
    单表查询必会13条
    「Django」rest_framework学习系列-路由
    Linux下将时间修改为当地时间(通过ntpdate服务)
    iptables拒绝发起大量请求(针对单IP地址)
    .webp格式win7无法显示缩略图
    永久修改/etc/proc下的项的配置文件
    通过vsftpd实现虚拟用户登录,不同的用户登录不同的目录,使用不同的权限
  • 原文地址:https://www.cnblogs.com/pengdonglin137/p/4321936.html
Copyright © 2011-2022 走看看