新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > Linux串口上網(wǎng)的程序?qū)崿F(xiàn)方法

Linux串口上網(wǎng)的程序?qū)崿F(xiàn)方法

作者: 時間:2011-05-10 來源:網(wǎng)絡(luò) 收藏
操作int device_open(struct inode *inode,struct file *file)是設(shè)備節(jié)點上的第一個操作,如果多個設(shè)備共享這一個操作函數(shù),必須區(qū)分設(shè)備的設(shè)備號。我們使用inode->i_rdev >> 8 語句獲得設(shè)備的主設(shè)備號,本文中的接收設(shè)備主設(shè)備號是200,發(fā)送設(shè)備號是201。每個字符設(shè)備的file>private_data指向打開設(shè)備時候使用的file結(jié)構(gòu),private_data實際上可以指向用戶定義的任何結(jié)構(gòu),這里只指向我們自己定義的struct ed_device,用來保存字符設(shè)備的一些基本信息,比如設(shè)備名、內(nèi)核緩存區(qū)等。 操作ssize_t device_read(struct file *file,char *buffer,size_t length, loff_t *offset)是讀取設(shè)備數(shù)據(jù)的操作。device_read()結(jié)構(gòu)如圖4所示。 圖4 從設(shè)備中讀取數(shù)據(jù)(用戶空間調(diào)用read()系統(tǒng)調(diào)用)的時候,需要從內(nèi)核空間把數(shù)據(jù)拷貝到用戶空間,copy_to_user()可完成此功能,它和memcpy()此類函數(shù)有本質(zhì)的區(qū)別,memcpy()不能完成不同用戶空間數(shù)據(jù)的交換。如果需要數(shù)據(jù)臨界區(qū)的保護,使用spin_lock()內(nèi)核API負責(zé)加鎖,spin_unlock()負責(zé)解鎖,防止數(shù)據(jù)污染。由于守候進程server需要不斷輪詢設(shè)備,以查詢是否有數(shù)據(jù)可讀,如果用戶進程不處于休眠狀態(tài),在用戶空間查看進程使用資源情況,發(fā)現(xiàn)server占用了很多CPU資源。所以我們改進device_read(),使之在內(nèi)核中輪詢,當發(fā)現(xiàn)當前設(shè)備沒有數(shù)據(jù)可讀取,那么就阻塞用戶進程,使用內(nèi)核API add_wait_queue()可完成此功能,這時候用戶進程并沒有占用很多CPU資源,而是處于休眠狀態(tài)。當內(nèi)核發(fā)現(xiàn)有數(shù)據(jù)可讀的時候,調(diào)用remove_wait_queue()即可喚醒等待進程,這段 代碼如下: DECLARE_WAITQUEUE(wait,current);

本文引用地址:http://butianyuan.cn/article/150775.htm

add_wait_queue(edp->rwait,wait);

for(;;){

set_current_state(TASK_INTERRUPTIBLE);

if ( file->f_flags O_NONBLOCK)

break;

/*其他代碼 */

if ( signal_pending(current))

break;

schedule();

}

set_current_state(TASK_RUNNING);

remove_wait_queue(edp->rwait,wait);

操作ssize_t device_write(struct file *file,const char *buffer, size_t length,loff_t *offset)向設(shè)備寫入數(shù)據(jù)??截悢?shù)據(jù)的copy_from_user()和copy_to_user()的功能恰恰相反,它是從用戶空間拷貝數(shù)據(jù)到內(nèi)核空間,如圖5所示。

圖 5

編寫偽網(wǎng)絡(luò)設(shè)備驅(qū)動

偽網(wǎng)絡(luò)驅(qū)動和字符設(shè)備驅(qū)動一樣,也必須初始化和注冊。網(wǎng)絡(luò)驅(qū)動需記錄其發(fā)送和接收數(shù)據(jù)量的統(tǒng)計信息,所以我們定義一個記錄這些信息的數(shù)據(jù)結(jié)構(gòu)。

struct ednet_priv {

#ifdef LINUX_24

struct net_device_stats stats;

#else

struct enet_statistics stats;

#endif

struct sk_buff *skb;

spinlock_t lock;

};

