国产一区二区三区在线电影 ,思思99精品国产自在现线,97精品视频在线http://www.bjzhda.cnzh-cn曙海教育集團論壇http://www.bjzhda.cnRss Generator By Dvbbs.Netofficeoffice@126.comimages/logo.gif曙海教育集團論壇LINUX下的設備驅動程序http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1745&Page=1wangxinxin2010-11-24 11:51:37  
    3.1、UNIX下設備驅動程序的基本結構
  
    在UNIX系統里,對用戶程序而言,設備驅動程序隱藏了設備的具體細節,對各種不同設備提供了一致的接口,一般來說是把設備映射為一個特殊的設備文件,用戶程序可以象對其它文件一樣對此設備文件進行操作。UNIX對硬件設備支持兩個標準接口:塊特別設備文件和字符特別設備文件,通過塊(字符)特別設備文件存取的設備稱為塊(字符)設備或具有塊(字符)設備接口。
  
    塊設備接口僅支持面向塊的I/O操作,所有I/O操作都通過在內核地址空間中的I/O緩沖區進行,它可以支持幾乎任意長度和任意位置上的I/O請求,即提供隨機存取的功能。
  
    字符設備接口支持面向字符的I/O操作,它不經過系統的快速緩存,所以它們負責管理自己的緩沖區結構。字符設備接口只支持順序存取的功能,一般不能進行任意長度的I/O請求,而是限制I/O請求的長度必須是設備要求的基本塊長的倍數。顯然,本程序所驅動的串行卡只能提供順序存取的功能,屬于是字符設備,因此后面的討論在兩種設備有所區別時都只涉及字符型設備接口。
  
    設備由一個主設備號和一個次設備號標識。主設備號唯一標識了設備類型,即設備驅動程序類型,它是塊設備表或字符設備表中設備表項的索引。次設備號僅由設備驅動程序解釋,一般用于識別在若干可能的硬件設備中,I/O請求所涉及到的那個設備。
  
    設備驅動程序可以分為三個主要組成部分:
  
    (1) 自動配置和初始化子程序,負責檢測所要驅動的硬件設備是否存在和是否能正常工作。如果該設備正常,則對這個設備及其相關的、設備驅動程序需要的軟件狀態進行初始化。這部分驅動程序僅在初始化的時候被調用一次。
  
    (2) 服務于I/O請求的子程序,又稱為驅動程序的上半部分。調用這部分是由于系統調用的結果。這部分程序在執行的時候,系統仍認為是和進行調用的進程屬于同一個進程,只是由用戶態變成了核心態,具有進行此系統調用的用戶程序的運行環境,因此可以在其中調用sleep()等與進程運行環境有關的函數。
  
    (3) 中斷服務子程序,又稱為驅動程序的下半部分。在UNIX系統中,并不是直接從中斷向量表中調用設備驅動程序的中斷服務子程序,而是由UNIX系統來接收硬件中斷,再由系統調用中斷服務子程序。中斷可以產生在任何一個進程運行的時候,因此在中斷服務程序被調用的時候,不能依賴于任何進程的狀態,也就不能調用任何與進程運行環境有關的函數。因為設備驅動程序一般支持同一類型的若干設備,所以一般在系統調用中斷服務子程序的時候,都帶有一個或多個參數,以唯一標識請求服務的設備。
  
    在系統內部,I/O設備的存取通過一組固定的入口點來進行,這組入口點是由每個設備的設備驅動程序提供的。一般來說,字符型設備驅動程序能夠提供如下幾個入口點:
  
    (1) open入口點。打開設備準備I/O操作。對字符特別設備文件進行打開操作,都會調用設備的open入口點。open子程序必須對將要進行的I/O操作做好必要的準備工作,如清除緩沖區等。如果設備是獨占的,即同一 時刻只能有一個程序訪問此設備,則open子程序必須設置一些標志以表示設備處于忙狀態。
  
    (2) close入口點。關閉一個設備。當最后一次使用設備終結后,調用close子程序。獨占設備必須標記設備可再次使用。
  
    (3) read入口點。從設備上讀數據。對于有緩沖區的I/O操作,一般是從緩沖區里讀數據。對字符特別設備文件進行讀操作將調用read子程序。
  
    (4) write入口點。往設備上寫數據。對于有緩沖區的I/O操作,一般是把數據寫入緩沖區里。對字符特別設備文件進行寫操作將調用write子程序。
  
    (5) ioctl入口點。執行讀、寫之外的操作。
  
    (6) select入口點。檢查設備,看數據是否可讀或設備是否可用于寫數據。select系統調用在檢查與設備特別文件相關的文件描述符時使用select入口點。如果設備驅動程序沒有提供上述入口點中的某一個,系統會用缺省的子程序來代替。對于不同的系統,也還有一些其它的入口點。
  
    3.2、LINUX系統下的設備驅動程序
  
    具體到LINUX系統里,設備驅動程序所提供的這組入口點由一個結構來向系統進行說明,此結構定義為:
  
  #include
  struct file_operations {
   int (*lseek)(struct inode *inode,struct file *filp,
   off_t off,int pos);
   int (*read)(struct inode *inode,struct file *filp,
   char *buf, int count);
   int (*write)(struct inode *inode,struct file *filp,
   char *buf,int count);
   int (*readdir)(struct inode *inode,struct file *filp,
   struct dirent *dirent,int count);
   int (*select)(struct inode *inode,struct file *filp,
   int sel_type,select_table *wait);
   int (*ioctl) (struct inode *inode,struct file *filp,
   unsigned int cmd,unsigned int arg);
   int (*mmap) (void);
   int (*open) (struct inode *inode, struct file *filp);
   void (*release) (struct inode *inode, struct file *filp);
   int (*fsync) (struct inode *inode, struct file *filp);
  };
  
    其中,struct inode提供了關于特別設備文件/dev/driver(假設此設備名為driver)的信息,它的定義為:
  
  #include
  struct inode {
   dev_t i_dev;
   unsigned long i_ino; /* Inode number */
   umode_t i_mode; /* Mode of the file */
   nlink_t i_nlink;
   uid_t i_uid;
   gid_t i_gid;
   dev_t i_rdev; /* Device major and minor numbers*/
   off_t i_size;
   time_t i_atime;
   time_t i_mtime;
   time_t i_ctime;
   unsigned long i_blksize;
   unsigned long i_blocks;
   struct inode_operations * i_op;
   struct super_block * i_sb;
   struct wait_queue * i_wait;
   struct file_lock * i_flock;
   struct vm_area_struct * i_mmap;
   struct inode * i_next, * i_prev;
   struct inode * i_hash_next, * i_hash_prev;
   struct inode * i_bound_to, * i_bound_by;
   unsigned short i_count;
   unsigned short i_flags; /* Mount flags (see fs.h) */
   unsigned char i_lock;
   unsigned char i_dirt;
   unsigned char i_pipe;
   unsigned char i_mount;
   unsigned char i_seek;
   unsigned char i_update;
   union {
   struct pipe_inode_info pipe_i;
   struct minix_inode_info minix_i;
   struct ext_inode_info ext_i;
   struct msdos_inode_info msdos_i;
   struct iso_inode_info isofs_i;
   struct nfs_inode_info nfs_i;
   } u;
  };
  
    struct file主要用于與文件系統對應的設備驅動程序使用。當然,其它設備驅動程序也可以使用它。它提供關于被打開的文件的信息,定義為:
  
  #include
  struct file {
   mode_t f_mode;
   dev_t f_rdev; /* needed for /dev/tty */
   off_t f_pos; /* Curr. posn in file */
   unsigned short f_flags; /* The flags arg passed to open */
   unsigned short f_count; /* Number of opens on this file */
   unsigned short f_reada;
   struct inode *f_inode; /* pointer to the inode struct */
   struct file_operations *f_op;/* pointer to the fops struct*/
  };
  
    在結構file_operations里,指出了設備驅動程序所提供的入口點位置,分別是:
  
    (1) lseek,移動文件指針的位置,顯然只能用于可以隨機存取的設備。
  
    (2) read,進行讀操作,參數buf為存放讀取結果的緩沖區,count為所要讀取的數據長度。返回值為負表示讀取操作發生錯誤,否則返回實際讀取的字節數。對于字符型,要求讀取的字節數和返回的實際讀取字節數都必須是inode->i_blksize的的倍數。
  
    (3) write,進行寫操作,與read類似。
  
    (4) readdir,取得下一個目錄入口點,只有與文件系統相關的設備驅動程序才使用。
  
    (5) selec,進行選擇操作,如果驅動程序沒有提供select入口,select操作將會認為設備已經準備好進行任何的I/O操作。
  
    (6) ioctl,進行讀、寫以外的其它操作,參數cmd為自定義的的命令。
]]>
嵌入式Linux下的AU1200MAE驅動程序設計http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1744&Page=1wangxinxin2010-11-24 11:48:38MAE的相關寄存器中,在每處理一幀后,上述宏塊信息都要及時更新。MAE的前端對宏塊數據進行反量化,其結果輸入至逆變換單元進行逆變換運算。逆變換運算單元通過查看maefe_config寄存器的COD標志位來決定采用哪種標準的視頻Cod-ec,從而執行相應的運算。逆變換運算完成后,數據送入運動補償單元。運動補償單元根據運動向量計算出參考像素值,并把它們插入當前幀中,從而完成一次解碼。以上過程通過編寫函數fe_process_mb來實現,該函數的幾個參數分別是:

  mae_fe_cfg:MAE前端的配置信息;mb_in:輸入的宏塊數據;cur_y_frame:當前幀Y分量;cur_cb_frame:當前幀Cb分量;cur_cr_fr-ame:當前幀Cr分量;mb_num:表示宏塊數據的序號。該函數的流程圖如圖3所示。

圖片點擊可在新窗口打開查看

  函數中的關鍵部分及說明注釋如下所示:

圖片點擊可在新窗口打開查看

圖片點擊可在新窗口打開查看

  3.2 驅動軟件測試

  將以上程序交叉編譯,生成mae-driver.ko,動態加載到Linux內核:#insmod - f mae-driver.ko。用MAIplayer驗證是否可以正常工作:啟動minicom,進入MAIplayer所在路徑,該路徑下有編譯好的播放器應用程序及各種視頻解碼庫。執行視頻文件播放命令MYM./maipl-ayer auto-a-l jolin.mpg。播放效果如圖4所示。經測試,MAIplayer可正常播放多媒體視頻,說明MAE已被驅動起來完成視頻解碼工作。

圖片點擊可在新窗口打開查看

  4 結語

  本文給出了AU 1200 MAE驅動程序開發的流程,包括開發環境的搭建及驅動程序的編寫。MAE作為AU 1200片上專用于圖像、視頻的外部設備,相當于一個視頻協處理器。它的使用大大提高了MIPS核的工作效率,而以AU 1200為核心的多媒體終端具有更低的成本,因此。其市場前景將更加廣闊。

]]>
嵌入式Linux下的USB設備驅動技術http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1743&Page=1wangxinxin2010-11-24 11:47:41

        該函數的形參對應的就是總線兩條鏈表里的設備和驅動。當總線上有新設備和驅動時,這個函數就會被調用。

 

        3 USB驅動程序的描述符

        一個設備可以有多個接口,一個接口可代表一個功能,因此,每個接口都對應著一個驅動。例如一個USB設備有兩種功能,一個鍵盤,上面還帶一個揚聲器,這就是兩個接口,就需要兩個驅動程序,一個是鍵盤驅動程序,一個是音頻流驅動程序。

        一個驅動程序是否支持一個設備,要通過讀取設備的描述符來判斷。那么,什么是USB的描述符呢?USB的描述符是一個帶有預定義格式的數據結構,里面保存有USB設備的各種屬性和相關信息,可以通過向設備請求獲得它們的描述符內容來深刻了解和感知一個USB設備。主要有四種USB描述符,分別為:接口描述符、端點描述符、設備描述符和配置描述符。

        協議規定:一個USB設備必須支持這四大描述符,還有些描述符不是必須包含的,有些特殊設備用來描述設備的不同特性,但這四大描述符是一個都不能少的。USB設備里有一個eeprom,可用來存儲設備本身信息,設備的描述符就存儲在這里。

        上述四個描述符分別放在了include/linux/usb.h文件中的struct usb_host_interface、structusb_host_endpoint、struct usb_device、struetusb_host_config里,而描述符結構體本身定義在include/linux/usb/ch9.h里.并分別用struct usb_interface_descriptor、struct usb_host_endpoint、structusb_device_descriptor和struct usb_config_descriptor來表示。描述符結構體的定義應完全按照USB協議對描述符的規定來定義。

        4 USB接口驅動

        4.1 接口結構

        平時編寫的USB驅動通常指的是寫USB接口的驅動,一個接口對應一個接口驅動程序,需要以一個struct usb_driver結構的對象為中心,并以設備的接口提供的功能為基礎,來進行USB驅動程序的編寫。struct usb_driver結構體一般定義在include/linux/usb.h文件里。具體如下:

