久久亚洲春色中文字幕久久久,亚洲中文字幕久久精品无码喷水,中文亚洲成a人片在线观看 http://www.bjzhda.cnzh-cn曙海教育集團論壇http://www.bjzhda.cnRss Generator By Dvbbs.Netofficeoffice@126.comimages/logo.gif曙海教育集團論壇LINUX下的設(shè)備驅(qū)動程序http://www.hufushizhe.com/bbs/dispbbs.asp?BoardID=33&ID=1745&Page=1wangxinxin2010-11-24 11:51:37  
    3.1、UNIX下設(shè)備驅(qū)動程序的基本結(jié)構(gòu)
  
    在UNIX系統(tǒng)里,對用戶程序而言,設(shè)備驅(qū)動程序隱藏了設(shè)備的具體細節(jié),對各種不同設(shè)備提供了一致的接口,一般來說是把設(shè)備映射為一個特殊的設(shè)備文件,用戶程序可以象對其它文件一樣對此設(shè)備文件進行操作。UNIX對硬件設(shè)備支持兩個標準接口:塊特別設(shè)備文件和字符特別設(shè)備文件,通過塊(字符)特別設(shè)備文件存取的設(shè)備稱為塊(字符)設(shè)備或具有塊(字符)設(shè)備接口。
  
    塊設(shè)備接口僅支持面向塊的I/O操作,所有I/O操作都通過在內(nèi)核地址空間中的I/O緩沖區(qū)進行,它可以支持幾乎任意長度和任意位置上的I/O請求,即提供隨機存取的功能。
  
    字符設(shè)備接口支持面向字符的I/O操作,它不經(jīng)過系統(tǒng)的快速緩存,所以它們負責管理自己的緩沖區(qū)結(jié)構(gòu)。字符設(shè)備接口只支持順序存取的功能,一般不能進行任意長度的I/O請求,而是限制I/O請求的長度必須是設(shè)備要求的基本塊長的倍數(shù)。顯然,本程序所驅(qū)動的串行卡只能提供順序存取的功能,屬于是字符設(shè)備,因此后面的討論在兩種設(shè)備有所區(qū)別時都只涉及字符型設(shè)備接口。
  
    設(shè)備由一個主設(shè)備號和一個次設(shè)備號標識。主設(shè)備號唯一標識了設(shè)備類型,即設(shè)備驅(qū)動程序類型,它是塊設(shè)備表或字符設(shè)備表中設(shè)備表項的索引。次設(shè)備號僅由設(shè)備驅(qū)動程序解釋,一般用于識別在若干可能的硬件設(shè)備中,I/O請求所涉及到的那個設(shè)備。
  
    設(shè)備驅(qū)動程序可以分為三個主要組成部分:
  
    (1) 自動配置和初始化子程序,負責檢測所要驅(qū)動的硬件設(shè)備是否存在和是否能正常工作。如果該設(shè)備正常,則對這個設(shè)備及其相關(guān)的、設(shè)備驅(qū)動程序需要的軟件狀態(tài)進行初始化。這部分驅(qū)動程序僅在初始化的時候被調(diào)用一次。
  
    (2) 服務(wù)于I/O請求的子程序,又稱為驅(qū)動程序的上半部分。調(diào)用這部分是由于系統(tǒng)調(diào)用的結(jié)果。這部分程序在執(zhí)行的時候,系統(tǒng)仍認為是和進行調(diào)用的進程屬于同一個進程,只是由用戶態(tài)變成了核心態(tài),具有進行此系統(tǒng)調(diào)用的用戶程序的運行環(huán)境,因此可以在其中調(diào)用sleep()等與進程運行環(huán)境有關(guān)的函數(shù)。
  
    (3) 中斷服務(wù)子程序,又稱為驅(qū)動程序的下半部分。在UNIX系統(tǒng)中,并不是直接從中斷向量表中調(diào)用設(shè)備驅(qū)動程序的中斷服務(wù)子程序,而是由UNIX系統(tǒng)來接收硬件中斷,再由系統(tǒng)調(diào)用中斷服務(wù)子程序。中斷可以產(chǎn)生在任何一個進程運行的時候,因此在中斷服務(wù)程序被調(diào)用的時候,不能依賴于任何進程的狀態(tài),也就不能調(diào)用任何與進程運行環(huán)境有關(guān)的函數(shù)。因為設(shè)備驅(qū)動程序一般支持同一類型的若干設(shè)備,所以一般在系統(tǒng)調(diào)用中斷服務(wù)子程序的時候,都帶有一個或多個參數(shù),以唯一標識請求服務(wù)的設(shè)備。
  
    在系統(tǒng)內(nèi)部,I/O設(shè)備的存取通過一組固定的入口點來進行,這組入口點是由每個設(shè)備的設(shè)備驅(qū)動程序提供的。一般來說,字符型設(shè)備驅(qū)動程序能夠提供如下幾個入口點:
  
    (1) open入口點。打開設(shè)備準備I/O操作。對字符特別設(shè)備文件進行打開操作,都會調(diào)用設(shè)備的open入口點。open子程序必須對將要進行的I/O操作做好必要的準備工作,如清除緩沖區(qū)等。如果設(shè)備是獨占的,即同一 時刻只能有一個程序訪問此設(shè)備,則open子程序必須設(shè)置一些標志以表示設(shè)備處于忙狀態(tài)。
  
    (2) close入口點。關(guān)閉一個設(shè)備。當最后一次使用設(shè)備終結(jié)后,調(diào)用close子程序。獨占設(shè)備必須標記設(shè)備可再次使用。
  
    (3) read入口點。從設(shè)備上讀數(shù)據(jù)。對于有緩沖區(qū)的I/O操作,一般是從緩沖區(qū)里讀數(shù)據(jù)。對字符特別設(shè)備文件進行讀操作將調(diào)用read子程序。
  
    (4) write入口點。往設(shè)備上寫數(shù)據(jù)。對于有緩沖區(qū)的I/O操作,一般是把數(shù)據(jù)寫入緩沖區(qū)里。對字符特別設(shè)備文件進行寫操作將調(diào)用write子程序。
  
    (5) ioctl入口點。執(zhí)行讀、寫之外的操作。
  
    (6) select入口點。檢查設(shè)備,看數(shù)據(jù)是否可讀或設(shè)備是否可用于寫數(shù)據(jù)。select系統(tǒng)調(diào)用在檢查與設(shè)備特別文件相關(guān)的文件描述符時使用select入口點。如果設(shè)備驅(qū)動程序沒有提供上述入口點中的某一個,系統(tǒng)會用缺省的子程序來代替。對于不同的系統(tǒng),也還有一些其它的入口點。
  
    3.2、LINUX系統(tǒng)下的設(shè)備驅(qū)動程序
  
    具體到LINUX系統(tǒng)里,設(shè)備驅(qū)動程序所提供的這組入口點由一個結(jié)構(gòu)來向系統(tǒng)進行說明,此結(jié)構(gòu)定義為:
  
  #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提供了關(guān)于特別設(shè)備文件/dev/driver(假設(shè)此設(shè)備名為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主要用于與文件系統(tǒng)對應(yīng)的設(shè)備驅(qū)動程序使用。當然,其它設(shè)備驅(qū)動程序也可以使用它。它提供關(guān)于被打開的文件的信息,定義為:
  
  #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*/
  };
  
    在結(jié)構(gòu)file_operations里,指出了設(shè)備驅(qū)動程序所提供的入口點位置,分別是:
  
    (1) lseek,移動文件指針的位置,顯然只能用于可以隨機存取的設(shè)備。
  
    (2) read,進行讀操作,參數(shù)buf為存放讀取結(jié)果的緩沖區(qū),count為所要讀取的數(shù)據(jù)長度。返回值為負表示讀取操作發(fā)生錯誤,否則返回實際讀取的字節(jié)數(shù)。對于字符型,要求讀取的字節(jié)數(shù)和返回的實際讀取字節(jié)數(shù)都必須是inode->i_blksize的的倍數(shù)。
  
    (3) write,進行寫操作,與read類似。
  
    (4) readdir,取得下一個目錄入口點,只有與文件系統(tǒng)相關(guān)的設(shè)備驅(qū)動程序才使用。
  
    (5) selec,進行選擇操作,如果驅(qū)動程序沒有提供select入口,select操作將會認為設(shè)備已經(jīng)準備好進行任何的I/O操作。
  
    (6) ioctl,進行讀、寫以外的其它操作,參數(shù)cmd為自定義的的命令。
]]>
嵌入式Linux下的AU1200MAE驅(qū)動程序設(shè)計http://www.hufushizhe.com/bbs/dispbbs.asp?BoardID=33&ID=1744&Page=1wangxinxin2010-11-24 11:48:38MAE的相關(guān)寄存器中,在每處理一幀后,上述宏塊信息都要及時更新。MAE的前端對宏塊數(shù)據(jù)進行反量化,其結(jié)果輸入至逆變換單元進行逆變換運算。逆變換運算單元通過查看maefe_config寄存器的COD標志位來決定采用哪種標準的視頻Cod-ec,從而執(zhí)行相應(yīng)的運算。逆變換運算完成后,數(shù)據(jù)送入運動補償單元。運動補償單元根據(jù)運動向量計算出參考像素值,并把它們插入當前幀中,從而完成一次解碼。以上過程通過編寫函數(shù)fe_process_mb來實現(xiàn),該函數(shù)的幾個參數(shù)分別是:

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

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

  函數(shù)中的關(guān)鍵部分及說明注釋如下所示:

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

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

  3.2 驅(qū)動軟件測試

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

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

  4 結(jié)語

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

]]>
嵌入式Linux下的USB設(shè)備驅(qū)動技術(shù)http://www.hufushizhe.com/bbs/dispbbs.asp?BoardID=33&ID=1743&Page=1wangxinxin2010-11-24 11:47:41

        該函數(shù)的形參對應(yīng)的就是總線兩條鏈表里的設(shè)備和驅(qū)動。當總線上有新設(shè)備和驅(qū)動時,這個函數(shù)就會被調(diào)用。

 

        3 USB驅(qū)動程序的描述符

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

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

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

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

        4 USB接口驅(qū)動

        4.1 接口結(jié)構(gòu)

        平時編寫的USB驅(qū)動通常指的是寫USB接口的驅(qū)動,一個接口對應(yīng)一個接口驅(qū)動程序,需要以一個struct usb_driver結(jié)構(gòu)的對象為中心,并以設(shè)備的接口提供的功能為基礎(chǔ),來進行USB驅(qū)動程序的編寫。struct usb_driver結(jié)構(gòu)體一般定義在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為驅(qū)動程序的名字,對應(yīng)于/sys/bus/usb/drivers/下面的子目錄名稱。它只是彼此區(qū)別的一個代號,這里的名字在所有的USB驅(qū)動中必須是唯一的。probe用來看看這個USB驅(qū)動是否愿意接受某個接口的函數(shù)。Disconnect函數(shù)將在接口失去聯(lián)系或使用rmmod卸載驅(qū)動將它和接口強行分開時被調(diào)用。Ioctl函數(shù)則用在驅(qū)動通過usbfs和用戶空間進行交流時使用。Suspend、esume分別在設(shè)備被掛起和喚醒時使用。pre_reset、post_reset分別在設(shè)備將要復(fù)位(reset)和已經(jīng)復(fù)位后使用。id_table的變量可用來判斷是否支持某個設(shè)備接口。Dynids是支持動態(tài)id的。實際上,即使驅(qū)動已經(jīng)加載了,也可以添加新的id給它。drvwrap是給USB core區(qū)分設(shè)備驅(qū)動和接口驅(qū)動用的。no_dynamic_id可以用來禁止動態(tài)id。supports_autosuspend可對autosuspend提供支持,如果設(shè)置為0,則不再允許綁定到這個驅(qū)動的接口autosuspend。

        接口驅(qū)動

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

]]>
深入淺出Linux設(shè)備驅(qū)動之并發(fā)控制(2)http://www.hufushizhe.com/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從內(nèi)核空間復(fù)制到用戶空間
 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;
 }

 //將用戶空間的數(shù)據(jù)復(fù)制到內(nèi)核空間的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的驅(qū)動程序增加open()和release()函數(shù),并在其中借助自旋鎖來保護對全局變量int globalvar_count(記錄打開設(shè)備的進程數(shù))的訪問來實現(xiàn)設(shè)備只能被一個進程打開(必須確保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);

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

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

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

