ch438q驱动 for linux

  • 介绍
  • 使用说明

该驱动程序经过了长期的负载运行测试,所有串口同时全双工高速率(baudrate115200)下工作稳定。移植时需要根据平台速度,设置合适的读写速度,读写端口的速度宁慢勿快,这是ch438q能长期稳定工作的关键。linux tty设备在文件打开时,读写端口的瞬间,如果读写速度过快,会导致ch438q的访问时序建立失败,而无法正常通讯。该驱动没有使用8250的驱动框架,它们之间存在明显区别。也是担心修改了8250的代码导致其他类8250芯片无法正常工作。
制作硬件电路时,建议使用外置有源晶振。芯片内置的振荡电路配合无源晶振极不稳定,随时都可能停振。这个问题比较难查,希望使用该芯片的朋友引起重视。
如要看更新的代码请看这里,帖子就不同步了。
https://gitee.com/iamyhw/ch438q
硬件接口电路图:

头文件ch438.h:

#ifndef __CH438_H__
#define __CH438_H__/*
** hardware interface
** E17:CS, E16:RD, E15:WR
** E14~E08: Address
** E07~E00: Data
*/#define CH438_BASE  (SUNXI_PIO_PBASE+0x90) /* PE base */
/* ch438_membase = ioremap(CH438_BASE, GPIO_RANGE); */
#define CH438_CFG0  (ch438_membase+0x00) /* PE Configure Register 0, PE7~PE0 */
#define CH438_CFG1  (ch438_membase+0x04) /* PE Configure Register 1, PE15~PE8 */
#define CH438_CFG2  (ch438_membase+0x08) /* PE Configure Register 2, PE17~PE16 */
#define CH438_CFG3  (ch438_membase+0x0C)
#define CH438_DAT   (ch438_membase+0x10) /* PE Data Register, PE17~PE0 */
#define CH438_DRV0  (ch438_membase+0x14) /* PE Multi-Driving 0, PE15~PE0 */
#define CH438_DRV1  (ch438_membase+0x18) /* PE Multi-Driving 1, PE17~PE16 */
#define CH438_PULL0 (ch438_membase+0x1C) /* PE Pull 0, PE15~PE0 */
#define CH438_PULL1 (ch438_membase+0x20) /* PE Pull 1, PE17~PE16 */#define GPIO_RANGE  0x24#define CH438_CS    17
#define CH438_RD    16
#define CH438_WR    15struct ch438_pin {int rst,pint;int irqnum;
};struct ch438_port {struct uart_port port;char           name[16];unsigned char  id;unsigned char  acr;unsigned char  ier;unsigned char  lcr;unsigned char  mcr;unsigned char  fcr;unsigned char  dll;unsigned char  dlh;unsigned char  msr_saved_flags;unsigned int   lsr_break_flag;unsigned int io_num;struct ch438_pin *pin;
};#define CH438_MEM_BASE  SUNXI_GPIOE_BASE#define RBR_RO        0x00      /* Received Buffer Register */
#define THR_WO        0x00      /* Transmit Holding Register */
#define IER_RW        0x01      /* Interrupt Enable Register */
#define IIR_RO        0x02      /* Interrupt Identification Register */
#define FCR_WO        0x02      /* FIFO Control Register */
#define LCR_RW        0x03      /* Line Control Register */
#define MCR_RW        0x04      /* Modem Control Register */
#define LSR_RO        0x05      /* Line Status Register */
#define MSR_RO        0x06      /* Modem Status Register */
#define SCR_RW        0x07      /* Scratch Register */
#define DLL_RW        0x00      /* Divisor latch (LSB) */
#define DLH_RW        0x01      /* Divisor latch (MSB) *//* CH438 inside uart0~7 Status Register */#define SSR_RO        0x4F       /* all interrupt status *//* IER register bits */#define BIT_IER_RESET       BIT(7)      /* 1: reset */
#define BIT_IER_LOWPWR      BIT(6)      /* 1: close inside base clk */
#define BIT_IER_SLP         BIT(5)      /* 1: SLP[uart0] */
#define BIT_IER_CK2X        BIT(5)      /* 1: CK2X[uart1~7]*/
#define BIT_IER_MSI         BIT(3)      /* 1: enable MODEM status change interrupt */
#define BIT_IER_RLSI        BIT(2)      /* 1: enable receive line state interrupt */
#define BIT_IER_THRI        BIT(1)      /* 1: enable transmit is null interrupt */
#define BIT_IER_RDI         BIT(0)      /* 1: enable receive data interrupt *//* IIR Interrupt ID Register */
#define BIT_IIR_FIFOENS1    BIT(7)      /* FIFOs is used */
#define BIT_IIR_FIFOENS0    BIT(6)      /* FIFOs is used */
/* interrupt type:*/
#define BIT_IIR_IID3        BIT(3)
#define BIT_IIR_IID2        BIT(2)
#define BIT_IIR_IID1        BIT(1)
#define BIT_IIR_NOINT       BIT(0)
#define   BIT_IIR_IID_MASK      (BIT(3)|BIT(2)|BIT(1)|BIT(0))
#define   BIT_IIR_FEFLAG_MASK   (BIT(7)|BIT(6))/* Interrupt ID */
#define   INT_MODEM_CHANGE  0x00      /* MODEM input change */
#define   INT_NOINT         0x01      /* none interrupt */
#define   INT_THR_EMPTY     0x02      /* THR is empty */
#define   INT_RCV_SUCCESS   0x04      /* receive success */
#define   INT_RCV_LINES     0x06      /* receive line status */
#define   INT_RCV_OVERTIME  0x0C      /* receive overtime *//* FIFO Control Register */
/* Trigger Point: 00:1Byte, 01:16Byte, 10:64Byte, 11:112Byte */
#define BIT_FCR_RECVTG1     BIT(7)      /* Receiver TriGger 1 */
#define BIT_FCR_RECVTG0     BIT(6)      /* Receiver TriGger 0 */
#define   BIT_FCR_FT_112    (BIT(7)|BIT(6))
#define   BIT_FCR_FT_64     BIT(7)
#define   BIT_FCR_FT_16     BIT(6)
#define   BIT_FCR_FT_01     0x00#define BIT_FCR_TFIFORST    BIT(2)      /* Transmit FIFO Reset */
#define BIT_FCR_RFIFORST    BIT(1)      /* Receive FIFO Reset */
#define BIT_FCR_FIFOEN      BIT(0)      /* FIFO Enable *//* Line Control Register */
/* DLAB: 1:read/write DLL/DLH, 0:read/write RBR/THR/IER */
#define BIT_LCR_DLAB        BIT(7)
#define BIT_LCR_SBC         BIT(6)      /* 1:create BREAK *//* Parity Format: when PAREN=1, 00:ODD,01:EVEN,10:MARK,11:SPACE */
#define BIT_LCR_PARMODE1    BIT(5)      /* Set Parity Style */
#define BIT_LCR_PARMODE0    BIT(4)      /* Set parity Style */
#define   BIT_PARITY_MASK     (BIT(5)|BIT(4))    /* Parity Mask */
#define   BIT_LCR_EPAR        (1<<4)    /* Parity EVEN */
#define   BIT_LCR_OPAR        (0<<4)    /* Parity ODD */#define BIT_LCR_PARITY      BIT(3)      /* 1:enable Parity */
#define BIT_LCR_STOP        BIT(2)      /* 1:2StopBits, 0:1StopBits */
/* Set WrodLen: 00:5, 01:6, 10:7, 11:8 */
#define BIT_LCR_WORDSZ1     BIT(1)      /* Word Length Select Bit1 */
#define BIT_LCR_WORDSZ0     BIT(0)      /* Word Length Select Bit0 */
#define   BIT_LCR_WLEN_MASK   (BIT(1)|BIT(0))
#define   BIT_LCR_WLEN_8      0x03
#define   BIT_LCR_WLEN_7      0x02
#define   BIT_LCR_WLEN_6      0x01
#define   BIT_LCR_WLEN_5      0x00/* Modem Control Register */
#define BIT_MCR_AFE         BIT(5)      /* 1:enable CTS/RTS */
#define BIT_MCR_LOOP        BIT(4)      /* 1:enable Loop-back mode */
#define BIT_MCR_OUT2        BIT(3)      /* 1:enable irq hw output !!! */
#define BIT_MCR_OUT1        BIT(2)      /* user defined output bit */
#define BIT_MCR_RTS         BIT(1)      /* Reauest to Send */
#define BIT_MCR_DTR         BIT(0)      /* Data Terminal Ready *//* Line Status Register */
#define BIT_LSR_RXFIFOE     BIT(7)      /* Receive FIFO Error */
#define BIT_LSR_TEMT        BIT(6)      /* Transmit Empty */
#define BIT_LSR_THRE        BIT(5)      /* Transmit Holding Register Empty */
#define BIT_LSR_BI          BIT(4)      /* Break Interrupt */
#define BIT_LSR_FE          BIT(3)      /* Framing Error */
#define BIT_LSR_PE          BIT(2)      /* Parity Error */
#define BIT_LSR_OE          BIT(1)      /* Overrun Error */
#define BIT_LSR_DR          BIT(0)      /* Data Ready */#define LSR_BRK_ERROR_BITS  0x1E        /* BI|FE|PE|OE bits *//* Modem Status Register */
#define BIT_MSR_DCD         BIT(7)      /* Data Carrier Detect */
#define BIT_MSR_RI          BIT(6)      /* Ring Indicator */
#define BIT_MSR_DSR         BIT(5)      /* Data Set Ready */
#define BIT_MSR_CTS         BIT(4)      /* Clear to Send */
#define BIT_MSR_DDCD        BIT(3)      /* Delta Data Carrier Detect */
#define BIT_MSR_TERI        BIT(2)      /* Trailing Edge Ring Indicator */
#define BIT_MSR_DDSR        BIT(1)      /* Delta Data Set Ready */
#define BIT_MSR_DCTS        BIT(0)      /* Delta Clear to Send */
#define BIT_MSR_ANY_DELTA   0x0F
#define MSR_SAVE_FLAGS BIT_MSR_ANY_DELTA#define CH438_IIR_FIFOS_ENABLED 0xC0  /* enable FIFO */#endif