struct usb_driver{
const char*name;
int(*probe)  (struct usb_interface*intf,const
struct usb_device_jd*id);
void(*disconnect)  (struct usb_interface*intf);
int(*ioctl)  (struct usb_interface*intf,unsigned
int code,void*buf);
int  (*suspend)  (struct usb_interface*intf,
pm_message_t message);
int(*resume)  (struct usb_interface*intf);
void(*pre_reset)  (struct usb_interface*intf);
void(*post_reset)(struct usb_interface*intf);
const struct usb_device_id*id_table;
struct usb_dynids dynids;
struct usbdrv_wrap drvwrap;
unsigned int no_dynamic_id:1;
unsigned int supports_autosuspend:1;
};

        Name為驅動程序的名字,對應于/sys/bus/usb/drivers/下面的子目錄名稱。它只是彼此區別的一個代號,這里的名字在所有的USB驅動中必須是唯一的。probe用來看看這個USB驅動是否愿意接受某個接口的函數。Disconnect函數將在接口失去聯系或使用rmmod卸載驅動將它和接口強行分開時被調用。Ioctl函數則用在驅動通過usbfs和用戶空間進行交流時使用。Suspend、esume分別在設備被掛起和喚醒時使用。pre_reset、post_reset分別在設備將要復位(reset)和已經復位后使用。id_table的變量可用來判斷是否支持某個設備接口。Dynids是支持動態id的。實際上,即使驅動已經加載了,也可以添加新的id給它。drvwrap是給USB core區分設備驅動和接口驅動用的。no_dynamic_id可以用來禁止動態id。supports_autosuspend可對autosuspend提供支持,如果設置為0,則不再允許綁定到這個驅動的接口autosuspend。

        接口驅動

        當insmod或modprobe驅動的時候,經過一個曲折的過程,就會調用相應USB驅動里的xxx_init函數,進而去調用usb_register (),以將相應的USB驅動提交給設備模型,添加到USB總線的驅動鏈表里。當rmmod驅動時,同樣,在經過一個曲折的過程之后,再調用相應驅動里的xxx_cleanup函數,進而調用usb_deregister ()將相應的USB驅動從USB總線的驅動鏈表里刪除。

]]>
深入淺出Linux設備驅動之并發控制(2)http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1742&Page=1wangxinxin2010-11-24 11:46:56
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>
MODULE_LICENSE("GPL");

#define MAJOR_NUM 254

static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*);
static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);

struct file_operations globalvar_fops =
{
 read: globalvar_read, write: globalvar_write,
};
static int global_var = 0;
static struct semaphore sem;

static int __init globalvar_init(void)
{
 int ret;
 ret = register_chrdev(MAJOR_NUM, "globalvar", &globalvar_fops);
 if (ret)
 {
  printk("globalvar register failure");
 }
 else
 {
  printk("globalvar register success");
  init_MUTEX(&sem);
 }
 return ret;
}

static void __exit globalvar_exit(void)
{
 int ret;
 ret = unregister_chrdev(MAJOR_NUM, "globalvar");
 if (ret)
 {
  printk("globalvar unregister failure");
 }
 else
 {
  printk("globalvar unregister success");
 }
}

static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
 //獲得信號量
 if (down_interruptible(&sem))
 {
  return - ERESTARTSYS;
 }

 //將global_var從內核空間復制到用戶空間
 if (copy_to_user(buf, &global_var, sizeof(int)))
 {
  up(&sem);
  return - EFAULT;
 }

 //釋放信號量
 up(&sem);

 return sizeof(int);
}

ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off)
{
 //獲得信號量
 if (down_interruptible(&sem))
 {
  return - ERESTARTSYS;
 }

 //將用戶空間的數據復制到內核空間的global_var
 if (copy_from_user(&global_var, buf, sizeof(int)))
 {
  up(&sem);
  return - EFAULT;
 }

 //釋放信號量
 up(&sem);
 return sizeof(int);
}

module_init(globalvar_init);
module_exit(globalvar_exit);

  接下來,我們給globalvar的驅動程序增加open()和release()函數,并在其中借助自旋鎖來保護對全局變量int globalvar_count(記錄打開設備的進程數)的訪問來實現設備只能被一個進程打開(必須確保globalvar_count最多只能為1):

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>

MODULE_LICENSE("GPL");

#define MAJOR_NUM 254

static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*);
static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);
static int globalvar_open(struct inode *inode, struct file *filp);
static int globalvar_release(struct inode *inode, struct file *filp);

struct file_operations globalvar_fops =
{
 read: globalvar_read, write: globalvar_write, open: globalvar_open, release:
globalvar_release,
};

static int global_var = 0;
static int globalvar_count = 0;
static struct semaphore sem;
static spinlock_t spin = SPIN_LOCK_UNLOCKED;

static int __init globalvar_init(void)
{
 int ret;
 ret = register_chrdev(MAJOR_NUM, "globalvar", &globalvar_fops);
 if (ret)
 {
  printk("globalvar register failure");
 }
 else
 {
  printk("globalvar register success");
  init_MUTEX(&sem);
 }
 return ret;
}

static void __exit globalvar_exit(void)
{
 int ret;
 ret = unregister_chrdev(MAJOR_NUM, "globalvar");
 if (ret)
 {
  printk("globalvar unregister failure");
 }
 else
 {
  printk("globalvar unregister success");
 }
}

static int globalvar_open(struct inode *inode, struct file *filp)
{
 //獲得自選鎖
 spin_lock(&spin);

 //臨界資源訪問
 if (globalvar_count)
 {
  spin_unlock(&spin);
  return - EBUSY;
 }
 globalvar_count++;

 //釋放自選鎖
 spin_unlock(&spin);
 return 0;
}

static int globalvar_release(struct inode *inode, struct file *filp)
{
 globalvar_count--;
 return 0;
}

static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t
*off)
{
 if (down_interruptible(&sem))
 {
  return - ERESTARTSYS;
 }
 if (copy_to_user(buf, &global_var, sizeof(int)))
 {
  up(&sem);
  return - EFAULT;
 }
 up(&sem);
 return sizeof(int);
}

static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len,
loff_t *off)
{
 if (down_interruptible(&sem))
 {
  return - ERESTARTSYS;
 }
 if (copy_from_user(&global_var, buf, sizeof(int)))
 {
  up(&sem);
  return - EFAULT;
 }
 up(&sem);
 return sizeof(int);
}

module_init(globalvar_init);
module_exit(globalvar_exit);

  為了上述驅動程序的效果,我們啟動兩個進程分別打開/dev/globalvar。在兩個終端中調用./globalvartest.o測試程序,當一個進程打開/dev/globalvar后,另外一個進程將打開失敗,輸出"device open failure",如下圖:

圖片點擊可在新窗口打開查看
輸出結果
]]>
Linux內核修煉之道》 之 高效學習Linux驅動開發http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1741&Page=1wangxinxin2010-11-24 11:45:48前一篇我們談到了如何高效學習Linux內核,現在我們開始另外一個話題,就是如何高效學習 linux 驅動開發。至于為什么會選擇這樣一個 topic ,主要是基于這樣兩個原因:

第一個原因是:目前幾乎所有的驅動開發方面的參考書,內容結構都是先介紹介紹什么是 linux 驅動,它分為哪些種類,然后是各種類型設備的驅動程序的內容細節。大都是只注重各種驅動本身的細節,而沒有站在一個全局整體的角度講解一下驅動開發的方法。這樣導致的后果就是,大多數的驅動開發者雖然可以正確的編寫驅動程序,但往往都是只知其一不知其二,知其然而不知其所以然。

第二個原因是:目前很多驅動開發者,即使是已經有多年經驗的開發者,在開發驅動的時候也就是填充填充 driver 的結構體,對于比較成熟的平臺,就是網上找個類似的驅動修改一下,即使寫十個百個千個驅動,也就是對某些硬件比較熟,遇到全新的芯片全新的平臺就束手無策。應該說這樣對驅動的理解是很有限的。這也是目前linux 驅動開發領域的現狀。

我們首先認識一下 linux 驅動的基本面,我們認識一個新事物的的第一件事就是了解它的一些基本信息,就像我們人與人之間互相認識首先也是通過個人的基本信息一樣。

linux 驅動在本質上就是一種軟件程序,上層軟件可以在不用了解硬件特性的情況下,通過驅動提供的接口,和計算機硬件進行通信。

系統調用是內核和應用程序之間的接口,而驅動程序是內核和硬件之間的接口,也就是內核和硬件之間的橋梁。它為應用程序屏蔽了硬件的細節,這樣在應用程序看來,硬件設備只是一個設備文件,應用程序可以象操作普通文件一樣對硬件設備進行操作。

linux 驅動程序是內核的一部分,管理著系統中的設備控制器和相應的設備。它主要完成這么幾個功能:對設備初始化和釋放;傳送數據到硬件和從硬件讀取數據;檢測和處理設備出現的錯誤。

一般來說,一個驅動可以管理一種類型的設備。例如不同的 U 盤都屬于 mass storage 設備,我們不需要為每一個 U 盤編寫驅動,而只需要一個驅動就可以管理所有這些 mass storage 設備。

為方便我們加入各種驅動來支持不同的硬件,內核抽象出了很多層次結構,這些層次結構是 linux 設備驅動的上層。它們抽象出各種的驅動接口,驅動只需要填寫相應的回調函數,就能很容易把新的驅動添加到內核。

一般來說, linux 驅動可以分為三類,就是塊設備驅動,字符設備驅動和網絡設備驅動。塊設備的讀寫都有緩存來支持,并且塊設備必須能夠隨機存取。塊設備驅動主要用于磁盤驅動器。

而字符設備的 I/O 操作沒有通過緩存。字符設備操作以字節為基礎,但不是說一次只能執行一個字節操作。例如對于字符設備我們可以通過 mmap 一次進行大量數據交換。字符設備實現比較簡單和靈活。

    網絡設備在 Linux 里做專門的處理。 Linux 的網絡系統主要是基于 BSD  socket 機制。網絡設備驅動為網絡操作提供接口,管理網絡數據的接送和收發。為了屏蔽網絡環境中物理網絡設備的多樣性, Linux 對所有的物理設備進行抽象并定義了一個統一的概念,稱之為接口( interface )。所有對網絡硬件的訪問都是通過接口進行的,接口對上層協議提供一致化的操作集合來處理基本數據的發送和接收,對下層屏蔽硬件差異。它與字符設備及塊設備不同之處其一就是網絡接口不存在于 Linux 的設備文件系統 /dev/ 中。

和前一篇的介紹一樣,看完外表,我們再看內涵,就是 Linux 驅動的工作流程。大概有四個部分:使用 insmod 加載,模塊的初始化,進行設備操作,使用 rmmod 卸載。

Linux 驅動有兩種存在形式,一種是直接編譯進內核,就是我們在配置內核的時候,在相應選項上選 Y ,另外一種就是編譯成模塊,按需加載和卸載。通常我們使用insmod 命令完成模塊的加載,在加載時還可以指定模塊參數。另外一個常用的加載工具是 modprobe ,它與 insmod 的不同在于它會檢查模塊之間的依賴關系,將該模塊依賴的模塊也加載到內核。

每個驅動都有自己的初始化函數,完成一些新功能的注冊,這個初始化函數只是在初始化的時候被使用。在 linux 系統里,設備以文件的形式存在,應用程序可以通過 open  read 等函數操作設備,通過設備文件實現對設備的訪問。設備不再使用時,我們使用 rmmod 命令來卸載它,卸載的過程會調用到驅動的推出函數,每個驅動都必須有一個退出函數,沒有的話,內核就不會允許去卸載它。

在對 linux 驅動的外表和內涵都有了一個初步的認識之后,我們來看看作為一個驅動開發者,我們需要注意哪些問題。

首先,對模塊機制的了解是開發 linux 驅動的基礎,因為我們編寫驅動的過程也就是在編寫一個內核模塊的過程。早期版本的內核是整體式的,也就是說所有的部分都靜態地連接成一個很大的執行文件。但是現在的內核采用的是新的機制,即模塊機制:許多功能包含在模塊內,當你需要時可以使用 insmod 去擁抱它,將它動態地載入到內核里,當你不需要時,則可以使用 rmmod 將它一腳踢開。這就使得 kernel 的內核很小,而且在運行的時候可以不用 reboot 就能夠載入和替代模塊。

其次,我們要注重對設備模型的理解。其實從 2.6 內核開始,隨著設備模型的出現,驅動的開發就不再是個困難的問題,毫不夸張得說,理解了設備模型,再去看那些五花八門的驅動程序,你會發現自己站在了另一個高度,從而有了一種俯視的感覺,就像鳳姐俯視知音和故事會,韓峰同志俯視女下屬。不過貌似大部分驅動開發者都沒意識到這個問題。

最后,是要養成使用協議的 spec 、設備的 datasheet 、內核參考代碼去解決問題的習慣,而不是一碰到問題就到處尋找所謂的牛人去問怎么解決。

中間的那些內容和前面精華版的博文里差不多,就不貼了,…………

]]>
AMD顯卡催化劑8.6 Linux驅動正式版http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1740&Page=1wangxinxin2010-11-24 11:43:11AMD目前對Linux系統越來越重視,每月發布催化劑系列驅動中都包括了For Linux的驅動版本,而且驅動名稱和版本號已經和Windows版本保持同步。畢竟,隨著Linux系統的不斷推廣,許許多多的電腦愛好者加入其中,對驅動的需求也隨之大幅度增長,Linux這塊大蛋糕是任何硬件廠商都不可小視的。

最新8.6版Linux驅動更新包括:

1.新增對UYVY和YUY2像素格式的支持,從而為視頻播放程序TVTime和MythTV提供interleaved stream隔行掃描技術支持。

2.修正了運行《深入敵后:雷神戰爭》demo時,畫面顯示不正確的問題。

3.解決了在運行《Quake3》游戲時改變分辨率模式會導致畫面顯示出錯的問題。

4.修正了當使用xcommgr后,桌面背景顏色顯示不正常的問題。

5.修正了在Ubuntu 7.10系統上安裝AMD顯示驅動后,音頻功能會出錯的問題。

6.解決了當開啟composing后,試圖調整2D應用程序窗口大小時,性能下降嚴重的問題。

7.修正了和Maya 2008之間存在的兼容性問題。

如果想正確使用AMD Linux版催化劑驅動,系統必須符合如下需求:

