1. 程式人生 > >Linux內核Socket CAN中文文檔

Linux內核Socket CAN中文文檔

ddr 數據 設計 同一時間 union 結構 asc 打開 然而

轉載自:http://blog.csdn.net/zhangxiaopeng0829/article/details/7646639

自己在年假中空閑之余翻譯的內核中Socket CAN的文檔,原文地址在:

http://lxr.linux.no/linux+v2.6.34/Documentation/networking/can.txt

但是這篇文檔沒有涉及廣播管理協議套接字 (SOCK_DGRAM) 的內容。

另外一篇比較好的Socket CAN的英文文檔是(詳細介紹了廣播管理協議套接字):

Low Level CAN Framework Application Programmers Interface

http://www.brownhat.org/docs/socketcan/llcf-api.html#SECTION00051000000000000000

自己暫時沒時間翻譯這篇文章了,有空再說吧。

自己英文水平有限,希望高人指正。

================================================================================

這篇文章主要針對can協議簇(aka socket can)

這篇文章包含以下內容: =============== 1 概述--什麽是Socket CAN? 2 動機--為什麽使用socket API接口? 3 Socket CAN詳解 3.1 接收隊列 3.2 發送幀的本地回環 3.3 網絡安全相關 3.4 網絡故障監測 4 如何使用Socket CAN 4.1 使用can_filter的原始套接字 (RAW socket) 4.1.1 原始套接字選項 CAN_RAW_FILTER 4.1.2 原始套接字選項 CAN_RAW_ERR_FILTER 4.1.3 原始套接字選項 CAN_RAW_LOOPBACK 4.1.4 原始套接字選項 CAN_RAW_RECV_OWN_MSGS 4.2 廣播管理協議套接字 (SOCK_DGRAM) 4.3 面向連接的傳輸協議 (SOCK_SEQPACKET) 4.4 無連接的傳輸協議 (SOCK_DGRAM) 5 Socket CAN核心模塊 5.1 can.ko模塊的參數 5.2 procfs接口 5.3 寫一個自己的CAN協議模塊 6 CAN網絡驅動 6.1 常見設置 6.2 發送幀的本地回環 6.3 CAN控制器的硬件過濾 6.4 虛擬的CAN驅動 (vcan) 6.5 CAN網絡設備驅動接口 6.5.1 Netlink接口--設置/獲取設備屬性 6.5.2 設置CAN的比特_時序 6.5.3 啟動和停止CAN網絡設備 6.6 支持Socket CAN的硬件 7 學習Socket CAN的相關資源 8 貢獻者名單 ==============現在開始=================== 1. 概述--什麽是Socket CAN? ================== socketcan子系統是在Linux下CAN協議(Controller Area Network)實現的一種實現方法。 CAN是一種在世界範圍內廣泛用於自動控制、嵌入式設備和汽車領域的網絡技術。Linux下最早使用CAN的方法是基於字符設備來實現的,與之不同的是Socket CAN使用伯克利的socket接口和linux網絡協議棧,這種方法使得can設備驅動可以通過網絡接口來調用。Socket CAN的接口被設計的盡量接近TCP/IP的協議,讓那些熟悉網絡編程的程序員能夠比較容易的學習和使用。 2. 動機--為什麽使用socket API接口? ======================= 在Socket CAN之前Linux中已經有了一些CAN的實現方法,那為什麽還要啟動Socket CAN這個項目呢?大多數已經存在的實現方法僅僅作為某個具體硬件的設備驅動,它們往往基於字符設備並且提供的功能很少。那些方案通常是由一個針對具體硬件的設備驅動提供的字符設備接口來實現原始can幀的發送和接收,並且直接和控制器硬件打交道。幀隊列和ISO-TP這樣的高層協議必須在用戶空間來實現。就像串口設備接口一樣,大多數基於字符設備的實現在同一時刻僅僅支持一個進程的訪問。如果更換了CAN控制器,那麽同時也要更換另一個設備驅動,並且需要大多數應用程序重新調整以適應新驅動的API。 Socket CAN被設計用來克服以上種種不足。這種新的協議族實現了用戶空間的socket接口,它構建於Linux網絡層之上,因此可以直接使用已有的隊列功能。CAN控制器的設備驅動將自己作為一個網絡設備註冊進Linux的網絡層,CAN控制器收到的CAN幀可以傳輸給高層的網絡協議和CAN協議族,反之,發送的幀也會通過高層給CAN控制器。傳輸協議模塊可以使用協議族提供的接口註冊自己,所以可以動態的加載和卸載多個傳輸協議。事實上,CAN核心模塊不提供任何協議,也不能在沒有加載其它協議的情況下單獨使用。同一時間可以在相同或者不同的協議上打開多個套接字,可以在相同或者不同的CAN ID上同時監聽和發送(listen/send)。幾個同時監聽具有相同ID幀的套接字可以在匹配的幀到來後接收到相同的內容。如果一個應用程序希望使用一個特殊的協議(比如ISO-TP)進行通信,只要在打開套接字的時候選擇那個協議就可以了,接下來就可以讀取和寫入應用數據流了,根本無需關心CAN-ID和幀的結構等信息。 使用字符設備也可以讓用戶空間獲得類似的便利,但是這中解決方案在技術上不夠優雅,原因如下: *復雜的操作。使用Socket CAN,只需要向socket(2)函數傳遞協議參數並使用bind(2)選擇CAN接口和CAN ID,基於字符設備實現這樣的功能卻要使用ioctl(2)來完成所有的操作。 *無法代碼復用。字符設備無法使用Linux的網絡隊列代碼,所以必須為CAN 網絡重寫這部分功能。 *缺乏抽象性。在大多數已經實現的字符設備方案中,硬件相關的CAN控制器設備驅動直接提供應用程序需要的字符設備。在Unix系統中,無論對於字符設備還是塊設備,這都是不常見的。比如,你不會為某個串口、電腦上的某個聲卡、訪問磁帶和硬盤的SCSI/IDE控制器直接創建一個字符設備。相反,你會將向應用程序提供統一字符設備/塊設備接口的功能交給一個抽象層來做,你自己僅僅提供硬件相關的設備驅動接口。這種抽象是通過子系統來完成的,比如tty層子系統、聲卡子系統和SCSI/IDE子系統。 實現CAN設備驅動最簡單的辦法就是直接提供一個字符設備而不使用(或不完全使用)抽象層,大多數已經存在的驅動也是這麽做的。但是正確的方法卻要增加抽象層以支持各種功能,如註冊一個特定的CAN-ID,支持多次打開的操作和這些操作之間的CAN幀復用,支持CAN幀復雜的隊列功能,還要提供註冊設備驅動的API。然而使用Linux內核提供的網絡框架將會大大簡化,這就是Socket CAN要做的。 在Linux中實現CAN功能最自然和合適的方式就是使用內核的網絡框架。 3. Socket CAN詳解 ============ 就像第二章所講的那樣,使用Socket CAN的主要目的就是為用戶空間的應用程序提供基於Linux網絡層的套接字接口。與廣為人知的TCP/IP協議以及以太網不同,CAN總線沒有類似以太網的MAC層地址,只能用於廣播。CAN ID僅僅用來進行總線的仲裁。因此CAN ID在總線上必須是唯一的。當設計一個CAN-ECU(Electronic Control Unit 電子控制單元)網絡的時候,CAN-ID可以映射到具體的ECU。因此CAN-ID可以當作發送源的地址來使用。 3.1 接收隊列 --------------- 允許多個應用程序同時訪問網絡導致了新的問題出現,那就是不同的應用程序可能會在同一個CAN網絡接口上對具有相同CAN-ID的幀感興趣。Socket CAN的核心部分實現了Socket CAN的協議族,通過高效的接收隊列解決了這個問題。比如一個用戶空間的程序打開了一個原始CAN套接字,原始協議模塊將向CAN套接字的核心模塊申請用戶空間需要的一系列CAN-ID。Socket CAN的核心向CAN協議模塊提供預約和解約CAN-ID的接口--can_rx_(un)register(),無論這個CAN-ID是針對一個具體的CAN接口還是所有已知的CAN接口(參考第5章)。


