1. 硬件體驗(yàn)
使用 Linux 自帶的 USB Gadget 驅(qū)動(dòng) /drivers/usb/gadget/legacy/serial.c
使用 USB 線,連接板子的 OTG 口和 PC 的 USB 口。
然后在板子加載驅(qū)動(dòng)程序后,可以看到新的設(shè)備節(jié)點(diǎn) /dev/ttyGS0:
# modprobe g_serial
g_serial gadget: Gadget Serial v2.4
g_serial gadget: g_serial ready
g_serial gadget: high-speed config #2: CDC ACM config
# ls /dev/ttyGS0 -l
crw-rw---- 1 root dialout 246, 0 Jan 1 00:30 /dev/ttyGS0
在 PC 上,如果是 Windows 系統(tǒng),可以在設(shè)備管理器里看到新的 USB 串口:
在 PC 上,如果是 VMware 上的 Linux 系統(tǒng),按下圖操作,先把 USB 串口連接到 VMware:
然后在 PC Linux 中可以看到新的設(shè)備節(jié)點(diǎn):
book@100ask:~$ dmesg
[ 286.903239] usb 1-1: new high-speed USB device number 2 using ehci-pci
[ 287.254549] usb 1-1: New USB device found, idVendor=0525, idProduct=a4a7, bcdDevice= 4.09
[ 287.254550] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 287.254551] usb 1-1: Product: Gadget Serial v2.4
[ 287.254552] usb 1-1: Manufacturer: Linux 4.9.88 with 2184000.usb
[ 287.342786] cdc_acm 1-1:2.0: ttyACM0: USB ACM device
[ 287.343202] usbcore: registered new interface driver cdc_acm
[ 287.343202] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
book@100ask:~$ ls /dev/ttyACM0 -l
crw-rw---- 1 root dialout 166, 0 Mar 5 22:38 /dev/ttyACM0
2. Serial分析
2.1 軟件框架
Gadget 串口的框架如下:
u_serial 提供了有 2 種方法來(lái)使用 Gadget 串口:
- u_serial.c 里注冊(cè) tty_driver 結(jié)構(gòu)體 gs_tty_driver,在板子上編寫(xiě) APP 訪問(wèn)設(shè)備 /dev/ttyGS0 即可與 Host 交互(Host 要打開(kāi) USB 串口)
- u_serial.c 里注冊(cè) console 結(jié)構(gòu)體 gserial_cons。啟動(dòng) Linux 內(nèi)核時(shí)傳入 commandline 參數(shù)"console=ttyGS0"后,內(nèi)核的 printk 的信息通過(guò) Gadget 串口打印出來(lái)(Host 要打開(kāi) USB 串口):
注冊(cè) TTY 和 console 的過(guò)程:
gs_bind // driversusbgadgetlegacyserial.c
status = serial_register_ports(cdev, &serial_config_driver,"acm");
fi_serial[i] = usb_get_function_instance(f_name);
acm_alloc_instance // driversusbgadgetfunctionf_acm.c
ret = gserial_alloc_line(&opts- >port_num); // driversusbgadgetfunctionu_serial.c
// 注冊(cè)TTY
tty_dev = tty_port_register_device(&ports[port_num].port- >port,
gs_tty_driver, port_num, NULL);
// 注冊(cè)console
gserial_console_init();
register_console(&gserial_cons);
2.2 數(shù)據(jù)傳輸
2.2.1 APP 訪問(wèn)
注意,在 USB 中數(shù)據(jù)傳輸總是由 Host 發(fā)起,所以:
- 板子要事先準(zhǔn)備好空間(設(shè)置好 out 方向的 usb_request 并放入隊(duì)列),以便接收 Host 發(fā)來(lái)的數(shù)據(jù);
- 板子有數(shù)據(jù)想發(fā)送給 Host 時(shí)需要設(shè)置 in 方向的 usb_request,以便 Host 讀取。
板子上的 APP 訪問(wèn) /dev/ttyGS0 時(shí),就會(huì)導(dǎo)致 gs_tty_ops 結(jié)構(gòu)體的對(duì)應(yīng)函數(shù)被調(diào)用:
APP 調(diào)用 open 函數(shù)時(shí),會(huì)導(dǎo)致如下調(diào)用:
gs_open
gs_start_io(port);
// 取出 out 端點(diǎn)(對(duì)應(yīng) Host 來(lái)說(shuō)是 out, 對(duì)于板子來(lái)說(shuō)就是輸入)
struct usb_ep *ep = port- >port_usb- >out;
// 給 out 端點(diǎn)分配 usb_request
status = gs_alloc_requests(ep, head, gs_read_complete,&port- >read_allocated);
// 給 in 端點(diǎn)分配 usb_request, 但是在 open 時(shí)并沒(méi)有把 in 方向的 usb_request 放入隊(duì)列
status = gs_alloc_requests(port- >port_usb- >in, &port- >write_pool,gs_write_complete, &port- >write_allocated);
// 把 usb_request 放入隊(duì)列, 如果 Host 發(fā)來(lái)數(shù)據(jù), 這個(gè) usb_request 的 complete 函數(shù)被調(diào)用
started = gs_start_rx(port);
status = usb_ep_queue(out, req, GFP_ATOMIC);
APP 調(diào)用 write 函數(shù)時(shí),會(huì)導(dǎo)致如下調(diào)用:
gs_write
gs_start_tx(port);
// 把 usb_request 放入隊(duì)列, Host讀取數(shù)據(jù)時(shí)就可以從中得到數(shù)據(jù)
status = usb_ep_queue(in, req, GFP_ATOMIC);
2.2.2 printk
啟動(dòng) Linux 內(nèi)核時(shí)傳入 commandline 參數(shù)"console=ttyGS0"后,內(nèi)核的 printk 的信息通過(guò) Gadget 串口打印出來(lái)(Host 要打開(kāi) USB 串口)。
內(nèi)核的 printk 函數(shù)會(huì)導(dǎo)致 gserial_cons 結(jié)構(gòu)體中的 write 指針即gs_console_write
函數(shù)被調(diào)用:
gs_console_write 函數(shù)的調(diào)用關(guān)系如下:
gs_console_write
// 把要打印的數(shù)據(jù)放入環(huán)形 buffer
gs_buf_put(&info- >con_buf, buf, count);
// 喚醒內(nèi)核線程
wake_up_process(info- >console_thread);
// 內(nèi)核線程
gs_console_thread
// 被喚醒后
// 取出輸入端點(diǎn)和它的 usb_request
req = info- >console_req;
ep = port- >port_usb- >in;
// 從環(huán)形 buffer 得到數(shù)據(jù)、設(shè)置 usb_request
xfer = gs_buf_get(&info- >con_buf, req- >buf, size);
req- >length = xfer;
// 把 usb_request 放入隊(duì)列,以便 Host 讀取
ret = usb_ep_queue(ep, req, GFP_ATOMIC);
-
嵌入式
+關(guān)注
關(guān)注
5089文章
19170瀏覽量
306794 -
驅(qū)動(dòng)
+關(guān)注
關(guān)注
12文章
1848瀏覽量
85467 -
Linux
+關(guān)注
關(guān)注
87文章
11336瀏覽量
210097
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論