1.XOrg 6.7、6.8、6.9、7.0、7.1、7.2、7.3。2.Linux Kernel 2.6或更高版本。3.glibc版本2.2或2.3。4.支持POSIX Shared Memory (/dev/shm)(運行3D應用程序必需)。

此款驅動具體支持顯卡型號包括:

ATI Workstation Product Support

The ATI Catalyst? Linux software suite is designed to support the following ATI Workstation products:
ATI FireGL? V8650 ATI FireGL? V3300
ATI FireGL? V8600 ATI FireGL? V3200
ATI FireGL? V7700 ATI FireGL? V3100
ATI FireGL? V7600 ATI FireGL? X3-256
ATI FireGL? V7350 ATI FireGL? X3
ATI FireGL? V7300 ATI FireGL? V5000
ATI FireGL? V7200 ATI FireGL? X2-256
ATI FireGL? V7100 ATI FireGL? Z1-128
ATI FireGL? V5600 ATI FireGL? T2-128
ATI FireGL? V5200 ATI FireGL? X1-128
ATI FireGL? V5100 ATI FireGL? X1-256p
ATI FireGL? V5000 ATI FireMV? 2200 (Single card PCI-e configuration)
ATI FireGL? V3600 ATI Mobility? FireGL? V5000
ATI FireGL? V3400 ATI Mobility? FireGL? T2

ATI Mobility? and Integrated Product Family Support

The ATI Catalyst? Linux software suite is designed to support the following ATI Mobility? products:
ATI Mobility? Radeon? X1800 ATI Mobility? Radeon? X600
ATI Mobility? Radeon? X1600 ATI Mobility? Radeon? X300
ATI Mobility? Radeon? X1400 ATI Mobility? Radeon? X200
ATI Mobility? Radeon? X1300 ATI Mobility? Radeon? 9800
ATI Mobility? Radeon? X1200 ATI Mobility? Radeon? 9600
ATI Mobility? Radeon? X1100 ATI Mobility? Radeon? 9550
ATI Mobility? Radeon? X800 ATI Mobility? Radeon? 9500
ATI Mobility? Radeon? X700 ATI Mobility? Radeon? Xpress 1100 series
ATI Mobility? Radeon? Xpress 1200 series ATI Mobility? Radeon? Xpress 200 series

ATI Desktop and Integrated Product Family Support

The ATI Catalyst? Linux software suite is designed to support the following ATI desktop products:
ATI Radeon? HD 3800 series ATI Radeon? X800 series
ATI Radeon? HD 3600 series ATI Radeon? X700 series
ATI Radeon? HD 3400 series ATI Radeon? Xpress1200 series
ATI Radeon? HD 3200 series ATI Radeon? Xpress 200 series
ATI Radeon? HD 3100 series ATI Radeon? X600 series
ATI Radeon? HD 2900 series ATI Radeon? X550/X300 series
ATI Radeon? HD 2400 series ATI Radeon? 9800 series
ATI Radeon? HD 2600 series ATI Radeon? 9700 series
ATI Radeon? X1900 series ATI Radeon? 9600 series
ATI Radeon? X1800 series ATI Radeon? 9550 series
ATI Radeon? X1600 series ATI Radeon? 9500 series
ATI Radeon? X1300 series ATI Radeon? Xpress 1100 series
ATI Radeon? X850 series

]]>
Linux設計液晶顯示屏驅動技術http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1739&Page=1wangxinxin2010-11-24 11:41:15接口,同時完全隱蔽了設備的工作細節。用戶通過一組和具體設備驅動無關的標準化的調用來完成相關操作,驅動程序的任務就是把這些調用映射到具體設備對于實際硬件的特定操作上。

  硬件設備只是一個設備文件,應用程序可以像操作普通文件一樣對硬件設備進行操作。設備驅動程序是內核的一部分,它實現以下功能:

  ①對設備初始化和釋放。

  ②把數據從內核傳送到硬件和從硬件讀取數據。

  ③讀取應用程序傳送給設備文件的數據和回送應用程序請求的數據。

  ④檢測和處理設備出現的錯誤。

  可以把設備驅動作為內核的一部分直接編譯到內核中(即靜態編譯),也可以單獨作為一個模塊編譯,在需要的時候動態地插入到內核中,在不需要的時候可把它從內核中刪除(即動態鏈接)。

  目前Linux支持的設備驅動可以分為3種:字符設備(character device)、塊設備(block device)、網絡接口設備(network device)。當然它們之間也并不是嚴格地加以區分。字符設備是所有能夠像字節流一樣被訪問的設備(如文件等),在Linux中通過字符設備驅動程序來實現。在Linux中它們也被映射為文件系統的1個節點,通常在/dev目錄下。字符設備驅動程序一般要包含openclose、read、write等幾個系統調用。

  本文為開發字符設備驅動實例,對于其他兩類不再贅述。

  1 Linux關于字符設備的管理

  驅動程序在Linux內核中往往是以模塊形式出現的。與應用程序的執行過程不同,模塊通常只是預先向內核注冊自己,當內核需要時響應請求。模塊中包含2個重要的函數init_module和cleanup_module。前者是模塊的入口,它為模塊調用做好準備工作,而后者是在模塊即將卸載時被調用,做一些清掃工作。

圖片點擊可在新窗口打開查看


  驅動程序模塊通過函數int register_chrdev(unsignedint major,const char*name,struct file_operations*fops)來完成向內核注冊。其中unsigned int major為主設備號,const char*name為設備名,struct file_operations*fops為驅動設備管理中重要的結構指針,此結構中每個字段都必須指向驅動程序中實現特定操作的操作函數。

  2 FYD12864-0402B液晶模塊簡介

  FYD12864-0402B是一種具有4位/8位并行、2線或3線串行多種接口方式,內部含有國標一級、二級簡體中文字庫的點陣圖形液晶顯示模塊,低電壓,低功耗。其顯示分辨率為128×64,內置8 192個16×16點陣的漢字,以及128個16×8點ASCII字符集。利用該模塊靈活的接口方式和簡單、方便的操作指令,可構成全中文人機交互圖形界面,可以顯示8×4行16×16點陣的漢字。也可完成圖形顯示。FYD12864-0402B液晶模塊框圖如圖1所示,其中ST7920為液晶顯示控制芯片ST7921為液晶顯示驅動芯片

  FYD12864-0402B控制器接口信號說明如下:

  ①RS、R/W的配合選擇決定控制界面的4種模式,如表1所列。

圖片點擊可在新窗口打開查看


  ②E信號如表2所列。

圖片點擊可在新窗口打開查看


  3 LCD讀寫原理

  FYD12864-0402B每屏可顯示4行8列共32個16×16點陣的漢字,每個顯示RAM可顯示1個中文字符或2個16×8點陣全高ASCII碼字符,即每屏最多可顯示32個中文字符或64個ASCII碼字符。FYD12864-0402B內部提供128×2字節的字符顯示RAM緩沖區(DDRAM)。字符顯示是通過將字符顯示編碼寫入該字符顯示RAM實現的。根據寫入內容的不同,可分別在液晶屏上顯示CGROM(中文字庫)、HCGROM(ASCII碼字庫)及CGRAM(自定義字形)的內容。3種不同字符/字型的選擇編碼范圍為:0000~0006H(其代碼分別是0000、0002、0004、0006,共4個)顯示自定義字型,02H~7FH顯示半寬ASCII碼字符,A1A0H~F7FFH顯示8 192種GB2312中文字庫字形。字符顯示RAM在液晶模塊中的地址80H~9FH。字符顯示的RAM的地址與32個字符顯示區域有著一一對應的關系。

  4 部分代碼解析

圖片點擊可在新窗口打開查看


  5 編寫Makefile和用戶級測試程序

圖片點擊可在新窗口打開查看


  下面2行宏變量定義使用armv41-unknown-linux-gcc編譯器編譯驅動,默認使用gcc編譯器、X86 PC平臺。

  結  語

  對Linux設備驅動程序作了詳細的介紹,在實際開發板AT91RM9200上加入FYD12864-0402B驅動模塊,該液晶驅動采用通用化接口和調用方法,對開發Linux其他設備驅動程序具有很好的指導意義。(單片機與嵌入式系統應用

]]>
基于Linux的MISC類設備AD7859L的驅動程序開發http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1738&Page=1wangxinxin2010-11-24 11:38:53
以下內容含腳本,或可能導致頁面不正常的代碼
說明:上面顯示的是代碼內容。您可以先檢查過代碼沒問題,或修改之后再運行.
]]>
基于Linux平臺的FPGA驅動開發http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1737&Page=1wangxinxin2010-11-24 11:35:19Linux內核兩個部分共同組成的一個操作系統。該系統中所有組件的源代碼都是自由的,可以有效保護學習成果,因而在嵌入式領域得到了廣泛的應用。

    FPGA是英文Field Programmable Gate Array的縮寫,即現場可編程門陣列,該器件是作為專用集成電路ASIC (Application Specific Integrated Circuit)領域中的一種半定制電路而出現的,它的出現既解決了定制電路的不足,又克服了原有可編程器件門電路數有限的缺點。在通信行業、傳輸網、醫療儀器、各種電子儀器、安防監控、電力系統、汽車電子以及消費類電子中都大面積使用。隨著產品研發周期的逐步縮短,定制型產品的開發使FPGA在后面的應用面越來越廣。例如在2G和3G通信,以及以后的4G通信和wimax等等通信類設備中,它與DSP、MPU一起將大量出現在其中。

    S3C2410微處理器是一款由Samsung為手持設備設計的低功耗、高度集成的微處理器,采用272腳FBGA封裝,內含一個ARM920T內核和一些片內外圍設備。在時鐘方面,該芯片集成了一個具有日歷功能的RTC和具有PLL (MPLL和UPLL)的芯片時鐘發生器。MPLL產生的主時鐘能夠使處理器工作頻率最高達到203MHz。這個工作頻率能夠使處理器輕松運行于Windows CE,Linux等操作系統并進行較為復雜的信息處理。為此,本文以S3C2410上使用Altera公司的EP2S30F67214為例,系統地介紹了在Linux系統環境下的FPGA的驅動方法。


1 基本原理

    Linux下的設備驅動程序通常是一個存在于應用程序和實際設備間的軟件層。許多設備驅動都是與用戶程序一起發行的,可以幫助配置和存取目標設備。

    在Linux下驅動FPGA,其本質上就是字符設備的驅動,慣例上它們位于/dev目錄。

1.1 主次編號

    在內核中,dev_t類型(在中定義)用來持有設備編號。通常2.6內核版本限制在255個主編號和255個次編號。

    建立一個字符驅動時,需要做的第一件事是獲取一個或多個設備編號。其必要的函數是regis-ter_chrdev_region,設計時可在中聲明:

int register_chrdev_region(dev_t first,unsigned int count,char*name);

    如同大部分內核函數一樣,如果分配成功,register_chrdev_region的返回值將是0。出錯時,則返回一個負的錯誤碼,但不能存取請求的區域。

1.2 重要數據結構

    注冊設備編號僅僅是驅動代碼必須進行的諸多任務中的第一個。驅動操作包括三個重要的內核數據結構,稱為file_operations、file和inode。其中,對于FPGA驅動來說,最值得關注的是文件操作(file_operations)。

    file_operation結構是一個用字符驅動方式建立設備編號和設備操作的連接結構,定義在.是一個函數指針的集合。每個打開文件與它自身的函數集合相關,這些操作大部分可由系統調用,例如:open(),read ()等等。典型的file_operation結構可用FPGA設備列表所示,其代碼如下:

圖片點擊可在新窗口打開查看

    第一個file_operations元素根本不是一個操作,它是一個指向擁有這個結構的模塊指針,或用來在操作使用時阻止模塊被卸載,它也是在中定義的宏;

    llseek主要用于改變文件中的當前讀/寫位置,同時可將新位置作為(正的)返回值。其定義如下:

loff_t(*llseek) (struct file*,loff_t,int);

     ioctl可為系統調用提供一個發出設備特定命令的方法。如果設備不提供ioctl方法,那么,對于任何未事先定義的請求,系統調用將返回一個錯誤。定義如下:

int(*ioctl) (struct inode*,struct file*,unsigned int,unsigned long):

1.3 設備注冊

     內核在內部將使用struct cdev類型結構來代表字符設備。在內核調用設備操作前,代碼應當包含。而如果想將cdev結構嵌入設備特定的結構中,則應當初始化已經分配的結構,其使用的代碼為:

void cdev_init(struct cdev*cdev,structfile_operations*fops);

1.4 open和release

    open主要用于提供驅動初始化,在大部分驅動中,open應當檢查設備特定的錯誤(例如設備沒準備好,或者類似的硬件錯誤),但是,其第一步常常是確定打開哪個設備。open的原代碼為:

int(*open) (struct inode*inode,structfile*flip);

 1.5 讀/寫操作

    讀和寫都是進行類似的任務,就是從設備到應用程序代碼的數據拷貝。因此,它們的原代碼比較相似:

ssize_t read(struct file*flip,char__user*buff,size_t count,loff_t*offp);
ssize_t write(struct file*filp,const char__user*buff,size_t count,loff_t*offp);

    read的任務是從設備拷貝數據到用戶空間(使用copy_to_user),而write方法則是從用戶空間拷貝數據到設備(使用copy_from_user)。

圖片點擊可在新窗口打開查看

    圖1所示是用read參數表示一個典型讀的實現過程。