為了優化CPU的運行效率,每個設備都對應一個接收隊列,這樣比較容易實現各種報文過濾規則。

3.2 發送幀的本地回環
-----------------

在其它種類的網絡中,在相同或者不同網絡節點上的應用程序都可以相互交換數據。

___ ___ ___ _______ ___ | _ | | _ | | _ | | _ _ | | _ | ||A|| ||B|| ||C|| ||A| |B|| ||C|| |___| |___| |___| |_______| |___| | | | | | -----------------(1)- CAN bus -(2)---------------
請看上圖的兩個例子。為了保證應用程序A在兩個例子中能夠接收到同樣的例子(例2中A和B在同一個CAN設備上),發送出去的CAN幀需要能夠在本地回環。

Linux下的網絡設備僅僅處理物理媒介上幀的發送和接受。總線仲裁機制下,高優先級的幀會將低優先級的幀延後。為了正確反映實際的通信活動,回環必須在正確傳輸成功之後進行。如果CAN網絡的硬件不支持回環功能,一種低效的方案是使用Socket CAN核心部分來實現軟件回環。具體的情況請參考6.2小節。

CAN網絡的回環功能是默認開啟的。由於RT-SocketCAN 的特殊需求,每個套接字的回環功能可以被獨立關閉。CAN原始套接字的控制選項請參考4.1小節。

