串口驱动程序的编写总结(二)

功能实现:

     通过虚拟多个串口,实现用户层与驱动层数据的回环测试

linux驱动有个特点:

    结构体定义都是在底层驱动程序所定义好的。

     通过container of()函数查找到被包含结构体的首地址。

    就比如结构体:

    底层  struct uart_8250_port canserial_ports[4];

           其结构体如下:

        

struct uart_8250_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 */
    bool            fifo_bug;    /* min RX trigger if enabled */
    unsigned int        tx_loadsz;    /* transmit fifo load size */
    unsigned char        acr;
    unsigned char        fcr;
    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        cur_iotype;    /* Running I/O type */
    unsigned int        rpm_tx_active;

    /*
     * Some bits in registers are cleared on a read, so they must
     * be saved whenever the register is read but the bits will not
     * be immediately processed.
     */
#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
    unsigned char        lsr_saved_flags;
#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
    unsigned char        msr_saved_flags;

    struct uart_8250_dma    *dma;
    struct serial_rs485     rs485;

    /* 8250 specific callbacks */
    int            (*dl_read)(struct uart_8250_port *);
    void            (*dl_write)(struct uart_8250_port *, int);
    int            (*rs485_config)(struct uart_8250_port *,
                        struct serial_rs485 *rs485);
};

     其结构体包含上层结构体struct uart_port。

     

2、

platform 虚拟总线的挂载方法:

   platform_device_alloc()、platform_device_add()、platform_driver_register()这三个函数

  两个结构体:

  

struct platform_device *canserial_isa_devs、
  

static struct platform_driver canserial_isa_driver = {
.probe    = canserial_probe,
.remove    = canserial_remove,
.suspend    = canserial_suspend,
.resume    = canserial_resume,
.driver    = {
.name    = "canserial8250",
.owner    = THIS_MODULE,
},
};

通过.name=“canserial8250”进行设备与驱动的匹配。

通过 canserial_register_ports(&canserial_reg, &canserial_isa_devs->dev);函数将platform device 与 uart_driver 做挂钩。

参看platform设备驱动的注册:http://blog.csdn.net/lidaqiang99/article/details/6602647

通过 uart_register_driver(&canserial_reg); 将底层驱动注册上。

serial_can_init()

    ->canserial_isa_init_ports()  串口初始化及绑定底层串口操作函数

   

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/tty.h>
#include <linux/ratelimit.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/serial_8250.h>
#include <linux/nmi.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/pm_runtime.h>

#include <asm/io.h>

#include <asm/serial.h>

#include "mycan_drive.h"



#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
#define PORT_8250 1

#define S3C2410_LCON_PNONE      (0x0)
#define S3C2410_LCON_PEVEN      (0x5 << 3)
#define S3C2410_LCON_PODD      (0x4 << 3)
#define UART_LCR_WLEN5  0x00    /* Wordlength: 5 bits */
#define UART_LCR_WLEN6  0x01    /* Wordlength: 6 bits */
#define UART_LCR_WLEN7  0x02    /* Wordlength: 7 bits */
#define UART_LCR_WLEN8  0x03    /* Wordlength: 8 bits */
#define UART_LCR_EPAR    0x10    /* Even parity select */
#define UART_LCR_SPAR    0x20    /* Stick parity (?) */
#define UART_LCR_STOP    0x04    /* Stop bits: 0=1 stop bit, 1= 2 stop bits */
#define UART_LCR_PARITY    0x08    /* Parity Enable */


#include <linux/netdevice.h>
#include <linux/can.h>

// {    X:jundasvnS1606_KernelV4	runksourcelinux-3.18includeuapilinuxcan.h

static DEFINE_MUTEX(serial_mutex);
/*
 * This "device" covers _all_ ISA 8250-compatible serial devices listed
 * in the table in include/asm/serial.h
 */
static struct platform_device *canserial_isa_devs;

static int cnt_flag = 0;

// #ifndef SERIAL_PORT_DFNS
// #define SERIAL_PORT_DFNS
// #endif
// static const struct old_serial_port old_serial_port[] = {
//     SERIAL_PORT_DFNS /* defined in asm/serial.h */
// };