2 硬件電路

    通常在大容量存儲項目中,S3C2410處理器一般作為主CPU,可對EP2S30F67214進行擴展,以使系統具有拍攝、存儲、下載、I/O口擴展的功能。由于FPGA的高速處理能力和易擴展性,ARM與FPGA的結合使用,將在嵌入式系統領域占據主導地位。

    本項目中的ARM主要讀取FPGA的數據,然后進行數據處理并送給上位機。其ARM處理器與FPGA的連接關系如圖2所示,其主要連接有32位寬數據線、27位寬地址線以及讀、寫、中斷和片選控制線等。

圖片點擊可在新窗口打開查看

    在S3C2410中,nGPCS4的物理地址為0x2000000—0x28000000,共計128MB的靜態物理空間。中斷方式為下降沿有效。


3 編程實現

3.1 設備驅動初始化

    初始化模塊在內核啟動時主要負責初始化FPGA工作。其實現由module_init () 和module_exit ()兩部分組成。其代碼如下:

圖片點擊可在新窗口打開查看

 3.2 異步中斷通知

    在應用程序中,可用如下代碼獲得中斷響應:

signal (SIGIO,test_handler);/*test_handler為函數名字*/
fcntl(fa,F_SETOWN,getpid ());
oflags=fcntl(fa,F_GETFL);/*fd為打開設備返回值*/
fcntl (fd,F_SETFL,oflags∣FASYNC);/*fd為打開設備返回值*/

    應當注意的是,不是所有的設備都支持異步通知。應用程序常常假定異步能力只對socket和tty可用。

3.3 地址映射

    在Linux設備驅動程序開發過程中,由于驅動程序操作的都是設備的虛擬地址,因此,要使驅動程序對虛擬地址的操作反映到正確的設備上,還需要通過內存管理單元MMU來將設備的虛擬地址映射到正確的物理地址上去,從而保證驅動程序對設備的虛擬地址的操作,也就是要對其相應的物理地址進行操作。使用內存映射的好處是處理大文件時,其速度明顯快于標準文件I/O,這樣無論讀和寫,都少了一次用戶空間與內核空間之間的復制。在用戶空間對FPGA設備的訪問可通過內存映射來實現。FPGA可以看作是硬件連接在S3C2410微處理器的片選信號nGPCS4上的一段物理地址的尋址。因此,必須先把物理地址映射到虛擬地址空間,然后才能對該段地址進行讀/寫。通常用戶可用如下代碼關聯FPGA的地址:

fpga_base=ioremap(FPGA_PHY_START,FPGA_PHY_SIZE);


4 結束語

    本文系統的介紹了ARM基于Linux平臺下的FPGA的驅動開發方法,并通過開發用戶程序,實現了數據的處理和傳輸,從而實現了FPGA在嵌入式領域的廣泛應用。

]]>
Linux環境中網卡設備的驅動http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1736&Page=1wangxinxin2010-11-24 11:31:02  但是,在桌面系統上,Linux與Windows 在易用性上還存在一定的差距,主要表現在其使用上過于復雜,還不適合初級用戶,尤其是硬件的配置工作。比如,在Linux的安裝過程中,有一些特殊設備Linux系統不能自動識別(如某些網卡等),這就需要在系統安裝完成之后進行手工設置。本文以臺灣Davicom公司的dm9102快速以太網卡為例,介紹在Linux環境中設置網卡設備驅動的步驟。

  系統環境: TurboLinux4.0.2,網卡、顯卡、Modem均集成在主板上。

  1.下載驅動

  登錄到Davicom公司網站上(www.davicom.com.tw)下載Linux下的最新驅動程序,版本為2.0.10-20,源程序名為dmfe.c。

  2.編譯

  gcc -DMODULE -D_ _KERNEL_I/usr/src/linux/net /inet -Wall -Wstrict-prototypes O6 c dmfe.c

  gcc(GNU C Compiler)為編譯命令,編譯完成后,在當前目錄下產生目標文件dmfe.o。

  3.設置模式

  利用insmod命令設置模式,其常用的幾個命令如下表所示:

其中,insmod dmfe命令的主要輸出結果如下:

  ID=91021282 ’ 網卡ID號

  NAME=eth0 ’ 網卡名稱

  IO=e000 ’ 輸入輸出口地址

  IRQ=11’ 中斷向量

  4.設置參數

  為網卡配置IP地址、子網掩碼、網關等參數:

  ifconfig eth0 *.*.*.*

  netmask 255.255.255.*

  broadcast *.*.*.*

  5.配置路由

  route add-net *.*.*.* eth0

  6.啟動網卡

  使用命令ifup eth0 啟動網卡即可。

  至此,我們已經成功配置了dm9102網卡。通過命令ping *.*.*.* (127.0.0.1為本機循環地址,可用于檢測網卡)可以檢測網卡是否正常工作,其中*.*.*.* 為該計算機網絡能夠達到的機器的IP地址。如果配置正確,應該返回響應時間、發送接受字節數等信息; 若返回“request time out”等信息,則說明網卡沒有正常工作。

  上述4~6步也可以通過turbolnetcfg等工具進行配置。

  通過以上步驟,我們以手工方式驅動了網卡,但是在系統啟動時網卡尚不能馬上工作,需要重復執行上述3~6步。如果要在系統啟動期間使網卡設置自動生效,需要執行以下操作:

  首先,編寫shell程序。啟動vi,使用命令vi startnet:

  #!/bin/sh

  insmod dmfe

  ifup eth0

  保存文件startnet,并更改startnet文件屬性為可執行屬性:

  chmod +x startnet

  然后,將目標文件拷貝到/etc/rc.d目錄下:

  cp /dmfe.o /etc/rc.d/

  最后,修改inet文件:

  vi /etc/rc.d/init.d/inet

  在其中加入以下命令:

  /etc/rc.d/startnet (執行/etc/rc.d/目錄下的startnet文件)

  這樣,用reboot命令重新啟動機器后,在系統啟動過程中可以看到網卡被驅動起來

]]>
AMD攜手開源社區 解決Linux平臺ATI顯卡驅動問題http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1735&Page=1wangxinxin2010-11-24 11:30:16本周四,AMD跟隨英特爾,成為Linux平臺上支持ATI顯卡芯片的驅動和軟件開發的芯片廠商。ATI已經為Linux平臺提供自家的顯卡驅動,但它存在工程和支持上的問題,因此,ATI決定資助創建一種開源性質的顯卡驅動項目。

AMD顯卡產品集團的軟件副總裁Ben Bar-Haim說:“近來,我們看到自己的產品越來越多的被使用于臺式機,而非專業工作站上。我們感到自己需要介入這一領域,要允許社區開發出一種良好的開源軟件。”

AMD正在資助Novell Suse Linux程序員創建驅動,共享硬件細節及提供工程方面的協助。下周一,相關的驅動就會面世,AMD將幫助程序員們逐漸擴展Linux平臺上二維和三維顯卡加速方面的功能支持。

對ATI和AMD來說,支持開源驅動工作代表著一種巨大的轉變。以前,ATI對于開源Linux驅動工作一直較為冷淡。但為Linux平臺用戶來說,專有驅動不太好用。另外,ATI在軟件升級和其它支持方面也碰到了問題。在Linux平臺逐步擁抱三維顯卡功能的過程中,專有驅動所產生的問題進一步凸顯。

2006年,英特爾開始研制針對Linux平臺的開源顯卡驅動。

目前,和ATI一樣生產獨立顯卡的廠商Nvidia仍然堅持開發專有驅動,Nvidia方面尚未為ATI的舉措發表看法。

即使開源驅動發展得非常成熟,也不可能完全取代專有驅動。在復制保護以及數字版權管理等涉及到知識產權技術的地方,AMD不會向開源社區的程序員們公開相關的細節。

但AMD說,開放一半總比完全封閉的好。

Suse Linux項目組的程序經理John Bridgman說:“過去,我們碰到的最大問題是,我們收到反饋說,為了進行驅動開發工作,我們必須全部開放硬件細節。顯示的情況是,現在的顯卡發展速度相當快,我們只能借助社區的力量來開發驅動,以便在不危及知識產權的前提下滿足用戶的需求。”

Bar-Heim說,2006年AMD收購ATI后,這種需求進一步增強了。

據悉,第一批開源性質的驅動將支持ATI的Radeon X1000和HD 2000顯卡芯片,AMD未來還將繼續公布相關的支持信息。