*當你在同一個節點上運行CAN分析命令“candump”或者“cansniffer”的時候就會發現回環功能真的很有用。

3.3 網絡安全相關
--------------------

CAN網絡是一種現場總線,僅僅支持沒有路由和安全控制的廣播傳輸。大部分應用程序都需要要直接處理原始CAN幀,所以和其它類型的網絡一樣,CAN網絡對所有的用戶(而不僅僅是root用戶)訪問沒有任何限制。由於當前CAN_RAW和CAN_BCM的實現僅僅支持對CAN接口的讀寫,所以允許所有的用戶訪問CAN並不影響其它類型網絡的安全性。為了使能非root用戶對CAN_RAW和CAN_BCM協議套接字的訪問,必須在編譯內核的時候選上Kconfig的CAN_RAW_USER/CAN_BCM_USER選項。 3.4 網絡故障監測 -------------------- 使用CAN總線的時候,可能會遇到物理和mac(media access control)層的問題。為了方便用戶分析物理收發器的硬件錯誤、總線仲裁錯誤和不同的ECU( Electrical Conversion Unit,電氣轉換裝置)引起的錯誤,對於底層(物理和mac層)的監測和記錄是至關重要的。擁有精確時間戳的錯誤監測對於診斷錯誤是非常重要的。基於以上原因,CAN接口的驅動可以可以選擇性的產生所謂的錯誤幀,這些幀以和其它的CAN幀一樣的方式傳遞給用戶程序。當一個物理層或者MAC層的錯誤被(CAN控制器)檢測到之後,驅動創建一個相應的錯誤幀。錯誤幀可以被應用程序通過CAN的過濾機制請求得到。過濾機制允許選擇需要的錯誤幀的類型。默認情況下,接收錯誤幀的功能是禁止的。 CAN錯誤幀的詳細格式定義在linux頭文件中:include/linux/can/error.h。 4. 如何使用Socket CAN =============== 就像TCP/IP協議一樣,在使用CAN網絡之前你首先需要打開一個套接字。CAN的套接字使用到了一個新的協議族,所以在調用socket(2)這個系統函數的時候需要將PF_CAN作為第一個參數。當前有兩個CAN的協議可以選擇,一個是原始套接字協議( raw socket protocol),另一個是廣播管理協議BCM(broadcast manager)。你可以這樣來打開一個套接字: s = socket(PF_CAN, SOCK_RAW, CAN_RAW); 或者 s = socket(PF_CAN, SOCK_DGRAM, CAN_BCM); 在成功創建一個套接字之後,你通常需要使用bind(2)函數將套接字綁定在某個CAN接口上(這和TCP/IP使用不同的IP地址不同,參見第3章)。在綁定 (CAN_RAW)或連接(CAN_BCM)套接字之後,你可以在套接字上使用read(2)/write(2),也可以使用send(2)/sendto(2)/sendmsg(2)和對應的recv*操作。當然也會有CAN特有的套接字選項,下面將會說明。 基本的CAN幀結構體和套接字地址結構體定義在include/linux/can.h: /* * 擴展格式識別符由 29 位組成。其格式包含兩個部分:11 位基本 ID、18 位擴展 ID。
* Controller Area Network Identifier structure
*
* bit 0-28 : CAN識別符 (11/29 bit)
* bit 29 : 錯誤幀標誌 (0 = data frame, 1 = error frame)
* bit 30 : 遠程發送請求標誌 (1 = rtr frame) * bit 31 :幀格式標誌 (0 = standard 11 bit, 1 = extended 29 bit)
*/
typedef __u32 canid_t; struct can_frame { canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
__u8 can_dlc; /* 數據長度: 0 .. 8 */
__u8 data[8] __attribute__((aligned(8))); }; 結構體的有效數據在data[]數組中,它的字節對齊是64bit的,所以用戶可以比較方便的在data[]中傳輸自己定義的結構體和共用體。CAN總線中沒有默認的字節序。在CAN_RAW套接字上調用read(2),返回給用戶空間的數據是一個struct can_frame結構體。 就像PF_PACKET套接字一樣,sockaddr_can結構體也有接口的索引,這個索引綁定了特定接口: struct sockaddr_can { sa_family_t can_family;
int can_ifindex;
union { /* transport protocol class address info (e.g. ISOTP) */
struct { canid_t rx_id, tx_id; } tp;
/* reserved for future CAN protocols address information */ } can_addr; }; 指定接口索引需要調用ioctl()(比如對於沒有錯誤檢查CAN_RAW套接字): int s; struct sockaddr_can addr; struct ifreq ifr; s = socket(PF_CAN, SOCK_RAW, CAN_RAW); strcpy(ifr.ifr_name, "can0" ); ioctl(s, SIOCGIFINDEX, &ifr); addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; bind(s, (struct sockaddr *)&addr, sizeof(addr)); (..) 為了將套接字和所有的CAN接口綁定,接口索引必須是0。這樣套接字便可以從所有使能的CAN接口接收CAN幀。recvfrom(2)可以指定從哪個接口接收。在一個已經和所有CAN接口綁定的套接字上,sendto(2)可以指定從哪個接口發送。 從一個CAN_RAW套接字上讀取CAN幀也就是讀取struct can_frame結構體: struct can_frame frame; nbytes = read(s, &frame, sizeof(struct can_frame)); if (nbytes < 0) { perror("can raw socket read"); return 1; } /* paranoid check ... */ if (nbytes < sizeof(struct can_frame)) { fprintf(stderr, "read: incomplete CAN frame\n"); return 1; } /* do something with the received CAN frame */ 寫CAN幀也是類似的,需要用到write (2)函數: nbytes = write(s, &frame, sizeof(struct can_frame)); 如果套接字跟所有的CAN接口都綁定了(addr.can_index = 0),推薦使用recvfrom(2)獲取數據源接口的信息:

