簡介
RS232是kernel中常用到的一種週邊元件,在以前主要是拿來連接終端設備的通訊介面,而今日則是常被拿來做控制或者和其它設備通訊的介面,因為其硬體簡單及成本低所以會被很多設備做為最基本的通訊介面,因為其發展有很長一段時間,所以在Linux中有為它特別規劃一組軟體堆疊,主要分為實體控制層以及上層的終端控制層,其架構如下:
初始化
所以通常我們要寫的是實體層的驅動程式,在driver的進入點,若是PCI介面,則先註冊一個PCI的call back function,若是local bus則是可以註冊一個platform device call back function,當系統進入你所註冊的call back function,必須對你所使用的硬體介面位址做一個remapping的動作,就是將實體位址轉換成虛擬位址,接著後面的所有程式碼都要使用這個虛擬位址,這時要開始初始化你的硬體及driver,首先建立一個struct tty_driver的資料結構,其宣告如下:
struct tty_driver {
int magic; /* magic number for this structure */
struct cdev cdev;
struct module *owner;
const char *driver_name;
const char *name;
int name_base; /* offset of printed name */
int major; /* major device number */
int minor_start; /* start of minor device number */
int minor_num; /* number of *possible* devices */
int num; /* number of devices allocated */
short type; /* type of tty driver */
short subtype; /* subtype of tty driver */
struct ktermios init_termios; /* Initial termios */
int flags; /* tty driver flags */
int refcount; /* for loadable tty drivers */
struct proc_dir_entry *proc_entry; /* /proc fs entry */
struct tty_driver *other; /* only used for the PTY driver */
/*
* Pointer to the tty data structures
*/
struct tty_struct **ttys;
struct ktermios **termios;
struct ktermios **termios_locked;
void *driver_state; /* only used for the PTY driver */
/*
* Interface routines from the upper tty layer to the tty
* driver. Will be replaced with struct tty_operations.
*/
int (*open)(struct tty_struct * tty, struct file * filp);
void (*close)(struct tty_struct * tty, struct file * filp);
int (*write)(struct tty_struct * tty,
const unsigned char *buf, int count);
void (*put_char)(struct tty_struct *tty, unsigned char ch);
void (*flush_chars)(struct tty_struct *tty);
int (*write_room)(struct tty_struct *tty);
int (*chars_in_buffer)(struct tty_struct *tty);
int (*ioctl)(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg);
long (*compat_ioctl)(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg);
void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
void (*throttle)(struct tty_struct * tty);
void (*unthrottle)(struct tty_struct * tty);
void (*stop)(struct tty_struct *tty);
void (*start)(struct tty_struct *tty);
void (*hangup)(struct tty_struct *tty);
void (*break_ctl)(struct tty_struct *tty, int state);
void (*flush_buffer)(struct tty_struct *tty);
void (*set_ldisc)(struct tty_struct *tty);
void (*wait_until_sent)(struct tty_struct *tty, int timeout);
void (*send_xchar)(struct tty_struct *tty, char ch);
int (*read_proc)(char *page, char **start, off_t off,
int count, int *eof, void *data);
int (*write_proc)(struct file *file, const char __user *buffer,
unsigned long count, void *data);
int (*tiocmget)(struct tty_struct *tty, struct file *file);
int (*tiocmset)(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear);
struct list_head tty_drivers;
};
在這個資料結構中其中分為幾個部分,第一個部分是所以謂的callback function的註冊,在這些個callback function中你必須金對你有實現的部分給予註冊,若沒有就可以給予不註冊,但相對的你的驅動程式所支援的功能會較少。另一個部分則是驅動程式本身資訊的資料,如major number、starting minor number、tty device name等等,以下是一個例子:
struct tty_driver MySerialDriver;
#define MAX_PORTS 4
struct ktermios *my_termios[MAX_PORTS];
struct ktermios *my_termios_locked[MAX_PORTS];
memset(&MySerialDriver, 0 sizeof(MySerialDriver));
MySerialDriver.name = "ttyN";
MySerialDriver.major = 40;
MySerialDriver.minor_start = 0;
MySerialDriver.minor_num = MAX_PORTS;
MySerialDriver.num = MAX_PORTS;
MySerialDriver.magic = TTY_DRIVER_MAGIC;
MySerialDriver.type = TTY_DRIVER_TYPE_SERIAL;
MySerialDriver.subtype = SERAL_TYPE_NORMAL;
MySerialDriver.init_termios = tty_std_termios;
MySerialDriver.init_termios.c_cflag = B9600 CS8 CREAD CLOCAL;
MySerialDriver.flags = TTY_DRIVER_REAL_RAW;
MySerialDriver.termios = my_termios;
MySerialDriver.termios_locked = my_termios_locked;
MySerialDriver.open = my_open;
以下為driver initialize的工作內容
1. allocate tty driver資料結構使用
- struct tty_driver *mxvar_sdriver=alloc_tty_driver(MXSER_PORTS + 1);
- mxvar_sdriver->owner = THIS_MODULE;
- mxvar_sdriver->magic = TTY_DRIVER_MAGIC;
- mxvar_sdriver->name = "ttyMI";
- mxvar_sdriver->major = ttymajor;
- mxvar_sdriver->minor_start = 0;
- mxvar_sdriver->num = MXSER_PORTS + 1;
- mxvar_sdriver->type = TTY_DRIVER_TYPE_SERIAL;
- mxvar_sdriver->subtype = SERIAL_TYPE_NORMAL;
- mxvar_sdriver->init_termios = tty_std_termios;
- mxvar_sdriver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL;
- mxvar_sdriver->flags = TTY_DRIVER_REAL_RAW|TTY_DRIVER_DYNAMIC_DEV;
3. 設定tty會使用的各個call back function
- tty_set_operations(mxvar_sdriver, &mxser_ops);
4. 向tty_io註冊driver
- retval = tty_register_driver(mxvar_sdriver);
5. 註冊 PCI client或者platform的driver, 等待其call back所設定probe程序
- retval = pci_register_driver(&mxser_driver);
- retval = platform_register_driver(&mxser_driver);
probe call back function內做的事情
1. 取得所分配到的resource, 包含I/O及IRQ
2. 向kernel註冊所需的resource, 若是使用memory的方式來存取, 記得需要將取得的physical address用ioremap將其轉換為virtual address
3. 註冊IRQ service routine (ISR)
4. 針對每一個實體的port向上層tty做註冊
- tty_register_device(mxvar_sdriver, brd->idx + i, &pdev->dev);
remove call back function內做的事情
1. 將取得resource釋放出來
2. 將已remap過的virtual address做unremap的動作
3. 取消已註冊的ISR
4. 針對每一個已註冊的實體port向上層tty做取消的動作
- tty_unregister_device(mxvar_sdriver, brd->idx + i);
flush_chars 不是將 在輸出緩衝區的資料全數寫出到硬體 嗎?
回覆刪除不是,因為沒有這樣的call back function,flush通常是將buffer中的資料給予放棄
回覆刪除