據悉,AMD將繼續開發專有顯卡驅動,下周,新版HD 2000顯卡驅動就將面世。10月份,ATI將支持AIGLX三維顯卡功能,第四季度,AMD還將面向工作站用戶推出新的顯卡驅動程序。

]]>
NAPI技術在Linux網絡驅動上的應用http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1734&Page=1wangxinxin2010-11-24 11:29:37
  static int cp_rx_poll (struct net_device *dev, int *budget)   

  參數 budget 的上層任務所需要底層傳遞的數據包的數量,這個數值不能超過netdev_max_backlog 的值。   

  總而言之,POLL 方法被網絡層調用,只負責按照網絡層的要求值("預算"值)提交對應數量的數據包。8139CP 的 POLL 方法注冊通常在設備驅動程序模塊初始化(調用 probe)的時候進行,如下:

  static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)

  {

  … …

  dev->poll = cp_rx_poll;

  … …

  }

  

  設備的 POLL 方法正如前所說的是被網絡層上的軟中斷 net_rx_action 調用,我們現在來看具體的流程:

  

  static int cp_rx_poll (struct net_device *dev, int *budget)

  {

   struct cp_private *cp = netdev_priv(dev);

   unsigned rx_tail = cp->rx_tail;

   /*設定每次進行調度的時候從設備發送到網絡層次最大的數據包的大小*/

  unsigned rx_work = dev->quota;

   unsigned rx;

  

  rx_status_loop:

   rx = 0;

  /*重新打開NIC中斷,在 cp_interrupt 中斷句柄中中斷關閉了,現在 POLl 已經開始處理環行緩沖隊列中的數據,

  所以中斷可以打開,準備接收新的數據包*/

   cpw16(IntrStatus, cp_rx_intr_mask);  

   while (1) {/*POLL循環的開始*/

   u32 status, len;

   dma_addr_t mapping;

   struct sk_buff *skb, *new_skb;

   struct cp_desc *desc;

   unsigned buflen;

  /*從下標為rx_tail的內存中的環行緩沖隊列接收隊列rx_skb上"摘下"套接字緩沖區*/

   skb = cp->rx_skb[rx_tail].skb;

   if (!skb)

   BUG();

  

   desc = &cp->rx_ring[rx_tail];

  /*檢查在 NIC 的環形隊列(rx_ring)上的最后的數據接收狀態,是否有出現接收或者 FIFO 的錯誤,是否*/

   status = le32_to_cpu(desc->opts1);

   if (status & DescOwn)

   break;

  

   len = (status & 0x1fff) - 4;

   mapping = cp->rx_skb[rx_tail].mapping;

  

   if ((status & (FirstFrag   LastFrag)) != (FirstFrag   LastFrag)) {

   /* we don't support incoming fragmented frames.

   * instead, we attempt to ensure that the

   * pre-allocated RX skbs are properly sized such

   * that RX fragments are never encountered

   */

   cp_rx_err_acct(cp, rx_tail, status, len);

   cp->net_stats.rx_dropped++;

   cp->cp_stats.rx_frags++;

   goto rx_next;

   }

  

   if (status & (RxError   RxErrFIFO)) {

   cp_rx_err_acct(cp, rx_tail, status, len);

   goto rx_next;

   }

  

   if (netif_msg_rx_status(cp))

   printk(KERN_DEBUG "%s: rx slot %d status 0x%x len %d\n",

   cp->dev->name, rx_tail, status, len);

  

   buflen = cp->rx_buf_sz + RX_OFFSET;

  /*創建新的套接字緩沖區*/

   new_skb = dev_alloc_skb (buflen);

   if (!new_skb) {

   cp->net_stats.rx_dropped++;

   goto rx_next;

   }

  

   skb_reserve(new_skb, RX_OFFSET);

   new_skb->dev = cp->dev;

  /*解除原先映射的環行隊列上的映射區域*/

   pci_unmap_single(cp->pdev, mapping,

   buflen, PCI_DMA_FROMDEVICE);

  /*檢查套接字緩沖區(sk_buff)上得到的數據校驗和是否正確*/

   /* Handle checksum offloading for incoming packets. */

   if (cp_rx_csum_ok(status))

   skb->ip_summed = CHECKSUM_UNNECESSARY;

   else

   skb->ip_summed = CHECKSUM_NONE;

  /*按照數據的實際大小重新定義套接字緩沖區的大小*/

   skb_put(skb, len);  

   mapping =

   cp->rx_skb[rx_tail].mapping =

  /*DMA影射在前面新創建的套接字緩沖區虛擬地址new_buf->tail到實際的物理地址上,

  并且把這個物理地址掛在接收緩沖區的隊列中*/

   pci_map_single(cp->pdev, new_skb->tail,

   buflen, PCI_DMA_FROMDEVICE);

  /*把新建立的緩沖區的虛擬地址掛在接收緩沖區的隊列中,在下一次訪問rx_skb數組的這個結構時候,

  POLL方法會從這個虛擬地址讀出接收到的數據包*/

   cp->rx_skb[rx_tail].skb = new_skb;

  /*在cp_rx_skb調用netif_rx_skb,填充接收數據包隊列,等待網絡層在Bottom half隊列中調用ip_rcv接收網絡數據,

  這個函數替代了以前使用的netif_rx*/

   cp_rx_skb(cp, skb, desc);

   rx++;  

  rx_next:

  /*把前面映射的物理地址掛在NIC設備的環行隊列上(也就是rx_ring上,它是在和NIC中物理存儲區進行了DMA映射的,

  而不是驅動在內存中動態建立的),準備提交給下層(NIC)進行數據傳輸*/

   cp->rx_ring[rx_tail].opts2 = 0;

   cp->rx_ring[rx_tail].addr = cpu_to_le64(mapping);

  /*在相應的傳輸寄存器中寫入控制字,把rx_ring的控制權從驅動程序交還給NIC硬件*/

   if (rx_tail == (CP_RX_RING_SIZE - 1))

   desc->opts1 = cpu_to_le32(DescOwn   RingEnd  

   cp->rx_buf_sz);

   else

   desc->opts1 = cpu_to_le32(DescOwn   cp->rx_buf_sz);

  /*步進到下一個接收緩沖隊列的下一個單元*/

   rx_tail = NEXT_RX(rx_tail);

  

   if (!rx_work--)

   break;  

   cp->rx_tail = rx_tail;

  /*遞減配額值quota,一旦quota遞減到0表示這次的POLL傳輸已經完成了使命,

  就等待有數據到來的時候再次喚醒軟中斷執行POLL方法*/

   dev->quota -= rx;

   *budget -= rx;  

   /* if we did not reach work limit, then we're done with

   * this round of polling

   */

   if (rx_work) {

  /*如果仍然有數據達到,那么返回POLL方法循環的開始,繼續接收數據*/

   if (cpr16(IntrStatus) & cp_rx_intr_mask)

   goto rx_status_loop;

  /*這里表示數據已經接收完畢,而且沒有新的接收中斷產生了,這個時候使能NIC的接收中斷,

  并且調用__netif_rx_complete把已經完成POLL的設備從poll_list上摘除,等待下一次中斷產生的時候,

  再次把設備掛上poll_list隊列中。*/

   local_irq_disable();

   cpw16_f(IntrMask, cp_intr_mask);

   __netif_rx_complete(dev);

   local_irq_enable();  

   return 0; /* done */

   }  

   return 1; /* not done */

  }  

  其他的使用 NAPI 的驅動程序和 8139CP 大同小異,只是使用了網絡層專門提供的 POLL 方法--proecess_backlog(/net/dev.c),在 NIC 中斷接收到了數據包后,調用網絡層上的 netif_rx(/net/dev.c)將硬件中斷中接收到數據幀存入 sk_buff 結構, 然后檢查硬件幀頭,識別幀類型, 放入接收隊列(softnet_data 結構中的 input_pkt_queue 隊列上), 激活接收軟中斷作進一步處理. 軟中斷函數(net_rx_action)提取接收包,而 process_backlog(也就是 POLL 方法)向上層提交數據。

]]>
設計Linux系統網絡設備驅動程序http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1733&Page=1wangxinxin2010-11-24 11:28:57Linux網絡設備驅動程序是Linux操作系統網絡應用中的一個重要組成部分。分析其運行機理,對于設計Linux網絡應用程序是很有幫助的。我們可以在網絡驅動程序這一級做一些與應用相關聯的特殊事情,例如在設計Linux防火墻和網絡入侵檢測系統時,可以在網絡驅動程序的基礎上攔截網絡數據包,繼而對其進行分析。由于Linux是開放源代碼的,所以給我們提供了一個分析和改造網絡驅動程序,并使其滿足特殊應用的絕好機會。本文對Linux內核中的網絡驅動程序部分進行了詳細討論,并給出了實現Linux網絡驅動程序的重要過程、一種實現模式和具體實例。
  
  
  運行機理
  
  
  1.體系結構
  
  Linux網絡驅動程序的體系結構如圖1所示。可以劃分為四層,從上到下分別為協議接口層、網絡設備接口層、提供實際功能的設備驅動功能層,以及網絡設備和網絡媒介層。在設計網絡驅動程序時,最主要的工作就是完成設備驅動功能層,使其滿足我們自己所需的功能。在Linux中,把所有網絡設備都抽象為一個接口。這個接口提供了對所有網絡設備的操作集合。由數據結構 struct device來表示網絡設備在內核中的運行情況,即網絡設備接口。它既包括純軟件網絡設備接口,如環路(Loopback),也可以包括硬件網絡設備接口,如以太網卡。它由以dev_base為頭指針的設備鏈表來集中管理所有網絡設備。該設備鏈表中的每個元素代表一個網絡設備接口。數據結構device中有很多供系統訪問和協議層調用的設備方法,包括供設備初始化和往系統注冊用的init函數、打開和關閉網絡設備的open和stop函數、處理數據包發送的函數hard_ start_xmit,以及中斷處理函數等。有關device數據結構(在內核中也就是net_device)的詳細內容,請參看/linux/include/linux/netdevice.h
  
   圖片點擊可在新窗口打開查看
  2.初始化
  
  網絡設備的初始化主要是由device數據結構中的init函數指針所指的初始化函數來完成的。當內核啟動或加載網絡驅動模塊的時候,就會調用初始化過程。這個過程將首先檢測網絡物理設備是否存在。它通過檢測物理設備的硬件特征來完成,然后再對設備進行資源配置。這些完成之后就要構造設備的device數據結構,用檢測到的數值來對device中的變量初始化。這一步很重要。最后向Linux內核注冊該設備并申請內存空間。
  
  3. 數據包的發送與接收
  
  數據包的發送和接收是實現Linux網絡驅動程序中兩個最關鍵的過程。對這兩個過程處理的好壞將直接影響到驅動程序的整體運行質量。圖1中也很明確地說明了網絡數據包的傳輸過程。首先在網絡設備驅動加載時,通過device域中的init函數指針調用網絡設備的初始化函數,對設備進行初始化。如果操作成功就可以通過device域中的open函數指針調用網絡設備的打開函數打開設備,再通過device域中的建立硬件包頭函數指針hard_header來建立硬件包頭信息。最后通過協議接口層函數dev_queue_xmit(詳見/linux/net/core/dev.c)來調用device域中的hard_start_xmit函數指針,完成數據包的發送。該函數將把存放在套接字緩沖區中的數據發送到物理設備。該緩沖區是由數據結構sk_buff (詳見/linux/include/linux/sk_buff.h)來表示的。
  
  數據包的接收是通過中斷機制來完成的。當有數據到達時,就產生中斷信號,網絡設備驅動功能層就調用中斷處理程序,即數據包接收程序來處理數據包的接收。然后,網絡協議接口層調用netif_rx函數(詳見/linux/net/core/dev.c),把接收到的數據包傳輸到網絡協議的上層進行處理。
  
  實現模式
  
  實現Linux網絡設備驅動功能主要有兩種形式:一是通過內核來進行加載,當內核啟動的時候,就開始加載網絡設備驅動程序,內核啟動完成之后,網絡驅動功能也隨即實現了;再就是通過模塊加載的形式。比較兩者,第二種形式更加靈活。在此著重對模塊加載形式進行討論。
  
  模塊設計是Linux中特有的技術,它使Linux內核功能更容易擴展。采用模塊來設計Linux網絡設備驅動程序會很輕松,并且能夠形成固定的模式。任何人只要依照這個模式去設計,都能設計出優良的網絡驅動程序。先簡要介紹一下基于模塊加載網絡驅動程序的設計步驟,后面還結合具體實例來講解。首先通過模塊加載命令insmod來把網絡設備驅動程序插入到內核之中。然后,insmod將調用init_module()函數首先對網絡設備的init函數指針初始化,再通過調用register_netdev()函數在Linux系統中注冊該網絡設備。如果成功,再調用init函數指針所指的網絡設備初始化函數來對設備初始化,將設備的device數據結構插入到dev_base鏈表的末尾。最后可以通過執行模塊卸載命令rmmod,來調用網絡驅動程序中的cleanup_module()函數,對網絡驅動程序模塊進行卸載。具體實現過程見圖2所示。
  圖片點擊可在新窗口打開查看
  通過模塊初始化網絡接口是在編譯內核時標記為編譯為模塊。系統在啟動時并不知道該接口的存在,需要用戶在/etc/rc.d/目錄中定義的初始啟動腳本中寫入命令或手動將模塊插入內核空間來激活網絡接口。這也給我們在何時加載網絡設備驅動程序提供了靈活性。
  應用實例
  
  
  我們以NE2000兼容網卡為例,來具體介紹基于模塊的網絡驅動程序的設計過程。可以參考文件linux/drivers/net/ne.c和linux/drivers/net/8390.c。
  
  1.模塊加載和卸載
  
  NE2000網卡的模塊加載功能由init_module()函數完成。具體過程及解釋如下:
  
  int init_module(void)
  {
  int this_dev, found = 0;
  //循環檢測ne2000類型的網絡設備接口
  for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++)
  {
  //獲得網絡接口對應的net-device結構指針
   struct net_device *dev = &#38;dev_ne[this_dev];
   dev->irq = irq[this_dev]; //初始化該接口的中斷請求號
   dev->mem_end = bad[this_dev];  //初始化接收緩沖區的終點位置
   dev->base_addr = io[this_dev];   //初始化網絡接口的I/O基地址
   dev->init = ne_probe;       //初始化init為ne_probe,后面介紹此函數
  //調用registre_netdevice()向系統登記網絡接口,在這個函數中將分配給網絡接口在系統中惟一
  的名稱。并且將該網絡接口設備添加到系統管理的鏈表dev-base中進行管理。
  if (register_netdev(dev) == 0) {
   found++;
   continue; }
  … //省略
  }
  return 0;}
  
  
  
  模塊卸載功能由cleanup_module()函數來實現。如下所示:
  
  void cleanup_module(void)
  {
  int this_dev;
  //遍歷整個dev-ne數組
  for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
  //獲得net-device結構指針
  struct net_device *dev = &#38;dev_ne[this_dev];
  if (dev->priv != NULL) {
    void *priv = dev->priv;
    struct pci_dev *idev = (struct pci_dev *)ei_status.priv;
  //調用函數指針 idev->deactive將已經激活的網卡關閉使用
  if (idev) idev->deactivate(idev); 
  free_irq(dev->irq, dev);
  //調用函數release_region()釋放該網卡占用的I/O地址空間
  release_region(dev->base_addr, NE_IO_EXTENT);
  //調用unregister_netdev()注銷 這個net_device()結構
  unregister_netdev(dev);
  kfree(priv); //釋放priv空間
   }
   }
  }
  
  
  
  2.網絡接口初始化
  
  實現此功能是由ne_probe()函數來完成的。前面已經提到過,在init_module()函數中用它來初始化init函數指針。它主要對網卡進行檢測,并且初始化系統中網絡設備信息,用于后面的網絡數據的發送和接收。具體過程及解釋如下:
  
  int __init ne_probe(struct net_device *dev)
  {
   unsigned int base_addr = dev->base_addr;
   //初始化dev-owner成員,因為使用模塊類型驅動,會將dev-owner指向對象modules結構指針。
   SET_MODULE_OWNER(dev);
   //檢測dev->base_addr是否合法,是則執行ne-probe1()函數檢測過程。不是,則需要自動檢測。
   if (base_addr > 0x1ff) 
  return ne_probe1(dev, base_addr);
   else if (base_addr != 0)   
  return -ENXIO;
   //如果有ISAPnP設備,則調用ne_probe_isapnp()檢測這種類型的網卡。
  if (isapnp_present() &#38;&#38; (ne_probe_isapnp(dev) == 0))
  return 0;
   …//省略
  return -ENODEV;
  }
  
  
  
  這其中兩個函數ne_probe_isapnp()和ne_probe19()的區別在于檢測中斷號上。PCI方式只需指定I/O基地址就可以自動獲得IRQ,是由BIOS自動分配的;而ISA方式需要獲得空閑的中斷資源才能分配。
  
  3.網絡接口設備打開和關閉
  
  網絡接口設備打開就是激活網絡接口,使它能接收來自網絡的數據并且傳遞到網絡協議棧的上面,也可以將數據發送到網絡上。設備關閉就是停止操作。
  
  在NE2000網絡驅動程序中,網絡設備打開由dev_open()和ne_open()完成,設備關閉有dev_close()和ne_close()完成。它們相應調用底層函數ei_open()和ei_close()來完成。其實現過程相對簡單,不再贅述。
]]>
S3被指缺乏Linux驅動開發能力http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1732&Page=1wangxinxin2010-11-24 11:28:16

  這些Linux用戶討論最多的就是Linux平臺上的OpenGL 3.0支持問題。S3的官方新聞稿表示Chrome540GTX支持Linux,甚至提到了GPGPU引擎。不過Linux用戶指出,去年11月S3在Chrome530GT的新聞稿中有同樣的表述,說Chrome 530GT支持OpenGL3.0,支持Linux。不過,至今S3未放出任何Linux驅動程序。

  當時,S3的美國公關代表曾表示Chrome 530GT的Linux驅動將在12月中旬提供,并有測試版的OpenGL3.0加速驅動。不過,12月下旬這款Linux驅動仍然下落不明,甚至連OpenGL1.x或2.x支持的驅動都未曾出現。

  現在Chrome500系列的Linux驅動問題又再次被提起,到底何時S3才能放出Chrome500系列的Linux驅動成立用戶最大的疑問。要知道當初S3就是因為驅動問題而在眾多顯卡廠商競爭中倒下。

]]>
Linux驅動開發必看:詳解神秘內核(1)http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1731&Page=1wangxinxin2010-11-24 11:27:27
<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->static unsigned int bootmode = 1;
static int __init
is_bootmode_setup(
char *str)
{
  get_option(
&str, &bootmode);
  
return 1;
}

/* Handle parameter "bootmode=" */
__setup(
"bootmode=", is_bootmode_setup);

if (bootmode) {
  
/* Print verbose output */
  
/* ... */
}

/* ... */

/* If bootmode is 1, choose an init runlevel of 3, else
   switch to a run level of 2
*/
if (bootmode) {
  argv_init[
++args] = "3";
}
else {
  argv_init[
++args] = "2";
}