// static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;
// #define UART_NR    CONFIG_SERIAL_8250_NR_UARTS
static unsigned int nr_uarts = 15;
#define UART_NR    15

static struct uart_driver canserial_reg;


extern struct sja1000_priv m_ppriv;



// can 发送数据格式
struct can_frame cf;
/*
 * 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


#include <asm/serial.h>

struct can_serial_port {
    unsigned char            rx_claimed;
    unsigned char            tx_claimed;

    // struct s3c24xx_uart_info    *info;
    // struct s3c24xx_uart_clksrc    *clksrc;
    struct clk            *clk;
    struct clk            *baudclk;
    struct uart_port        port;
};


// 参数: 串口地址、功能码、读写位、版本号
uint32_t GetCanAddrMask(uint32_t PortComID,uint8_t ngnvlaue,uint8_t nrwstatus,uint8_t nrver)
{
    uint32_t u32ID=0;
    uint8_t naddr=0,nstrport=0,nrw=0,ngn=0,data_group=0;
    if(PortComID<5){
        naddr=1;    
    }
    else if(PortComID<10){
        naddr=2;    
    }
    else if(PortComID<15){
        naddr=3;    
    }
    nstrport=PortComID%5;//子地址 端口号 0~4
    nrw=nrwstatus;//读写
    ngn=ngnvlaue;//功能码
    u32ID |=(nrver<<24);//版本号
    u32ID |=(data_group<<16); // 数据组
    u32ID |=(ngn<<8);//功能码
    u32ID |=(nrw<<7);// 读写位
    u32ID |=(nstrport<<4); //子地址
    u32ID |=naddr; //设备地址

    
    return u32ID;

}



//额外的函数
static inline struct can_serial_port *to_ourport(struct uart_port *port)
{
    return container_of(port, struct can_serial_port, port);
}




 /**************************************************************/