struct sockaddr_can addr; struct ifreq ifr; socklen_t len = sizeof(addr); struct can_frame frame; nbytes = recvfrom(s, &frame, sizeof(struct can_frame), 0, (struct sockaddr*)&addr, &len); /* get interface name of the received CAN frame */ ifr.ifr_ifindex = addr.can_ifindex; ioctl(s, SIOCGIFNAME, &ifr); printf("Received a CAN frame from interface %s", ifr.ifr_name); 對於綁定了所有接口的套接字,向某個端口發送數據必須指定接口的詳細信息: strcpy(ifr.ifr_name, "can0"); ioctl(s, SIOCGIFINDEX, &ifr); addr.can_ifindex = ifr.ifr_ifindex; addr.can_family = AF_CAN; nbytes = sendto(s, &frame, sizeof(struct can_frame), 0, (struct sockaddr*)&addr, sizeof(addr)); 4.1 使用can_filter的原始套接字 (RAW socket) ---------------------------------------------------- CAN_RAW套接字的用法和CAN字符設備的用法是類似的。為了使用CAN套接字的新特性,在綁定原始套接字的時候將會默認開啟以下特性: - filter將會接收所有的數據 - 套接字僅僅接收有效的數據幀(=> no error frames) - 發送幀的回環功能被開啟(參見 3.2節) - (回環模式下)套接字不接收它自己發送的幀 這些特性的設置可以在綁定之前和之後修改。為了使用CAN_RAW套接字相關的選項,必須包含<linux/can/raw.h>。
  • 4.1.1 原始套接字選項 CAN_RAW_FILTER
CAN_RAW套接字的接收可以使用CAN_RAW_FILTER套接字選項指定的多個過濾規則(過濾器)來過濾。 過濾規則(過濾器)的定義在 include/linux/can.h中: struct can_filter { canid_t can_id; canid_t can_mask; }; 過濾規則的匹配: <received_can_id> & mask == can_id & mask /* #define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */ #define CAN_ERR_FLAG 0x20000000U /* error frame */ */ 這和大家所熟知的CAN控制器硬件過濾非常相似。可以使用 CAN_INV_FILTER這個宏將can_filter結構體的成員can_id中的比特位反轉。和CAN控制器的硬件過濾形成鮮明對比的是,用戶可以為每一個打開的套接字設置多個獨立的過濾規則(過濾器): /* /* valid bits in CAN ID for frame formats */