/* ... */

  請重新編譯內核并嘗試運行新的修改。


  2.1.4 Calibrating delay...1197.46 BogoMIPS (lpj=2394935)

  在啟動過程中,內核會計算處理器在一個jiffy時間內運行一個內部的延遲循環的次數。jiffy的含義是系統定時器2個連續的節拍之間的間隔。正如所料,該計算必須被校準到所用CPU的處理速度。校準的結果被存儲 target=_blank>存儲在稱為loops_per_jiffy的內核變量中。使用loops_per_jiffy的一種情況是某設備驅動程序希望進行小的微秒級別的延遲的時候。

  為了理解延遲—循環校準代碼,讓我們看一下定義于init/calibrate.c文件中的calibrate_ delay()函數。該函數靈活地使用整型運算得到了浮點的精度。如下的代碼片段(有一些注釋)顯示了該函數的開始部分,這部分用于得到一個loops_per_jiffy的粗略值:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->loops_per_jiffy = (1 << 12); /* Initial approximation = 4096 */
printk(KERN_DEBUG “Calibrating delay loop...“);
while ((loops_per_jiffy <<= 1) != 0) {
ticks
= jiffies;  /* As you will find out in the section, “Kernel
                     Timers," the jiffies variable contains the
                     number of timer ticks since the kernel
                     started, and is incremented in the timer
                     interrupt handler
*/

  
while (ticks == jiffies); /* Wait until the start of the next jiffy */
  ticks
= jiffies;
  
/* Delay */
  __delay(loops_per_jiffy);
  
/* Did the wait outlast the current jiffy? Continue if it didn't */
  ticks
= jiffies - ticks;
  
if (ticks) break;
}

loops_per_jiffy
>>= 1; /* This fixes the most significant bit and is
                          the lower-bound of loops_per_jiffy
*/

  上述代碼首先假定loops_per_jiffy大于4096,這可以轉化為處理器速度大約為每秒100萬條指令,即1 MIPS。接下來,它等待jiffy被刷新(1個新的節拍的開始),并開始運行延遲循環__delay(loops_per_jiffy)。如果這個延遲循環持續了1個jiffy以上,將使用以前的loops_per_jiffy值(將當前值右移1位)修復當前loops_per_jiffy的最高位;否則,該函數繼續通過左移loops_per_jiffy值來探測出其最高位。在內核計算出最高位后,它開始計算低位并微調其精度:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->loopbit = loops_per_jiffy;

/* Gradually work on the lower-order bits */
while (lps_precision-- && (loopbit >>= 1)) {
  loops_per_jiffy
|= loopbit;
  ticks
= jiffies;
  
while (ticks == jiffies); /* Wait until the start of the next jiffy */
ticks
= jiffies;

  
/* Delay */
  __delay(loops_per_jiffy);

  
if (jiffies != ticks)        /* longer than 1 tick */
    loops_per_jiffy
&= ~loopbit;
}

  上述代碼計算出了延遲循環跨越jiffy邊界時loops_per_jiffy的低位值。這個被校準的值可被用于獲取BogoMIPS(其實它是一個并非科學的處理器速度指標)。可以使用BogoMIPS作為衡量處理器運行速度的相對尺度。在1.6G Hz 基于Pentium M的筆記本電腦上,根據前述啟動過程的打印信息,循環校準的結果是:loops_per_jiffy的值為2394935。獲得BogoMIPS的方式如下:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->BogoMIPS = loops_per_jiffy * 1秒內的jiffy數*延遲循環消耗的指令數(以百萬為單位)
= (2394935 * HZ * 2) / (1000000)
= (2394935 * 250 * 2) / (1000000)
= 1197.46(與啟動過程打印信息中的值一致)

  在2.4節將更深入闡述jiffy、HZ和loops_per_jiffy。


  2.1.5 Checking HLT instruction

  由于Linux內核支持多種硬件平臺,啟動代碼會檢查體系架構相關的bug。其中一項工作就是驗證停機(HLT)指令。

  x86處理器的HLT指令會將CPU置入一種低功耗睡眠模式,直到下一次硬件中斷發生之前維持不變。當內核想讓CPU進入空閑狀態時(查看arch/x86/kernel/process_32.c文件中定義的cpu_idle()函數),它會使用HLT指令。對于有問題的CPU而言,命令行參數no-hlt可以禁止HLT指令。如果no-hlt被設置,在空閑的時候,內核會進行忙等待而不是通過HLT給CPU降溫。

  當init/main.c中的啟動代碼調用include/asm-your-arch/bugs.h中定義的check_bugs()時,會打印上述信息。

]]>
嵌入式Linux系統中MMC卡驅動管理技術研究http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1730&Page=1wangxinxin2010-11-24 11:25:39摘要  簡明介紹MMC卡驅動程序的體系結構,設計并實現MMC卡的底層驅動;對傳統的塊設備驅動程序中的單塊讀寫進行改進,實現MMC卡的集群讀寫,同時實現了卡的電源管理和即插即用功能。
關鍵詞 Linux MMC卡 底層驅動 集群讀寫 熱拔插

引 言
    MMC(Multitmedia Card)是一種體積小巧、容量大、使用方便的存儲器,目前在手機等嵌入式系統中有著廣泛的應用。MMC通過卡內的一個集成片內控制器對MMC卡進行控制和管理,當主機正確地驅動MMC卡后,就可以像磁盤一樣方便地存取數據。本文所研究與實現的Linux驅動程序,以Intel XScale的PXA250為硬件平臺,在遵循MMC卡通信協議規范的基礎上,實現了卡的底層讀寫。然后對傳統的塊設備驅動程序中的單塊讀寫進行了改進,實現了集群讀寫技術,提高了卡的讀寫速度;同時增加了電源管理功能,滿足了嵌入式系統低功耗的需求;增加了即插即用功能,方便了用戶的使用。

1 MMC卡驅動程序的體系結構
    MMC卡僅通過5個引腳與主機的控制器相連,通過串行協議與主機通信。MMC卡在硬件上的簡單構造必然導致在實現驅動程序上的復雜。依據MMC卡的通信擲議規范和Linux驅動程序的結構,把驅動程序原有的底層驅動、守護線程、單塊讀寫進行改進和擴展,其結構層次再劃分為底層驅動、守護線程、集群讀寫、電源管理及熱拔插管理5個部分,如圖l所示。

圖片點擊可在新窗口打開查看

    圖1中各部分的功能為:
    ①底層驅動——處理直接涉及與MMC卡硬件寄存器端口的操作,包括:命令的發布和響應、中斷響應和處理、PIO或者DMA通道數據傳輸等。
    ②集群讀寫——將磁盤相鄰數據塊的讀寫請求合并起來一起發布讀寫命令,以加快數據讀寫,并在讀寫中實現并發控制。
    ③電源管理——實現MMC卡的低功耗管理。 
    ④熱拔插管理——實現MMC卡的即插即用功能。
    ⑤守護線程——響應文件系統的讀寫請求并啟動對卡的1/O。

2 MMC卡驅動程序的實現
2.1 底層驅動
   
底層驅動指的是直接對MMC卡進行操作。MMC卡采用串行的數據傳輸方式;是一種比較“精細”的卡,對它的操作比較復雜而且必須有準確的時序安排。以下從命令的發布和響應、中斷響應和處理、DMA數據傳輸3個方面講述如何進行底層讀寫驅動。

(1)命令發布和響應

    MMC卡的操作是通過對其18個控制寄存器的讀寫實現的。首先,設置時鐘起停寄存器MMC_STRCPL的最低兩位為01.關閉MMC卡內部時鐘。然后,設置中斷屏蔽寄存器MMC_LMASK的最低7位都為1,屏蔽所有對MMC控制器的中斷,再向指定的MMC控制寄存器中寫入命令參數,如時鐘頻率設置寄存器MMC_CLKRT,讀寫塊數寄存器MMC_NOB,命令寄存器MMC_CMD等。最后,打開內部時鐘,解除屏蔽的中斷。這時,當前讀寫進程進入睡眠狀態,等待中斷處理程序的喚醒。

(2)中斷響應和處理
    MMC卡在數據傳輸請求、內部時鐘關閉、命令發布完畢、數據傳輸完畢的情況下都會產生中斷,但足MMC卡的控制器只通過1裉GPIO23的引腳與CPU相連,用于中斷信號線的復用;因此在中斷處理程序中,必須首先判斷到底是哪種原因產生的中斷,然后再進行相應的處理。這里,MMC卡在正確發布讀寫命令以后,系統會產生1次中斷,中斷處理程序中讀取MMC_IREG的值,判斷命令已經發布成功,同時喚醒等待命令完成的進程。

    讀寫進程被中斷喚醒后,首先讀取MMC卡響應寄存器MMC_RES中的狀態信息,再根據這些狀態信息判斷命令是否發布成功和卡的當前狀態。如果這些狀態信息表示命令執行成功,則通過讀寫緩沖寄存器MMC_RXFIFO和MMC_TXFIFO進行數據的讀寫(這里使用DMA進行數據傳輸,提高了數據的傳輸速度);如果返回的狀態信息表明命令執行不成功,則根據狀念信息進行相應的出錯處理。

(3)DMA數據傳輸

    驅動程序中對MMC卡的數據讀寫是通過DMA通道進行傳輸的。為了保汪操作的連續性,驅動程序對MMC卡的輸入和輸出緩沖各設置1個DMA通道,在進行實際數據傳輸時,讀寫進程也進入睡眠狀態,等待DMA數據傳輸完畢后,被DMA中斷喚醒。實現一次讀操作的偽代碼如下:
Pxa_read_mmc(){

    關閉時鐘,屏蔽中斷;
    設置讀寫寄存器的內容; /*讀寫塊數,起始塊數,讀寫速度等*/
    打開時鐘,發布讀寫命令;
    Interruptible_sleep_on(); /*進入可打斷睡眠狀態,等待中斷程序的喚醒*/
    被中斷程序喚醒,打開DMA通道,進行數據傳輸,再次進入可打斷睡眠狀態;
    被DMA傳輸完畢中斷喚醒,發布結束傳輸命令,結束數據傳輸;

2.2 集群(clustering)讀寫和并發控制
2.2.1 傳統的塊設備驅動程序結構和不足
   
塊沒備驅動程序是Linux系統中最復雜的驅動程序之一,參閱文獻[3,4]可以詳細了解Linux塊設備驅動程序。這里簡單介紹與集群讀寫相關的數據結構和操作。扇區(seetor)是塊設備硬件傳輸數據的基本單位,而塊(block)是塊設備請求1次I/O操作所涉及的一組相鄰扇區,每個塊都需要有自己的內存緩沖區。緩沖區首部(buffer_head)是與每個緩沖區相關的數據結構,每次對塊沒備的I/O傳輸都必須經過塊的緩沖區。

    Linux塊沒備驅動程序采取一種延遲I/O策略。當進程有I/O請求時,驅動程序延遲一段時間,把塊設備上相連續的buffer_head結構關聯在一起形成一個I/O請求描述符(struct request),再把request結構按照電梯算法排隊到設備的請求隊列(request_queue_t)。這樣實際執行I/O傳輸時,順次處理對應塊設備的請求隊列。
    對于request結構的電梯排隊算法,避免由于頻繁的移動磁頭而導致塊設備性能下降;然而,目前在Linux塊設備驅動程序中,對一個request結構中的各個buffer_head結構分別發布I/O讀寫命令,會導致每次對一個buffer_head的輸入/輸出時,磁頭都會停頓一段時間,進行DMA數據讀寫。這樣頻繁的磁頭啟停會導致磁盤性能下降。

2.2.2 集群讀寫的實現

    傳統的塊設備驅動程序中每次發布讀寫命令都只對一個buffer_head緩沖而導致塊設備性能下降。針對這一問題,我們對傳統塊設備進行改進,實現了集群讀寫。由于每一個request結構的buffer_head結構鏈對應的物理塊都是相鄰的,因此為進行集群讀寫創造了條件。request結構中的nr_sectors表示該request結構需要讀寫的塊數。進行讀寫時,一次性發布讀寫塊數為nr_seetors,讀入塊設備內容到requem結構指向的第一個buffer_head結構對應的內存區域。在一個buffer_head結構的緩沖區讀寫滿了以后,就調整讀寫緩沖區地址為下一個buffer_head所指向的緩沖區,同時配合DMA進行數據傳輸,提高了讀寫速度。對一個request結構操作完成以后,釋放request結構資源。實現集群讀操作偽碼如下:
Read_mmc(){

    發布讀寫命令,讀入的數據塊數為一個rcquest一>nr_sectors的塊數;
    緩沖區的指針指向第1個bh結構所指的緩沖區;
    while(數據還沒有讀完){
    讀入數據到buffer_head結構所指定的緩沖區;/*調用Pxa_read_mmc()*/
    調整緩沖區的指針到下一個buffer_head結構所指向的緩沖區;
    }

}

2.2.3集群讀寫中的并發控制
   
如果I/O請求隊列request_queue_t是在內核中的許多地方都被訪問的,則該隊列就成為了臨界資源。為了對該隊列進行互斥保護,Linux2.4中所有的請求隊列都受一個單獨的全局自旋鎖io_request_lock的保護。所有對清求隊列的操作必須要求擁有該鎖并禁止中斷,然而,在驅動程序擁有這個鎖的同時,其他任何讀寫請求不能排隊到系統的任何塊設備上,其他讀寫處理函數也不能運行。為了盡量減輕由于驅動程序長期的擁有該鎖而導致系統性能下降的問題,在實現集群讀寫時必須遵循以下原則:

    ①對請求隊列進行讀寫操作時要獲得鎖;
    ②對請求隊列操作完畢后釋放請求鎖;
    ③為了減少占用鎖的時間,可先把隊列中的request結構從隊列中取下來,再打開鎖,然后在開鎖的情況下對取下的request結構進行操作。

    基于以上原則,讀/寫處理函數的偽碼如下所示:
mmc_request_fn()
    whilc(1){
    加鎖io_request_lock;
    讀取當前mmc卡請求隊列的第一個請求結構request;
    釋放鎖io_request_lock;
    if(request為空)
    cxit(O); /*沒有可以處理的隊列,返回*/
    read_mmc(); /*調用集群讀寫函數*/
    加鎖io_request_lock;
    在queue結構中取處理完畢的request結構,釋放request資源;
    釋放鎖io_request_lock;
    }

}

