1 一.I.MX6 UART驅動 2 文件路徑:\linux_IMX6_CoreC_3.0.35_for_Linux\drivers\tty\serial\imx.c 3 1.驅動入口函數:imx_serial_init() 4 1.1 static int __init imx_serial_in ...
1 一.I.MX6 UART驅動 2 文件路徑:\linux_IMX6_CoreC_3.0.35_for_Linux\drivers\tty\serial\imx.c 3 1.驅動入口函數:imx_serial_init() 4 1.1 static int __init imx_serial_init(void) 5 ret = uart_register_driver(&imx_reg); //驅動載入的時候調用了這個函數註冊串口驅動,將參數imx_reg註冊進了tty層 6 struct tty_driver *normal; 7 normal = alloc_tty_driver(drv->nr); //申請tty驅動,串口設備套上了一層tty驅動的外殼 8 ... 9 tty_set_operations(normal, &uart_ops); 10 //上面這段代碼可以得出一個結論,uart_driver的數據類型其實就是tty_driver,兩者進行數據轉換之後註冊進了tty層。 11 //tty_set_operations(normal, &uart_ops);將uart的操作函數和tty關聯起來,應用層對於tty的操作都將對應到uart的操作 12 13 retval = tty_register_driver(normal); //向TTY核心層註冊一個TTY驅動,所以串口設備其實就是一個tty類型的設備 14 alloc_chrdev_region(&dev, driver->minor_start, driver->num, driver->name); 15 register_chrdev_region(dev, driver->num, driver->name); 16 cdev_init(&driver->cdev, &tty_fops); //初始化設備,註意這裡將指針調用關係賦給了cdev 17 18 ret = platform_driver_register(&serial_imx_driver); //註冊平臺驅動 19 //serial_imx_driver變數類型如下 20 static struct platform_driver serial_imx_driver = { 21 .probe = serial_imx_probe, //當匹配到設備之後,這句函數得到調用,轉去分析這個函數 22 .remove = serial_imx_remove, 23 24 .suspend = serial_imx_suspend, 25 .resume = serial_imx_resume, 26 .driver = { 27 .name = "imx-uart", 28 .owner = THIS_MODULE, 29 }, 30 }; 31 1.2 serial_imx_probe 32 static int serial_imx_probe(struct platform_device *pdev) 33 sport->port.dev = &pdev->dev; 34 //imx_pops是串口接收數據、發送數據的相關函數(註意這裡要區別前面的串口操作函數) 35 //我覺得前面的串口操作函數是從應用層或者tty的角度來看,使用者也僅僅是應用層(tty層) 36 //這裡的imx_pops裡面眾多的操作函數是從底層的角度去看,也就是底層和硬體相關的中斷接收、發送相關。 37 sport->port.ops = &imx_pops; 38 sport->port.flags = UPF_BOOT_AUTOCONF; 39 sport->port.line = pdev->id; 40 init_timer(&sport->timer); 41 sport->timer.function = imx_timeout; 42 sport->timer.data = (unsigned long)sport; 43 ... 44 ... 45 ... 46 ret = uart_add_one_port(&imx_reg, &sport->port); //關鍵:為uart_driver增加一個埠 47 //列出imx_pops重要的幾個函數 48 static struct uart_ops imx_pops = { 49 ... 50 .stop_tx = imx_stop_tx, 51 .start_tx = imx_start_tx, //串口發送 52 .stop_rx = imx_stop_rx, 53 .startup = imx_startup, //中斷發送相關的函數,接下來我們分析這個發送函數 54 ... 55 }; 56 57 1.3 static int imx_startup(struct uart_port *port) //做一些串口的初始化操作 58 { 59 struct imx_port *sport = (struct imx_port *)port; 60 struct tty_struct *tty; 61 ... 62 clk_enable(sport->clk); //使能時鐘 63 64 /* disable the DREN bit (Data Ready interrupt enable) before 65 * requesting IRQs 66 */ 67 temp = readl(sport->port.membase + UCR4); 68 ... 69 writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); 70 ... 71 /* 72 * Allocate the IRQ(s) i.MX1 has three interrupts whereas later 73 * chips only have one interrupt. 74 */ 75 retval = request_irq(sport->rxirq, imx_rxint, 0, DRIVER_NAME, sport);//關鍵:在這裡申請了接收中斷函數(同時註冊了中斷接收函數imx_rxint) 76 ... 77 retval = request_irq(sport->txirq, imx_txint, 0, DRIVER_NAME, sport);//申請註冊發送中斷函數(這次分析我們不關心發送中斷) 78 /* Enable the SDMA for uart. */ 79 if (sport->enable_dma) //如果配置串口使用DMA 80 { 81 int ret; 82 ret = imx_uart_dma_init(sport); //DMA的初始化 83 //下麵幾句代碼都是DMA初始化相關,平臺通用,不展開說明啦 84 sport->dma_data.priority = DMA_PRIO_HIGH; 85 sport->dma_data.dma_request = pdata->dma_req_rx; 86 sport->dma_data.peripheral_type = IMX_DMATYPE_UART; 87 sport->dma_chan_rx = dma_request_channel(mask, imx_uart_filter, sport); 88 ... 89 slave_config.direction = DMA_DEV_TO_MEM; 90 slave_config.src_addr = sport->port.mapbase + URXD0; 91 //註意:如果配置串口使用DMA,這裡初始化了一個工作隊列(中斷下半部的一種),來進行數據的接收和發送 92 //聽說3.0.35版本的內核DMA驅動有問題,不知道是不是真的,待證實,實際使用的時候,確實是發現DMA在連續傳輸大量數據的時候會出現數據丟失的情況 93 INIT_WORK(&sport->tsk_dma_tx, dma_tx_work); 94 INIT_WORK(&sport->tsk_dma_rx, dma_rx_work); //基於DMA的串口中斷接收函數,也是在這裡註冊了一個工作隊列 95 dma_rx_work(struct work_struct *w) 96 tty_insert_flip_string(tty, sport->rx_buf, sport->rx_bytes);//關鍵的函數調用:將數據放到tty數據緩衝區 97 tty_insert_flip_string_fixed_flag(tty, chars, TTY_NORMAL, size); 98 struct tty_buffer *tb = tty->buf.tail; 99 memcpy(tb->char_buf_ptr + tb->used, chars, space); 100 memset(tb->flag_buf_ptr + tb->used, flag, space); 101 //如果不調用下麵的函數,只是將數據放到tty緩衝區,tty也是獲取不到數據的 102 tty_flip_buffer_push(tty); //將緩衝區的數據推到tty當中,其實內部是一個工作隊列的調度(在tty初始化的時候註冊了這個工作隊列),等於是通知tty的線路規程獲取數據的意思 103 schedule_work(&tty->buf.work); //工作調度,實際上調用了flush_to_ldisc函數(這個函數好像不能在中斷中調用 104 //,但是我發現也有其他驅動直接在中斷調用了,實際上我在中斷中調用業一直沒發現有什麼問題) 105 init_waitqueue_head(&sport->dma_wait); 106 } 107 ... 108 spin_lock_irqsave(&sport->port.lock, flags); 109 /* 110 * Finally, clear and enable interrupts 111 */ 112 writel(USR1_RTSD, sport->port.membase + USR1); //使能中斷 113 114 } 115 116 1.4 //在前面剛進入函數的時候,我們申請了串口中斷,同時註冊了imx_rxint接收函數,接下來分析這個函數 117 retval = request_irq(sport->rxirq, imx_rxint, 0, DRIVER_NAME, sport); 118 spin_lock_irqsave(&sport->port.lock,flags); //先獲取自旋鎖 119 120 while (readl(sport->port.membase + USR2) & USR2_RDR) //判斷串口寄存器狀態,開始接收位元組數據 121 { 122 flg = TTY_NORMAL; 123 sport->port.icount.rx++; 124 rx = readl(sport->port.membase + URXD0); 125 temp = readl(sport->port.membase + USR2); 126 ... 127 if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) 128 continue; 129 if (rx & URXD_BRK) 130 flg = TTY_BREAK; 131 else if (rx & URXD_PRERR) 132 flg = TTY_PARITY; 133 else if (rx & URXD_FRMERR) 134 flg = TTY_FRAME; 135 if (rx & URXD_OVRRUN) 136 flg = TTY_OVERRUN; 137 } 138 tty_insert_flip_char(tty, rx, flg); 139 tty_insert_flip_string_flags(tty, &ch, &flag, 1); 140 struct tty_buffer *tb = tty->buf.tail; 141 memcpy(tb->char_buf_ptr + tb->used, chars, space); 142 memcpy(tb->flag_buf_ptr + tb->used, flags, space); 143 out: 144 spin_unlock_irqrestore(&sport->port.lock,flags);//釋放自旋鎖 145 tty_flip_buffer_push(tty);//裡面有工作隊列的調度,將數據“刷新”到tty 146 return IRQ_HANDLED; 147 148 2.uart操作函數結構體 149 static const struct tty_operations uart_ops = { 150 .open = uart_open, 151 .close = uart_close, 152 .write = uart_write, 153 .put_char = uart_put_char, 154 ... 155 ... 156 ... 157 }; 158 //從結構體的類型我們知道,uart_ops的數據類型就是tty_operations 類型,對應tty的文件操作函數 159 //所以tty的操作路徑就對應著tty_ops-->uart_driver-->uart->ops 160 161 3.uart_open函數 162 static int uart_open(struct tty_struct *tty, struct file *filp) 163 struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state; 164 165 4.uart_close函數 166 static void uart_close(struct tty_struct *tty, struct file *filp) 167 struct uart_state *state = tty->driver_data; 168 169 5.uart_write函數 170 static int uart_write(struct tty_struct *tty, const unsigned char *buf, int count) 171 struct uart_state *state = tty->driver_data; 172 //在前面,normal->driver_state = drv;將串口的私有數據賦值給tty 173 //上面3、4、5這幾個函數是在我們打開串口或者做串口操作的時候,將數據從tty拿出來(因為打開串口的時候, 174 //就是tty去操作設備號對應將設備的信息傳遞到驅動中來) 175 //這幾個函數都是應用層在操作串口的時候才會調用,與串口數據發送沒有關係,所以就不展開分析了。 176 177 178 二、tty驅動分析(不針對特定處理器平臺) 179 文件路徑:\linux_IMX6_CoreC_3.0.35_for_Linux\drivers\tty\tty_io.c 180 \linux_IMX6_CoreC_3.0.35_for_Linux\drivers\tty\tty_buffer.c 181 \linux_IMX6_CoreC_3.0.35_for_Linux\drivers\tty\tty_ldisc.c 182 tty由上往下分為tty核心層、tty線路規程、tty驅動,用戶空間對應tty設備的操作函數定義及實現都在tty_io.c中。 183 tty核心層不能直接從tty驅動獲取數據,底層的數據經過tty驅動,在經過tty線路規程,再到tty核心層。 184 1. 線路規程的初始化 185 //線路規程的初始化是在內核剛啟動的時候完成的,文件路徑是\init\main.c 186 asmlinkage void __init start_kernel(void) 187 console_init(); 188 /* Setup the default TTY line discipline. */ 189 tty_ldisc_begin(); 190 /* Setup the default TTY line discipline. */ 191 (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); 192 //分析tty_ldisc_N_TTY結構體變數(屬於tty_ldisc_ops類型,接著分析tty_ldisc_ops) 193 1.1 tty_ldisc_ops(線路規程操作函數結構體) 194 struct tty_ldisc_ops tty_ldisc_N_TTY = 195 { 196 .magic = TTY_LDISC_MAGIC, 197 .name = "n_tty", 198 .open = n_tty_open, 199 .close = n_tty_close, 200 .flush_buffer = n_tty_flush_buffer, 201 .chars_in_buffer = n_tty_chars_in_buffer, 202 .read = n_tty_read, 203 .write = n_tty_write, 204 .ioctl = n_tty_ioctl, 205 .set_termios = n_tty_set_termios, 206 .poll = n_tty_poll, 207 .receive_buf = n_tty_receive_buf, 208 .write_wakeup = n_tty_write_wakeup 209 }; 210 //以上這些函數就是tty線路規程在操作數據時候用到的函數,從tty應用層操作向下看就是tty_ops--->tty_ldisc_ops 211 //前面分析uart驅動時候uart_ops和實際的數據接收和發送函數是分開的,在這裡也是類似。 212 //操作設備是一組函數,實際做數據收發的又是另外一組函數。 213 1.1.1 n_tty_open函數 214 static int n_tty_open(struct tty_struct *tty) 215 tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);//分配tty緩衝區給tty_read_buf,其中N_TTY_BUF_SIZE為4096 216 ... 217 reset_buffer_flags(tty);//初始化頭尾指針、讀計數等 218 tty->read_head = tty->read_tail = tty->read_cnt = 0; 219 1.1.2 n_tty_write函數 220 static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t nr) 221 ... 222 //這裡調用到了tty_driver操作函數,因為在之前的tty_open函數中有了tty->ops=driver->ops這樣的操作。 223 //註冊的時候初始化了ops 224 tty->ops->flush_chars(tty); 225 ... 226 c = tty->ops->write(tty, b, nr); 227 228 1.1.3 n_tty_read函數 229 static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, unsigned char __user *buf, size_t nr) 230 ... 231 ... 232 while (nr) 233 { 234 c = tty->read_buf[tty->read_tail];//從tty讀緩衝區獲取數據 235 ... 236 tty_put_user(tty, c, b++); 237 tty_audit_add_data(tty, &x, 1); 238 put_user(x, ptr); 239 uncopied = copy_from_read_buf(tty, &b, &nr); 240 uncopied += copy_from_read_buf(tty, &b, &nr); 241 } 242 //註意這裡tty線路規程對數據的讀操作並沒有調用到tty驅動層的讀函數。 243 //和tty線路規程對數據的寫操作要做個對比和區別。 244 //tty的數據讀取是“被動”讀取的,簡單講就是通過串口中斷接收位元組,然後調度tty初始化時候註冊的工作隊列來實現數據的讀取, 245 //對應的函數是: 246 flush_to_ldisc(); 247 ... 248 disc->ops->receive_buf(tty, char_buf,flag_buf, count);//具體調用的函數是n_tty_receive_buf函數 249 ... 250 2. int __init tty_init(void) 251 //初始化tty設備,tty設備本質上也是一個字元設備 252 //用戶空間對於tty設備的操作都對應到tty_fops結構體裡面的函數 253 cdev_init(&tty_cdev, &tty_fops);//我們重點分析tty操作函數結構體裡面的函數集合 254 cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1); 255 register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty"); 256 device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");//創建設備 257 //初始化tty設終端(關於tty的終端,我們暫時不分析) 258 cdev_init(&console_cdev, &console_fops); 259 register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console"); 260 consdev = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, "console"); 261 ... 262 2.1 tty_buffer_init()函數 263 INIT_WORK(&tty_buf.work, flush_to_ldisc);//flush_to_ldisc函數綁定到工作隊列,當tty_filp_buffer_push函數中調用schedule_work(&tty->buf.work);時候,函數得到調用 264 2.2 tty_fops結構體 265 static const struct file_operations tty_fops = { 266 .llseek = no_llseek, 267 .read = tty_read, //註意這裡的read函數不是對應uart操作函數裡面的read函數(需要做關聯),後面會講到 268 .write = tty_write, //同上 269 .poll = tty_poll, 270 .unlocked_ioctl = tty_ioctl, 271 .compat_ioctl = tty_compat_ioctl, 272 .open = tty_open, 273 .release = tty_release, 274 .fasync = tty_fasync, 275 }; 276 277 2.3 tty_open函數(打開tty設備時候做的一些初始化操作,建立tty核心和線路規程以及硬體驅動的聯繫) 278 static int tty_open(struct inode *inode, struct file *filp) 279 struct tty_driver *driver; 280 int index; 281 dev_t device = inode->i_rdev; 282 ... 283 tty = get_current_tty(); 284 tty_free_file(filp); 285 ... 286 driver = tty_driver_kref_get(tty->driver); 287 ... 288 driver = get_tty_driver(device, &index); //根據設備號,來查找到tty_driver 289 tty = tty_init_dev(driver, index, 0); //初始化tty結構體 290 initialize_tty_struct(tty, driver, idx); //關鍵的一個初始化,在裡面建立tty核心層和線路規程的聯繫 291 tty_ldisc_init(tty); //初始化線路規程 292 tty->buf.head = tty->buf.tail = NULL; //緩衝區頭尾指空 293 tty_buffer_init(tty); //初始化tty緩衝區 294 spin_lock_init(&tty->buf.lock); 295 tty->buf.head= NULL; 296 tty->buf.tail= NULL; 297 tty->buf.free= NULL; 298 tty->buf.memory_used= 0; 299 //初始化了一個工作隊列 300 //當在驅動層里receive_chars的最後調用了tty_flip_buffer_push這個函數的時候這個隊里得到調度 301 INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);//在中斷下半部中進行調度時候,flush_to_ldisc會得到調用,接下來分析這個函數 302 ... 303 tty->ops = driver->ops; //tty操作指向tty設備操作(註意這裡是指向具體的硬體設備驅動操作) 304 tty->dev = tty_get_device(tty); 305 dev_t devt = tty_devnum(tty); //獲得設備號 306 ... 307 retval = tty_driver_install_tty(driver, tty); 308 ... 309 retval = tty_ldisc_setup(tty, tty->link); //設置線路規程 310 retval = tty_ldisc_open(tty, ld); //打開線路規程 311 tty_ldisc_enable(tty); //使能線路規程 312 ... 313 tty_add_file(tty, filp); 314 check_tty_count(tty, "tty_open"); //檢查tty被打開多少次 315 ... 316 retval = tty->ops->open(tty, filp); //實際上就是driver->ops->open(註意這裡的driver指的是具體硬體的driver) 317 2.3.1 tty_flip_buffer_push函數 318 void tty_flip_buffer_push(struct tty_struct *tty) 319 ... 320 ... 321 ... 322 if (tty->low_latency) 323 flush_to_ldisc(&tty->buf.work.work); 324 else 325 schedule_work(&tty->buf.work);//工作隊列調度,內核線程???會調用flush_to_ldisc 326 327 2.3.2 flush_to_ldisc函數 328 //從tty_buffer中找到數據緩衝區char_buf_ptr,並將這個緩衝區指針傳遞給線路規程的操作函數receive_buf。 329 //拷貝數據進tty的read_buf 330 flush_to_ldisc(struct work_struct *work) 331 disc->ops->receive_buf(tty, char_buf, flag_buf,count); 332 n_tty_receive_buf(struct tty_struct *tty, const unsigned char*cp, char *fp, int count); 333 n_tty_receive_char(tty,*p); 334 ... 335 if(tty->ops->flush_chars) 336 tty->ops->flush_chars(tty); 337 338 2.4 tty_write函數(應用層調用) 339 static ssize_t tty_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 340 struct tty_struct *tty = file_tty(file); 341 struct tty_ldisc *ld; //tty線路規程結構體,這個結構體很重要 342 ... 343 ld = tty_ldisc_ref_wait(tty); //等待tty線路規程(所有從tty驅動上來的數據或者從應用層到tty驅動的數據,都要經過線路規程) 344 ret = do_tty_write(ld->ops->write, tty, file, buf, count); //do_tty_write這裡實際上是調用了ld->ops->write(也就是線路規程的write函數,將數據寫入tty緩衝區) 345 //等價於調用n_tty_write函數 346 //在n_tty_write函數中調用下麵tty driver中的操作函數 347 tty->ops->flush_chars(tty); 348 ... 349 c = tty->ops->write(tty, b, nr); 350 copy_from_user(tty->write_buf, buf, size); //從用戶空間拷貝數據到tty寫緩衝中 351 352 2.5 tty_read函數(應用層調用) 353 static ssize_t tty_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 354 ... 355 struct tty_struct *tty = file_tty(file); 356 ... 357 i = (ld->ops->read)(tty, file, buf, count);//調用線路規程裡面的read函數(n_tty_read) 358 //下麵這幾句代碼是n_tty_read函數的 359 while (nr) 360 { 361 c = tty->read_buf[tty->read_tail];//從tty讀緩衝區獲取數據,實際上是在tty->read_buf的末尾tty->read_tail中讀取數據 362 ... 363 tty_put_user(tty, c, b++); 364 tty_audit_add_data(tty, &x, 1); 365 put_user(x, ptr); 366 uncopied = copy_from_read_buf(tty, &b, &nr); 367 uncopied += copy_from_read_buf(tty, &b, &nr); 368 }