第二個原因是:目前很多驅(qū)動開發(fā)者,即使是已經(jīng)有多年經(jīng)驗的開發(fā)者,在開發(fā)驅(qū)動的時候也就是填充填充 driver 的結(jié)構(gòu)體,對于比較成熟的平臺,就是網(wǎng)上找個類似的驅(qū)動修改一下,即使寫十個百個千個驅(qū)動,也就是對某些硬件比較熟,遇到全新的芯片全新的平臺就束手無策。應(yīng)該說這樣對驅(qū)動的理解是很有限的。這也是目前linux 驅(qū)動開發(fā)領(lǐng)域的現(xiàn)狀。

我們首先認識一下 linux 驅(qū)動的基本面,我們認識一個新事物的的第一件事就是了解它的一些基本信息,就像我們?nèi)伺c人之間互相認識首先也是通過個人的基本信息一樣。

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

系統(tǒng)調(diào)用是內(nèi)核和應(yīng)用程序之間的接口,而驅(qū)動程序是內(nèi)核和硬件之間的接口,也就是內(nèi)核和硬件之間的橋梁。它為應(yīng)用程序屏蔽了硬件的細節(jié),這樣在應(yīng)用程序看來,硬件設(shè)備只是一個設(shè)備文件,應(yīng)用程序可以象操作普通文件一樣對硬件設(shè)備進行操作。