2.3 守護線程
   
在MMC卡驅動程序初始化的時候,啟動守護線程mme_block_thread。它平時處于睡眠狀態,當有對MMC卡的讀/寫請求時,mmc_blok_thread被喚醒。該線程調用上述讀/寫處理函數mmc_request_fn(),處理完畢后再進入睡眠狀態。

2.4 電源管理
   
嵌入式系統一般有低功耗要求,當某設備長期沒有運行時,就應該停止給該設備供電,以減少電能消耗。在內核中有一個需要注冊的電源管理設備的隊列pm_list,同時也有電源管理線程kpowered,它的優先級是所有運行進程中最低的。當系統長時間沒有進程運行時,kpowered被喚醒,掃描pm_list隊列各個注冊的設備。如果發現該設備長期沒有運行,則向該設備發出PM_SUSPEND事件;而當設備重新開始使用時,則向pm_list隊列發出:PM_RESUME事件。

    在MMC卡驅動模塊中注冊了電源管理的回調函數mme_block_callback,即pm_register(PM_UNKNOWN_DEV,0,mme_pm_callback)。這樣MMC卡就注冊到了pm_list隊列中去了。當有電源事件時,就觸發mmc_pm_callback函數。該函數處理各種電源事件。

    程序中的電源事件有兩種:
    ①PM_SUSPEND事件。該事件使MMC卡進入省電模式。這時驅動程序保存MMC卡的當前狀態和重要寄存器的內容,如時鐘寄存器MMC_CLKRT和狀態寄存器MMC_STAT等。然后,設置MMC卡的供電GPIO為高電平,關閉MMC卡的電源供應,沒置MMC卡在時鐘使能寄存器CKEN的相應位為O,關閉MMC卡的時鐘脈沖。這時,MMC卡就進入了省電模式。
    ②PM_RESUME事件。該事件使MMC卡進入正常工作模式。這時程序恢復在進入省電模式前保存的寄存器,打開電源供應和時鐘脈沖,MMC卡恢復到正常的工作模式。
    當然電源事件也可以由用戶進程自愿觸發。在文件系統的接口file_operaion io_control中留有電源理管理接口,用戶可以通過io_contol向卡發送電源事件請求。

2.5 熱插拔管理

    在手機、PDA等嵌入式系統中,都要求提供對設備的即插即用功能,使用戶無須安裝驅動程序就可以即時使用設備。Linux在系統層和應用層都要對熱插拔事件進行處理。在系統層,一方面要探測MMC卡的熱插拔事件,分配或釋放系統資源,并驅動MMC卡;另一方面,要將此事件準確及時地通知給應用層,應用層則根據熱插拔事件作相應的處理。

    在操作系統層,需要注冊一個字符型設備mmc_plug文件,用于應用層探測MMC卡的熱插拔事什。CPU通過GPIO12引腳與MMC卡相連,用于卡插拔的中斷探測。同時驅動程序巾設置一個信號量MMC_EVENT,它取MMC_INSERT和MMC_REMOVAL兩個值。當卡插入和或者拔出時,在中斷處理程序中被分別設置為MMC_INSERT和MMC_REMCOVAL;并同時傳給字符設備mmc_plug,供上層的應用程序使用。為了讓應用層能夠知曉卡的拔插事件,在字符設備mmc_plug使用異步I/O機制poll,需要接收內核拔插事件的進程通過poll在一個等待隊列上睡眠,當有卡拔插事件時產生中斷,中斷處理程序喚醒在隊列上等待的進程。上層進程在被喚醒后就讀取字符設備,獲取所發生的事件。

    在應用層,進程通過select機制監聽MMC卡所發生的熱插拔事件,在沒有拔插事件的時候,進程進入阻塞狀態,讓出CPU資源;當發生熱拔插事件時,系統喚醒通過poll加入到等待隊列中的進程,然后應用層通過read函數得到MMC卡的熱插拔事件,進行相應的應用層處理。當然,應用層也可以通過write方法通知系統層對卡進行處理。

結語
   
本文研究實現的MMC卡驅動程序,其實現的集群讀寫證明有穩定而較高的讀/寫速度;增加了電源管理功能,降低了電源的功耗,滿足了嵌入式系統低功耗的要求;增加的即插即用功能,大大方便了用戶的使用。驅動程序的體系結構是實現嵌入式系統塊設備驅動的一種好方法。

]]>
Linux液晶屏驅動開發http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1729&Page=1wangxinxin2010-11-24 11:24:26隨著高性能嵌入式處理器的普及和硬件成本的不斷降低,尤其是arm 系列處理器的推出,嵌入式系統的功能越來越強。單色LCD也因為色彩單調,存儲信息小,處理速度慢而不能符合人們的需求。

  在多媒體應用的推動下,彩色LCD越來越多地應用到了嵌入式系統中 如新一代手機和掌上電腦多采用TFT顯示器件,該顯示器件支持彩色圖形界面和視頻媒體播放。Linux作為開放源代碼的操作系統也在市場中占據了一席之地。由于Linux成本低廉,源代碼開放,因此成為國內外廠商極力發展的操作系統。在應用需求的推動下,Linux下也出現了許多圖形界面軟件包,如MiniGUI、Trolletech公司的Embedded QT等,其圖形界面及開發工具與Windows CE不相上下。在圖形軟件包的開發和移植工作中都牽涉到底層LCD的驅動問題。筆者有幸參與了Linux操作系統下LCD部分的開發,其主要功能是點亮液晶屏,將在攝像頭上采集的BMP圖片在液晶屏上顯示并將BMP 格式壓縮成JPEG格式,使得存儲量減少。因此筆者就在開發過程中遇到的問題一一闡述。


   背景知識


   在切入正題之前,先來了解在做驅動過程中需要預先知道的知識。


   1 硬件平臺


   MC9328MX1(以下簡稱MX1)是Motorola 公司基于ARM核心的第一款MCU,主要面向高端嵌入式應用。內部采用arm920T內核,并集成了SDRAM/Flash、LCD,USB、藍牙.多媒體閃存卡(MMC/SD、Memory Stick)和CMOS攝像頭等控制器。


   LCD控制器的功能是產生顯示驅動信號,驅動LCD顯示器。用戶只需要通過讀寫一系列的寄存器,完成配制和顯示控制。MX1中的LCD控制器可支持單色/彩色LCD 顯示器。支持彩色TFT時,可提供4/8/12/16位顏色模式,其中16位顏色模式下可以顯示64k種顏色。配置LCD控制器重要的一步是指定顯示緩沖區,顯示的內容就是從緩沖區中讀出的,其大小由屏幕分辨率和顯示顏色數決定。在本例中,采用KYocera 公司的KCS057QV1AJ液晶屏,在240×320分辨率下可提供8位彩色顯示,即最大256色位圖。


   2.Linux下的設備驅動


   在Linux操作系統下有兩類主要的設備文件類型,一種是字符設備,另一種是塊設備。字符設備和塊設備的主要區別是在對字符設備發出讀/寫請求時,實際的硬件I/O一般就緊接著發生了,塊設備則不然,它利用一塊系統內存作緩沖區,當用戶進程對設備請求讀/寫時,它首先察看緩沖區的內容,如果緩沖區的數據能滿足用戶的要求,就返回請求的數據,如果不能,就調用請求函數來進行實際的I/O操作。


   Linux的設備管理是和文件系統解密結合的,各種設備都以文件的形式存放在/dev目錄下,稱為設備文件。應用程序可以打開、關閉和讀寫這些設備文件,完成對設備的操作,就像操作普通的數據文件一樣。為了管理這些設備,系統為設備編了號,每個設備號又分為主設備號和次設備號。主設備號用來區分不同種類的設備,而次設備號標識使用同一個設備驅動程序的不同的硬件設備,比如有兩個軟盤,就可以用從設備號來區分它們。設備文件的主設備號必須與設備驅動程序在登記時申請的主設備號一致,否則用戶進程將無法訪問到驅動程序。幀緩沖設備為標準字符設備,主設備號為29,次設備號則從0到31。


   3.Linux的幀緩沖設備


   幀緩沖區是出現在Linux 2.2.xx及以后版本內核當中的一種驅動程序接口,這種接口將顯示設備抽象為幀緩沖區設備區。它允許上層應用程序在圖形模式下直接對顯示緩沖區進行讀寫操作。這種操作是抽象的、統一的,用戶不必關心物理顯存的位置、換頁機制等具體細節。這些都由Framebufer設備驅動來完成。幀緩沖設備對應的設備文件為/dev/fb*,如果系統有多個顯示卡,Linux下還可支持多個幀緩沖設備,最多可達32個,分別為/dev/fb0到/dev/fb31,而/dev/fb則為當前缺省的幀緩沖設備,通常指向/dev/fb0。當然在嵌入式系統中支持一個顯示設備就夠了。在使用Framebufer時,Linux是將顯卡置于圖形模式下的.在應用程序中,一般通過將Frame-Buffer設備映射到進程地址空間的方式使用,對于幀緩沖來說,可以把它看成是一段內存,用于讀寫內存的函數均可對這段地址進行讀寫,只不過這段內存被專門用于放置要在LCD上顯示的內容,其目的就是通過配置LCDC寄存器在一段指定內存與LCD 之間建立一個自動傳輸的通道。這樣,任何程序只要修改這段內存中的數據,就可以改變LCD 上的顯示內容。


   FrameBuffer設備驅動基于linux/include/linux/fb.h和linux/drivers/video/fbmem.c這兩個文件,下面就詳細分析這兩個文件。


   首先分析linux/include/linux/fb.h文件,幾乎主要的結構都是在這個文件中定義的。這些結構包括:


   fb_var_screeninfo 這個結構描述了顯示卡的特性,記錄了幀緩沖設備和指定顯示模式的可修改信息。其中變量xres定義了屏幕一行所占的像素數,yres定義了屏幕一列所占的像素數,bits_per_pixel定義了每個像素用多少個位來表示。
   fb_fix_screeninfon 這個結構在顯卡被設定模式后創建,它描述顯示卡的屬性,并且系統運行時不能被修改;比如FrameBuffer內存的起始地址。
   struct fb_info Linux為幀緩沖設備定義的驅動層接口。它不僅包含了底層函數,而且還有記錄設備狀態的數據。每個幀緩沖設備都與一個fb_info結構相對應。其中成員變量modename為設備名稱,fontname為顯示字體,fbops為指向底層操作的函數的指針。
   fb_cmap描述設備無關的顏色映射信息。可以通過FBIOGETCMAP 和FBIOPUTCMAP 對應的ioctl操作設定或獲取顏色映射信息。然后分析fbmem.h文件。

    幀緩沖設備屬于字符設備,采用“文件層-驅動層”的接口方式。在文件層為之定義了以下數據結構:

 


    
    其成員函數都在Linux/driver/video/fbmem.c中定義,其中的函數對具體的硬件進行操作,對寄存器進行設置,對顯示緩沖進行映射。


   對于/dev/fb,對顯示設備的操作主要有以下幾種:


   讀/寫(read/write)/dev/fb 相當于讀/寫屏幕緩沖區。
   映射(map)操作 由于Linux工作在保護模式和每個應用程序里都有自己的虛擬地址空間,在應用程序中是不能直接訪問物理緩沖區地址的。因此,Linux在文件操作file_operations結構中提供了mmap函數,可將文件的內容映射到用戶空間。對于幀緩沖設備,則可通過映射操作,可將屏幕緩沖區的物理地址映射到用戶空間的一段虛擬地址中,之后用戶就可以通過讀寫這段虛擬地址訪問屏幕緩沖區,在屏幕上繪圖。
   I/O控制 對于幀緩沖設備,對設備文件的ioctl操作可讀取/設置顯示設備及屏幕的參數,如分辨率、顯示顏色數和屏幕大小等。ioctl的操作是由底層的驅動程序來完成的。
   在應用程序中,操作/dev/fb的一般步驟為首先打開/dev/fb設備文件,然后用ioctl操作取得當前顯示屏幕的參數,如屏幕分辨率,每個像素點的比特數,根據屏幕參數可計算屏幕緩沖區的大小。接下來,將屏幕緩沖區映射到用戶空間。最后,映射后就可以直接讀寫屏幕緩沖區,進行繪圖和圖片顯示了。典型程序段如下:

   

 


    
    由于準備在LCD 上顯示一幅256色BMP圖片,關于BMP 圖片方面的知識請見相關鏈接。


   4.幀緩沖驅動的縮寫


   了解了上述知識后,在編寫驅動的時候就簡單多了。源程序共將程序分為初始化幀緩沖模塊fb_init(),調色板獲取色彩模塊get_cmap(),圖片顯示模塊display_bmp(),main函數4個函數。其中調色板獲取色彩模塊的功能是從文件中獲得圖像顯示色彩,重置系統調色板,使圖像能正確的顯示色彩。


  

 


    
    圖片顯示函數部分重要代碼為:
    
   

 


    
    在主函數中,建立一個進程調用圖片顯示函數
    
   

 


    
    至此LCD的驅動程序就編寫完成了,經過調試,編譯鏈接,然后用串口下載到實驗板上,一幅256色BMP圖片就可以出現在液晶屏幕上了。

 

  5.應用價值


   液晶屏點亮了,這只是第一步,我們可以在此基礎上進一步進行應用程序的開發,比如筆者將此應用在一個視頻監控系統中。在這個視頻監控系統中,圖像處理占很大的比重,基本的圖像處理構成如下:


   圖像采集模塊圖像采集模塊需要兩種裝置,一種是將光信號轉換成電信號的物理器件,如攝像機;另一種是能夠將模擬電信號轉換成數字信號的器件,如圖像采集卡。
   圖像處理模塊對圖像的處理一般可用算法的形式描述,但是對于特殊的問題需要特殊的解決方法,圖像處理模塊中不但包含了對圖像的一般處理方法,也包括一些特殊的算法處理。
   圖像顯示模塊對于采集得到的圖像,經過處理以后,最終需要顯示給用戶看。在系統的實時采集部分中,需要對展開的圖像進行滾屏顯示:在圖像編輯部分中,需要瀏覽所要拼接的圖像。所以圖像顯示對于系統來說非常重要。
   圖像儲存模塊由于圖像中包含有大量的信息,并且由于系統所采用8位真彩色格式,因此需要大量的空間。因此,在本系統中需要大容量和快速的圖像存儲器。
   圖像通信模塊隨著網絡的建設和發展,圖像通信傳輸也得到極大的重視。另外,圖像傳輸可以使不同的系統共享圖像數據資源,快速地將結果反映到遠處系統,所以極大推動了圖像在各個方面的應用。
   在這五步中,首先在點亮液晶屏之后,才能做下一步工作。比如說筆者能夠在LCD顯示一幅圖畫,但由于這幅圖是BMP格式的,它的存儲量非常驚人,一幅320×240的BMP格式就有320×240×8/8=76800,也就是77KB,這對于系統資源相對短缺的嵌入式設備來說占用系統RAM 太大了,因此我們就要將BMP格式的圖片壓縮成占用系統資源少的圖片格式,比如JPEG 格式或PNG格式,可以有效地減少存儲量。


   由于篇幅所限,不可能把完整的源代碼均做一番解釋,但主要的過程就是這些,在此拋磚引玉。隨著液晶屏在嵌入式設備中的用途越來越廣,會有很大的空間值得我們去研究。

 

]]>
Linux驅動開發學習筆記(1):LINUX驅動版本的hello worldhttp://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1728&Page=1wangxinxin2010-11-24 11:15:17
1、關于目錄
    /lib/modules/2.6.9-42.ELsmp/build/   這個是內核源碼所在的目錄
    一般使用這樣的命令進入這個目錄:cd /lib/modules/$(uname -r)/build/
   這個目錄實際上指向了:/usr/src/kernels/2.6.9-42.EL-smp-i686

