串口驱动程序的编写总结(二)
功能实现:
通过虚拟多个串口,实现用户层与驱动层数据的回环测试
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:\junda\svn\S1606_KernelV4\trunk\source\linux-3.18\include\uapi\linux\can.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\n"); } static void canserial_start_tx(struct uart_port *port) { unsigned char ch; printk("canserial_start_tx line:%d\n",port->line); //printk("the base_driver receive data is %s\n",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\n"); } static void canserial_unthrottle(struct uart_port *port) { printk("canserial_unthrottle\n"); } static void canserial_stop_rx(struct uart_port *port) { printk("canserial_stop_rx\n"); } static void canserial_enable_ms(struct uart_port *port) { printk("canserial_enable_ms\n"); } /* * 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\n"); // 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\n"); } static unsigned int canserial_tx_empty(struct uart_port *port) { printk("canserial_tx_empty\n"); return 0; } static unsigned int canserial_get_mctrl(struct uart_port *port) { printk("canserial_get_mctrl\n"); return 0; } static void canserial_set_mctrl(struct uart_port *port, unsigned int mctrl) { printk("canserial_set_mctrl\n"); } static void canserial_break_ctl(struct uart_port *port, int break_state) { printk("canserial_break_ctl\n"); } /* * Wait for transmitter & holding register to empty */ static void wait_for_xmitr(struct uart_8250_port *up, int bits) { printk("wait_for_xmitr\n"); } static int canserial_startup(struct uart_port *port) { printk("open first canserial_startup\n"); 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\n"); } static void canserial_shutdown(struct uart_port *port) { cnt_flag--; if(cnt_flag == 0) mysja1000_close(&m_ppriv); printk("canserial_shutdown\n"); } static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud) { printk("serial8250_get_divisor\n"); } 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\n"); 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\n",termios->c_cflag); // printk("termios->c_ispeed :%d\n",termios->c_ispeed); // printk("termios->c_ospeed :%d\n",termios->c_ospeed); // printk("termios->CBAUD :%d\n",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\n",cval); if (termios->c_cflag & CSTOPB) cval |= UART_LCR_STOP; // printk("serial8250_do_set_termios stopbit:%d\n",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\n",cval); // printk("port baud is %d\n",baud); } static void canserial_set_ldisc(struct uart_port *port, int new) { printk("canserial_set_ldisc\n"); } void canserial_do_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { printk("canserial_do_pm\n"); } static void canserial_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { printk("canserial_pm\n"); // canserial_do_pm(); // struct can_serial_port *ourport = to_ourport(port); // pm_runtime_get_sync(port->dev); // printk("pm_runtime_get_sync after\n"); } static unsigned int canserial_port_size(struct uart_8250_port *pt) { printk("canserial_port_size\n"); } /* * Resource handling. */ static int canserial_request_std_resource(struct uart_8250_port *up) { printk("canserial_request_std_resource\n"); return 0; } static void canserial_release_std_resource(struct uart_8250_port *up) { printk("canserial_release_std_resource\n"); } static int canserial_request_rsa_resource(struct uart_8250_port *up) { printk("canserial_request_rsa_resource\n"); return 0; } static void canserial_release_rsa_resource(struct uart_8250_port *up) { printk("canserial_release_rsa_resource\n"); } static void canserial_release_port(struct uart_port *port) { printk("canserial_release_port\n"); } static int canserial_request_port(struct uart_port *port) { printk("canserial_request_port\n"); return 0; } static int fcr_get_rxtrig_bytes(struct uart_8250_port *up) { printk("fcr_get_rxtrig_bytes\n"); return 0; } static int bytes_to_fcr_rxtrig(struct uart_8250_port *up, unsigned char bytes) { printk("bytes_to_fcr_rxtrig\n"); return 0; } static int do_get_rxtrig(struct tty_port *port) { printk("do_get_rxtrig\n"); return 0; } static int do_canserial_get_rxtrig(struct tty_port *port) { printk("do_canserial_get_rxtrig\n"); 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\n"); return 0; // return snprintf(buf, PAGE_SIZE, "%d\n", rxtrig_bytes); } static int do_set_rxtrig(struct tty_port *port, unsigned char bytes) { printk("do_set_rxtrig\n"); return 0; } static int do_canserial_set_rxtrig(struct tty_port *port, unsigned char bytes) { printk("do_canserial_set_rxtrig\n"); 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\n"); return 0; } static void register_dev_spec_attr_grp(struct uart_8250_port *up) { printk("register_dev_spec_attr_grp\n"); } static void canserial_config_port(struct uart_port *port, int flags) { printk("canserial_config_port\n"); } static int canserial_verify_port(struct uart_port *port, struct serial_struct *ser) { printk("canserial_verify_port\n"); return 0; } static int canserial_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) { // printk("canserial_ioctl cmd is %d\n ",cmd); // printk("canserial_ioctl\n"); // return 0; printk("canserial_ioctl cmd is %d\n ",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\n"); 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\n"); } 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\n"); ret = uart_add_one_port(drv, &up->port); printk("uart_add_one_port result %d\n",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\n"); } /* * 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!\n"); // 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!\n"); 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\n"); } 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\n"); printk("this ret:%d\n",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\n"); ret = platform_device_add(canserial_isa_devs); if (ret) goto put_dev; printk("platform_device_add success\n"); /*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");