linux 驅(qū)動程序是內(nèi)核的一部分,管理著系統(tǒng)中的設(shè)備控制器和相應(yīng)的設(shè)備。它主要完成這么幾個功能:對設(shè)備初始化和釋放;傳送數(shù)據(jù)到硬件和從硬件讀取數(shù)據(jù);檢測和處理設(shè)備出現(xiàn)的錯誤。

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

為方便我們加入各種驅(qū)動來支持不同的硬件,內(nèi)核抽象出了很多層次結(jié)構(gòu),這些層次結(jié)構(gòu)是 linux 設(shè)備驅(qū)動的上層。它們抽象出各種的驅(qū)動接口,驅(qū)動只需要填寫相應(yīng)的回調(diào)函數(shù),就能很容易把新的驅(qū)動添加到內(nèi)核。

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

而字符設(shè)備的 I/O 操作沒有通過緩存。字符設(shè)備操作以字節(jié)為基礎(chǔ),但不是說一次只能執(zhí)行一個字節(jié)操作。例如對于字符設(shè)備我們可以通過 mmap 一次進行大量數(shù)據(jù)交換。字符設(shè)備實現(xiàn)比較簡單和靈活。

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

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

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

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

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

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

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

最后,是要養(yǎng)成使用協(xié)議的 spec 、設(shè)備的 datasheet 、內(nèi)核參考代碼去解決問題的習(xí)慣,而不是一碰到問題就到處尋找所謂的牛人去問怎么解決。

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

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

