uart 디바이스드라이버 - :::: 국립한밭대학교 반도체설계실...
TRANSCRIPT
Hanbat National University Prof. Lee Jaeheung
UART 디바이스 드라이버
Hanbat National University Prof. Lee Jaeheung
목차? SA1110 Peripheral Control Module
? UART 제어 레지스터 종류
? Blocking I/O
? S5N8946 UART Device Driver
? SA1110 UART Device Driver
? UART Device Driver Program
? Test 응용 프로그램 (Host / SA1110)
Hanbat National University Prof. Lee Jaeheung
SA1110 Peripheral Control Module Block Diagram
DMAController
Serial Port 2ICP
Serial Port 3UART
Serial Port 4MCP / SSP
LCDController
Serial Port 0UDC
Serial Port 1GPCLK/UART
ARM System Bus
L_PCLK L_BIAS UDC+ UDC- TXD1 RXD1 TXD2 RXD2 TXD3 RXD3 TXD4 SCLK
… …
ARM Peripheral Bus
Hanbat National University Prof. Lee Jaeheung
SA1110 PeripheralControl Module
Peripheral
LCD Controller
Serial port 0 :
Serial port 1 :
Serial port 2 : ICP
Serial port 3 : UART
Serial port 4 :
Peripheral pin controller (PPC)
UART
HSSP
MPC
SSP
Register Width /DMA port Size
32
8
8
8
8
8
16
16
32
Base Address
0h B010 0000
0h 8000 0000
0h 8001 0000
0h 8003 0000
0h 8004 0000
0h 8005 0000
0h 8006 0000
0h 8007 0000
0h 9006 0000
Interrupt Number
12
13
15
16
17
18
19
8 0h 8002 0000
UART
GPCLK
Serial Protocol
USB
Hanbat National University Prof. Lee Jaeheung
UART 제어 레지스터 종류
상태 레지스터 1UART State Register 1읽기 전용UTSR10x 8001 0020
상태 레지스터 0UART State Register 0읽기/쓰기UTSR00x 8001 001C
데이터 레지스터UART Data Register읽기/쓰기UTDR0x 8001 0010
제어 레지스터 3UART Control Register 3읽기/쓰기UTCR30x 8001 000C
제어 레지스터 2UART Control Register 2읽기/쓰기UTCR20x 8001 0008
제어 레지스터 1UART Control Register 1읽기/쓰기UTCR10x 8001 0004
제어 레지스터 0UART Control Register 0읽기/쓰기UTCR00x 8001 0000
설명핀 설명접근이름레지스터 주소
Hanbat National University Prof. Lee Jaeheung
UART 레지스터? UTCR (UART Control Register 0)
– PE (Parity Enable) (0:패리티X / 1:패리티 비트 사용)
– OES (Odd / Even parity Select) (0:홀수 / 1:짝수)
– SBS : Stop Bit Select (0:1비트 / 1:2비트)
– DSS : Data Size Select (0:7비트 / 1:8비트)
– SCE : Sample Clock Enable (0:내부클럭 / 1:외부클럭(GPIO20))
– RCE : Receive Clock Edge Select (0:상승에지 / 1:하강에지)
– TCE : Transmit Clock Edge Select (0:상승에지 / 1:하강에지)
TCE RCE SCE DSS SBS OES PERESERVED
7 6 5 4 3 2 1 0
0 ? ? ? ? ? ? ?
Hanbat National University Prof. Lee Jaeheung
UART 레지스터? UTCR 1, 2 통신 속도를 설정하는 레지스터UTCR 1
UTCR 2
*내부클럭 사용시… .BaudRate = (3.6864 X 10^6) / (16 X (BRD + 1))BRD = (3.6864 X 10^6) / (16 X (BaudRate ) - 1*만약 9600baud 이면 …
BRD = (3.6864 X 10^6) / (16 X (9600 ) – 1 = 23
BRD 11 BRD 10 BRD 9 BRD 8RESERVED
7 6 5 4 3 2 1 0
BRD 6 BRD 5 BRD 4 BRD 3 BRD 2 BRD 1 BRD 0BRD 7
7 6 5 4 3 2 1 0
0 0 0 0 ? ? ? ?
0 ? ? ? ? ? ? ?
Hanbat National University Prof. Lee Jaeheung
UART 레지스터? UTCR (UART Control Register 3)
– RXE : Receiver Enable ( 0:수신불가 / 1:수신가능)
– TXE : Transmitter Enable ( 0:송신불가 / 1:송신가능)
– BRK : Break ( 0:정상처리 / 1:송신핀 LOW처리)
– RIE : Receive FIFO Interrupt Enable
( 0:수신 큐가 찼을 때 인터럽트X / 1:수신 큐가 찼을 때 인터럽트 발생)
– TIE : Transmit FIFO Interrupt Enable
( 0:송신 큐가 비었을 때 인터럽트X / 1:송신 큐가 비었을 때 인터럽트 발생)
– LBM : Loopback Mode ( 0:정상 처리 / 1:루프백 모드)
LBM TIE RIE BRK TXE RXERESERVED
7 6 5 4 3 2 1 0
0 0 ? ? ? ? ? ?
Hanbat National University Prof. Lee Jaeheung
UART 레지스터? UTDR (UART Data Register)
* ROR, FRE, PRE가 1이면 에러 발생
– ROR (Receive Over Run Error) : 수신처리가 되기 전에 다음 데이터가
수신 되었을 때 High 표시
– FRE (Framing Error) : 수신 신호 파형이 비정상일 때 High 표시
– PRE (Parity Error) : 패리티 에러 발생시 High 표시
ROR
7 6 5 4 3 2 1 0
FRE PRE 송 수신 DATA
8910
? ? ? ? ? ? ? ????
Hanbat National University Prof. Lee Jaeheung
UART 레지스터? UTSR0(UART State Register)
– TFS (Transmit FIFO Service Request Flag) : 송신 FIFO에 Write 가능 시 High
– RFS (Receiver FIFO Service Request Flag) : 수신 FIFO에 데이터 있을 때 High
– RID (Receiver Idle Status) : 수신 FIFO처리 데이터가 없을 때 High
– RBB (Receiver Begin of Break Status) : Break신호 검지 시 High
– REB (Receiver End of Break Status) : Break 신호 끝 검지 시 High
– EIF (Error in FIFO Flag) : 처음 수신된 4개 데이터 중 에러 시 High
EIF REB RBB RID RFS TFSRESERVED
7 6 5 4 3 2 1 0
0 0 ? ? ? ? ? ?
Hanbat National University Prof. Lee Jaeheung
UART 레지스터? UTSR1(UART State Register)
– TBY (Transmitter Busy Flag) : 데이터를 전송 시 High
– RNE (Receive FIFO Not Empty Flag) : 수신 FIFO가 비어 있으면 High
– TNF (Transmit FIFO Not Full Flag) : 송신 FIFO가 꽉 찼을 때 High
– PRE (Parity Error) : 패리티 에러 검출 시 High
– FRE (Framing Error) : 파형 에러 검출 시 High
– ROR (Receive Over Run Error) : 오버런 에러 검출 시 High
ROR FRE PRE TNF RNE TBYRESERVED
7 6 5 4 3 2 1 0
0 0 ? ? ? ? ? ?
Hanbat National University Prof. Lee Jaeheung
Blocking I/O
? Blocking, Nonblocking
- Blocking 동작
-프로세스가 read를 호출했을 때, 데이터가 아직 없으면, 프로세스는 블록
데이터가 도착하면 프로세스 활성화
-프로세스가 write를 호출했을 때, 버퍼에 여유가 없으면, 프로세스는 블록
출력버퍼에 여유공간이 생기면 프로세스 활성화
-Nonblocking
-Read / write 호출시 read / write가 불가능하면 기다리지 않고 바로
–EAGIN값을 반환
-Nonblock 모드로 열기
-open(“/dev/serial”, O_RDWR | O_NONBOCK);
Hanbat National University Prof. Lee Jaeheung
Blocking I/O? sleep_on / wake_up
#include <linux/sched.h>
void interruptible_sleep_on(struct wait_queue **q)
void sleep_on(struct wait_queue **q)
void wake_up_interruptible(struct wait_queue **q)
void wake_up(struct wait_queue **q)
? Waiting queue
- 프로세스를 sleep 상태로 만들 수 있는 각 사건마다 struct wait_queue*
변수를 선언
- 이 변수의 포인터를 sleep_on과 wake_up 함수에 인자로 전달
Hanbat National University Prof. Lee Jaeheung
Blocking I/Ostruct wait_queue *wq = NULL;
read_write_t sleepy_read(struct inode *inode, struct file *filp, char *buf,
count_t count)
{
if ( filp -> f_flags & O_NONBLOCK){
printk(KERN_DEBUG”procesor %i is nonblocking mode \n”, current -> pid_;
return –EAGAIN;
}else{
printk(KERN_DEBUG “process %i (%s) going to sleep\n”,urrent_pid, courrent -> comm);
interruptible_sleep_on(&wq);
}
return 0;
}
read_write_t sleepy_write(struct inode *inode, struct file *filp, const char *buf, count_t cout)
{
printk(KERN_DEBUG “ process %i (%s) (&commn);
wake_up_interrupt (&wq);
return count;
}
Hanbat National University Prof. Lee Jaeheung
UART Device Driver
processInput ring buffer
Output ring buffer
read()
write()
UART
rx_isr
tx_isr
inq
outq
sleep_on() wake_up()
wake_up()sleep_on()
rp wp
rpwp
xmit
Hanbat National University Prof. Lee Jaeheung
S5N8946 Device driver 초기화#define UART_BASE(BASE_ADDR+0xd000)
struct uart_regs{
u_32 ulcon; /* line control register */
u_32 ucon; /* control register */
u_32 ustat; /* status register */
u_32 utxbuf; /* transmit buffer register */
u_32 urxvuf; /* receive buffer register */
u_32 ubrdiv; /* baud rate divisor register */
u_32 brdcnt; /* baud rate count register */
u_32 brdclk; /* baud rate clock monitor */
};
Hanbat National University Prof. Lee Jaeheung
S5N8946 Device driver 초기화/* UART line control register */
#define UART_WL(x) ((x) & 0x3) /* 00:5bit, 01:6bit, 10:7bit, 11:8bit */
#define UART_STB (1<<2) /* stop bit, 0:one, 1:two */
#define UART_PMD(x) ((x)<<3 & 0x38) /* parity,0xx:no, 100:odd, 101:even */
#define UART_SC (1<<6) /* serial clock, 0:internal 1:external */
#define UART_IR (1<<7) /* infra-red mode */
/* UART control register */
#define UART_RxM(x) ((x) & 0x3) /*receive mode, 00:disable, 01: interrupt, 10:GDMA ch0,
11:GDMA ch1*/
#define UART_RXSI (1<<2) /* receive status interrupt enable */
#define UART_TxM(x) ((x)<<3 & 0x18) /* transmit mode, same as UART_RxM(x) */
#define UART_DSR (1<<5) /* data set ready */
#define UART_SBK (1<<6) /* send break */
#define UART_LPM (1<<7) /* look-back mode */
Hanbat National University Prof. Lee Jaeheung
S5N8946 Device driver 초기화
/* UART status register */
#define UART_OV (1) /* overrun error */
#define UART_PE (1<<1) /* parity error */
#define UART_FE (1<<2) /* frame error */
#define UART_BKD (1<<3) /* break interrupt */
#define UART_DTR (1<<4) /* data terminal ready */
#define UART_RDR (1<<5) /* receive data ready */
#define UART_TBE (1<<6) /* Tx buffer register empty */
#define UART_TC (1<<7) /* transmit complete */
Hanbat National University Prof. Lee Jaeheung
S5N8946 Device driver 초기화SERIAL_MAJOR = 150;
struct file_operations sserial_fops = {
read: sserial_read, write:sserial_write, open: sserial_open,
release : sserial_release};
int sserial_init(void)
{
int result;
result=register_chrdev(SERIAL_MAJOR , "serial",&sserial_fops);
if(result<0){
printk(“sserial:can't get major %d\n", SERIAL_MAJOR );
return result;
}
return 0;
}
Hanbat National University Prof. Lee Jaeheung
S5N8946 Data structure? Data structure for device
#define MAX_SIZE 48
typedef struct sserial_dev_t {
struct wait_queue *inq, *outq;
char inbuf[MAX_SIZE];
char outbuf[MAX_SIZE];
int inrp, inwp;
int outrp, outwp;
int buf_empty;
}sserial_dev_t;
sserial_dev_t sserial_dev;
Hanbat National University Prof. Lee Jaeheung
S5N8946 Open function ? sserial_open
– Device data structure 초기화– Device 초기화– Register interrupt service routines
int sserial_open(struct inode *inode, struct file *filp)
{
sserial_dev_t *dev;
dev = &sserial_dev;
memset(dev, 0, sizeof(serial_dev_t));
dev->buf_empty = 1; filp->private_data = dev;
s5n8946_sserial_init();
request_irq(5, sserial_rx_isr, 0, “sserial_dev_rx", NULL);
request_irq(4, sserial_tx_isr, 0, “sserial_dev_tx", NULL);
return 0;
}
Hanbat National University Prof. Lee Jaeheung
S5N8946 Release function? sserial_release
– Flush data in write buffer– Unregister interrupt
void sserial_release ( struct inode *inode, struct file *filp)
{
sserial_dev_t *dev;
dev = filp->private_data;
while(dev->outwp != dev->outrp) {
interruptible_sleep_on(&(dev->outq));
}
free_irq(5, NULL);
free_irq(4, NULL);
}
Hanbat National University Prof. Lee Jaeheung
S5N8946 Read functionint sserial_read(struct inode *inode, struct file *filp, char *buf, int count){
sserial_dev_t *dev;char *b2;int i;
dev = filp->private_data;b2 = kmalloc(count, GFP_ATOMIC);
for(i=0; i<count;) {if(dev->inwp!=dev->inrp) {
b2[i] = dev->inbuf[dev->inrp];dev->inrp = (dev->inrp+1)%MAX_SIZE;i++;}
else{interruptible_sleep_on(&(dev->inq));
}}memcpy_tofs(buf, b2, count);kfree(b2);return count;
}
Hanbat National University Prof. Lee Jaeheung
S5N8946 Write function(1)
int sserial_write(struct inode *inode, struct file *filp, const char *buf, int count){
volatile struct uart_regs *uart = (volatile struct uart_regs*)UART_BASE;sserial_dev_t *dev;char *b2;int i;register unsigned long flags;
if(count == 0) return 0;
dev = filp->private_data;b2 = kmalloc(count, GFP_ATOMIC);memcpy_fromfs(b2, buf, count);
Hanbat National University Prof. Lee Jaeheung
S5N8946 Write function(2)
for(i=0; i<count;) {if(((dev->outwp+1)%MAX_SIZE) == dev->outrp) {
sserial_tx_go(dev);interruptible_sleep_on(&(dev->outq));
}else{
dev->outbuf[dev->outwp]=b2[i];dev->outwp=(dev->outwp+1) %MAX_SIZE;i++;
}}kfree(b2);sserial_tx_go(dev);
return count;}
Hanbat National University Prof. Lee Jaeheung
S5N8946 sserial_tx_go functionvoid sserial_tx_go(sserial_dev_t *dev)
{
volatile struct uart_regs *uart = (volatile struct uart_regs *)UART_BASE;
register unsigned long flags;
if(dev->buf_empty) {
save_flags(flags);
cli();
dev->buf_empty = 0;
if(uart->ustat & UART_TBE) {
uart->utxbuf = dev->outbuf[dev->outrp];
dev->outrp = (dev->outrp + 1) % MAX_SIZE;
}
restore_flags(flags);
}
}
Hanbat National University Prof. Lee Jaeheung
S5N8946 sserial_rx_isr functionvoid sserial_rx_isr(int irq, void *data, struct pt_regs *reg)
{
volatile struct uart_regs *uart = (volatile struct uart_regs )UART_BASE;
sserial_dev_t *dev;
dev = &sserial_dev;
if(uart->ustat & UART_RDR) {
if( ((dev->inwp+1)%MAX_SIZE) == dev->inrp) {}
else {
dev->inbuf[dev->inwp] = uart->urxbuf;
dev->inwp = (dev->inwp +1) % MAX_SIZE;
}
}
wake_up_interruptible(&(dev->inq));
}
Hanbat National University Prof. Lee Jaeheung
S5N8946 sserial_tx_isr functionvoid sserial_tx_isr(int irq, void *data, struct pt_regs *reg){
volatile struct uart_regs *uart = (volatile struct uart_regs *)UART_BASE;sserial_dev_t *dev;register unsigned long flags;
dev = &sserial_dev;if(dev->outwp == dev->outrp) {
save_flags(flags);cli();dev->buf_empty = 1;restore_flags(flags);
}else {
if(uart->ustat & UART_TBE) {uart->utxbuf = dev->outbuf[dev->outrp];dev->outrp = (dev->outrp + 1) % MAX_SIZE;
}}wake_up_interruptible(&(dev->outq));
}
Hanbat National University Prof. Lee Jaeheung
Kernel에 등록
% cp sserial.c ~/linux/drivers/char/
int char_dev_init(void){
… … …#ifdef CONFIG_SSERIAL
sserial_init();#endif
… … …}
? Source file 복사
? Kernel에 등록– vi ~/linux/drivers/char/mem.c
? vi ~/linux/drivers/char/Makefile
? vi ~/linux/drivers/char/Config.inv
ifeq ($(CONFIG_SSERIAL),y)L_OBJS += sserial.o
endif
bool ‘Sserial device support’CONFIG_SSERIAL
Hanbat National University Prof. Lee Jaeheung
Module 형태의 등록
init_module(void){
sserial_init();}
cleanup_module(void){
sserial_release ();}
? Module의 초기 호출함수 추가
Hanbat National University Prof. Lee Jaeheung
SA1110 레지스터 정의? Source에서의 레지스터 정의(include/asm/arch/SA-1100.h)
#define _UTCR0(Nb) __REG(0x80010000 + ((Nb) -1)*0x00020000) /* UART Control Reg. 0 [1..3] */#define _UTCR1(Nb) __REG(0x80010004 + ((Nb) - 1)*0x00020000) /* UART Control Reg. 1 [1..3] */#define _UTCR2(Nb) __REG(0x80010008 + ((Nb) - 1)*0x00020000) /* UART Control Reg. 2 [1..3] */#define _UTCR3(Nb) __REG(0x8001000C + ((Nb) - 1)*0x00020000) /* UART Control Reg. 3 [1..3] */#define _UTCR4(Nb) __REG(0x80010010 + ((Nb) - 1)*0x00020000) /* UART Control Reg. 4 [2] */#define _UTDR(Nb) __REG(0x80010014 + ((Nb) - 1)*0x00020000) /* UART Data Reg. [1..3] */#define _UTSR0(Nb) __REG(0x8001001C + ((Nb) - 1)*0x00020000) /* UART Status Reg. 0 [1..3] */#define _UTSR1(Nb) __REG(0x80010020 + ((Nb) - 1)*0x00020000) /* UART Status Reg. 1 [1..3] */
#define Ser1UTCR0 _UTCR0 (1) /* Ser. port 1 UART Control Reg. 0 */#define Ser1UTCR1 _UTCR1 (1) /* Ser. port 1 UART Control Reg. 1 */#define Ser1UTCR2 _UTCR2 (1) /* Ser. port 1 UART Control Reg. 2 */#define Ser1UTCR3 _UTCR3 (1) /* Ser. port 1 UART Control Reg. 3 */#define Ser1UTDR _UTDR (1) /* Ser. port 1 UART Data Reg. */#define Ser1UTSR0 _UTSR0 (1) /* Ser. port 1 UART Status Reg. 0 */#define Ser1UTSR1 _UTSR1 (1) /* Ser. port 1 UART Status Reg. 1 */
#define Ser2UTCR0 _UTCR0 (2) /* Ser. port 2 UART Control Reg. 0 */#define Ser2UTCR1 _UTCR1 (2) /* Ser. port 2 UART Control Reg. 1 */#define Ser2UTCR2 _UTCR2 (2) /* Ser. port 2 UART Control Reg. 2 */#define Ser2UTCR3 _UTCR3 (2) /* Ser. port 2 UART Control Reg. 3 */#define Ser2UTCR4 _UTCR4 (2) /* Ser. port 2 UART Control Reg. 4 */#define Ser2UTDR _UTDR (2) /* Ser. port 2 UART Data Reg. */#define Ser2UTSR0 _UTSR0 (2) /* Ser. port 2 UART Status Reg. 0 */#define Ser2UTSR1 _UTSR1 (2) /* Ser. port 2 UART Status Reg. 1 */
. . .
. . .
Hanbat National University Prof. Lee Jaeheung
SA1110 Data Structure
struct uart_port {
u_int iobase; /* in/out[bwl] */
void *membase; /* read/write[bwl] */
u_int irq;
u_int uartclk;
u_char fifosize; /* tx fifo size */
u_char x_char;
u_char regshift ; /* reg offset shift */
u_char iotype; /* io access style */
u_char hub6;
u_char unused[7];
u_int read_status_mask;
u_int ignore_status_mask;
u_int flags;
u_int type; /* port type */
struct uart_ops *ops;
struct uart_icount icount ;
u_int line;
u_long mapbase; /* for ioremap */
};
struct uart_info {
spinlock_t lock;
struct uart_port *port;
struct uart_ops *ops;
struct uart_state *state;
struct tty_struct *tty;
struct circ_buf xmit ;
u_int flags;
u_int event;
u_int timeout;
u_int mctrl;
u_int driver_priv;
int blocked_open;
pid_t session;
pid_t pgrp;
struct tasklet_struct tlet;
wait_queue_head_t open_wait;
wait_queue_head_t close_wait;
wait_queue_head_t delta_msr_wait ;
struct uart_info *next_info;
};
Hanbat National University Prof. Lee Jaeheung
SA1110 Data Structure
struct uart_ops {
u_int (*tx_empty)(struct uart_port *);
void (*set_mctrl)(struct uart_port *, u_int mctrl);
u_int (*get_mctrl)(struct uart_port *);
void (*stop_tx)(struct uart_port *, u_int from_tty);
void (*start_tx)(struct uart_port *, u_int nonempty, u_int from_tty);
void (*stop_rx)(struct uart_port *);
void (*enable_ms)(struct uart_port *);
void (*break_ctl)(struct uart_port *, int ctl);
int (*startup)(struct uart_port *, struct uart_info *);
void (*shutdown)(struct uart_port *, struct uart_info *);
void (*change_speed)(struct uart_port *, u_int cflag, u_int iflag, u_intquot);
void (*pm)(struct uart_port *, u_int state, u_int oldstate);
int (*set_wake)(struct uart_port *, u_int state);
const char *(*type)(struct uart_port *);
void (*release_port)(struct uart_port *);
int (*request_port)(struct uart_port *);
void (*config_port)(struct uart_port *, int);
int (*verify_port)(struct uart_port *, struct serial_struct *);
int (*ioctl)(struct uart_port *, u_int , u_long);
};
struct uart_driver {
struct module *owner;
int normal_major;const char *normal_name;
struct tty_driver *normal_driver;
int callout_major;const char *callout_name;
struct tty_driver *callout_driver;
struct tty_struct **table;struct termios **termios;
struct termios **termios_locked;
int minor;int nr;
struct uart_state *state; /* driver should pass NULL */
struct uart_port *port; /* array of port information */struct console *cons;
};struct termios {
tcflag_t c_iflag; /* input mode flags */tcflag_t c_oflag; /* output mode flags */tcflag_t c_cflag; /* control mode flags */tcflag_t c_lflag; /* local mode flags */cc_t c_line; /* line discipline */cc_t c_cc[NCCS]; /* control characters */speed_t c_ispeed; /* input speed */speed_t c_ospeed; /* output speed */
};
Hanbat National University Prof. Lee Jaeheung
serial_sa1100.c
static struct uart_driver sa1100_reg = {
owner: THIS_MODULE,
normal_major: SERIAL_SA1100_MAJOR,
normal_name: "test",
callout_name:"cusa",
normal_driver: &normal,
callout_major: CALLOUT_SA1100_MAJOR,
callout_driver: &callout,
table: sa1100_table,
termios: sa1100_termios,
termios_locked: sa1100_termios_locked,
minor: MINOR_START,
nr: NR_PORTS,
port: sa1100_ports,
};
static int __init sa1100_serial_init(void){
sa1100_init_ports();return uart_register_driver(&sa1100_reg);
}
? Driver Initializationstatic struct uart_ops sa1100_pops = {
tx_empty: sa1100_tx_empty,
set_mctrl: sa1100_set_mctrl,
get_mctrl: sa1100_get_mctrl,
stop_tx: sa1100_stop_tx,
start_tx: sa1100_start_tx,
stop_rx: sa1100_stop_rx,
enable_ms: sa1100_enable_ms,
break_ctl: sa1100_break_ctl,
startup: sa1100_startup,
shutdown: sa1100_shutdown,
change_speed: sa1100_change_speed,
type: sa1100_type,
release_port: sa1100_release_port,
request_port: sa1100_request_port,
config_port: sa1100_config_port,
verify_port: sa1100_verify_port,
};
Hanbat National University Prof. Lee Jaeheung
serial_sa1100.c? Driver Initialization
static void sa1100_init_ports(void){
static int first = 1;
int i;
if (!first)return;
first = 0;for (i = 0; i < NR_PORTS; i++) {
sa1100_ports[i].uartclk = 3686400; sa1100_ports[i].ops = &sa1100_pops;sa1100_ports[i].fifosize = 8;
}
sa1100_register_uart(0,1); //실질적 등록
Ser1SDCR0 |= SDCR0_UART; //1번 포트를 UART로
Ser2UTCR4 = 0; //2번 포트는 사용하지 않음
Ser2HSCR0 = 0;}
void __init sa1100_register_uart_fns(struct sa1100_port_fns *fns){
if (fns->enable_ms)sa1100_pops.enable_ms = fns->enable_ms;
if (fns->get_mctrl)sa1100_pops.get_mctrl = fns->get_mctrl;
if (fns->set_mctrl)sa1100_pops.set_mctrl = fns->set_mctrl;sa1100_open = fns->open;sa1100_close = fns->close;sa1100_pops.pm = fns->pm;sa1100_pops.set_wake = fns->set_wake;
}
void __init sa1100_register_uart(int idx, int port){
if (idx >= NR_PORTS) {printk(KERN_ERR __FUNCTION__ ": bad index number %d\n",
idx);return;}
sa1100_ports[idx].membase = (void *)&Ser1UTCR0; //UART 1 사용
sa1100_ports[idx].mapbase = _Ser1UTCR0;sa1100_ports[idx].irq = IRQ_Ser1UART;sa1100_ports[idx].iotype = SERIAL_IO_MEM;sa1100_ports[idx].flags = ASYNC_BOOT_AUTOCONF;
}
Hanbat National University Prof. Lee Jaeheung
serial_sa1100.c
static int sa1100_verify_port(struct uart_port *port, struct serial_struct *ser){
int ret = 0;if (ser->type != PORT_UNKNOWN && ser->type != PORT_SA1100)
ret = -EINVAL;if (port->irq != ser->irq)
ret = -EINVAL;if (ser->io_type != SERIAL_IO_MEM)
ret = -EINVAL;if (port->uartclk / 16 != ser->baud_base)
ret = -EINVAL;if ((void *)port->mapbase != ser->iomem_base)
ret = -EINVAL;if (port->iobase != ser->port)
ret = -EINVAL;if (ser->hub6 != 0)
ret = -EINVAL;return ret;
}
? Port Verification function
Hanbat National University Prof. Lee Jaeheung
serial_sa1100.c
static int sa1100_startup(struct uart_port *port, struct uart_info *info){
int retval;
retval = request_irq(port->irq, sa1100_int, 0, "serial_test", info);// irq=15, name:serial_test
if (retval)return retval;
if (sa1100_open) {retval = sa1100_open(port, info);if (retval) {
free_irq(port->irq, info);return retval;
}}
/** Finally, clear and enable interrupts*/UART_PUT_UTSR0(port, -1);UART_PUT_UTCR3(port, UTCR3_RXE | UTCR3_TXE | UTCR3_RIE);
return 0;}
? startup function
Hanbat National University Prof. Lee Jaeheung
serial_sa1100.c
static void sa1100_int(int irq, void *dev_id, struct pt_regs *regs){
struct uart_info *info = dev_id;struct uart_port *port = info->port;unsigned int status, pass_counter = 0;
status = UART_GET_UTSR0(port);status &= (SM_TO_UTSR0(port->read_status_mask) | ~UTSR0_TFS);
do {if (status & (UTSR0_RFS | UTSR0_RID)) {
/* Clear the receiver idle bit, if set */if (status & UTSR0_RID)
UART_PUT_UTSR0(port, UTSR0_RID);sa1100_rx_chars(info, regs);
}/* Clear the relevent break bits */
if (status & (UTSR0_RBB | UTSR0_REB))UART_PUT_UTSR0(port, status & (UTSR0_RBB | UTSR0_REB));
? Interrupt Service Routine
if (status & UTSR0_RBB)port->icount.brk++;
if (status & UTSR0_TFS)sa1100_tx_chars(info);
if (pass_counter++ > SA1100_ISR_PASS_LIMIT)break;
status = UART_GET_UTSR0(port);status &= (SM_TO_UTSR0(port->read_status_mask)
| ~UTSR0_TFS);} while (status & (UTSR0_TFS | UTSR0_RFS | UTSR0_RID));}
Hanbat National University Prof. Lee Jaeheung
serial_sa1100.c
static void sa1100_start_tx(struct uart_port *port, u_int nonempty, u_int from_tty){
if (nonempty) {unsigned long flags;u32 utcr3;
local_irq_save(flags);utcr3 = UART_GET_UTCR3(port);
port->read_status_mask |= UTSR0_TO_SM(UTSR0_TFS);UART_PUT_UTCR3(port, utcr3 | UTCR3_TIE);local_irq_restore(flags);
}}
static void sa1100_stop_tx(struct uart_port *port, u_int from_tty){
u32 utcr3 = UART_GET_UTCR3(port);UART_PUT_UTCR3(port, utcr3 & ~UTCR3_TIE);port->read_status_mask &= ~UTSR0_TO_SM(UTSR0_TFS);
}
? sa1100_start_tx, sa1100_stop_tx function
Hanbat National University Prof. Lee Jaeheung
serial_sa1100.c
static void sa1100_tx_chars(struct uart_info *info){
struct uart_port *port = info->port;
if (port->x_char) {UART_PUT_CHAR(port, port->x_char);
port->icount.tx++;port->x_char = 0;return;
}if (info->xmit.head == info->xmit.tail
|| info->tty->stopped
|| info->tty->hw_stopped) {sa1100_stop_tx(info->port, 0);return;
}
/*
* Tried using FIFO (not checking TNF) for fifo fill:* still had the '4 bytes repeated' problem.*/
? sa1100_tx_chars function
while (UART_GET_UTSR1(port) & UTSR1_TNF) {UART_PUT_CHAR(port, info->xmit.buf[info->xmit.tail]);
info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE - 1);port->icount.tx++;if (info->xmit.head == info->xmit.tail)
break;}
if (CIRC_CNT(info->xmit.head, info->xmit.tail, UART_XMIT_SIZE) <WAKEUP_CHARS)
uart_event(info, EVT_WRITE_WAKEUP);
if (info->xmit.head == info->xmit.tail)sa1100_stop_tx(info->port, 0);
}
Hanbat National University Prof. Lee Jaeheung
serial_sa1100.c
sa1100_rx_chars(struct uart_info *info, struct pt_regs *regs){
struct tty_struct *tty = info->tty;unsigned int status, ch, flg, ignored = 0;struct uart_port *port = info->port;
status = UTSR1_TO_SM(UART_GET_UTSR1(port)) | UTSR0_TO_SM(UART_GET_UTSR0(port));while (status & UTSR1_TO_SM(UTSR1_RNE)) {
ch = UART_GET_CHAR(port);
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
goto ignore_char;port->icount.rx++;flg = TTY_NORMAL;if (status & UTSR1_TO_SM(UTSR1_PRE |
UTSR1_FRE | UTSR1_ROR))goto handle_error;
if (uart_handle_sysrq_char(info, ch, regs))goto ignore_char;
? sa1100_rx_chars function(1)
error_return:*tty->flip.flag_buf_ptr ++ = flg;*tty->flip.char_buf_ptr ++ = ch;tty->flip.count++;ignore_char:
status = UTSR1_TO_SM(UART_GET_UTSR1(port)) | UTSR0_TO_SM(UART_GET_UTSR0(port));
}out:
tty_flip_buffer_push(tty);return;
handle_error:if (status & UTSR1_TO_SM(UTSR1_PRE))
port->icount.parity++;else if (status & UTSR1_TO_SM(UTSR1_FRE))
port->icount.frame++;if (status & UTSR1_TO_SM(UTSR1_ROR))
port->icount.overrun++;if (status & port->ignore_status_mask) {
if (++ignored > 100)goto out;
goto ignore_char;}
Hanbat National University Prof. Lee Jaeheung
serial_sa1110.c
status &= port->read_status_mask;
if (status & UTSR1_TO_SM(UTSR1_PRE))flg = TTY_PARITY;
else if (status & UTSR1_TO_SM(UTSR1_FRE))flg = TTY_FRAME;
if (status & UTSR1_TO_SM(UTSR1_ROR)) {
/** overrun does *not* affect the character* we read from the FIFO*/*tty->flip.flag_buf_ptr ++ = flg;*tty->flip.char_buf_ptr ++ = ch;
tty->flip.count++;if (tty->flip.count >= TTY_FLIPBUF_SIZE)
goto ignore_char;ch = 0;flg = TTY_OVERRUN;
}
goto error_return;}
? sa1100_rx_chars function(2)
Hanbat National University Prof. Lee Jaeheung
serial_sa1100.c
static void sa1100_stop_rx(struct uart_port *port)
{
u32 utcr3 = UART_GET_UTCR3(port);
UART_PUT_UTCR3(port, utcr3 & ~UTCR3_RIE);
}
static void __exit sa1100_serial_exit(void)
{
uart_unregister_driver(&sa1100_reg);
}
? sa1100_stop_rx, sa1100_serial_exit function
Hanbat National University Prof. Lee Jaeheung
Serial_test_host.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>
#include <string.h>#include <fcntl.h>#include <termios.h>#include <sys/ioctl.h>
int main( int argc, char **argv ){
int sd; struct termios oldtio,newtio;
char Buff[255]; int DataCount ;int ending;
sd = open( "/dev/ttyS0", O_RDWR | O_NOCTTY );if(sd < 0 ) {
printf( "Serial Open Fail [/dev/ttyS00]\r\n " );exit(0);
}
tcgetattr(sd, &oldtio );memset( &newtio, 0, sizeof(newtio) );
cfsetispeed(&newtio,B115200);cfsetospeed(&newtio,B115200);
newtio.c_cflag &= ~PARENB; newtio.c_cflag &= ~CSTOPB; newtio.c_cflag &= ~CSIZE ; newtio.c_cflag |= CS8 | CLOCAL | CREAD ; newtio.c_iflag = IGNPAR;newtio.c_oflag &= ~OPOST;newtio.c_lflag = 0; newtio.c_cc[VTIME] = 30; newtio.c_cc[VMIN] = 0;
tcflush(sd, TCIFLUSH );tcsetattr(sd, TCSANOW, &newtio );
ending = 0;while(!ending){scanf("%s",Buff);
write(sd, Buff, strlen( Buff ) ); } tcsetattr(sd, TCSANOW, &oldtio ); close(sd); return 0;
}
Hanbat National University Prof. Lee Jaeheung
Serial_test_SA1110.c#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <fcntl.h>#include <termios.h>#include <sys/ioctl.h>#define BAUDRATE B115200 //termios.h에 정의
int main( int argc, char **argv ){
int sd; struct termios oldtio,newtio;
char Buff[255];int DataCount ;int ending;
/* 읽기/쓰기 모드로 모뎀 장치를 연다.(O_RDWR) 데이터 전송 시에 <CTRL>-C 문자가 오면 프로그램이 종료되지 않도록 하기 위해 controlling tty가 안되도록 한다.(O_NOCTTY) */
sd = open( "/dev/test0", O_RDWR | O_NOCTTY );if(sd < 0 ) {
//시리얼 장치 열기 실패printf( "Serial Open Fail [/dev/test0]\r\n " );exit(0);
}
tcgetattr( sd, &oldtio ); //설정을 oldtio에 저장memset( &newtio, 0, sizeof(newtio) ); //newtio 만큼 메모리 설정
cfsetispeed(&newtio, BAUDRATE); //시리얼 입력 스피드cfsetospeed(&newtio, BAUDRATE); //시리얼 출력 스피드
newtio.c_cflag &= ~PARENB; //패리티 enablenewtio.c_cflag &= ~CSIZE ; //mask character sizebitnewtio.c_cflag &= ~CSTOPB; //stop bit enablenewtio.c_cflag |= CS8 | CLOCAL | CREAD ;
//8N1 , 포트변경무시, 문자수신 가능 설정newtio.c_iflag = IGNPAR; //parity error 문자 무시newtio.c_oflag &= ~OPOST; //출력 금지 설정newtio.c_lflag = 0; //canonical 입력 가능 설정newtio.c_cc[VTIME] = 30; //time_out 값 설정 : TIME*0.1초newtio.c_cc[VMIN] = 0; //최소문자 설정tcflush( sd, TCIFLUSH ); //설정 종료tcsetattr( sd, TCSANOW, &newtio ); //포트 입력 시작ending = 0;while(!ending){DataCount = read( sd, Buff, 255 );
//datacount는 입력 받은 문자 개수if(DataCount < 0 ) { printf( "Read Error\n" );
break;}if(DataCount != 0 ) { Buff[DataCount] = 0;
printf( "Read Data [%s]\n", Buff );if( Buff[0] == 'x' ) ending = 1; //x입력 시 종료
} tcsetattr( sd, TCSANOW, &oldtio ); //설정 값 복구close( sd ); //장치 종료return 0; }
Hanbat National University Prof. Lee Jaeheung
실습 방법? serial_sa1100.c 모듈로 컴파일? Serial_test_host.c gcc 컴파일
? Serial_test_SA1110.c arm-linux-gcc 컴파일? 보드에 serial_sa1100.o, Serial_test_SA1110 다운로드
? serial_sa1100.o모듈 추가? 보드에서 Serial_test_SA1110 실행? Host PC에서 Serial_test_host 실행? Host PC에서 데이터 입력
? 보드로 데이터 입력되는지 확인