#define CAN_SFF_MASK 0x000007FFU /* 標準幀格式 (SFF) */
#define CAN_EFF_MASK 0x1FFFFFFFU /* 擴展幀格式 (EFF) */
#define CAN_ERR_MASK 0x1FFFFFFFU /* 忽略EFF, RTR, ERR標誌 */

*/ struct can_filter rfilter[2]; rfilter[0].can_id = 0x123; rfilter[0].can_mask = CAN_SFF_MASK; rfilter[1].can_id = 0x200; rfilter[1].can_mask = 0x700; setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter)); 為了在指定的CAN_RAW套接字上禁用接收過濾規則,可以這樣: setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); 在一些極端情況下不需要讀取數據,可以把過濾規則清零(所有成員設為0),這樣原始套接字就會忽略接收到的CAN幀。在這種僅僅發送數據(不讀取)的應用中可以在內核中省略接收隊列,以此減少CPU的負載(雖然只能減少一點點)。
  • 4.1.2 原始套接字選項 CAN_RAW_ERR_FILTER
正如3.4節所說,CAN接口驅動可以選擇性的產生錯誤幀,錯誤幀和正常幀以相同的方式傳給應用程序。可能產生的錯誤被分為不同的種類,使用適當的錯誤掩碼可以過濾它們。為了註冊所有可能的錯誤情況,CAN_ERR_MASK(0x1FFFFFFFU)這個宏可以用來作為錯誤掩碼。這個錯誤掩碼定義在linux/can/error.h。 can_err_mask_t err_mask = ( CAN_ERR_TX_TIMEOUT | CAN_ERR_BUSOFF ); setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &err_mask, sizeof(err_mask));
  • 4.1.3 原始套接字選項 CAN_RAW_LOOPBACK
為了滿足眾多應用程序的需要,本地回環功能默認是開啟的(詳細情況參考3.2節)。但是在一些嵌入式應用場景中(比如只有一個用戶在使用CAN總線),回環功能可以被關閉(各個套接字之間是獨立的): int loopback = 0; /* 0 = disabled, 1 = enabled (default) */ setsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));
  • 4.1.4 原始套接字選項 CAN_RAW_RECV_OWN_MSGS
