「inside rtl8139」の編集履歴(バックアップ)一覧はこちら
inside rtl8139 - (2008/04/12 (土) 19:23:36) の最新版との変更点
追加された行は緑色になります。
削除された行は赤色になります。
*rtl8139(蟹チップ)のドライバ
パケット送信について調べてみた。
ドライバのソースは
/drivers/net/8139too.c
パケット送信は以下の関数で行われている模様。
626 static int rtl8139_start_xmit (struct sk_buff *skb,
627 struct net_device *dev);
その関数は以下の様になっています。
#highlight(C){{
1706 static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
1707 {
1708 struct rtl8139_private *tp = netdev_priv(dev);
1709 void __iomem *ioaddr = tp->mmio_addr;
1710 unsigned int entry;
1711 unsigned int len = skb->len;
1712 unsigned long flags;
1713
1714 /* Calculate the next Tx descriptor entry. */
1715 entry = tp->cur_tx % NUM_TX_DESC;
1716
1717 /* Note: the chip doesn't have auto-pad! */
1718 if (likely(len < TX_BUF_SIZE)) {
1719 if (len < ETH_ZLEN)
1720 memset(tp->tx_buf[entry], 0, ETH_ZLEN);
1721 skb_copy_and_csum_dev(skb, tp->tx_buf[entry]);
1722 dev_kfree_skb(skb);
1723 } else {
1724 dev_kfree_skb(skb);
1725 tp->stats.tx_dropped++;
1726 return 0;
1727 }
1728
1729 spin_lock_irqsave(&tp->lock, flags);
1730 RTL_W32_F (TxStatus0 + (entry * sizeof (u32)),
1731 tp->tx_flag | max(len, (unsigned int)ETH_ZLEN));
1732
1733 dev->trans_start = jiffies;
1734
1735 tp->cur_tx++;
1736 wmb();
1737
1738 if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx)
1739 netif_stop_queue (dev);
1740 spin_unlock_irqrestore(&tp->lock, flags);
1741
1742 if (netif_msg_tx_queued(tp))
1743 printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n",
1744 dev->name, len, entry);
1745
1746 return 0;
1747 }
}}
関数の実態を探ります。まずはこのあたり。
#highlight(C){{
1718 if (likely(len < TX_BUF_SIZE)) {
1719 if (len < ETH_ZLEN)
1720 memset(tp->tx_buf[entry], 0, ETH_ZLEN);
1721 skb_copy_and_csum_dev(skb, tp->tx_buf[entry]);
1722 dev_kfree_skb(skb);
1723 } else {
1724 dev_kfree_skb(skb);
1725 tp->stats.tx_dropped++;
1726 return 0;
1727 }
}}
***likely()
-/src/linux-2.6.18/include/linux/compiler.h
62 #define likely(x) __builtin_expect(!!(x), 1)
この先はありません。今の僕には理解できませんでした。
***memset()
-/src/linux-2.6.18/arch/i386/lib/memcpy.c
17 void *memset(void *s, int c, size_t count)
18 {
19 return __memset(s, c, count);
20 }
↓
-/src/linux-2.6.18/include/asm-i386/string.h
462 #define __memset(s, c, count) \
463 (__builtin_constant_p(count) ? \
464 __constant_count_memset((s),(c),(count)) : \
465 __memset_generic((s),(c),(count)))
( A ? B : C)の形の3項演算子ですね。Aが真ならB、そうでないならCという感じだったと思います。
****__builtin_constant_p()
GNUコンパイラの組み込み関数だそうです。これも今の僕には理解できませんでした。こちらに詳しく載っています。
[[参考URL>>http://caspar.hazymoon.jp/OpenBSD/docs/gcc-j/Other-Builtins.html]]
****__constant_count_memset()
-/src/linux-2.6.18/include/asm-i386/string.h
361 /* we might want to write optimized versions of these later */
362 #define __constant_count_memset(s,c,count) __memset_generic((s),(c),(count))
****__memset_generic()
-/src/linux-2.6.18/include/asm-i386/string.h
349 static inline void * __memset_generic(void * s, char c,size_t count)
350 {
351 int d0, d1;
352 __asm__ __volatile__(
353 "rep\n\t"
354 "stosb"
355 : "=&c" (d0), "=&D" (d1)
356 :"a" (c),"1" (s),"0" (count)
357 :"memory");
358 return s;
359 }
ということでmemsetはインラインアセンブラになるみたいですね。
インラインアセンブラの制約条件などについては[[こちら>http://yashiromann.sakura.ne.jp/memo/GCC-Inline-Assembly-HOWTO.html#ss6.1]]に詳しく書いてあります。
[[http://yashiromann.sakura.ne.jp/memo/GCC-Inline-Assembly-HOWTO.html#ss6.1>http://yashiromann.sakura.ne.jp/memo/GCC-Inline-Assembly-HOWTO.html#ss6.1]]
***skb_copy_and_csum_dev()
-/src/linux-2.6.18/net/core/skbuff.c
1396 void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to)
1397 {
1398 unsigned int csum;
1399 long csstart;
1400
1401 if (skb->ip_summed == CHECKSUM_HW)
1402 csstart = skb->h.raw - skb->data;
1403 else
1404 csstart = skb_headlen(skb);
1405
1406 BUG_ON(csstart > skb_headlen(skb));
1407
1408 memcpy(to, skb->data, csstart);
1409
1410 csum = 0;
1411 if (csstart != skb->len)
1412 csum = skb_copy_and_csum_bits (skb, csstart, to + csstart,
1413 skb->len - csstart, 0);
1414
1415 if (skb->ip_summed == CHECKSUM_HW) {
1416 long csstuff = csstart + skb->csum;
1417
1418 *((unsigned short *)(to + sstuff)) = csum_fold(csum);
1419 }
1420 }
です。関数としては
-BUG_ON
-memcpy
-skb_copy_and_csum_bits
-csum_fold
ですね。
****BUG_ON()
探したら定義が2つあった。
BUG_ON()一つ目。
-/src/linux-2.6.18/include/asm-generic/bug.h
14 #ifndef HAVE_ARCH_BUG_ON
15 #define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0)
16 #endif
中では
-unlikely()
-BUG()
が実行されています。BUG()も複数定義があるのでよくわかりません。
一応二つの定義をこぴぺしておきます。
*****BUG()
一つ目。
-/src/linux-2.6.18/include/asm-generic/bug.h
6 #ifdef CONFIG_BUG
7 #ifndef HAVE_ARCH_BUG
8 #define BUG() do { \
9 printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __FUNCTION__); \
10 panic("BUG!"); \
11 } while (0)
12 #endif
二つ目。
-/src/linux-2.6.18/include/asm-generic/bug.h
27 #else /* !CONFIG_BUG */
28 #ifndef HAVE_ARCH_BUG
29 #define BUG()
30 #endif
BUG_ON()2つ目。
-/src/linux-2.6.18/include/asm-generic/bug.h
32 #ifndef HAVE_ARCH_BUG_ON
33 #define BUG_ON(condition) do { if (condition) ; } while(0)
34 #endif
なんかコンパイルフラグですね。よくわかりません。
****memcpy()
-/src/linux-2.6.18/arch/i386/lib/memcpy.c
7 void *memcpy(void *to, const void *from, size_t n)
8 {
9 #ifdef CONFIG_X86_USE_3DNOW
10 return __memcpy3d(to, from, n);
11 #else
12 return __memcpy(to, from, n);
13 #endif
14 }
コンパイルフラグですね。
-__memcpy3d
-__memcpy
の二つです。
*****__memcpy3d()
-/src/linux-2.6.18/include/asm-i386/string.h
300 static __inline__ void *__memcpy3d(void *to, const void *from, size_t len)
301 {
302 if (len < 512)
303 return __memcpy(to, from, len);
304 return _mmx_memcpy(to, from, len);
305 }
_mmx_memcpy()は結構長いインラインアセンブラでした。
-src/linux-2.6.18/arch/i386/lib/mmx.c
にありあます。長いので省きます。コピーをする長さで条件分岐しているんですね。mmxはmulti media extentionの略らしいです。
*****__memcpy()
-/src/linux-2.6.18/include/asm-i386/string.h
203 static __always_inline void * __memcpy(void * to, const void * from, size_t n)
204 {
205 int d0, d1, d2;
206 __asm__ __volatile__(
207 "rep ; movsl\n\t"
208 "movl %4,%%ecx\n\t"
209 "andl $3,%%ecx\n\t"
210 #if 1 /* want to pay 2 byte penalty for a chance to skip microcoded rep? */
211 "jz 1f\n\t"
212 #endif
213 "rep ; movsb\n\t"
214 "1:"
215 : "=&c" (d0), "=&D" (d1), "=&S" (d2)
216 : "0" (n/4), "g" (n), "1" ((long) to), "2" ((long) from)
217 : "memory");
218 return (to);
219 }
こちらはインラインアセンブラになりますね。
****skb_copy_and_csum_bits()
長い・・・
-/src/linux-2.6.18/net/core/skbuff.c
#highlight(C){{
1317 unsigned int skb_copy_and_csum_bits(const struct sk_buff *skb, int offset,
1318 u8 *to, int len, unsigned int csum)
1319 {
1320 int start = skb_headlen(skb);
1321 int i, copy = start - offset;
1322 int pos = 0;
1323
1324 /* Copy header. */
1325 if (copy > 0) {
1326 if (copy > len)
1327 copy = len;
1328 csum = csum_partial_copy_nocheck(skb->data + offset, to,
1329 copy, csum);
1330 if ((len -= copy) == 0)
1331 return csum;
1332 offset += copy;
1333 to += copy;
1334 pos = copy;
1335 }
1336
1337 for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
1338 int end;
1339
1340 BUG_TRAP(start <= offset + len);
1341
1342 end = start + skb_shinfo(skb)->frags[i].size;
1343 if ((copy = end - offset) > 0) {
1344 unsigned int csum2;
1345 u8 *vaddr;
1346 skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
1347
1348 if (copy > len)
1349 copy = len;
1350 vaddr = kmap_skb_frag(frag);
1351 csum2 = csum_partial_copy_nocheck(vaddr +
1352 frag->page_offset +
1353 offset - start, to,
1354 copy, 0);
1355 kunmap_skb_frag(vaddr);
1356 csum = csum_block_add(csum, csum2, pos);
1357 if (!(len -= copy))
1358 return csum;
1359 offset += copy;
1360 to += copy;
1361 pos += copy;
1362 }
1363 start = end;
1364 }
1365
1366 if (skb_shinfo(skb)->frag_list) {
1367 struct sk_buff *list = skb_shinfo(skb)->frag_list;
1368
1369 for (; list; list = list->next) {
1370 unsigned int csum2;
1371 int end;
1372
1373 BUG_TRAP(start <= offset + len);
1374
1375 end = start + list->len;
1376 if ((copy = end - offset) > 0) {
1377 if (copy > len)
1378 copy = len;
1379 csum2 = skb_copy_and_csum_bits(list,
1380 offset - start,
1381 to, copy, 0);
1382 csum = csum_block_add(csum, csum2, pos);
1383 if ((len -= copy) == 0)
1384 return csum;
1385 offset += copy;
1386 to += copy;
1387 pos += copy;
1388 }
1389 start = end;
1390 }
1391 }
1392 BUG_ON(len);
1393 return csum;
1394 }
1395
}}
これの実態を追いかけるのは面倒だなー。省略。
****csum_fold()
-/src/linux-2.6.18/include/asm-i386/checksum.h
#highlight(c){{
99 static inline unsigned int csum_fold(unsigned int sum)
100 {
101 __asm__(
102 "addl %1, %0 ;\n"
103 "adcl $0xffff, %0 ;\n"
104 : "=r" (sum)
105 : "r" (sum << 16), "0" (sum & 0xffff0000)
106 );
107 return (~sum) >> 16;
108 }
}}
*rtl8139(蟹チップ)のドライバ
パケット送信について調べてみた。
ドライバのソースは
/drivers/net/8139too.c
パケット送信は以下の関数で行われている模様。
626 static int rtl8139_start_xmit (struct sk_buff *skb,
627 struct net_device *dev);
その関数は以下の様になっています。
#highlight(C){{
1706 static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
1707 {
1708 struct rtl8139_private *tp = netdev_priv(dev);
1709 void __iomem *ioaddr = tp->mmio_addr;
1710 unsigned int entry;
1711 unsigned int len = skb->len;
1712 unsigned long flags;
1713
1714 /* Calculate the next Tx descriptor entry. */
1715 entry = tp->cur_tx % NUM_TX_DESC;
1716
1717 /* Note: the chip doesn't have auto-pad! */
1718 if (likely(len < TX_BUF_SIZE)) {
1719 if (len < ETH_ZLEN)
1720 memset(tp->tx_buf[entry], 0, ETH_ZLEN);
1721 skb_copy_and_csum_dev(skb, tp->tx_buf[entry]);
1722 dev_kfree_skb(skb);
1723 } else {
1724 dev_kfree_skb(skb);
1725 tp->stats.tx_dropped++;
1726 return 0;
1727 }
1728
1729 spin_lock_irqsave(&tp->lock, flags);
1730 RTL_W32_F (TxStatus0 + (entry * sizeof (u32)),
1731 tp->tx_flag | max(len, (unsigned int)ETH_ZLEN));
1732
1733 dev->trans_start = jiffies;
1734
1735 tp->cur_tx++;
1736 wmb();
1737
1738 if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx)
1739 netif_stop_queue (dev);
1740 spin_unlock_irqrestore(&tp->lock, flags);
1741
1742 if (netif_msg_tx_queued(tp))
1743 printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n",
1744 dev->name, len, entry);
1745
1746 return 0;
1747 }
}}
**関数の実態を探ります。まずはこのあたり。
#highlight(C){{
1718 if (likely(len < TX_BUF_SIZE)) {
1719 if (len < ETH_ZLEN)
1720 memset(tp->tx_buf[entry], 0, ETH_ZLEN);
1721 skb_copy_and_csum_dev(skb, tp->tx_buf[entry]);
1722 dev_kfree_skb(skb);
1723 } else {
1724 dev_kfree_skb(skb);
1725 tp->stats.tx_dropped++;
1726 return 0;
1727 }
}}
***likely()
-/src/linux-2.6.18/include/linux/compiler.h
62 #define likely(x) __builtin_expect(!!(x), 1)
この先はありません。今の僕には理解できませんでした。
***memset()
-/src/linux-2.6.18/arch/i386/lib/memcpy.c
17 void *memset(void *s, int c, size_t count)
18 {
19 return __memset(s, c, count);
20 }
↓
-/src/linux-2.6.18/include/asm-i386/string.h
462 #define __memset(s, c, count) \
463 (__builtin_constant_p(count) ? \
464 __constant_count_memset((s),(c),(count)) : \
465 __memset_generic((s),(c),(count)))
( A ? B : C)の形の3項演算子ですね。Aが真ならB、そうでないならCという感じだったと思います。
****__builtin_constant_p()
GNUコンパイラの組み込み関数だそうです。これも今の僕には理解できませんでした。こちらに詳しく載っています。
[[参考URL>>http://caspar.hazymoon.jp/OpenBSD/docs/gcc-j/Other-Builtins.html]]
****__constant_count_memset()
-/src/linux-2.6.18/include/asm-i386/string.h
361 /* we might want to write optimized versions of these later */
362 #define __constant_count_memset(s,c,count) __memset_generic((s),(c),(count))
****__memset_generic()
-/src/linux-2.6.18/include/asm-i386/string.h
349 static inline void * __memset_generic(void * s, char c,size_t count)
350 {
351 int d0, d1;
352 __asm__ __volatile__(
353 "rep\n\t"
354 "stosb"
355 : "=&c" (d0), "=&D" (d1)
356 :"a" (c),"1" (s),"0" (count)
357 :"memory");
358 return s;
359 }
ということでmemsetはインラインアセンブラになるみたいですね。
インラインアセンブラの制約条件などについては[[こちら>http://yashiromann.sakura.ne.jp/memo/GCC-Inline-Assembly-HOWTO.html#ss6.1]]に詳しく書いてあります。
[[http://yashiromann.sakura.ne.jp/memo/GCC-Inline-Assembly-HOWTO.html#ss6.1>http://yashiromann.sakura.ne.jp/memo/GCC-Inline-Assembly-HOWTO.html#ss6.1]]
***skb_copy_and_csum_dev()
-/src/linux-2.6.18/net/core/skbuff.c
1396 void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to)
1397 {
1398 unsigned int csum;
1399 long csstart;
1400
1401 if (skb->ip_summed == CHECKSUM_HW)
1402 csstart = skb->h.raw - skb->data;
1403 else
1404 csstart = skb_headlen(skb);
1405
1406 BUG_ON(csstart > skb_headlen(skb));
1407
1408 memcpy(to, skb->data, csstart);
1409
1410 csum = 0;
1411 if (csstart != skb->len)
1412 csum = skb_copy_and_csum_bits (skb, csstart, to + csstart,
1413 skb->len - csstart, 0);
1414
1415 if (skb->ip_summed == CHECKSUM_HW) {
1416 long csstuff = csstart + skb->csum;
1417
1418 *((unsigned short *)(to + sstuff)) = csum_fold(csum);
1419 }
1420 }
です。関数としては
-BUG_ON
-memcpy
-skb_copy_and_csum_bits
-csum_fold
ですね。
****BUG_ON()
探したら定義が2つあった。
BUG_ON()一つ目。
-/src/linux-2.6.18/include/asm-generic/bug.h
14 #ifndef HAVE_ARCH_BUG_ON
15 #define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0)
16 #endif
中では
-unlikely()
-BUG()
が実行されています。BUG()も複数定義があるのでよくわかりません。
一応二つの定義をこぴぺしておきます。
*****BUG()
一つ目。
-/src/linux-2.6.18/include/asm-generic/bug.h
6 #ifdef CONFIG_BUG
7 #ifndef HAVE_ARCH_BUG
8 #define BUG() do { \
9 printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __FUNCTION__); \
10 panic("BUG!"); \
11 } while (0)
12 #endif
二つ目。
-/src/linux-2.6.18/include/asm-generic/bug.h
27 #else /* !CONFIG_BUG */
28 #ifndef HAVE_ARCH_BUG
29 #define BUG()
30 #endif
BUG_ON()2つ目。
-/src/linux-2.6.18/include/asm-generic/bug.h
32 #ifndef HAVE_ARCH_BUG_ON
33 #define BUG_ON(condition) do { if (condition) ; } while(0)
34 #endif
なんかコンパイルフラグですね。よくわかりません。
****memcpy()
-/src/linux-2.6.18/arch/i386/lib/memcpy.c
7 void *memcpy(void *to, const void *from, size_t n)
8 {
9 #ifdef CONFIG_X86_USE_3DNOW
10 return __memcpy3d(to, from, n);
11 #else
12 return __memcpy(to, from, n);
13 #endif
14 }
コンパイルフラグですね。
-__memcpy3d
-__memcpy
の二つです。
*****__memcpy3d()
-/src/linux-2.6.18/include/asm-i386/string.h
300 static __inline__ void *__memcpy3d(void *to, const void *from, size_t len)
301 {
302 if (len < 512)
303 return __memcpy(to, from, len);
304 return _mmx_memcpy(to, from, len);
305 }
_mmx_memcpy()は結構長いインラインアセンブラでした。
-src/linux-2.6.18/arch/i386/lib/mmx.c
にありあます。長いので省きます。コピーをする長さで条件分岐しているんですね。mmxはmulti media extentionの略らしいです。
*****__memcpy()
-/src/linux-2.6.18/include/asm-i386/string.h
203 static __always_inline void * __memcpy(void * to, const void * from, size_t n)
204 {
205 int d0, d1, d2;
206 __asm__ __volatile__(
207 "rep ; movsl\n\t"
208 "movl %4,%%ecx\n\t"
209 "andl $3,%%ecx\n\t"
210 #if 1 /* want to pay 2 byte penalty for a chance to skip microcoded rep? */
211 "jz 1f\n\t"
212 #endif
213 "rep ; movsb\n\t"
214 "1:"
215 : "=&c" (d0), "=&D" (d1), "=&S" (d2)
216 : "0" (n/4), "g" (n), "1" ((long) to), "2" ((long) from)
217 : "memory");
218 return (to);
219 }
こちらはインラインアセンブラになりますね。
****skb_copy_and_csum_bits()
長い・・・
-/src/linux-2.6.18/net/core/skbuff.c
#highlight(C){{
1317 unsigned int skb_copy_and_csum_bits(const struct sk_buff *skb, int offset,
1318 u8 *to, int len, unsigned int csum)
1319 {
1320 int start = skb_headlen(skb);
1321 int i, copy = start - offset;
1322 int pos = 0;
1323
1324 /* Copy header. */
1325 if (copy > 0) {
1326 if (copy > len)
1327 copy = len;
1328 csum = csum_partial_copy_nocheck(skb->data + offset, to,
1329 copy, csum);
1330 if ((len -= copy) == 0)
1331 return csum;
1332 offset += copy;
1333 to += copy;
1334 pos = copy;
1335 }
1336
1337 for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
1338 int end;
1339
1340 BUG_TRAP(start <= offset + len);
1341
1342 end = start + skb_shinfo(skb)->frags[i].size;
1343 if ((copy = end - offset) > 0) {
1344 unsigned int csum2;
1345 u8 *vaddr;
1346 skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
1347
1348 if (copy > len)
1349 copy = len;
1350 vaddr = kmap_skb_frag(frag);
1351 csum2 = csum_partial_copy_nocheck(vaddr +
1352 frag->page_offset +
1353 offset - start, to,
1354 copy, 0);
1355 kunmap_skb_frag(vaddr);
1356 csum = csum_block_add(csum, csum2, pos);
1357 if (!(len -= copy))
1358 return csum;
1359 offset += copy;
1360 to += copy;
1361 pos += copy;
1362 }
1363 start = end;
1364 }
1365
1366 if (skb_shinfo(skb)->frag_list) {
1367 struct sk_buff *list = skb_shinfo(skb)->frag_list;
1368
1369 for (; list; list = list->next) {
1370 unsigned int csum2;
1371 int end;
1372
1373 BUG_TRAP(start <= offset + len);
1374
1375 end = start + list->len;
1376 if ((copy = end - offset) > 0) {
1377 if (copy > len)
1378 copy = len;
1379 csum2 = skb_copy_and_csum_bits(list,
1380 offset - start,
1381 to, copy, 0);
1382 csum = csum_block_add(csum, csum2, pos);
1383 if ((len -= copy) == 0)
1384 return csum;
1385 offset += copy;
1386 to += copy;
1387 pos += copy;
1388 }
1389 start = end;
1390 }
1391 }
1392 BUG_ON(len);
1393 return csum;
1394 }
1395
}}
これの実態を追いかけるのは面倒だなー。省略。
****csum_fold()
-/src/linux-2.6.18/include/asm-i386/checksum.h
#highlight(c){{
99 static inline unsigned int csum_fold(unsigned int sum)
100 {
101 __asm__(
102 "addl %1, %0 ;\n"
103 "adcl $0xffff, %0 ;\n"
104 : "=r" (sum)
105 : "r" (sum << 16), "0" (sum & 0xffff0000)
106 );
107 return (~sum) >> 16;
108 }
}}
**次はこのあたり
#highlight(C){{
1729 spin_lock_irqsave(&tp->lock, flags);
1730 RTL_W32_F (TxStatus0 + (entry * sizeof (u32)),
1731 tp->tx_flag | max(len, (unsigned int)ETH_ZLEN));
1732
1733 dev->trans_start = jiffies;
1734
1735 tp->cur_tx++;
1736 wmb();
}}
-spin_lock_irqsave
-RTL_W32_F
-wmb
ですね。
***RTL_W32_F()
-8139too.c
同じソース内に定義がありました。
648 #define RTL_W32_F(reg, val32) do { iowrite32 ((val32), ioaddr + (reg)); ioread32 (ioaddr + (reg)); } while (0)
-iowrite
-ioread
この二つの関数で構成されています。ioread,iowriteは同じような命令に帰着するのでwriteだけしか調べません。
****iowrite
-/src/linux-2.6.18/lib/iomap.c
91 void fastcall iowrite32(u32 val, void __iomem *addr)
92 {
93 IO_COND(addr, outl(val,port), writel(val, addr));
94 }
↓
-/src/linux-2.6.18/lib/iomap.c
42 #define IO_COND(addr, is_pio, is_mmio) do { \
43 unsigned long port = (unsigned long __force)addr; \
44 if (port < PIO_RESERVED) { \
45 VERIFY_PIO(port); \
46 port &= PIO_MASK; \
47 is_pio; \
48 } else { \
49 is_mmio; \
50 } \
51 } while (0)
渡されたアドレスがPIO_RESERVEDより小さいときはoutlが実行される。
そうではないときはwritelが実行される。
****writel
-/src/linux-2.6.18/include/asm-i386/io.h
188 static inline void writel(unsigned int b, volatile void __iomem *addr)
189 {
190 *(volatile unsigned int __force *) addr = b;
191 }
****outl
-src/linux-2.6.18/include/asm-i386/io.h
334 #define BUILDIO(bwl,bw,type) \
335 static inline void out##bwl##_local(unsigned type value, int port) { \
336 __asm__ __volatile__("out" #bwl " %" #bw "0, %w1" : : "a"(value), "Nd"(port)); \
337 } \
338 static inline unsigned type in##bwl##_local(int port) { \
339 unsigned type value; \
340 __asm__ __volatile__("in" #bwl " %w1, %" #bw "0" : "=a"(value) : "Nd"(port)); \
341 return value; \
342 } \
343 static inline void out##bwl##_local_p(unsigned type value, int port) { \
344 out##bwl##_local(value, port); \
345 slow_down_io(); \
346 } \
347 static inline unsigned type in##bwl##_local_p(int port) { \
348 unsigned type value = in##bwl##_local(port); \
349 slow_down_io(); \
350 return value; \
351 } \
352 __BUILDIO(bwl,bw,type) \
353 static inline void out##bwl##_p(unsigned type value, int port) { \
354 out##bwl(value, port); \
355 slow_down_io(); \
356 } \
357 static inline unsigned type in##bwl##_p(int port) { \
358 unsigned type value = in##bwl(port); \
359 slow_down_io(); \
360 return value; \
361 } \
362 static inline void outs##bwl(int port, const void *addr, unsigned long count) { \
363 __asm__ __volatile__("rep; outs" #bwl : "+S"(addr), "+c"(count) : "d"(port)); \
364 } \
365 static inline void ins##bwl(int port, void *addr, unsigned long count) { \
366 __asm__ __volatile__("rep; ins" #bwl : "+D"(addr), "+c"(count) : "d"(port)); \
367 }
368
369 BUILDIO(b,b,char)
370 BUILDIO(w,w,short)
371 BUILDIO(l,,int)
372
プリプロセッサを通すと、下のようなものになります。
static inline void outl_local(unsigned int value, int port) { __asm__ __volatile__("out" "l" " %" "" "0, %w1" : : "a"(value), "Nd"(port)); }
static inline unsigned int inl_local(int port) { unsigned int value; __asm__ __volatile__("in" "l" " %w1, %" "" "0" : "=a"(value) : "Nd"(port)); return value; }
static inline void outl_local_p(unsigned int value, int port) { outl_local(value, port); slow_down_io(); }
static inline unsigned int inl_local_p(int port) { unsigned int value = inl_local(port); slow_down_io(); return value; }
static inline void outl(unsigned int value, int port) { outl_local(value, port); } static inline unsigned int inl(int port) { return inl_local(port); }
static inline void outl_p(unsigned int value, int port) { outl(value, port); slow_down_io(); }
static inline unsigned int inl_p(int port) { unsigned int value = inl(port); slow_down_io(); return value; }
static inline void outsl(int port, const void *addr, unsigned long count) { __asm__ __volatile__("rep; outs" "l" : "+S"(addr), "+c"(count) : "d"(port)); }
static inline void insl(int port, void *addr, unsigned long count) { __asm__ __volatile__("rep; ins" "l" : "+D"(addr), "+c"(count) : "d"(port)); }
inlなどの定義も混じってるけど気にしない。
結局のところインラインアセンブリのOUT系の命令になるようです。
PIOなどの定義は↓
-/src/linux-2.6.18/lib/iomap.c
24 #ifndef HAVE_ARCH_PIO_SIZE
25 /*
26 * We encode the physical PIO addresses (0-0xffff) into the
27 * pointer by offsetting them with a constant (0x10000) and
28 * assuming that all the low addresses are always PIO. That means
29 * we can do some sanity checks on the low bits, and don't
30 * need to just take things for granted.
31 */
32 #define PIO_OFFSET 0x10000UL
33 #define PIO_MASK 0x0ffffUL
34 #define PIO_RESERVED 0x40000UL
35 #endif
英文によれば、低アドレスは全部PIO用にしておくので細かくチェックしなくても良い。らしいです。