C文件ch438.c:

#include <linux/io.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/console.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
#include <mach/hardware.h>
#include <mach/platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinconf-sunxi.h>
#include <mach/sys_config.h>
#include <mach/gpio.h>
#include <mach/irqs.h>
#include <asm/io.h>
#include "ch438.h"#define CH438_UART_NAME     "usart"
#define CH438_DEV_NAME      "ttySC"
#define CH438_DEV_NUM       8#define CH438_CLOCK_RATE    (22118400/12)/* debug control */
enum {DBG_ERR  = 1U << 0,DBG_DBG  = 1U << 1,
};
static u32 ch438_debug_mask = 0;
#define dprintk(level, fmt, arg...) \do { \if (unlikely(ch438_debug_mask & level)) { \printk("%s()%d - ", __func__, __LINE__); \printk(fmt, ##arg); \} \} while (0)#define SERIAL_ERR(fmt, arg...) dprintk(DBG_ERR, fmt, ##arg)
#define SERIAL_DBG(fmt, arg...) dprintk(DBG_DBG, fmt, ##arg)#define UART_TO_SPORT(port) ((struct ch438_port*)port)static struct ch438_pin     ch438_pin;
static spinlock_t ch438_rwlock;
static unsigned long ch438_mapbase=0;
static unsigned char __iomem *ch438_membase=NULL; /*read/write[bwl]*/static const u8 REG[] = {0x00,0x10,0x20,0x30,0x08,0x18,0x28,0x38};/* NO CONFIG_SERIAL_SUNXI_CONSOLE, this device don't as console! */static struct platform_device ch438_device[CH438_DEV_NUM];
static struct ch438_port      ch438_ports[CH438_DEV_NUM];/* Access macros for the CH438 UART */#define UART_GET_CHR(p)      RdReg(REG[p->line]|RBR_RO)
#define UART_PUT_CHR(p, c)  WrReg(REG[p->line]|THR_WO, c)#define UART_GET_IER(p)     RdReg(REG[p->line]|IER_RW)
#define UART_PUT_IER(p, c)  WrReg(REG[p->line]|IER_RW, c)
#define UART_GET_IIR(p)     RdReg(REG[p->line]|IIR_RO)#define UART_GET_FCR(p)        RdReg(REG[p->line]|FCR_WO)
#define UART_PUT_FCR(p, c)  WrReg(REG[p->line]|FCR_WO, c)
#define UART_GET_MSR(p)     RdReg(REG[p->line]|MSR_RO)
#define UART_GET_LSR(p)     RdReg(REG[p->line]|LSR_RO)
#define UART_GET_LCR(p)     RdReg(REG[p->line]|LCR_RW)
#define UART_PUT_LCR(p, c)  WrReg(REG[p->line]|LCR_RW, c)
#define UART_GET_MCR(p)     RdReg(REG[p->line]|MCR_RW)
#define UART_PUT_MCR(p, c)  WrReg(REG[p->line]|MCR_RW, c)
#define UART_GET_SCR(p)     RdReg(REG[p->line]|SCR_RW)
#define UART_PUT_SCR(p, c)  WrReg(REG[p->line]|SCR_RW, c)#define UART_PUT_DLH(p, c)  WrReg(REG[p->line]|DLH_RW, c)
#define UART_PUT_DLL(p, c)  WrReg(REG[p->line]|DLL_RW, c)#define UART_GET_SSR()      RdReg(SSR_RO)static void ch438_gpio_init(void)
{struct ch438_pin *pin = &ch438_pin;script_item_u val;char *para = "ch438"; /* see as sys_config.fex */int ret=0;int reg = 0;SERIAL_DBG("ch438 gpio init.\n");__raw_writel(0x55555555, CH438_PULL0); /* all pull up */__raw_writel(0x00000005, CH438_PULL1); /* all pull up */__raw_writel(0x11111111, CH438_CFG0);  /* PE7~PE0: output */__raw_writel(0x11111111, CH438_CFG1);  /* WR[15] ADDR[14~8] output */__raw_writel(0x00000011, CH438_CFG2);  /* CS[17], RD[16] *//* ch438 RST: PF01 */script_get_item(para, "ch438_rst", &val);pin->rst = val.gpio.gpio;ret=gpio_request(pin->rst, NULL);gpio_direction_output(pin->rst, 1);/* ch438 INT: PB06 */script_get_item(para, "ch438_int", &val);pin->pint = val.gpio.gpio;ret=gpio_request(pin->pint, NULL);gpio_direction_input(pin->pint);/* Allocate the IRQ */pin->irqnum = gpio_to_irq(pin->pint);/* hw reset ch438 */SERIAL_DBG("ch438 chip hw reset!\n");gpio_set_value(pin->rst, 1);udelay(10);gpio_set_value(pin->rst, 0);udelay(100);gpio_set_value(pin->rst, 1);reg = __raw_readl(CH438_DAT);/* Reset default: CS=1, WR=1, RD=1 */__raw_writel(reg|(1<<CH438_CS)|(1<<CH438_WR)|(1<<CH438_RD), CH438_DAT);
}static void ch438_gpio_free(void)
{struct ch438_pin *pin = &ch438_pin;gpio_free(pin->rst);gpio_free(pin->pint);
}static void WrReg(unsigned char addr,unsigned char dat)
{unsigned long flags;int reg;spin_lock_irqsave(&ch438_rwlock, flags);/* addr output */__raw_writel(0x11111111, CH438_CFG0);ndelay(20);/* CS=0, RD=1, WR=0, 00... */reg = 0x00010000;reg |= (unsigned int)addr<<8;/* data port */reg |= dat;__raw_writel(reg, CH438_DAT);ndelay(20);/* set CS,WR as 0 */reg &= ~((1<<CH438_CS)|(1<<CH438_WR));__raw_writel(reg, CH438_DAT); ndelay(20);//set CS,WR as 1reg |= (1<<CH438_CS)|(1<<CH438_WR);__raw_writel(reg, CH438_DAT);ndelay(20);spin_unlock_irqrestore(&ch438_rwlock, flags);
}static unsigned char RdReg(unsigned char addr)
{unsigned long flags;unsigned char value=0;int reg;spin_lock_irqsave(&ch438_rwlock, flags);/* addr input */__raw_writel(0x00000000, CH438_CFG0);ndelay(20);/* CS=1,WR=1,RD=1, addr */reg = 0x00038000 | (unsigned int)addr<<8; __raw_writel(reg, CH438_DAT);ndelay(20);/* CS=0,RD=0 */reg &= ~((1<<CH438_CS)|(1<<CH438_RD));__raw_writel(reg, CH438_DAT);ndelay(20);/* read value */value = __raw_readl(CH438_DAT)&0xFF; /* RD=1,CS=1 */reg |= (1<<CH438_CS)|(1<<CH438_RD); __raw_writel(reg, CH438_DAT);  ndelay(20);spin_unlock_irqrestore(&ch438_rwlock, flags);return value;
}static inline void ch438_reset(struct uart_port *port)
{UART_PUT_IER(port, BIT_IER_RESET);
}static unsigned int ch438_handle_rx(struct uart_port *port, unsigned char lsr)
{struct tty_struct *tty = port->state->port.tty;unsigned char ch = 0;int max_count = 256;char flag;/* unsigned int cnt=0; *//* printk("ttySC%d RX: ", port->line); *//* cnt = port->icount.rx; */do {if(likely(lsr & BIT_LSR_DR)) {ch = UART_GET_CHR(port);/* printk("%02x ", ch); */}flag = TTY_NORMAL;port->icount.rx++;if(unlikely(lsr & LSR_BRK_ERROR_BITS)) {/* For statistics only */if(lsr & BIT_LSR_BI) {lsr &= ~(BIT_LSR_FE | BIT_LSR_PE);port->icount.brk++;/** We do the SysRQ and SAK checking* here because otherwise the barek* may be masked by ignore_status_mask* or read_status_mask.*/if(uart_handle_break(port))goto ignore_char;} else if(lsr & BIT_LSR_PE)port->icount.parity++;else if(lsr & BIT_LSR_FE)port->icount.frame++;else if(lsr & BIT_LSR_OE)port->icount.overrun++;/* Mask off conditions which should be ingored. */lsr &= port->read_status_mask;if(lsr & BIT_LSR_BI)flag = TTY_BREAK;else if(lsr & BIT_LSR_PE)flag = TTY_PARITY;else if(lsr & BIT_LSR_FE)flag = TTY_FRAME;}if(uart_handle_sysrq_char(port, ch))goto ignore_char;    uart_insert_char(port, lsr, BIT_LSR_OE, ch, flag);
ignore_char:lsr = UART_GET_LSR(port);} while ((lsr & (BIT_LSR_DR|BIT_LSR_BI)) && (max_count-- > 0));/* printk("(%d bytes)\n", port->icount.rx-cnt); */spin_unlock(&port->lock);tty_flip_buffer_push(tty);spin_lock(&port->lock);return lsr;
}static void ch438_stop_tx(struct uart_port *port)
{struct ch438_port *up = UART_TO_SPORT(port);if(up->ier & BIT_IER_THRI) {up->ier &= ~BIT_IER_THRI;SERIAL_DBG("ttySC%d stop tx, ier 0x%02X\n", port->line, up->ier);UART_PUT_IER(port, up->ier);}
}static void ch438_start_tx(struct uart_port *port)
{struct ch438_port *up = UART_TO_SPORT(port);/** when BIT_IER_THRI(0->1), will be generate "THR empty" interrupt.*/if(!(up->ier & BIT_IER_THRI)) {up->ier |= BIT_IER_THRI;SERIAL_DBG("ttySC%d start tx, ier 0x%02X\n", port->line, up->ier);UART_PUT_IER(port, up->ier);}
}static void ch438_handle_tx(struct uart_port *port)
{struct circ_buf *xmit = &port->state->xmit;int count;/* unsigned int cnt; */if(port->x_char) {/* Send special char - probebly flow control */UART_PUT_CHR(port, port->x_char);port->icount.tx++;port->x_char = 0;return;}if(uart_circ_empty(xmit) || uart_tx_stopped(port)) {ch438_stop_tx(port);return;}/* The IRQ is for TX FIFO half-empty */count = port->fifosize / 2;/* printk("ttySC%d TX:", port->line); *//* cnt = port->icount.tx; */do {/* printk("%02x ", xmit->buf[xmit->tail]); */UART_PUT_CHR(port, 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);/* printk("(%d bytes)\n", port->icount.tx-cnt); */if(uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {spin_unlock(&port->lock);uart_write_wakeup(port);spin_lock(&port->lock);}if(uart_circ_empty(xmit))ch438_stop_tx(port);
}static unsigned char ch438_modem_status(struct uart_port *port)
{struct ch438_port *up = UART_TO_SPORT(port);unsigned int status = UART_GET_MSR(port);status |= up->msr_saved_flags;up->msr_saved_flags = 0;if(status & BIT_MSR_ANY_DELTA && up->ier & BIT_IER_MSI && port->state != NULL) {if (status & BIT_MSR_TERI)port->icount.rng++;if (status & BIT_MSR_DDSR)port->icount.dsr++;if (status & BIT_MSR_DDCD)uart_handle_dcd_change(port, status & BIT_MSR_DCD);if (!(up->mcr & BIT_MCR_AFE) && status & BIT_MSR_DCTS)uart_handle_cts_change(port, status & BIT_MSR_CTS);wake_up_interruptible(&port->state->port.delta_msr_wait);}SERIAL_DBG("ttySC%d modem status: %x\n", port->line, status);return status;
}static void irq_todo(struct uart_port *port)
{unsigned long flags=0;unsigned char iir = UART_GET_IIR(port) & BIT_IIR_IID_MASK;unsigned char lsr = UART_GET_LSR(port);spin_lock_irqsave(&port->lock, flags);SERIAL_DBG("ttySC%d irq: iir=0x%02x, lsr=0x%02x, ier=0x%02x\n", port->line, iir, lsr, UART_GET_IER(port));if(lsr & (BIT_LSR_DR|BIT_LSR_BI) || iir == INT_RCV_OVERTIME)ch438_handle_rx(port, lsr);ch438_modem_status(port);if(lsr & BIT_LSR_THRE) {ch438_handle_tx(port);}spin_unlock_irqrestore(&port->lock, flags);
}static irqreturn_t ch438_irq(int irq, void *dev_id)
{unsigned char ssr = 0;ssr = UART_GET_SSR();if(ssr & 0x01)irq_todo(&ch438_ports[0].port);if(ssr & 0x02)irq_todo(&ch438_ports[1].port);if(ssr & 0x04)irq_todo(&ch438_ports[2].port);if(ssr & 0x08)irq_todo(&ch438_ports[3].port);if(ssr & 0x10) irq_todo(&ch438_ports[4].port);if(ssr & 0x20)irq_todo(&ch438_ports[5].port);if(ssr & 0x40)irq_todo(&ch438_ports[6].port);if(ssr & 0x80)irq_todo(&ch438_ports[7].port);return IRQ_HANDLED;
}static inline void wait_for_xmitr(struct uart_port *port)
{struct ch438_port *up = UART_TO_SPORT(port);unsigned int status, tmout = 1000;unsigned char mask = BIT_LSR_TEMT|BIT_LSR_THRE;SERIAL_DBG("ttySC%d\n", port->line);/* Wait up to 1ms for the character to be sent. */do {status = UART_GET_LSR(port);if(status & BIT_LSR_BI)up->lsr_break_flag = BIT_LSR_BI;if (--tmout == 0)break;udelay(1);} while ((status & mask) != mask);/* CTS is unsupported by the 2-line UART, so ignore it. */if(up->io_num == 2)return;/* Wait up to 1s for flow control if necessary */if (port->flags & UPF_CONS_FLOW) {tmout = 1000000;while(--tmout && (UART_GET_MSR(port)&BIT_MSR_CTS)==0) {udelay(1);}}
}static unsigned int ch438_tx_empty(struct uart_port *port)
{unsigned long flags = 0;unsigned int ret = 0;spin_lock_irqsave(&port->lock, flags);ret = (UART_GET_LSR(port) & BIT_LSR_TEMT) ? TIOCSER_TEMT : 0;spin_unlock_irqrestore(&port->lock, flags);return ret;
}static void ch438_set_mctrl(struct uart_port *port, u_int mctrl)
{struct ch438_port *up = UART_TO_SPORT(port);unsigned int mcr=0;/* ch438q not used RTS & DTR */if(mctrl & TIOCM_LOOP) mcr |= BIT_MCR_LOOP;if(mctrl & TIOCM_OUT2)mcr |= BIT_MCR_OUT2;up->mcr &= ~(BIT_MCR_RTS|BIT_MCR_DTR|BIT_MCR_LOOP|BIT_MCR_OUT2);up->mcr |= mcr; SERIAL_DBG("ttySC%d set mcr 0x%02X\n", port->line, mcr);UART_PUT_MCR(port, up->mcr);
}static unsigned int ch438_get_mctrl(struct uart_port *port)
{unsigned int msr;unsigned int ret = 0;msr = ch438_modem_status(port);if (msr & BIT_MSR_DCD) ret |= TIOCM_CAR;if (msr & BIT_MSR_RI)  ret |= TIOCM_RNG;if (msr & BIT_MSR_DSR) ret |= TIOCM_DSR;if (msr & BIT_MSR_CTS) ret |= TIOCM_CTS;SERIAL_DBG("ttySC%d get msr 0x%02X\n", port->line, msr);return ret;
}static void ch438_stop_rx(struct uart_port *port)
{struct ch438_port *up = UART_TO_SPORT(port);if (up->ier & BIT_IER_RLSI) {up->ier &= ~BIT_IER_RLSI;SERIAL_DBG("ttySC%d stop rx, ier 0x%02X\n", port->line, up->ier);port->read_status_mask &= ~BIT_LSR_DR;UART_PUT_IER(port, up->ier);}
}static void ch438_enable_ms(struct uart_port *port)
{struct ch438_port *up = UART_TO_SPORT(port);if(!(up->ier & BIT_IER_MSI)) {up->ier |= BIT_IER_MSI;SERIAL_DBG("ttySC%d en msi, ier 0x%02X\n", port->line, up->ier);UART_PUT_IER(port, up->ier);}
}static void ch438_break_ctl(struct uart_port *port, int break_state)
{struct ch438_port *up = UART_TO_SPORT(port);unsigned long flags;spin_lock_irqsave(&port->lock, flags);if (break_state == -1)up->lcr |= BIT_LCR_SBC;elseup->lcr &= ~BIT_LCR_SBC;UART_PUT_LCR(port, up->lcr);spin_unlock_irqrestore(&port->lock, flags);
}static int ch438_startup(struct uart_port *port)
{struct ch438_port *up = UART_TO_SPORT(port);SERIAL_DBG("ttySC%d start up ...\n", port->line);up->msr_saved_flags = 0;up->ier = BIT_IER_RLSI |BIT_IER_THRI | BIT_IER_RDI;UART_PUT_IER(port, up->ier); /* Interrupt when starting */return 0;
}static void ch438_shutdown(struct uart_port *port)
{struct ch438_port *up = UART_TO_SPORT(port);SERIAL_DBG("ttySC%d shut down ...\n", port->line);up->lcr = 0;up->mcr = 0;up->fcr = 0;up->ier &= ~(BIT_IER_RLSI|BIT_IER_THRI|BIT_IER_RDI);/* the value of the register must be modified! */UART_PUT_IER(port, up->ier);
}static void ch438_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old)
{struct ch438_port *up = UART_TO_SPORT(port);unsigned long flags;unsigned int baud, quot, lcr = 0, dll, dlh;if(1) {/* define custom termios settings for NMEA protocol */termios->c_iflag /* input modes - */&= ~(IGNBRK  /* disable ignore break */| BRKINT     /* disable break causes interrupt */| PARMRK     /* disable mark parity errors */| ISTRIP     /* disable clear high bit of input char */| INLCR      /* disable translate NL to CR */| IGNCR      /* disable ignore CR */| ICRNL      /* disable translate CR to NL */| IXON);     /* disable enable XON/XOFF flow control */termios->c_oflag /* output modes */&= ~OPOST;    /* disable postprocess output char */termios->c_lflag /* line discipline modes */&= ~(ECHO     /* disable echo input characters */| ECHONL      /* disable echo new line */| ICANON      /* disable erase, kill, werase, and rprntspecial characters */| ISIG        /* disable interrupt, quit, and suspendspecial characters */| IEXTEN);    /* disable non-POSIX special characters */} /* CT_CYPHIDCOM: Application should handle this for device */switch (termios->c_cflag & CSIZE) {case CS5: lcr = BIT_LCR_WLEN_5; break;case CS6: lcr = BIT_LCR_WLEN_6; break;case CS7: lcr = BIT_LCR_WLEN_7; break;case CS8:default:  lcr = BIT_LCR_WLEN_8; break;}if (termios->c_cflag & CSTOPB)lcr |= BIT_LCR_STOP;if (termios->c_cflag & PARENB)lcr |= BIT_LCR_PARITY;if (!(termios->c_cflag & PARODD))lcr |= BIT_LCR_EPAR;/* set baudrate */baud = uart_get_baud_rate(port, termios, old, port->uartclk / 16 / 0xffff, port->uartclk / 16);quot = uart_get_divisor(port, baud);dll = quot & 0xFF;dlh = quot >> 8;/* SERIAL_DBG("baud=%d, quot=%d\n", baud, quot); */spin_lock_irqsave(&port->lock, flags);/* Update the per-port timeout. */uart_update_timeout(port, termios->c_cflag, baud);port->read_status_mask = BIT_LSR_OE | BIT_LSR_THRE | BIT_LSR_DR;if (termios->c_iflag & INPCK)port->read_status_mask |= BIT_LSR_FE | BIT_LSR_PE;if (termios->c_iflag & (BRKINT | PARMRK))port->read_status_mask |= BIT_LSR_BI;/* Characters to ignore */port->ignore_status_mask = 0;if (termios->c_iflag & IGNPAR)port->ignore_status_mask |= BIT_LSR_PE | BIT_LSR_FE;if (termios->c_iflag & IGNBRK) {port->ignore_status_mask |= BIT_LSR_BI;/** If we're ignoring parity and break indicators,* ignore overruns too (for real raw support).*/if (termios->c_iflag & IGNPAR)port->ignore_status_mask |= BIT_LSR_OE;}/** Ignore all characters if CREAD is not set.*/if ((termios->c_cflag & CREAD) == 0)port->ignore_status_mask |= BIT_LSR_DR;/* * if lcr & baud are changed, reset controller to disable transfer */if(lcr != up->lcr || dll != up->dll || dlh != up->dlh) {/* SERIAL_DBG("ttySC%d reset controller...\n", port->line); */ch438_reset(port);}up->dll = dll;up->dlh = dlh;/* flow control */up->mcr &= ~ BIT_MCR_AFE;if(termios->c_cflag & CRTSCTS)up->mcr |= BIT_MCR_AFE;UART_PUT_MCR(port, up->mcr);/* * CTS flow control flag and modem status interrupts */up->ier &= ~BIT_IER_MSI;if(UART_ENABLE_MS(port, termios->c_cflag))up->ier |= BIT_IER_MSI;UART_PUT_IER(port, up->ier);up->fcr = BIT_FCR_FT_112|BIT_FCR_FIFOEN;UART_PUT_FCR(port, up->fcr);up->lcr = lcr;UART_PUT_LCR(port, up->lcr|BIT_LCR_DLAB); /* set DLAB */UART_PUT_DLL(port, up->dll);UART_PUT_DLH(port, up->dlh);UART_PUT_LCR(port, up->lcr); /* reset DLAB *//* Don't rewrite B0 */if(tty_termios_baud_rate(termios))tty_termios_encode_baud_rate(termios, baud, baud);ch438_set_mctrl(port, port->mctrl);spin_unlock_irqrestore(&port->lock, flags);
}static const char *ch438_type(struct uart_port *port)
{return "CH438";
}static void ch438_release_port(struct uart_port *port)
{}/** Request the memory region(s) being used by 'port'*/
static int ch438_request_port(struct uart_port *port)
{return 0;
}/** Configure/autoconfigure the port.*/
static void ch438_config_port(struct uart_port *port, int flags)
{if(flags & UART_CONFIG_TYPE){port->type = PORT_CH438;ch438_request_port(port);}
}/** verify the new serial_struct (for TIOCSSERIAL).*/
static int ch438_verify_port(struct uart_port *port, struct serial_struct *ser)
{/* We don't want the core code to modify any port params */return -EINVAL;
}static void ch438_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
{struct ch438_port *up = UART_TO_SPORT(port);switch(state) {case 0: /* Power up */up->ier &= ~BIT_IER_LOWPWR;UART_PUT_IER(port, up->ier);SERIAL_DBG("ttySC%d PM state:%d --ON--\n", port->line, state);break;case 3: /* Power down */up->ier |= BIT_IER_LOWPWR;UART_PUT_IER(port, up->ier);SERIAL_DBG("ttySC%d PM state:%d --OFF--\n", port->line, state);break;default:SERIAL_DBG("usart%d, Unknown PM state %d\n", up->id, state);}
}static struct uart_ops ch438_uart_ops = {.tx_empty        = ch438_tx_empty,.set_mctrl        = ch438_set_mctrl,.get_mctrl       = ch438_get_mctrl,.stop_tx     = ch438_stop_tx,.start_tx      = ch438_start_tx,.stop_rx      = ch438_stop_rx,.enable_ms     = ch438_enable_ms,.break_ctl       = ch438_break_ctl,.startup     = ch438_startup,.shutdown      = ch438_shutdown,.set_termios  = ch438_set_termios,.type          = ch438_type,.release_port = ch438_release_port,.request_port = ch438_request_port,.config_port  = ch438_config_port,.verify_port   = ch438_verify_port,.pm             = ch438_pm,
};static ssize_t ch438_dev_info_show(struct device *dev,struct device_attribute *attr, char *buf)
{struct uart_port *port = dev_get_drvdata(dev);struct ch438_port *up = UART_TO_SPORT(port);return snprintf(buf, PAGE_SIZE,"id     = %d \n""name   = %s \n""irq    = %d \n""io_num = %d \n""port->membase = 0x%08x \n""port->iobase  = 0x%08x \n",up->id, up->name, port->irq, 2,(unsigned int)port->membase,  (unsigned int)port->iobase);
}static struct device_attribute ch438_dev_info_attr = __ATTR(dev_info, S_IRUGO, ch438_dev_info_show, NULL);static ssize_t ch438_status_show(struct device *dev,struct device_attribute *attr, char *buf)
{struct uart_port *port = dev_get_drvdata(dev);return snprintf(buf, PAGE_SIZE, "uartclk = %d \n""RBR = 0x%02X, IER = 0x%02X, IIR = 0x%02X \n""FCR = 0x%02X, LCR = 0x%02X, MCR = 0x%02X \n""LSR = 0x%02X, MSR = 0x%02X, SCR = 0x%02X \n",port->uartclk,UART_GET_CHR(port),UART_GET_IER(port),UART_GET_IIR(port),UART_GET_FCR(port),UART_GET_LCR(port),UART_GET_MCR(port),UART_GET_LSR(port),UART_GET_MSR(port),UART_GET_SCR(port));
}static struct device_attribute ch438_status_attr =__ATTR(status, S_IRUGO, ch438_status_show, NULL);static ssize_t ch438_loopback_show(struct device *dev,struct device_attribute *attr, char *buf)
{int mcr = 0;struct uart_port *port = dev_get_drvdata(dev);mcr = UART_GET_MCR(port);return snprintf(buf, PAGE_SIZE,"MCR: 0x%08x, Loopback: %d\n", mcr, mcr&BIT_MCR_LOOP ? 1 : 0);
}static ssize_t ch438_loopback_store(struct device *dev,struct device_attribute *attr,const char *buf, size_t count)
{int mcr = 0;int enable = 0;struct uart_port *port = dev_get_drvdata(dev);if (!strncmp(buf, "enable", 6))enable = 1;SERIAL_DBG("Set loopback: %d \n", enable);mcr = UART_GET_MCR(port);if (enable)UART_PUT_MCR(port, mcr | BIT_MCR_LOOP);elseUART_PUT_MCR(port, mcr & ~BIT_MCR_LOOP);return count;
}
static struct device_attribute ch438_loopback_attr =__ATTR(loopback, S_IRUGO|S_IWUSR, ch438_loopback_show, ch438_loopback_store);static ssize_t ch438_ctrl_info_show(struct device *dev,struct device_attribute *attr, char *buf)
{struct uart_port *port = dev_get_drvdata(dev);struct ch438_port *up = UART_TO_SPORT(port);u32 dl = (u32)up->dlh << 8 | (u32)up->dll;if(dl == 0) dl = 1000;return snprintf(buf, PAGE_SIZE, "ier : 0x%02X\n""lcr : 0x%02X\n""mcr : 0x%02X\n""fcr : 0x%02X\n""dll : 0x%02X\n""dlh : 0x%02X\n""baud: %d (dl = %d)\n\n""TxRx Statistics:\n""tx     : %d\n""rx     : %d\n""parity : %d\n""frame  : %d\n""overrun: %d\n",up->ier, up->lcr, up->mcr, up->fcr, up->dll, up->dlh,(port->uartclk>>4)/dl, dl, port->icount.tx,port->icount.rx,port->icount.parity,port->icount.frame,port->icount.overrun);
}
static struct device_attribute ch438_ctrl_info_attr =__ATTR(ctrl_info, S_IRUGO, ch438_ctrl_info_show, NULL);static void ch438_sysfs(struct platform_device *pdev)
{device_create_file(&pdev->dev, &ch438_dev_info_attr);device_create_file(&pdev->dev, &ch438_status_attr);device_create_file(&pdev->dev, &ch438_loopback_attr);device_create_file(&pdev->dev, &ch438_ctrl_info_attr);
}static void __init ch328_device_scan(void)
{struct uart_port *port;int i;memset(ch438_device, 0, sizeof(ch438_device));memset(ch438_ports,  0, sizeof(ch438_ports));for(i=0; i<CH438_DEV_NUM; i++) {port = &ch438_ports[i].port;ch438_device[i].name = CH438_UART_NAME;ch438_device[i].id   = i;ch438_device[i].dev.platform_data = port;port->iotype   = UPIO_PORT;port->ops      = &ch438_uart_ops;port->fifosize = 128; /* max: 128 */port->line     = i;}
}static struct uart_driver ch438_uart_driver = {.owner         = THIS_MODULE,.driver_name = CH438_UART_NAME,.dev_name        = CH438_DEV_NAME,.nr               = CH438_DEV_NUM,.cons          = NULL,
};struct platform_device *ch438_get_pdev(int uart_id)
{if(ch438_ports[uart_id].port.dev)return to_platform_device(ch438_ports[uart_id].port.dev);else return NULL;
}
EXPORT_SYMBOL(ch438_get_pdev);static int __devinit ch438_probe(struct platform_device *pdev)
{struct uart_port *port;struct ch438_port *up;struct ch438_pin *pin = &ch438_pin;int id = pdev->id;int ret;port = &ch438_ports[pdev->id].port;port->dev = &pdev->dev;up = UART_TO_SPORT(port);up->id = id;up->ier = 0;up->lcr = 0;up->mcr = 0;up->fcr = 0;up->dll = 0;up->dlh = 0;up->io_num = 2;sprintf(up->name, CH438_UART_NAME"%d", id);pdev->dev.init_name = up->name;port->uartclk = CH438_CLOCK_RATE;port->type = PORT_CH438;port->flags = UPF_BOOT_AUTOCONF;port->mapbase = 0; /* it was not used! */port->irq = pin->irqnum;port->mctrl = TIOCM_OUT2; /* use BIT_MCR_OUT2 generate an interrupt */platform_set_drvdata(pdev, port);ret = request_irq(port->irq, ch438_irq, IRQF_TRIGGER_FALLING|IRQF_DISABLED|IRQF_SHARED, up->name, port);if(unlikely(ret)) {SERIAL_DBG("usart%d cannot get irq%d\n", up->id, port->irq);}uart_add_one_port(&ch438_uart_driver, port);return 0;
}static int __devexit ch438_remove(struct platform_device *pdev)
{struct uart_port *port = platform_get_drvdata(pdev);SERIAL_DBG("release uart%d port\n", port->line);free_irq(port->irq, port);return 0;
}/* UART power management code */
#ifdef CONFIG_PM_SLEEP
static int ch438_suspend(struct device *dev)
{struct uart_port *port = dev_get_drvdata(dev);if (port) {SERIAL_DBG("usart%d suspend.\n", port->line);uart_suspend_port(&ch438_uart_driver, port);}return 0;
}static int ch438_resume(struct device *dev)
{struct uart_port *port = dev_get_drvdata(dev);if (port) {uart_resume_port(&ch438_uart_driver, port);SERIAL_DBG("usart%d resume.\n", port->line);}return 0;
}static const struct dev_pm_ops ch438_pm_ops = {.suspend = ch438_suspend,.resume = ch438_resume,
};
#define CH438_PM_OPS    (&ch438_pm_ops)
#else /* !CONFIG_PM_SLEEP */
#define CH438_PM_OPS    NULL
#endif /* CONFIG_PM_SLEEP */static struct platform_driver ch438_platform_driver =
{.probe  = ch438_probe,.remove = ch438_remove,.driver = {.name  = CH438_UART_NAME,.pm    = CH438_PM_OPS,.owner = THIS_MODULE,},
};static int __init ch438_uart_init(void)
{int i;spin_lock_init(&ch438_rwlock);SERIAL_DBG("ch438 uart init\n");ch438_mapbase = CH438_BASE;ch438_membase = ioremap(ch438_mapbase, GPIO_RANGE);if(!ch438_membase) {SERIAL_DBG("ch438, ioremap failed\n");release_mem_region(ch438_mapbase, GPIO_RANGE);}ch438_gpio_init();ch328_device_scan();/* SERIAL_DBG("register ch438 driver...\n"); */uart_register_driver(&ch438_uart_driver);/* SERIAL_DBG("register platform_device...\n"); */for(i=0; i<CH438_DEV_NUM; i++) {platform_device_register(&ch438_device[i]);ch438_sysfs(&ch438_device[i]);}return platform_driver_register(&ch438_platform_driver);
}static void __exit ch438_uart_exit(void)
{release_mem_region(ch438_mapbase, GPIO_RANGE);iounmap(ch438_membase);ch438_membase = NULL;platform_driver_unregister(&ch438_platform_driver);uart_unregister_driver(&ch438_uart_driver);ch438_gpio_free();
}module_init(ch438_uart_init);
module_exit(ch438_uart_exit);MODULE_DESCRIPTION("CH438 serial port driver");
MODULE_AUTHOR("iamyhw@foxmail.com");
MODULE_LICENSE("GPL");

介绍

CH438 for Allwinner A33 platform, 1 to 8 serial ports.
在全志A33平台扩展Ch438Q芯片,它是一个1并转8串的串口扩展芯片.

使用说明

这里讲解的是基于Allwinner A33平台的,其他平台的需自行处理.

  1. 将代码放在linux-3.4/drivers/tty/serial目录下

  2. 在同目录的Kconfig中加入以下配置:
    config SERIAL_CH438
    bool “Support for ch438 serial port”
    select SERIAL_CORE
    default y
    help
    If you have an CH438 chip and want to use the built-in UART of
    the chip, say Y to this option.

  3. 在同目录的Makefile中加入以下语句:
    obj-$(CONFIG_SERIAL_CH438) += ch438.o

  4. 在A33项目的sys_config.fex中加入ch438配置项:
    [ch438]
    ch438_used = 1
    uart_type = 2

    ch438_cs = port:PE17<1><1><1>
    ch438_rst = port:PF01<1><1><1>
    ch438_int = port:PB06<2><1>
    请根据自己的硬件修改以上IO的配置

  5. 完成!
    注意:扩展出来的串口被映射到/dev目录下为ttySC0-ttySC7,
    对应的port->line是0-7,而系统默认的ttyS0(port->line==0)一般是作为debug串口使用的,
    所以不应对扩展的串口检查是否console的判断,否则会导致ttySC0被认为是console而产生
    错误,这是linux内核判断某个串口是否也是console的机制造成的,应用中需要避免!

    如果想让扩展串口也能作为console使用,可以将扩展串口映射为ttyS4-ttyS11,让对应line的
    为4-11.不过ch438内部的port还是0~7,用起来太麻烦!

应用测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <asm/termios.h>
//#include <linux/delay.h>static void set_baudrate(struct termios *opt, unsigned int baudrate)
{cfsetispeed(opt, baudrate);cfsetospeed(opt, baudrate);
}static void set_data_bit(struct termios *opt, unsigned int databit)
{opt->c_cflag &= ~CSIZE;switch(databit) {case 8: opt->c_cflag |= CS8; break;case 7: opt->c_cflag |= CS7; break;case 6: opt->c_cflag |= CS6; break;case 5: opt->c_cflag |= CS5; break;default:opt->c_cflag |= CS8; break;}
}static void set_parity(struct termios *opt, char parity)
{switch(parity) {case 'N': opt->c_cflag &= ~PARENB;break;case 'E':opt->c_cflag |= PARENB;opt->c_cflag &= ~PARODD;break;case 'O':opt->c_cflag |= PARENB;opt->c_cflag |= ~PARODD;break;default:opt->c_cflag &= ~PARENB;break;}
}static void set_stopbit(struct termios *opt, const char *stopbit)
{if (0 == strcmp(stopbit, "1")) {opt->c_cflag &= ~CSTOPB;    //1 stop bit} else if (0 == strcmp(stopbit, "1")) {opt->c_cflag &= ~CSTOPB;    //1.5 stop bit} else if (0 == strcmp(stopbit, "2")) {opt->c_cflag |= CSTOPB;     //2 stop bit} else {opt->c_cflag &= ~CSTOPB;    //1 stop bit}
}//baudrate: B1200, B2400, B4800, B9600,... B115200
//databit : 5, 6, 7, 8
//stopbit : "1", "1.5", "2"
//parity  : N(o), O(dd), E(ven)
int setting(int fd, int baudrate, int databit, const char *stopbit, char parity, int vtime, int vmin)
{static struct termios term;tcgetattr(fd, &term);//baudrateset_baudrate(&term, baudrate);term.c_cflag |= CLOCAL | CREAD; //local link, recv enable//data bitset_data_bit(&term, databit);//parityset_parity(&term, parity);//stop bitset_stopbit(&term, stopbit);//other settingterm.c_oflag      = 0;term.c_lflag     |= 0;term.c_lflag     &= ~(ICANON | ECHO | ECHOE | ISIG); //Input RAW Modeterm.c_oflag     &= ~OPOST; //Outputterm.c_cc[VTIME]  = vtime;term.c_cc[VMIN]   = vmin;tcflush(fd, TCIFLUSH);return (tcsetattr(fd, TCSANOW, &term));
}int baudrate_index(int baud)
{int baudrate = B9600; //default valueswitch(baud) {case 4800:  baudrate = B4800;  break;case 9600:  baudrate = B9600;  break;case 19200: baudrate = B19200; break;case 38400: baudrate = B38400; break;case 57600: baudrate = B57600; break;case 115200:baudrate = B115200;break;default: baudrate = B9600; break;} return baudrate;
}int baudrate_value(int index)
{int baud = 9600; //default valueswitch(index) {case B4800:  baud = 4800;  break;case B9600:  baud = 9600;  break;case B19200: baud = 19200; break;case B38400: baud = 38400; break;case B57600: baud = 57600; break;case B115200:baud = 115200;break;default: baud = 9600; break;} return baud;
}static char *ss = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!!";int main(int argc, char *argv[])
{int fd;int len, cnt=0, ret;char buf[260]={0};char *name = NULL;int baudrate = B115200;if(argc==1) {printf("Usage: serial_send [dev] [baudrate]\n");return 0;} else if(argc==2) { //[file_name]name = argv[1];} else if(argc==3) { //[file_name] [baudrate]int baud;name = argv[1];baud = atoi(argv[2]);baudrate = baudrate_index(baud);}fd = open(name, O_RDWR);if(fd < 0) {printf("open \"%s\" failed!\n", name);perror(name);return -1;}printf("open  \"%s\" ok! %d!\n", name, baudrate_value(baudrate));setting(fd, baudrate, 8, "1", 'N', 0, 0);while(cnt<11) {len = write(fd, ss, strlen(ss));if(len < 0) {printf("write data error \n");}cnt++;usleep(100000);//delay us}if(close(fd) < 0) {printf("close \"%s\" failed!\n", name);return -1;}    printf("close \"%s\" ok!\n", name);return 0;
}

linux的CH438Q驱动程序相关推荐

  1. Linux下触摸屏驱动程序分析

    [摘要: 本文以 linux 3.5--Exynos4412仄台,剖析 触摸屏 驱动焦点内容.Linux下触摸屏驱动(以ft5x06_ts为例)须要懂得以下学问: 1. I2C协定 2. Exynos ...

  2. linux网卡驱动 pdf,Linux下网卡驱动程序.pdf

    zekairecv 于 2015-10-04 00:58:57发表: 谢谢 weilee1 于 2015-04-19 17:41:05发表: 看看 雪语阑风 于 2014-12-04 11:03:39 ...

  3. linux无线网卡驱动分析,基于Linux的无线网卡驱动程序

    基于Linux的无线网卡驱动程序 文章分析了Linux下无线网卡驱动程序结构及设计方法,着重 (本文共4页) 阅读全文>> 无线局域网是当前较为常见的一种无线接入技术,具有运行速度高一级灵 ...

  4. Linux NAND FLASH驱动程序分析(mini2440)

    Linux NAND FLASH驱动程序分析(mini2440) 一.Linux-MTD Subsystem介绍 FLASH在嵌入式系统中是必不可少的,它是bootloader.linux内核和文件系 ...

  5. Linux内核 LCD 驱动程序框架

    Linux 内核 LCD 驱动程序框架 1. framebuffer 简介 1.1 什么是 framebuffer 1.2 framebuffer的作用 2. framebuffer 驱动的框架 3. ...

  6. Linux操作系统网络驱动程序编写

    2019独角兽企业重金招聘Python工程师标准>>> Linux操作系统网络驱动程序编写 一.Linux系统设备驱动程序概述     1.1 Linux设备驱动程序分类     1 ...

  7. linux网卡驱动开发视频,Linux下网卡驱动程序的开发.doc

    Linux下网卡驱动程序的开发 论文题目:Linux下网卡驱动程序的开发 专 业: 年 级: 学生学号: 学生姓名: 指导教师: 完成时间: Linux下网卡驱动程序的开发 八年经验 专业指导毕业设计 ...

  8. 嵌入式linux查看usb设备驱动程序,嵌入式Linux下USB驱动程序的设计

    嵌入式Linux下USB驱动程序的设计 usb概念:  USB(Universal Serial Bus)即通用串行总线,是一种全新的双向同步传输的支持热插拔的数据传输总线,其目的是为了提供一种兼容不 ...

  9. linux 图像采集卡驱动程序,基于Linux操作系统的视频采集卡驱动程序设计

    DMA结构: struct saa7146_video_dma { u32 base_odd; u32 base_even; u32 prot_addr; u32 pitch; u32 base_pa ...

最新文章

  1. iOS Core Data
  2. Nacos源码NacosServiceRegistryAutoConfiguration
  3. 疫情伤了谁?反正不是这8大直播行业
  4. 在UnitTest中读取*.config文件的郁闷
  5. 时过境迁:Oracle跨平台迁移之XTTS方案与实践
  6. “土豪机”8848出新品 手机数据备份保险箱1699起
  7. Windows Server 2012 +WDK7600.16385.1+VS2010驱动开发环境搭建
  8. asp.net编程基础
  9. matlab画折现_用matlab画折线图
  10. 日常生活开支记账明细_你的理财小管家!简单明了!皮面理财记账本明细流水支出笔记本!...
  11. {黑科技}哔哩哔哩视频三倍速播放
  12. 如何用python爬视频_如何使用python网络爬虫抓取视频?
  13. 小书MybatisPlus第1篇-整合SpringBoot快速开始增删改查
  14. 微信气泡主题设置_微信皮肤主题怎么弄 微信设置更换修改气泡和主题教程
  15. 政务智能办体验升级、乳腺癌创新药加速研发,飞桨和文心大模型驱动应用智能涌现...
  16. [高通MSM8953_64][Android10]解决制作差分包不生成system_manifest.xml的问题
  17. 基于HTML+CSS+JavaScript技术设计的博客网站(web前端期末大作业)
  18. 让SpringBoot不需要Controller、Service、DAO、Mapper,卧槽!这款工具
  19. 【实战篇】39 # 如何实现世界地图的新冠肺炎疫情可视化?
  20. BP神经网络解决什么问题

热门文章

  1. c# virtual 和 abstract
  2. Opencv之图像边缘检测:1.Sobel算子(cv2.Sobel)
  3. 怎样寻找好的心理医生
  4. c语言arc函数带几个参数,ARC的使用简要总结
  5. HT合泰单片机入门教程(第六章 时基中断)
  6. Vue框架中使用a标签去掉下划线
  7. awesome-javascript
  8. iOS逆向之反HOOK的基本防护
  9. WebRTC-节拍器[翻译]
  10. 十三、SpringBoot与分布式