struct ednet_priv只有3個數(shù)據(jù)成員。2.4.x 使用的網(wǎng)絡(luò)數(shù)據(jù)狀態(tài)統(tǒng)計結(jié)構(gòu)是struct net_device_stats,而 2.2.x則使用的是struct enet_statistics。同樣,對控制網(wǎng)絡(luò)接口設(shè)備的設(shè)備結(jié)構(gòu)也有不同的定義:2.4.x使用的是struct net_device,而Linux2.2.x卻是struct device。

#ifdef LINUX_24

struct net_device ednet_dev;

#else

struct device ednet_dev;

#endif

偽網(wǎng)絡(luò)驅(qū)動程序的也需要初始化和注冊。和字符設(shè)備的注冊不同之處是,它使用的是register_netdev(net_device *) kernel API。

int ednet_module_init(void)

{

int err;

strcpy(ednet_dev.name, ed0);

ednet_dev.init = ednet_init;

if ( (err = register_netdev(ednet_dev)) )

printk(ednet: error %i registering pseudo network device %sn,

err, ednet_dev.name);

return err;

}

ednet_dev的name域是接口名,ednet_module_init()中賦予網(wǎng)絡(luò)接口的名字為ed0,如果本網(wǎng)絡(luò)設(shè)備被加載,使用ifconfig命令可以看到ed0。

[root@localhost pku]# /sbin/ifconfig

ed0 Link encap:Ethernet HWaddr 00:45:44:30:30:30

inet addr:192.168.3.9 Bcast:192.168.3.255 Mask:255.255.255.0

UP BROADCAST RUNNING NOARP MULTICAST MTU:1500 Metric:1

RX packets:0 errors:0 dropped:0 overruns:0 frame:0

TX packets:0 errors:0 dropped:0 overruns:0 carrier:0

collisions:0 txqueuelen:100

RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)

我們看到我們的偽網(wǎng)絡(luò)接口沒有Interrupt和Base address,這是因為這個偽網(wǎng)絡(luò)接口不和硬件打交道,也沒有分配中斷號和IO基址。否則,如果你看一個實實在在的網(wǎng)絡(luò)接口(如下面的eth1),可以看到它的Interrupt號是11和IO Base address是0xa000。

eth1 Link encap:Ethernet HWaddr 50:78:4C:43:1D:01

inet addr:192.168.21.202 Bcast:192.168.21.255 Mask:255.255.255.0

UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1

RX packets:356523 errors:0 dropped:0 overruns:0 frame:0

TX packets:266 errors:0 dropped:0 overruns:0 carrier:0

collisions:0 txqueuelen:100

RX bytes:21542043 (20.5 Mb) TX bytes:19510 (19.0 Kb)

Interrupt:11 Base address:0xa000

ednet_dev的init域是一個函數(shù)指針,指向用戶定義的ednet_init()例程。ednet_init()添充net_device結(jié)構(gòu),只有ednet_init()初始化成功后,系統(tǒng)才被加入到設(shè)備鏈表中。ednet_dev的初始化例程ednet_init()如下:

#ifdef LINUX_24

int ednet_init(struct net_device *dev)

#else

int ednet_init(struct device *dev)

#endif

{

ether_setup(dev);

dev->open = ednet_open;

dev->stop = ednet_release;

dev->hard_start_xmit = ednet_tx;

dev->get_stats = ednet_stats;

dev->change_mtu = ednet_change_mtu;

#ifdef LINUX_24

dev->hard_header = ednet_header;

#endif

dev->rebuild_header = ednet_rebuild_header;

#ifdef LINUX_24

dev->tx_timeout = ednet_tx_timeout;

dev->watchdog_timeo = timeout;

#endif

/* We do not need the ARP protocol. */

dev->flags |= IFF_NOARP;

#ifndef LINUX_20

dev->hard_header_cache = NULL;

#endif

#ifdef LINUX_24

SET_MODULE_OWNER(dev);

#endif

dev->priv = kmalloc(sizeof(struct ednet_priv), GFP_KERNEL);

if (dev->priv == NULL)

return -ENOMEM;

memset(dev->priv, 0, sizeof(struct ednet_priv));

spin_lock_init( ((struct ednet_priv *) dev->priv)->lock);

return 0;

}

linux操作系統(tǒng)文章專題:linux操作系統(tǒng)詳解(linux不再難懂)

linux相關(guān)文章:linux教程




評論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