最新8.6版Linux驅(qū)動更新包括:

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

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

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

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

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

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

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

如果想正確使用AMD Linux版催化劑驅(qū)動,系統(tǒng)必須符合如下需求:

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應(yīng)用程序必需)。

此款驅(qū)動具體支持顯卡型號包括:

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設(shè)計液晶顯示屏驅(qū)動技術(shù)http://www.hufushizhe.com/bbs/dispbbs.asp?BoardID=33&ID=1739&Page=1wangxinxin2010-11-24 11:41:15接口,同時完全隱蔽了設(shè)備的工作細節(jié)。用戶通過一組和具體設(shè)備驅(qū)動無關(guān)的標準化的調(diào)用來完成相關(guān)操作,驅(qū)動程序的任務(wù)就是把這些調(diào)用映射到具體設(shè)備對于實際硬件的特定操作上。

  硬件設(shè)備只是一個設(shè)備文件,應(yīng)用程序可以像操作普通文件一樣對硬件設(shè)備進行操作。設(shè)備驅(qū)動程序是內(nèi)核的一部分,它實現(xiàn)以下功能:

  ①對設(shè)備初始化和釋放。

  ②把數(shù)據(jù)從內(nèi)核傳送到硬件和從硬件讀取數(shù)據(jù)。

  ③讀取應(yīng)用程序傳送給設(shè)備文件的數(shù)據(jù)和回送應(yīng)用程序請求的數(shù)據(jù)。

  ④檢測和處理設(shè)備出現(xiàn)的錯誤。

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

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

  本文為開發(fā)字符設(shè)備驅(qū)動實例,對于其他兩類不再贅述。

  1 Linux關(guān)于字符設(shè)備的管理

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

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


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

  2 FYD12864-0402B液晶模塊簡介

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

  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內(nèi)部提供128×2字節(jié)的字符顯示RAM緩沖區(qū)(DDRAM)。字符顯示是通過將字符顯示編碼寫入該字符顯示RAM實現(xiàn)的。根據(jù)寫入內(nèi)容的不同,可分別在液晶屏上顯示CGROM(中文字庫)、HCGROM(ASCII碼字庫)及CGRAM(自定義字形)的內(nèi)容。3種不同字符/字型的選擇編碼范圍為:0000~0006H(其代碼分別是0000、0002、0004、0006,共4個)顯示自定義字型,02H~7FH顯示半寬ASCII碼字符,A1A0H~F7FFH顯示8 192種GB2312中文字庫字形。字符顯示RAM在液晶模塊中的地址80H~9FH。字符顯示的RAM的地址與32個字符顯示區(qū)域有著一一對應(yīng)的關(guān)系。

  4 部分代碼解析

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


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

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


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

  結(jié)  語

  對Linux設(shè)備驅(qū)動程序作了詳細的介紹,在實際開發(fā)板AT91RM9200上加入FYD12864-0402B驅(qū)動模塊,該液晶驅(qū)動采用通用化接口和調(diào)用方法,對開發(fā)Linux其他設(shè)備驅(qū)動程序具有很好的指導(dǎo)意義。(單片機與嵌入式系統(tǒng)應(yīng)用

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

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

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


1 基本原理

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

    在Linux下驅(qū)動FPGA,其本質(zhì)上就是字符設(shè)備的驅(qū)動,慣例上它們位于/dev目錄。

1.1 主次編號

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

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

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

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

1.2 重要數(shù)據(jù)結(jié)構(gòu)

    注冊設(shè)備編號僅僅是驅(qū)動代碼必須進行的諸多任務(wù)中的第一個。驅(qū)動操作包括三個重要的內(nèi)核數(shù)據(jù)結(jié)構(gòu),稱為file_operations、file和inode。其中,對于FPGA驅(qū)動來說,最值得關(guān)注的是文件操作(file_operations)。

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

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

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

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

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

     ioctl可為系統(tǒng)調(diào)用提供一個發(fā)出設(shè)備特定命令的方法。如果設(shè)備不提供ioctl方法,那么,對于任何未事先定義的請求,系統(tǒng)調(diào)用將返回一個錯誤。定義如下:

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

1.3 設(shè)備注冊

     內(nèi)核在內(nèi)部將使用struct cdev類型結(jié)構(gòu)來代表字符設(shè)備。在內(nèi)核調(diào)用設(shè)備操作前,代碼應(yīng)當包含。而如果想將cdev結(jié)構(gòu)嵌入設(shè)備特定的結(jié)構(gòu)中,則應(yīng)當初始化已經(jīng)分配的結(jié)構(gòu),其使用的代碼為:

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

1.4 open和release

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

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

 1.5 讀/寫操作

    讀和寫都是進行類似的任務(wù),就是從設(shè)備到應(yīng)用程序代碼的數(shù)據(jù)拷貝。因此,它們的原代碼比較相似:

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的任務(wù)是從設(shè)備拷貝數(shù)據(jù)到用戶空間(使用copy_to_user),而write方法則是從用戶空間拷貝數(shù)據(jù)到設(shè)備(使用copy_from_user)。

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

    圖1所示是用read參數(shù)表示一個典型讀的實現(xiàn)過程。


2 硬件電路

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

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

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

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


3 編程實現(xiàn)

3.1 設(shè)備驅(qū)動初始化

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

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

 3.2 異步中斷通知

    在應(yīng)用程序中,可用如下代碼獲得中斷響應(yīng):

signal (SIGIO,test_handler);/*test_handler為函數(shù)名字*/
fcntl(fa,F(xiàn)_SETOWN,getpid ());
oflags=fcntl(fa,F(xiàn)_GETFL);/*fd為打開設(shè)備返回值*/
fcntl (fd,F(xiàn)_SETFL,oflags∣FASYNC);/*fd為打開設(shè)備返回值*/

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

3.3 地址映射

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

fpga_base=ioremap(FPGA_PHY_START,F(xiàn)PGA_PHY_SIZE);


4 結(jié)束語

    本文系統(tǒng)的介紹了ARM基于Linux平臺下的FPGA的驅(qū)動開發(fā)方法,并通過開發(fā)用戶程序,實現(xiàn)了數(shù)據(jù)的處理和傳輸,從而實現(xiàn)了FPGA在嵌入式領(lǐng)域的廣泛應(yīng)用。

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

  系統(tǒng)環(huán)境: TurboLinux4.0.2,網(wǎng)卡、顯卡、Modem均集成在主板上。

  1.下載驅(qū)動

  登錄到Davicom公司網(wǎng)站上(www.davicom.com.tw)下載Linux下的最新驅(qū)動程序,版本為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)為編譯命令,編譯完成后,在當前目錄下產(chǎn)生目標文件dmfe.o。

  3.設(shè)置模式

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

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

  ID=91021282 ’ 網(wǎng)卡ID號

  NAME=eth0 ’ 網(wǎng)卡名稱

  IO=e000 ’ 輸入輸出口地址

  IRQ=11’ 中斷向量

  4.設(shè)置參數(shù)

  為網(wǎng)卡配置IP地址、子網(wǎng)掩碼、網(wǎng)關(guān)等參數(shù):

  ifconfig eth0 *.*.*.*

  netmask 255.255.255.*

  broadcast *.*.*.*

  5.配置路由

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

  6.啟動網(wǎng)卡

  使用命令ifup eth0 啟動網(wǎng)卡即可。

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

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

  通過以上步驟,我們以手工方式驅(qū)動了網(wǎng)卡,但是在系統(tǒng)啟動時網(wǎng)卡尚不能馬上工作,需要重復(fù)執(zhí)行上述3~6步。如果要在系統(tǒng)啟動期間使網(wǎng)卡設(shè)置自動生效,需要執(zhí)行以下操作:

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

  #!/bin/sh

  insmod dmfe

  ifup eth0

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

  chmod +x startnet

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

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

  最后,修改inet文件:

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

  在其中加入以下命令:

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

  這樣,用reboot命令重新啟動機器后,在系統(tǒng)啟動過程中可以看到網(wǎng)卡被驅(qū)動起來

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

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

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

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

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

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