static void canserial_stop_tx(struct uart_port *port)
{
    printk("canserial_stop_tx
");
}

static void canserial_start_tx(struct uart_port *port)
{

    unsigned char ch;

    printk("canserial_start_tx line:%d
",port->line);

    //printk("the base_driver receive data  is %s
",port->state->xmit.buf);

    struct circ_buf *xmit = &port->state->xmit;

    // 进行数据的打包
    // 设备地址4bit、子地址(串口号地址)3、读写1、功能码8、数据组8、版本4
     
     /*
        参数: 串口地址、功能码、读写位、版本号
      */
    int m_can_id = GetCanAddrMask(port->line,0,0,1);


    // 在这里调用can驱动程序进行数据发送
     // cf.can_id = CAN_EFF_FLAG | 0x00000123; 
     cf.can_id = CAN_EFF_FLAG | m_can_id; 
     int tx_cnt_port=0;
      while(!uart_circ_empty(xmit))
     {
        ch= xmit->buf[xmit->tail];
        cf.data[tx_cnt_port] =  ch;
        tx_cnt_port++;
        xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
        port->icount.rx++;
     }

     cf.can_dlc = tx_cnt_port;

    mysja1000_start_xmit(&cf, &m_ppriv);












    uart_circ_clear(&port->state->xmit);

}

static void canserial_throttle(struct uart_port *port)
{
    printk("canserial_throttle
");
    
}

static void canserial_unthrottle(struct uart_port *port)
{
    printk("canserial_unthrottle
");
}

static void canserial_stop_rx(struct uart_port *port)
{
    printk("canserial_stop_rx
");
    
}

static void canserial_enable_ms(struct uart_port *port)
{
    printk("canserial_enable_ms
");

}

/*
 * canserial_rx_chars: processes according to the passed in LSR
 * value, and returns the remaining LSR bits not handled
 * by this Rx routine.
 */
unsigned char
canserial_rx_chars(struct uart_8250_port *up, unsigned char lsr)
{
    printk("canserial_rx_chars
");

 //    spin_unlock(&port->lock);
    // tty_flip_buffer_push(&port->state->port);
    // spin_lock(&port->lock);
    return 0;
}

void canserial_tx_chars(struct uart_8250_port *up)
{
    printk("canserial_tx_chars
");
    
}

static unsigned int canserial_tx_empty(struct uart_port *port)
{
    printk("canserial_tx_empty
");

    return 0;
}

static unsigned int canserial_get_mctrl(struct uart_port *port)
{

    printk("canserial_get_mctrl
");

    return 0;
}

static void canserial_set_mctrl(struct uart_port *port, unsigned int mctrl)
{

    printk("canserial_set_mctrl
");

}

static void canserial_break_ctl(struct uart_port *port, int break_state)
{
    printk("canserial_break_ctl
");

}

/*
 *    Wait for transmitter & holding register to empty
 */
static void wait_for_xmitr(struct uart_8250_port *up, int bits)
{
    printk("wait_for_xmitr
");

}



static int canserial_startup(struct uart_port *port)
{
    printk("open first canserial_startup
");

 
   if(cnt_flag==0)
    {
        mytscan1_probe(&m_ppriv);
        mysja1000_set_bittiming(&m_ppriv);
        mysja1000_open(&m_ppriv);
    }
    
    cnt_flag++;

    // mysja1000_set_bittiming(&m_ppriv);
    return 0;
}

void canserial_do_shutdown(struct uart_port *port)
{
    

    printk("canserial_do_shutdown
");
}


static void canserial_shutdown(struct uart_port *port)
{

    cnt_flag--;
    if(cnt_flag == 0)
       mysja1000_close(&m_ppriv);

    printk("canserial_shutdown
");
}

static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud)
{
    printk("serial8250_get_divisor
");
}




static void
canserial_set_termios(struct uart_port *port, struct ktermios *termios,
               struct ktermios *old)
{

 //    struct net_device netdev;
 //    netdev.name = "can0";
 //    // netdev.ifindex = 


 
 //    struct can_frame can_data;
 //    can_data.can_id =  CAN_EFF_FLAG | 0x11;
 //    can_data.can_dlc = 1;
 //    can_data.data[0] = "Y";


 //    sk_buff_data.data = &can_data;
    // sja1000_start_xmit(&sk_buff_data,&netdev);


    printk("canserial_set_termios
");
    unsigned int baud, quot;
    unsigned char cval;
    unsigned int bitval;

   baud = uart_get_baud_rate(port, termios, old,0, 115200*8);

   
   // printk("termios->c_cflag :%d
",termios->c_cflag);
   // printk("termios->c_ispeed :%d
",termios->c_ispeed);
   // printk("termios->c_ospeed :%d
",termios->c_ospeed);
   // printk("termios->CBAUD :%d
",termios->c_cflag & CBAUD);


     switch (termios->c_cflag & CSIZE) {
    case CS5:
        cval = UART_LCR_WLEN5;
        break;
    case CS6:
        cval = UART_LCR_WLEN6;
        break;
    case CS7:
        cval = UART_LCR_WLEN7;
        break;
    default:
    case CS8:
        cval = UART_LCR_WLEN8;
        break;
    }
    // printk("serial8250_do_set_termios databit:%d
",cval);


    if (termios->c_cflag & CSTOPB)
        cval |= UART_LCR_STOP;
    
    // printk("serial8250_do_set_termios stopbit:%d
",cval);


    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

    // printk("serial8250_do_set_termios chkbit:%d
",cval);



   // printk("port baud is %d
",baud);
}

static void
canserial_set_ldisc(struct uart_port *port, int new)
{
    printk("canserial_set_ldisc
");
}


void canserial_do_pm(struct uart_port *port, unsigned int state,
              unsigned int oldstate)
{    
    printk("canserial_do_pm
");
}

static void
canserial_pm(struct uart_port *port, unsigned int state,
          unsigned int oldstate)
{

    printk("canserial_pm
");
    // canserial_do_pm();
    // struct can_serial_port *ourport =  to_ourport(port);
     // pm_runtime_get_sync(port->dev);

     // printk("pm_runtime_get_sync after
");
}

static unsigned int canserial_port_size(struct uart_8250_port *pt)
{

    printk("canserial_port_size
");

    
}

/*
 * Resource handling.
 */
static int canserial_request_std_resource(struct uart_8250_port *up)
{
    printk("canserial_request_std_resource
");
    return 0;
}

static void canserial_release_std_resource(struct uart_8250_port *up)
{
    printk("canserial_release_std_resource
");
}

static int canserial_request_rsa_resource(struct uart_8250_port *up)
{ 
    printk("canserial_request_rsa_resource
");
    return 0;
}

static void canserial_release_rsa_resource(struct uart_8250_port *up)
{
    printk("canserial_release_rsa_resource
");
}

static void canserial_release_port(struct uart_port *port)
{

    printk("canserial_release_port
");


}

static int canserial_request_port(struct uart_port *port)
{
    printk("canserial_request_port
");
    return 0;
}

static int fcr_get_rxtrig_bytes(struct uart_8250_port *up)
{
    printk("fcr_get_rxtrig_bytes
");
    return 0;
}

static int bytes_to_fcr_rxtrig(struct uart_8250_port *up, unsigned char bytes)
{
    printk("bytes_to_fcr_rxtrig
");
    return 0;
}

static int do_get_rxtrig(struct tty_port *port)
{
    printk("do_get_rxtrig
");
    return 0;
}

static int do_canserial_get_rxtrig(struct tty_port *port)
{
    printk("do_canserial_get_rxtrig
");
    return 0;
}

static ssize_t canserial_get_attr_rx_trig_bytes(struct device *dev,
    struct device_attribute *attr, char *buf)
{
    
    printk("canserial_get_attr_rx_trig_bytes
");
    return 0;
    // return snprintf(buf, PAGE_SIZE, "%d
", rxtrig_bytes);
}

static int do_set_rxtrig(struct tty_port *port, unsigned char bytes)
{
    printk("do_set_rxtrig
");
    return 0;
}

static int do_canserial_set_rxtrig(struct tty_port *port, unsigned char bytes)
{
    printk("do_canserial_set_rxtrig
");
    return 0;
}

static ssize_t canserial_set_attr_rx_trig_bytes(struct device *dev,
    struct device_attribute *attr, const char *buf, size_t count)
{
    printk("canserial_set_attr_rx_trig_bytes
");
    return 0;
}




static void register_dev_spec_attr_grp(struct uart_8250_port *up)
{
    printk("register_dev_spec_attr_grp
");
}

static void canserial_config_port(struct uart_port *port, int flags)
{
    printk("canserial_config_port
");

}

static int
canserial_verify_port(struct uart_port *port, struct serial_struct *ser)
{
    printk("canserial_verify_port
");
    return 0;
}

static int canserial_ioctl(struct uart_port *port, unsigned int cmd,
               unsigned long arg)
{
    // printk("canserial_ioctl cmd is %d
 ",cmd);
    // printk("canserial_ioctl
");

    // return 0;


    printk("canserial_ioctl cmd is %d
 ",cmd);
    
    struct uart_8250_port *up =
        container_of(port, struct uart_8250_port, port);
    int ret;
    struct serial_rs485 rs485_config;

    if (!up->rs485_config)
        return -ENOIOCTLCMD;

    switch (cmd) {
    case TIOCSRS485:
        if (copy_from_user(&rs485_config, (void __user *)arg,
                   sizeof(rs485_config)))
            return -EFAULT;

        ret = up->rs485_config(up, &rs485_config);
        if (ret)
            return ret;

        memcpy(&up->rs485, &rs485_config, sizeof(rs485_config));

        return 0;
    case TIOCGRS485:
        if (copy_to_user((void __user *)arg, &up->rs485,
                 sizeof(up->rs485)))
            return -EFAULT;
        return 0;
    default:
        break;
    }

    return -ENOIOCTLCMD;



}

static const char *
canserial_type(struct uart_port *port)
{

}

static struct uart_ops canserial_pops = {
    .tx_empty    = canserial_tx_empty,
    .set_mctrl    = canserial_set_mctrl,
    .get_mctrl    = canserial_get_mctrl,
    .stop_tx    = canserial_stop_tx,
    .start_tx    = canserial_start_tx,
    .throttle    = canserial_throttle,
    .unthrottle    = canserial_unthrottle,
    .stop_rx    = canserial_stop_rx,
    .enable_ms    = canserial_enable_ms,
    .break_ctl    = canserial_break_ctl,
    .startup    = canserial_startup,
    .shutdown    = canserial_shutdown,
    .set_termios    = canserial_set_termios,
    .set_ldisc    = canserial_set_ldisc,
    .pm        = canserial_pm,
    .type        = canserial_type,
    .release_port    = canserial_release_port,
    .request_port    = canserial_request_port,
    .config_port    = canserial_config_port,
    .verify_port    = canserial_verify_port,
    .ioctl        = canserial_ioctl,
};

struct uart_8250_port canserial_ports[UART_NR];

/**
 * canserial_get_port - retrieve struct uart_8250_port
 * @line: serial line number
 *
 * This function retrieves struct uart_8250_port for the specific line.
 * This struct *must* *not* be used to perform a 8250 or serial core operation
 * which is not accessible otherwise. Its only purpose is to make the struct
 * accessible to the runtime-pm callbacks for context suspend/restore.
 * The lock assumption made here is none because runtime-pm suspend/resume
 * callbacks should not be invoked if there is any operation performed on the
 * port.
 */
struct uart_8250_port *canserial_get_port(int line)
{
    return &canserial_ports[line];
}

static void (*canserial_isa_config)(int port, struct uart_port *up,
    unsigned short *capabilities);

void canserial_set_isa_configurator(
    void (*v)(int port, struct uart_port *up, unsigned short *capabilities))
{
    canserial_isa_config = v;
}
//去除硬件信息
static void __init canserial_isa_init_ports(void)
{
    struct uart_8250_port *up;
    int i, irqflag = 0;


    if (nr_uarts > UART_NR)
        nr_uarts = UART_NR;

    for (i = 0; i < nr_uarts; i++) {
        struct uart_8250_port *up = &canserial_ports[i];
        struct uart_port *port = &up->port;
        port->line = i;
        port->ops = &canserial_pops;
    }



      for (i = 0, up = canserial_ports;i < nr_uarts;
         i++, up++) {
          printk("enter serial initialize
");
        struct uart_port *port = &up->port;

        // port->iobase   = old_serial_port[i].port;
        // port->irq      = 4;
        // port->irqflags = old_serial_port[i].irqflags;
        port->uartclk  = 1843200 ;
        port->flags    = STD_COM_FLAGS;
        port->type = PORT_8250_CIR;
        // port->hub6     = old_serial_port[i].hub6;
        // port->membase  = old_serial_port[i].iomem_base;
        // port->iotype   = old_serial_port[i].io_type;
        // port->regshift = old_serial_port[i].iomem_reg_shift;
        // set_io_from_upio(port);
        // port->irqflags |= irqflag;
        // if (serial8250_isa_config != NULL)
        //     serial8250_isa_config(i, &up->port, &up->capabilities);

    }


    
}

static void
canserial_init_fixed_type_port(struct uart_8250_port *up, unsigned int type)
{
    printk("canserial_init_fixed_type_port
");
}

static void __init
canserial_register_ports(struct uart_driver *drv, struct device *dev)
{
    int i;
    int ret;
    for (i = 0; i < nr_uarts; i++) {
        struct uart_8250_port *up = &canserial_ports[i];

        if (up->port.dev)
            continue;

        up->port.dev = dev;

        printk("uart add one port before
");
        ret = uart_add_one_port(drv, &up->port);
        printk("uart_add_one_port result %d
",ret);
    }
}


static struct uart_driver canserial_reg = {
    .owner            = THIS_MODULE,
    .driver_name        = "ttySAC",
    .dev_name        = "ttyS100",
    .major            = 5,
    .minor            = 64,
    .cons            = NULL,
};


/**
 *    canserial_suspend_port - suspend one serial port
 *    @line:  serial line number
 *
 *    Suspend one serial port.
 */
void canserial_suspend_port(int line)
{
    uart_suspend_port(&canserial_reg, &canserial_ports[line].port);
}

/**
 *    canserial_resume_port - resume one serial port
 *    @line:  serial line number
 *
 *    Resume one serial port.
 */
void canserial_resume_port(int line)
{
    printk("canserial_resume_port
");
}

/*
 * Register a set of serial devices attached to a platform device.  The
 * list is terminated with a zero flags entry, which means we expect
 * all entries to have at least UPF_BOOT_AUTOCONF set.
 */
static int canserial_probe(struct platform_device *dev)
{
    printk("Driver found device which my driver can handle!
");

    // struct uart_8250_port uart;
 //    for (int i = 0; i < 4; ++i)
 //    {
 //        uart.port.type = PORT_8250;
 //        ret = canserial_register_8250_port(&uart);
 //    }

    return 0;
}

/**
 *    canserial_register_8250_port - register a serial port
 *    @up: 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 canserial_register_8250_port(struct uart_8250_port *up)
{
    // struct uart_8250_port *uart;
    // int ret = -ENOSPC;

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

    // mutex_lock(&serial_mutex);

    // // uart = serial8250_find_match_or_unused(&up->port);
    

    // mutex_unlock(&serial_mutex);

    // return ret;
}


void canserial_unregister_port(int line)
{
    struct uart_8250_port *uart = &canserial_ports[line];

    mutex_lock(&serial_mutex);
    uart_remove_one_port(&canserial_reg, &uart->port);
    if (canserial_isa_devs) {
        // uart->port.flags &= ~UPF_BOOT_AUTOCONF;
        // uart->port.type = PORT_UNKNOWN;
        // uart->port.dev = &canserial_isa_devs->dev;
        // uart->capabilities = uart_config[uart->port.type].flags;
        uart_add_one_port(&canserial_reg, &uart->port);
    } else {
        uart->port.dev = NULL;
    }
    mutex_unlock(&serial_mutex);
}



/*
 * Remove serial ports registered against a platform device.
 */
static int canserial_remove(struct platform_device *dev)
{
    printk("Driver found device unpluged!
");



    int i;
    for (i = 0; i < nr_uarts; i++) {
        struct uart_8250_port *up = &canserial_ports[i];

        if (up->port.dev == &dev->dev)
            canserial_unregister_port(i);
    }

    return 0;
}




static int canserial_suspend(struct platform_device *dev, pm_message_t state)
{
    return 0;
}

static int canserial_resume(struct platform_device *dev)
{

    return 0;
}

static struct platform_driver canserial_isa_driver = {
    .probe        = canserial_probe,
    .remove        = canserial_remove,
    .suspend    = canserial_suspend,
    .resume        = canserial_resume,
    .driver        = {
        .name    = "canserial8250",
        .owner    = THIS_MODULE,
    },
};



/*
 * canserial_register_8250_port and canserial_unregister_port allows for
 * 16x50 serial ports to be configured at run-time, to support PCMCIA
 * modems and PCI multiport cards.
 */

static struct uart_8250_port *canserial_find_match_or_unused(struct uart_port *port)
{
    printk("canserial_find_match_or_unused
");
}


static int __init serial_can_init(void)
{
    int ret;

    canserial_isa_init_ports();

    canserial_reg.nr = UART_NR;
    ret = uart_register_driver(&canserial_reg);
    printk("uart_register_driver
");

    printk("this ret:%d
",ret);
    if (ret)
        goto out;

    /*platform device *canserial_isa_devs */
    canserial_isa_devs = platform_device_alloc("canserial8250",-1);
    if (!canserial_isa_devs) {
        ret = -ENOMEM;
    }
    printk("platform_device_alloc success
");
    ret = platform_device_add(canserial_isa_devs);
    if (ret)
        goto put_dev;

    printk("platform_device_add success
");
    /*uart_driver canserial_reg*/
    canserial_register_ports(&canserial_reg, &canserial_isa_devs->dev);

    ret = platform_driver_register(&canserial_isa_driver);
    if (ret == 0)
        goto out;

put_dev:
    platform_device_del(canserial_isa_devs);
    platform_device_put(canserial_isa_devs);
unreg_uart_drv:
    uart_unregister_driver(&canserial_reg);

out:
    return ret;
}

static void __exit serial_can_exit(void)
{
    struct platform_device *isa_dev = canserial_isa_devs;

    /*
     * This tells canserial_unregister_port() not to re-register
     * the ports (thereby making canserial_isa_driver permanently
     * in use.)
     */
    canserial_isa_devs = NULL;

    platform_driver_unregister(&canserial_isa_driver);
    platform_device_unregister(isa_dev);

    // serial8250_pnp_exit();

    uart_unregister_driver(&canserial_reg);





}

module_init(serial_can_init);
module_exit(serial_can_exit);


MODULE_LICENSE("Dual BSD/GPL");