在本地回環功能開啟的情況下,所有的發送幀都會被回環到在相應CAN接口上註冊了同樣CAN-ID(和發送幀的相同)的套接字上。發送CAN幀的套接字被假設不想接收自己發送的CAN幀,因此在發送套接字上的回環功能默認是關閉的。可以在需要的時候改變這一默認行為: int recv_own_msgs = 1; /* 0 = disabled (default), 1 = enabled */ setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &recv_own_msgs, sizeof(recv_own_msgs)); (yll:下面三小節沒有內容) 4.2 廣播管理協議套接字 (SOCK_DGRAM) ----------------------------------------------- 4.3 面向連接的傳輸協議 (SOCK_SEQPACKET) ---------------------------------------------------- 4.4 無連接的傳輸協議 (SOCK_DGRAM) --------------------------------------------- 5. Socket CAN核心模塊 ============== CAN套接字的核心模塊實現了PF_CAN協議族。CAN協議模塊在核心模塊運行後加載。CAN核心模塊為協議模塊提供了申請需要的ID的接口(參考3.1小節)。 5.1 can.ko模塊的參數 ------------------------- - stats_timer: 為了計算CAN套接字的統計信息(比如最近一秒的幀數和每秒最大的幀數),can.ko調用一個定時間隔為1秒的定時器,默認情況下這個定時器是開啟的。這個定時器也可以在模塊參數中傳入stattimer=0來禁止。 - debug : (removed since SocketCAN SVN r546) 5.2 procfs接口 ----------------- 就像3.1節描述的那樣,CAN套接字核心借助於一些帶有過濾規則的隊列向CAN協議模塊傳遞接收到的CAN幀。可以在procfs中查看這些接收隊列的的過濾規則和匹配規則的次數。所有的條目都包了設備名和協議模塊標識: [email protected]:~$ cat /proc/net/can/rcvlist_all
receive list ‘rx_all‘: (vcan3: no entry)
(vcan2: no entry)
(vcan1: no entry)
device can_id can_mask function userdata matches ident vcan0 000 00000000 f88e6370 f6c6f400 0 raw (any: no entry) (yll補充:function是一個函數指針,userdata是一個void *指針,所以這兩個值打印出來沒有太大意義) 在這個例子中,這個應用程序接收所有vcan0上傳輸的數據. rcvlist_all - 沒有過濾規則的隊列 rcvlist_eff - 擴展幀(EFF)的隊列 rcvlist_err - 錯誤幀隊列 rcvlist_fil - 通過過濾規則的隊列 rcvlist_inv - 未通過過濾規則的隊列 rcvlist_sff - 標準幀的隊列 在/proc/net/can中還有另外一些文件: stats - CAN套接字核心的統計信息(接收/發送的幀數,匹配率等) reset_stats - 復位統計信息 version - 打印CAN套接字核心的版本和ABI的版本 5.3 寫一個自己的CAN協議模塊 ----------------------------------- 要在PF_CAN中增加一個新的協議,必須在include/linux/can.h中為新的協議增加相應的定義。包含include/linux/can.h這個文件便可以使用增加的原型和定義。內核除了提供了註冊CAN協議和CAN設備的通知列表的功能,也提供了在一個特定CAN接口上註冊感興趣的CAN幀或者發送CAN幀的功能。 can_rx_register - 在一個特定接口上註冊希望接收到的CAN幀的信息 (yll:這個函數的定義在內核的net/can/af_can.c中) can_rx_unregister - 註銷上面的申請 can_send - 發送CAN幀(可以選擇是否開啟本地回環) 詳細的信息請參考內核中的源碼net/can/af_can.c、net/can/raw.c、net/can/bcm.c。 6. CAN網絡驅動 ========== 編寫一個CAN網絡設備驅動要比寫一個CAN字符設備驅動要容易的多。和編寫其它網絡設備驅動類似,你只要處理以下事宜: - TX :將套接字緩沖區的CAN幀發送到CAN控制器 - RX :從CAN控制器的CAN幀讀取到套接字緩沖區 Documentation/networking/netdevices.txt中是網絡設備驅動的例子。下面將會介紹CAN網絡設備驅動的一些獨有特性。 6.1 常見設置 --------------- dev->type = ARPHRD_CAN; /* the netdevice hardware type */
dev->flags = IFF_NOARP; /* CAN has no arp */
dev->mtu = sizeof(struct can_frame); 結構體can_frame是PF_CAN協議族套接字緩沖區的數組載體。 6.2 發送幀的本地回環 ------------------------- 如3.2小節所述,CAN網絡設備驅動應該支持類似TTY設備回顯功能的本地回環功能。如果驅動支持這個功能,則要設備IFF_ECHO標誌來防止PF_CAN核心回顯發送幀(又稱回環)。 dev->flags = (IFF_NOARP | IFF_ECHO); 6.3 CAN控制器的硬件過濾 ------------------------------ 為了減小一些嵌入式系統的中斷負載,一些CAN控制器支持多個CAN-ID或者多個CAN-ID區間的過濾功能。硬件過濾功能在不同的控制器之間差異很大,並且不能同時滿足多個用戶的不同過濾需求。在單用戶應用中使用控制器的硬件過濾或許還有意義,但是在一個多用戶系統中驅動層的過濾將會影響所有用戶。PF_CAN核心內置的過濾規則集合允許對每個套接字獨立的設置多個過濾規則。因此使用硬件過濾屬於嵌入式系統中“手動調整”的範疇。從2002年開始筆者一直使用擁有四路SJA1000 CAN控制器的MPC603e @133MHz,總線負載雖然很高,但是到目前為止還沒有什麽問題。 6.4 虛擬的CAN驅動 (vcan) ------------------------------ 和網絡回環設備一樣,vcan提供一個虛擬的本地CAN接口。CAN中一個有效的地址包括: - 一個有效的CAN標識符(CAN ID) - 這個CAN標識符將要發往的總線(比如 can0). 所以在一般的應用場景中往往需要多個vcan接口。 vcan接口允許在沒有控制器硬件的情況下進行發送和接收。vcan網絡設備的命名一般采用‘vcanX’,比如can1 vcan2等。當編譯為單獨的模塊的時候,vcan驅動的模塊名為vcan.ko。 vcan驅動從linux2.6.24開始支持netlink接口,使得創建vcan網絡設備變的可能。可以使用ip(8)命令工具管理vcan網絡設備的創建和移除: - 創建一個vcan網絡接口: $ ip link add type vcan - 使用給定的名字 ‘vcan42‘創建 一個vcan網絡接口: $ ip link add dev vcan42 type vcan - 移除vcan網絡接口‘vcan42‘: $ ip link del vcan42 6.5 CAN網絡設備驅動接口 ------------------------------ CAN網絡設備驅動提供了進行安裝、配置和監控CAN網絡設備的接口。可以使用IPROUTE2工具集中的“ip”命令通過netlink接口配置CAN設備,比如設置波特率。本章剩余的部分將會簡介如何使用這一工具。另外這些接口使用一些通用的數據結構並且提供了一系列常用的功能,這些功能都是CAN網絡設備驅動需要用到的。請參考SJA1000或者MSCAN的驅動去了解如何使用它們。模塊的名字是can-dev.ko。
  • 6.5.1 Netlink接口--設置/獲取設備屬性