2、編譯驅動所使用的makefile
    實際上編譯驅動的時候是使用預先提供的一個makefile的,位置在:
/lib/modules/$(uname -r)/build/Makefile
    注意:M是大寫的

3、網上抄錄的Linux驅動Hello world的源碼:
// hello.c
#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");

static int hello_init(void)
{
    printk(KERN_ALERT "hello world!\n");
    return 0;
}

static void hello_exit(void)
{
    printk(KERN_ALERT "goodbye!\n");
}

module_init(hello_init);
module_exit(hello_exit);

4、寫個makefile來編譯這個驅動:(版本一,最簡單的)
#下面這行是文件Makefile的內容,注意M是大寫的
obj-m := hello.o

把hello.c和Makefile保存在同一目錄,然后執行:
make -C /lib/modules/`uname -r`/build SUBDIRS=$PWD modules
這樣驅動就編譯好了,成果是hello.ko文件。
注意:makefile一定要寫成Makefile,如果寫成makefile就編譯不過。(折騰啊,就這一步耗了N多時間)

5、再寫另一種Makefile:(版本二:最省事的)
#以下是Makefile文件的內容
obj-m := hello.o
KERNEL_DIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
    make -C $(KERNEL_DIR) SUBDIRS=$(PWD) modules
clean:
    rm *.o *.ko

然后執行:make就編譯成功了,命令行不再加參數,很省事。
注意:all: 和clean:下面的行,前面是一個TAB鍵

6、加載驅動:
執行
insmod ./hello.ko
屏幕上沒反應。(因為我是在WINDOWS上用遠程終端連上去的嘛)
OK,先讓時光倒流,回到加載驅動以前,先另開一個窗口,執行:
tail -f /var/log/message
然后在原來的窗口里執行:
insmod ./hello.ko
哈哈,/var/log/message文件里面看見了盼望已久的hello world!

7、查看驅動:
lsmod   看見 hello這個驅動在其中

8、卸載驅動:
rmmod hello
看見/var/log/message里顯示了goodbye
]]>
基于嵌入式Linux的矩陣鍵盤驅動程序研究與開發http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1727&Page=1wangxinxin2010-11-24 11:10:26

隨著以計算機技術、通信技術和軟件技術為核心的信息技術的發展,嵌入式系統在各個行業中得到了廣泛的應用。嵌入式系統已成為當今IT行業的焦點之一。而在嵌入式系統中,鍵盤是重要的人機交互設備之一。嵌入式Linux是一種開放源碼、軟實時、多任務的操作系統,是開發嵌入式產品的優秀操作系統平臺,是在標準Linux基礎上針對嵌入式系統進行優化和裁剪后形成的,因此具有Linux的基本性質。在此提出的矩陣鍵盤驅動程序的設計方案是以嵌入式Linux和TIOMAP5912處理器為軟硬件平臺的,在設計的嵌入式語音識別應用平臺中,通過測試,表明其具有良好的穩定性和實時性。

l 硬件原理
OMAP5912處理器是由TI應用最為廣泛的TMS320C55X DSP內核與低功耗、增強型ARM926EJ—S微處理器組成的雙核應用處理器。用這樣一種組合方式將2個處理器整合在1個芯片后,開發人員可以根據實際情況,利用DSP運行復雜度較高的數字信號處理任務,利用ARM運行通信、控制和人機接口方面的任務,從而使便攜式設備在保持良好人機交互環境的基礎上,有效地降低功耗。在外設方面,OMAP5912微處理器支持常用的各種接口,其中通過MPUIO接口最多可支持8×8的矩陣鍵盤,系統中采用這個接口擴展了一個4×5的矩陣鍵盤。其硬件連接示意圖如圖1所示,其中按鍵行陣列必須提供上拉信號,列陣列加二極管,防止瞬間電流過大對MPUIO口造成沖擊。

圖片點擊可在新窗口打開查看

按照鍵盤的構造方式人們把鍵盤劃分為線性鍵盤和矩陣鍵盤。其中,線性鍵盤是指每個按鍵都占用嵌入式處理器的1個I/O端口,并通過這個I/O端口實現人機交互,各個按鍵之間互不影響。使用這種方案的優點是簡單、可靠,但是線性鍵盤對I/O端口的占用量很大。因此,嵌入式系統中很少采用這種方法。

另外一種矩陣鍵盤是指當按鍵數量過多時,采用矩陣的排列方法,將按鍵設計成n行m列的矩陣形式。其中,每個按鍵占用行和列的1個交叉點,并且以行和列為單位引出信號線。這樣只需要占用n m個I/O端口,卻可以驅動n×m個按鍵,大大節省了對嵌入式處理器I/O端口的占用,節省了寶貴的資源。矩陣鍵盤在減少嵌入式處理器I/O端口占用的問題上做出了很大的貢獻,但隨之而來的問題是如何確定矩陣中按鍵的位置,這里采用列掃描法,其思路如下:

在鍵盤初始化階段,所有的列信號(KBC)都被設置輸出為低電平。如果矩陣鍵盤中的1個按鍵按下,則相應的行信號和列信號線短路,行信號線(KBR)輸入由高電平變為低電平,產生1個中斷,然后在驅動的中斷服務程序中按照表1中的序列逐列掃描列信號,讀取行信號的狀態,根據讀回來的行信號狀態就可以判斷有那些按鍵按下。

另外,鍵盤驅動必須解決的一個問題是鍵盤的抖動。在按鍵按下和抬起的過程中,電壓信號會出現很多毛刺,這主要是由于機械按鍵的彈性作用引起的。盡管觸點看起來非常穩定,而且快速地閉合,但相對于嵌入式處理器的運行速度來說,這種動作是比較慢的。這種脈沖在某些按鍵功能設計時,如果處理不當可能會帶來災難性的后果。所以必須對按鍵信號進行防抖檢測。按鍵防抖檢測的核心思想是在嵌入式處理器的幾個時鐘周期內,通過對按鍵信號進行多次訪問,查看電平狀態是否保存一致。如果保持一致,則說明按鍵狀態已經穩定;否則,說明之前檢測到的按鍵信號是抖動信號或外界信號干擾,系統將不會對其進行任何處理。

2 嵌入式Linux設備驅動程序
在Linux內核源代碼中,各種驅動程序的代碼量占據了整個Linux代碼的85%。可見,Linux設備驅動在整個操作系統中起著舉足輕重的作用。設備驅動是操作系統內核和機器硬件之間的接口,它們控制著設備的操作動作,并且提供了一組API接口給應用程序,使得應用程序能夠與這個設備互動。而且,設備驅動為應用程序屏蔽了硬件的細節,在應用程序看來,硬件設備只是1個設備文件,應用程序就可以像操作普通文件一樣對硬件設備進行操作。在Linux操作系統中,通常將外圍設備分為3種類型:字符設備、塊設備和網絡設備。

而在Linux操作系統中,還有一類設備被定義為“平臺設備”,通常So(System on Chip)系統中集成的獨立的外設單元都被當作平臺設備來處理,這里把4×5的矩陣鍵盤也定義為平臺設備。所謂的“平臺設備”并不是與字符設備、塊設備和網絡設備并列的概念,而是Linux系統提供的一種附加手段,例如,鍵盤驅動,它本身是字符設備,但也將其歸納為平臺設備。

另外,鍵盤又屬于輸入設備,Linux內核提供了輸入子系統,如鍵盤、觸摸屏、鼠標等輸入設備都可以利用輸入子系統的接口函數來實現設備驅動。輸入子系統由核心層(Input Core)、驅動層和事件處理層(EventHandler)三部分組成。在Linux內核中,使用輸入子系統實現輸入設備驅動的時候,驅動的核心工作是向系統報告按鍵、觸摸屏、鼠標等輸入事件。而不再需要關心文件操作接口,因為輸入子系統已經完成了文件操作接口。通過輸入子系統,實現輸入設備驅動時只需要完成以下工作:
(1)在模塊加載函數中告知輸入子系統輸入設備可以報告的事件。例如,可通過_set_bit(EV_KEY,input_dex,一

 

]]>
終于調試成功Linux下的動態重構控制驅動http://www.xinguifushi.cn/bbs/dispbbs.asp?BoardID=33&ID=1726&Page=1wangxinxin2010-11-24 11:03:09經過數月的努力終于在2009226星期四下午6點調試成功了Linux下的動態重構配置控制器的設備驅動程序。項目可以按照原計劃進行下去了。Linux2.6.20作為自重構的操作系統原型。

硬件平臺為 ML505

開發環境為ISE9.2SP4PR10EDK9.2Sp2PlanAhead10.1Petalinux-MMU-V0.10.

支持可重構計算的操作系統,本身就是很難的課題。

我 們擬基于Linux構建OS4RC,以軟硬件統一多任務模型 來統一 可重構計算的 軟件任務和 硬件任務,其起源是支持動態部分重構的可編程器件的出現,如Xilinx的 Virtex系列FPGA。硬件實現的算法和計算任務可以像軟件線程和任務那樣動態加載、卸載,具有廣泛的用途和重要的學術價值,即計算機系統可以在運行過程中根據場景的變化實時改變自己的硬件結構,以適應新的需求。改變了以往研究計算機體系機構時,計算機硬件固定不變,提出新的體系結構時需要重新設計、制作芯片的 過長研究周期。

我們以前希望用操作系統來統一硬件任務,設計統一的具有一定通用性和靈活性的接口,與軟件類似的接口來管理硬件任務。

]]>
主站蜘蛛池模板: 99av在线播放| 欧美视频在线观 | 黑色丝袜美美女被躁视频 | 欧美一级性视频 | 欧美成人亚洲 | 国产成人综合高清在线观看 | 精品国产品欧美日产在线 | 日本一区二区不卡久久入口 | 亚洲欧美字幕 | 国产三级日本三级日产三 | 日本特爽特黄特刺激大片 | 中文字幕欧美日韩一 | 九九精品国产兔费观看久久 | 一区二区不卡久久精品 | 97免费视频在线 | 日本一级毛片免费播放 | 欧美视频自拍偷拍 | 国产在线99 | 男人女人真曰批视频播放 | 日韩欧美在线播放 | 亚洲国产二区三区 | 毛片的网址| 国产一区2区 | 欧美日韩一区二区三区在线观看 | 成人网视频在线观看免费 | 亚洲精品视频免费观看 | 成年视频在线 | 老司机午夜精品网站在线观看 | 国产精品一二区 | 久久一日本道色综合久久m 久久伊人成人网 | 亚洲高清免费在线观看 | 国内精品免费一区二区观看 | 亚洲综合爱久久影院 | 蝴蝶成人世界第八影院 | 欧美一区二区三区视频 | 亚洲一级毛片欧美一级说乱 | 欧美视频一区二区三区 | 国产精品视频视频久久 | 我要看三级毛片 | 玖玖精品视频在线观看 | 国产成人综合日韩精品婷婷九月 |