即使開源驅(qū)動發(fā)展得非常成熟,也不可能完全取代專有驅(qū)動。在復(fù)制保護以及數(shù)字版權(quán)管理等涉及到知識產(chǎn)權(quán)技術(shù)的地方,AMD不會向開源社區(qū)的程序員們公開相關(guān)的細節(jié)。

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

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

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

據(jù)悉,第一批開源性質(zhì)的驅(qū)動將支持ATI的Radeon X1000和HD 2000顯卡芯片,AMD未來還將繼續(xù)公布相關(guān)的支持信息。

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

]]>
NAPI技術(shù)在Linux網(wǎng)絡(luò)驅(qū)動上的應(yīng)用http://www.hufushizhe.com/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)   

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

  總而言之,POLL 方法被網(wǎng)絡(luò)層調(diào)用,只負責按照網(wǎng)絡(luò)層的要求值("預(yù)算"值)提交對應(yīng)數(shù)量的數(shù)據(jù)包。8139CP 的 POLL 方法注冊通常在設(shè)備驅(qū)動程序模塊初始化(調(diào)用 probe)的時候進行,如下:

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

  {

  … …

  dev->poll = cp_rx_poll;

  … …

  }

  

  設(shè)備的 POLL 方法正如前所說的是被網(wǎng)絡(luò)層上的軟中斷 net_rx_action 調(diào)用,我們現(xiàn)在來看具體的流程:

  

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

  {

   struct cp_private *cp = netdev_priv(dev);

   unsigned rx_tail = cp->rx_tail;

   /*設(shè)定每次進行調(diào)度的時候從設(shè)備發(fā)送到網(wǎng)絡(luò)層次最大的數(shù)據(jù)包的大小*/

  unsigned rx_work = dev->quota;

   unsigned rx;

  

  rx_status_loop:

   rx = 0;

  /*重新打開NIC中斷,在 cp_interrupt 中斷句柄中中斷關(guān)閉了,現(xiàn)在 POLl 已經(jīng)開始處理環(huán)行緩沖隊列中的數(shù)據(jù),

  所以中斷可以打開,準備接收新的數(shù)據(jù)包*/

   cpw16(IntrStatus, cp_rx_intr_mask);  

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

   u32 status, len;

   dma_addr_t mapping;

   struct sk_buff *skb, *new_skb;

   struct cp_desc *desc;

   unsigned buflen;

  /*從下標為rx_tail的內(nèi)存中的環(huán)行緩沖隊列接收隊列rx_skb上"摘下"套接字緩沖區(qū)*/

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

   if (!skb)

   BUG();

  

   desc = &cp->rx_ring[rx_tail];

  /*檢查在 NIC 的環(huán)形隊列(rx_ring)上的最后的數(shù)據(jù)接收狀態(tài),是否有出現(xiàn)接收或者 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;

  /*創(chuàng)建新的套接字緩沖區(qū)*/

   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;

  /*解除原先映射的環(huán)行隊列上的映射區(qū)域*/

   pci_unmap_single(cp->pdev, mapping,

   buflen, PCI_DMA_FROMDEVICE);

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

   /* Handle checksum offloading for incoming packets. */

   if (cp_rx_csum_ok(status))

   skb->ip_summed = CHECKSUM_UNNECESSARY;

   else

   skb->ip_summed = CHECKSUM_NONE;

  /*按照數(shù)據(jù)的實際大小重新定義套接字緩沖區(qū)的大小*/

   skb_put(skb, len);  

   mapping =

   cp->rx_skb[rx_tail].mapping =

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

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

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

   buflen, PCI_DMA_FROMDEVICE);

  /*把新建立的緩沖區(qū)的虛擬地址掛在接收緩沖區(qū)的隊列中,在下一次訪問rx_skb數(shù)組的這個結(jié)構(gòu)時候,

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

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

  /*在cp_rx_skb調(diào)用netif_rx_skb,填充接收數(shù)據(jù)包隊列,等待網(wǎng)絡(luò)層在Bottom half隊列中調(diào)用ip_rcv接收網(wǎng)絡(luò)數(shù)據(jù),

  這個函數(shù)替代了以前使用的netif_rx*/

   cp_rx_skb(cp, skb, desc);

   rx++;  

  rx_next:

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

  而不是驅(qū)動在內(nèi)存中動態(tài)建立的),準備提交給下層(NIC)進行數(shù)據(jù)傳輸*/

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

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

  /*在相應(yīng)的傳輸寄存器中寫入控制字,把rx_ring的控制權(quán)從驅(qū)動程序交還給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傳輸已經(jīng)完成了使命,

  就等待有數(shù)據(jù)到來的時候再次喚醒軟中斷執(zhí)行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) {

  /*如果仍然有數(shù)據(jù)達到,那么返回POLL方法循環(huán)的開始,繼續(xù)接收數(shù)據(jù)*/

   if (cpr16(IntrStatus) & cp_rx_intr_mask)

   goto rx_status_loop;

  /*這里表示數(shù)據(jù)已經(jīng)接收完畢,而且沒有新的接收中斷產(chǎn)生了,這個時候使能NIC的接收中斷,

  并且調(diào)用__netif_rx_complete把已經(jīng)完成POLL的設(shè)備從poll_list上摘除,等待下一次中斷產(chǎn)生的時候,

  再次把設(shè)備掛上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 的驅(qū)動程序和 8139CP 大同小異,只是使用了網(wǎng)絡(luò)層專門提供的 POLL 方法--proecess_backlog(/net/dev.c),在 NIC 中斷接收到了數(shù)據(jù)包后,調(diào)用網(wǎng)絡(luò)層上的 netif_rx(/net/dev.c)將硬件中斷中接收到數(shù)據(jù)幀存入 sk_buff 結(jié)構(gòu), 然后檢查硬件幀頭,識別幀類型, 放入接收隊列(softnet_data 結(jié)構(gòu)中的 input_pkt_queue 隊列上), 激活接收軟中斷作進一步處理. 軟中斷函數(shù)(net_rx_action)提取接收包,而 process_backlog(也就是 POLL 方法)向上層提交數(shù)據(jù)。

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

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

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

]]>
Linux驅(qū)動開發(fā)必看:詳解神秘內(nèi)核(1)http://www.hufushizhe.com/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";
}

/* ... */

  請重新編譯內(nèi)核并嘗試運行新的修改。


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

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

  為了理解延遲—循環(huán)校準代碼,讓我們看一下定義于init/calibrate.c文件中的calibrate_ delay()函數(shù)。該函數(shù)靈活地使用整型運算得到了浮點的精度。如下的代碼片段(有一些注釋)顯示了該函數(shù)的開始部分,這部分用于得到一個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,這可以轉(zhuǎn)化為處理器速度大約為每秒100萬條指令,即1 MIPS。接下來,它等待jiffy被刷新(1個新的節(jié)拍的開始),并開始運行延遲循環(huán)__delay(loops_per_jiffy)。如果這個延遲循環(huán)持續(xù)了1個jiffy以上,將使用以前的loops_per_jiffy值(將當前值右移1位)修復(fù)當前l(fā)oops_per_jiffy的最高位;否則,該函數(shù)繼續(xù)通過左移loops_per_jiffy值來探測出其最高位。在內(nèi)核計算出最高位后,它開始計算低位并微調(diào)其精度:

<!--

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;
}

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

<!--

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

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

  在2.4節(jié)將更深入闡述jiffy、HZ和loops_per_jiffy。


  2.1.5 Checking HLT instruction

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

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

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

]]>
嵌入式Linux系統(tǒng)中MMC卡驅(qū)動管理技術(shù)研究http://www.hufushizhe.com/bbs/dispbbs.asp?BoardID=33&ID=1730&Page=1wangxinxin2010-11-24 11:25:39摘要  簡明介紹MMC卡驅(qū)動程序的體系結(jié)構(gòu),設(shè)計并實現(xiàn)MMC卡的底層驅(qū)動;對傳統(tǒng)的塊設(shè)備驅(qū)動程序中的單塊讀寫進行改進,實現(xiàn)MMC卡的集群讀寫,同時實現(xiàn)了卡的電源管理和即插即用功能。
關(guān)鍵詞 Linux MMC卡 底層驅(qū)動 集群讀寫 熱拔插

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

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

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

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

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

(1)命令發(fā)布和響應(yīng)

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

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

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

(3)DMA數(shù)據(jù)傳輸

    驅(qū)動程序中對MMC卡的數(shù)據(jù)讀寫是通過DMA通道進行傳輸?shù)摹榱吮M舨僮鞯倪B續(xù)性,驅(qū)動程序?qū)MC卡的輸入和輸出緩沖各設(shè)置1個DMA通道,在進行實際數(shù)據(jù)傳輸時,讀寫進程也進入睡眠狀態(tài),等待DMA數(shù)據(jù)傳輸完畢后,被DMA中斷喚醒。實現(xiàn)一次讀操作的偽代碼如下:
Pxa_read_mmc(){

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

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

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

2.2.2 集群讀寫的實現(xiàn)

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

    發(fā)布讀寫命令,讀入的數(shù)據(jù)塊數(shù)為一個rcquest一>nr_sectors的塊數(shù);
    緩沖區(qū)的指針指向第1個bh結(jié)構(gòu)所指的緩沖區(qū);
    while(數(shù)據(jù)還沒有讀完){
    讀入數(shù)據(jù)到buffer_head結(jié)構(gòu)所指定的緩沖區(qū);/*調(diào)用Pxa_read_mmc()*/
    調(diào)整緩沖區(qū)的指針到下一個buffer_head結(jié)構(gòu)所指向的緩沖區(qū);
    }

}

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

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

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

}

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

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

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

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

2.5 熱插拔管理

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

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

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

結(jié)語
   
本文研究實現(xiàn)的MMC卡驅(qū)動程序,其實現(xiàn)的集群讀寫證明有穩(wěn)定而較高的讀/寫速度;增加了電源管理功能,降低了電源的功耗,滿足了嵌入式系統(tǒng)低功耗的要求;增加的即插即用功能,大大方便了用戶的使用。驅(qū)動程序的體系結(jié)構(gòu)是實現(xiàn)嵌入式系統(tǒng)塊設(shè)備驅(qū)動的一種好方法。

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

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


   背景知識


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


   1 硬件平臺


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


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


   2.Linux下的設(shè)備驅(qū)動


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


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


   3.Linux的幀緩沖設(shè)備


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


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


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


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

    幀緩沖設(shè)備屬于字符設(shè)備,采用“文件層-驅(qū)動層”的接口方式。在文件層為之定義了以下數(shù)據(jù)結(jié)構(gòu):

 


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


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


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

   

 


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


   4.幀緩沖驅(qū)動的縮寫


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


  

 


    
    圖片顯示函數(shù)部分重要代碼為:
    
   

 


    
    在主函數(shù)中,建立一個進程調(diào)用圖片顯示函數(shù)
    
   

 


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

 

  5.應(yīng)用價值


   液晶屏點亮了,這只是第一步,我們可以在此基礎(chǔ)上進一步進行應(yīng)用程序的開發(fā),比如筆者將此應(yīng)用在一個視頻監(jiān)控系統(tǒng)中。在這個視頻監(jiān)控系統(tǒng)中,圖像處理占很大的比重,基本的圖像處理構(gòu)成如下:


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


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

 

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

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