CAN設備必須使用netlink來配置。在"include/linux/can/netlink.h"有對netlink消息類型的定義和簡短描述。IPROUTE2工具集中的“ip”命令可以使用CAN的netlink支持,下面是使用示例: - 設置CAN設備屬性: $ ip link set can0 type can help Usage: ip link set DEVICE type can [ bitrate BITRATE [ sample-point SAMPLE-POINT] ] | [ tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1 phase-seg2 PHASE-SEG2 [ sjw SJW ] ] [ loopback { on | off } ] [ listen-only { on | off } ] [ triple-sampling { on | off } ] [ restart-ms TIME-MS ] [ restart ] Where: BITRATE := { 1..1000000 } SAMPLE-POINT := { 0.000..0.999 } TQ := { NUMBER } PROP-SEG := { 1..8 } PHASE-SEG1 := { 1..8 } PHASE-SEG2 := { 1..8 } SJW := { 1..4 } RESTART-MS := { 0 | NUMBER } - 顯示CAN設備的詳情和統計信息: $ ip -details -statistics link show can0 2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP qlen 10 link/can
can <TRIPLE-SAMPLING> state ERROR-ACTIVE restart-ms 100
bitrate 125000 sample_point 0.875
tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
clock 8000000
re-started bus-errors arbit-lost error-warn error-pass bus-off 41 17457 0 41 42 41
RX: bytes packets errors dropped overrun mcast 140859 17608 17457 0 0 0
TX: bytes packets errors dropped carrier collsns 861 112 0 41 0 0 下面是上面一些名詞的解釋: "<TRIPLE-SAMPLING>" 表示選中的CAN控制器的模式:LOOPBACK, LISTEN-ONLY, or TRIPLE-SAMPLING。 "state ERROR-ACTIVE" CAN控制器的當前狀態:"ERROR-ACTIVE", "ERROR-WARNING", "ERROR-PASSIVE", "BUS-OFF" or "STOPPED" "restart-ms 100" 自動重啟的延時時間。如果設為非零值, 在總線關閉的情況下,在設定的數量毫秒後CAN控制器被自動觸發。這個功能默認是關閉的。 "bitrate 125000 sample_point 0.875" 使用bits/sec作為單位顯示位時間並顯示0.000~0.999的采樣點位置。如果內核中使能了統計位時間的功能(CONFIG_CAN_CALC_BITTIMING=y),位時間可以使用"bitrate"參數來設置。可選的"sample-point"也是可以配置的。默認使用0.000這個推薦值。 "tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1" 以ns為單位顯示時間份額(tq-time quanta)、傳播段(prop-seg : propagation segment)、相位緩沖段1和2(phase-seg:phase buffer),以tq為單位顯示同步跳轉寬度(sjw:synchronisation jump width)。這些變量允許定義與硬件無關的位時序,這也是Bosch CAN 2.0 spec所推薦的(參考第八章http://www.semiconductors.bosch.de/pdf/can2spec.pdf)。 "sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1 clock 8000000" 顯示CAN控制器的比特時序常量,這裏的例子以sja1000為例。時間段(tseg -time segment)1和2的最小和最大值,以tq為單位的同步跳轉寬度,比特速率的預分頻器(brp--pre-scaler)和CAN系統時鐘(以HZ為單位)。這些常量可以被用戶空間的比特時序統計算法所使用。 "re-started bus-errors arbit-lost error-warn error-pass bus-off" 顯示重啟的次數、總線和仲裁丟失錯誤,錯誤主動(error-warning)、錯誤被動(error-passive)、和總線關閉的狀態變化。接收的過載錯誤在 統計信息的"overrun"域下面列出。
  • 6.5.2 設置CAN的比特時序
CAN比特時序參數可以使用硬件無關的定義方法。這些參數是: "tq", "prop_seg", "phase_seg1", "phase_seg2" 和 "sjw": $ ip link set canX type can tq 125 prop-seg 6 \
phase-seg1 7 phase-seg2 2 sjw 1 在內核選項CONFIG_CAN_CALC_BITTIMING被使能的情況下,如果比特率(波特率)參數 "bitrate"被設置了,CAN的這些參數將會生效: $ ip link set canX type can bitrate 125000 請註意,這條命令在大部分使用標準波特率的CAN控制器上工作良好,但是使用一些少見的波特率值(如115000)和時鐘頻率值將會失敗。禁用內核的CONFIG_CAN_CALC_BITTIMING選項可以節省一些內存空間並且允許用戶空間的命令工具完全的控制比特時序參數。使用CAN控制器的比特時序常量就可以達到這個目的(用戶空間控制比特時序)。下面的命令將會列出這些變量: $ ip -details link show can0 ... sja1000: clock 8000000 tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
  • 6.5.3 啟動和停止CAN網絡設備
一個CAN網絡設備可以使用"ifconfig canX up/down" 或者 "ip link set canX up/down"來開啟和關閉。為了避免錯誤的默認值,必須在啟動CAN設備之前設置它的比特時序參數: $ ip link set canX up type can bitrate 125000 如果總線上出現太多的錯誤設備可能進入總線關閉狀態(也就是從總線脫離)。進入總線關閉狀態之後設備不會再發送和接收信息。給"restart-ms"設置一個非零值可以開啟總線關閉自動恢復的功能(也就是進入總線關閉狀態後重新開啟),下面是一個示例: $ ip link set canX type can restart-ms 100 應用程序可以通過監測CAN的錯誤幀意識到已經進入總線關閉狀態,並且可以使用以下命令重啟: $ ip link set canX type can restart 註意,重啟也會生成一個CAN錯誤幀(參見3.4節)。 6.6 支持Socket CAN的硬件 ------------------------------ "drivers/net/can"中的“Kconfig”文件中可以查看到所有支持的硬件列表。在CAN套接字項目網站上(參見第7章)有更多驅動何以獲得,當然也有對早些時期版本內核的支持 。 7. 學習Socket CAN的相關資源 =================== 你可在BerliOS OSS項目網站的CAN套接字頁面中發現更多資源,比如用戶空間工具、對舊版內核的支持、更多的驅動、郵件列表等: http://developer.berlios.de/projects/socketcan 如果你有任何問題或者發現了BUG,不要遲疑,立馬發送郵件到CAN套接字的用戶郵件列表。但是在發送之前請首先搜索郵件記錄中是否已經有了相同的問題。 8. 貢獻者名單 =========
Oliver Hartkopp (PF_CAN core, filters, drivers, bcm, SJA1000 driver) Urs Thuermann (PF_CAN core, kernel integration, socket interfaces, raw, vcan) Jan Kizka (RT-SocketCAN core, Socket-API reconciliation)
Wolfgang Grandegger (RT-SocketCAN core & drivers, Raw Socket-API reviews, CAN device driver interface, MSCAN driver) Robert Schwebel (design reviews, PTXdist integration)
Marc Kleine-Budde (design reviews, Kernel 2.6 cleanups, drivers) Benedikt Spranger (reviews)
Thomas Gleixner (LKML reviews, coding style, posting hints)
Andrey Volkov (kernel subtree structure, ioctls, MSCAN driver) Matthias Brukner (first SJA1000 CAN netdevice implementation Q2/2003) Klaus Hitschler (PEAK driver integration)
Uwe Koppe (CAN netdevices with PF_PACKET approach)
Michael Schulze (driver layer loopback requirement, RT CAN drivers review) Pavel Pisa (Bit-timing calculation)
Sascha Hauer (SJA1000 platform driver)
Sebastian Haas (SJA1000 EMS PCI driver)
Markus Plessing (SJA1000 EMS PCI driver)
Per Dalen (SJA1000 Kvaser PCI driver)
Sam Ravnborg (reviews, coding style, kbuild help)

Linux內核Socket CAN中文文檔