3、網(wǎng)上抄錄的Linux驅(qū)動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來編譯這個驅(qū)動:(版本一,最簡單的)
#下面這行是文件Makefile的內(nèi)容,注意M是大寫的
obj-m := hello.o

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

5、再寫另一種Makefile:(版本二:最省事的)
#以下是Makefile文件的內(nèi)容
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

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

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

7、查看驅(qū)動:
lsmod   看見 hello這個驅(qū)動在其中

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

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

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

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

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

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

在鍵盤初始化階段,所有的列信號(KBC)都被設(shè)置輸出為低電平。如果矩陣鍵盤中的1個按鍵按下,則相應(yīng)的行信號和列信號線短路,行信號線(KBR)輸入由高電平變?yōu)榈碗娖剑a(chǎn)生1個中斷,然后在驅(qū)動的中斷服務(wù)程序中按照表1中的序列逐列掃描列信號,讀取行信號的狀態(tài),根據(jù)讀回來的行信號狀態(tài)就可以判斷有那些按鍵按下。

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

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

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

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

 

]]>
終于調(diào)試成功Linux下的動態(tài)重構(gòu)控制驅(qū)動http://www.hufushizhe.com/bbs/dispbbs.asp?BoardID=33&ID=1726&Page=1wangxinxin2010-11-24 11:03:09經(jīng)過數(shù)月的努力終于在2009226星期四下午6點調(diào)試成功了Linux下的動態(tài)重構(gòu)配置控制器的設(shè)備驅(qū)動程序。項目可以按照原計劃進行下去了。Linux2.6.20作為自重構(gòu)的操作系統(tǒng)原型。

硬件平臺為 ML505

開發(fā)環(huán)境為ISE9.2SP4PR10EDK9.2Sp2PlanAhead10.1Petalinux-MMU-V0.10.

支持可重構(gòu)計算的操作系統(tǒng),本身就是很難的課題。

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

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

]]>
主站蜘蛛池模板: 日本精品一卡二卡≡卡四卡| 黄a视频在线观看| 日本电车强视频在线播放| 免费无码午夜福利片69| 黄色网站免费在线观看| 夭天干天天做天天免费看| 久热这里有精品| 欧美日韩高清完整版在线观看免费| 国产熟女一区二区三区五月婷| 国产精品国产三级国产专播下 | 日韩精品专区av无码| 免费一级毛片在线播放泰国| 亚洲欧美日韩精品一区| 韩日视频在线观看| 欧美一级免费观看| 国产超薄肉色丝袜的免费网站| 久久久久国产一区二区| 欧美videosgratis蛇交| 公与2个熄乱理在线播放| 色噜噜狠狠色综合欧洲selulu| 国产精品久久久久久久久齐齐| xxxcom在线观看| 成年人免费小视频| 亚洲AV无码精品蜜桃| 欧美成人免费午夜影视| 免费无遮挡无码视频网站| 美女**视频一级毛片| 国产成人无码一区二区三区 | 国产亚洲精品bt天堂精选| 2021国产麻豆剧| 国模精品一区二区三区视频| 中文无码日韩欧免费视频| 日本中文字幕网| 亚洲va在线va天堂va手机| 欧美日韩一区视频| 免费大黄网站在线观看| 精品亚洲一区二区三区在线观看| 国产人妖系列在线精品| 香蕉精品高清在线观看视频| 国产精品成人免费视频网站| jizz国产精品网站|