<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 基于ARM的嵌入式Linux移植真實(shí)體驗(yàn)(4)――設(shè)備驅(qū)動

          基于ARM的嵌入式Linux移植真實(shí)體驗(yàn)(4)――設(shè)備驅(qū)動

          作者: 時(shí)間:2016-11-09 來源:網(wǎng)絡(luò) 收藏
          設(shè)備驅(qū)動程序是操作系統(tǒng)內(nèi)核和機(jī)器硬件之間的接口,它為應(yīng)用程序屏蔽硬件的細(xì)節(jié),一般來說,Linux的設(shè)備驅(qū)動程序需要完成如下功能:
          Ø 設(shè)備初始化、釋放;
          Ø 提供各類設(shè)備服務(wù);
          Ø 負(fù)責(zé)內(nèi)核和設(shè)備之間的數(shù)據(jù)交換;
          Ø 檢測和處理設(shè)備工作過程中出現(xiàn)的錯(cuò)誤。
          Linux下的設(shè)備驅(qū)動程序被組織為一組完成不同任務(wù)的函數(shù)的集合,通過這些函數(shù)使得Linux的設(shè)備操作猶如文件一般。在應(yīng)用程序看來,硬件設(shè)備只是一個(gè)設(shè)備文件,應(yīng)用程序可以象操作普通文件一樣對硬件設(shè)備進(jìn)行操作,如open ()、close ()、read ()、write () 等。
          Linux主要將設(shè)備分為二類:字符設(shè)備和塊設(shè)備。字符設(shè)備是指設(shè)備發(fā)送和接收數(shù)據(jù)以字符的形式進(jìn)行;而塊設(shè)備則以整個(gè)數(shù)據(jù)緩沖區(qū)的形式進(jìn)行。在對字符設(shè)備發(fā)出讀/寫請求時(shí),實(shí)際的硬件I/O一般就緊接著發(fā)生了;而塊設(shè)備則不然,它利用一塊系統(tǒng)內(nèi)存作緩沖區(qū),當(dāng)用戶進(jìn)程對設(shè)備請求能滿足用戶的要求,就返回請求的數(shù)據(jù),如果不能,就調(diào)用請求函數(shù)來進(jìn)行實(shí)際的I/O操作。塊設(shè)備主要針對磁盤等慢速設(shè)備。
          1.內(nèi)存分配
          由于Linux驅(qū)動程序在內(nèi)核中運(yùn)行,因此在設(shè)備驅(qū)動程序需要申請/釋放內(nèi)存時(shí),不能使用用戶級的malloc/free函數(shù),而需由內(nèi)核級的函數(shù)kmalloc/kfree () 來實(shí)現(xiàn),kmalloc()函數(shù)的原型為:
          void kmalloc (size_t size ,int priority);
          參數(shù)size為申請分配內(nèi)存的字節(jié)數(shù),kmalloc最多只能開辟128k的內(nèi)存;參數(shù)priority說明若kmalloc()不能馬上分配內(nèi)存時(shí)用戶進(jìn)程要采用的動作:GFP_KERNEL 表示等待,即等kmalloc()函數(shù)將一些內(nèi)存安排到交換區(qū)來滿足你的內(nèi)存需要,GFP_ATOMIC 表示不等待,如不能立即分配到內(nèi)存則返回0 值;函數(shù)的返回值指向已分配內(nèi)存的起始地址,出錯(cuò)時(shí),返回0。
          kmalloc ()分配的內(nèi)存需用kfree()函數(shù)來釋放,kfree ()被定義為:
          # define kfree (n) kfree_s( (n) ,0)
          其中kfree_s () 函數(shù)原型為:
          void kfree_s (void * ptr ,int size);
          參數(shù)ptr為kmalloc()返回的已分配內(nèi)存的指針,size是要釋放內(nèi)存的字節(jié)數(shù),若為0 時(shí),由內(nèi)核自動確定內(nèi)存的大小。
          2.中斷
          許多設(shè)備涉及到中斷操作,因此,在這樣的設(shè)備的驅(qū)動程序中需要對硬件產(chǎn)生的中斷請求提供中斷服務(wù)程序。與注冊基本入口點(diǎn)一樣,驅(qū)動程序也要請求內(nèi)核將特定的中斷請求和中斷服務(wù)程序聯(lián)系在一起。在Linux中,用request_irq()函數(shù)來實(shí)現(xiàn)請求:
          int request_irq (unsigned int irq ,void( * handler) int ,unsigned long type ,char * name);
          參數(shù)irq為要中斷請求號,參數(shù)handler為指向中斷服務(wù)程序的指針,參數(shù)type 用來確定是正常中斷還是快速中斷(正常中斷指中斷服務(wù)子程序返回后,內(nèi)核可以執(zhí)行調(diào)度程序來確定將運(yùn)行哪一個(gè)進(jìn)程;而快速中斷是指中斷服務(wù)子程序返回后,立即執(zhí)行被中斷程序,正常中斷type 取值為0 ,快速中斷type 取值為SA_INTERRUPT),參數(shù)name是設(shè)備驅(qū)動程序的名稱。
          3.字符設(shè)備驅(qū)動
          我們必須為字符設(shè)備提供一個(gè)初始化函數(shù),該函數(shù)用來完成對所控設(shè)備的初始化工作,并調(diào)用register_chrdev() 函數(shù)注冊字符設(shè)備。假設(shè)有一字符設(shè)備“exampledev”,則其init 函數(shù)為:
          void exampledev_init(void)
          {
          if (register_chrdev(MAJOR_NUM, " exampledev ", &exampledev_fops))
          TRACE_TXT("Device exampledev driver registered error");
          else
          TRACE_TXT("Device exampledev driver registered successfully");
          …//設(shè)備初始化
          }
          其中,register_chrdev函數(shù)中的參數(shù)MAJOR_NUM為主設(shè)備號,“exampledev”為設(shè)備名,exampledev_fops為包含基本函數(shù)入口點(diǎn)的結(jié)構(gòu)體,類型為file_operations。當(dāng)執(zhí)行exampledev_init時(shí),它將調(diào)用內(nèi)核函數(shù)register_chrdev,把驅(qū)動程序的基本入口點(diǎn)指針存放在內(nèi)核的字符設(shè)備地址表中,在用戶進(jìn)程對該設(shè)備執(zhí)行系統(tǒng)調(diào)用時(shí)提供入口地址。
          隨著內(nèi)核功能的加強(qiáng),file_operations結(jié)構(gòu)體也變得更加龐大。但是大多數(shù)的驅(qū)動程序只是利用了其中的一部分,對于驅(qū)動程序中無需提供的功能,只需要把相應(yīng)位置的值設(shè)為NULL。對于字符設(shè)備來說,要提供的主要入口有:open ()、release ()、read ()、write ()、ioctl ()等。
          open()函數(shù) 對設(shè)備特殊文件進(jìn)行open()系統(tǒng)調(diào)用時(shí),將調(diào)用驅(qū)動程序的open () 函數(shù):
          int (*open)(struct inode * inode,struct file *filp);
          其中參數(shù)inode為設(shè)備特殊文件的inode (索引結(jié)點(diǎn)) 結(jié)構(gòu)的指針,參數(shù)filp是指向這一設(shè)備的文件結(jié)構(gòu)的指針。open()的主要任務(wù)是確定硬件處在就緒狀態(tài)、驗(yàn)證次設(shè)備號的合法性(次設(shè)備號可以用MINOR(inode-> i_rdev) 取得)、控制使用設(shè)備的進(jìn)程數(shù)、根據(jù)執(zhí)行情況返回狀態(tài)碼(0表示成功,負(fù)數(shù)表示存在錯(cuò)誤) 等;
          release()函數(shù) 當(dāng)最后一個(gè)打開設(shè)備的用戶進(jìn)程執(zhí)行close ()系統(tǒng)調(diào)用時(shí),內(nèi)核將調(diào)用驅(qū)動程序的release () 函數(shù):
          void (*release) (struct inode * inode,struct file *filp) ;
          release 函數(shù)的主要任務(wù)是清理未結(jié)束的輸入/輸出操作、釋放資源、用戶自定義排他標(biāo)志的復(fù)位等。
          read()函數(shù) 當(dāng)對設(shè)備特殊文件進(jìn)行read() 系統(tǒng)調(diào)用時(shí),將調(diào)用驅(qū)動程序read() 函數(shù):
          ssize_t (*read) (struct file * filp, char * buf, size_t count, loff_t * offp);
          參數(shù)buf是指向用戶空間緩沖區(qū)的指針,由用戶進(jìn)程給出,count 為用戶進(jìn)程要求讀取的字節(jié)數(shù),也由用戶給出。
          read() 函數(shù)的功能就是從硬設(shè)備或內(nèi)核內(nèi)存中讀取或復(fù)制count個(gè)字節(jié)到buf 指定的緩沖區(qū)中。在復(fù)制數(shù)據(jù)時(shí)要注意,驅(qū)動程序運(yùn)行在內(nèi)核中,而buf指定的緩沖區(qū)在用戶內(nèi)存區(qū)中,是不能直接在內(nèi)核中訪問使用的,因此,必須使用特殊的復(fù)制函數(shù)來完成復(fù)制工作,這些函數(shù)在include/asm/uaccess.h中被聲明:
          unsigned long copy_to_user (void * to, void * from, unsigned long len);
          此外,put_user()函數(shù)用于內(nèi)核空間和用戶空間的單值交互(如char、int、long)。
          write( )函數(shù) 當(dāng)設(shè)備特殊文件進(jìn)行write () 系統(tǒng)調(diào)用時(shí),將調(diào)用驅(qū)動程序的write () 函數(shù):
          ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
          write ()的功能是將參數(shù)buf 指定的緩沖區(qū)中的count 個(gè)字節(jié)內(nèi)容復(fù)制到硬件或內(nèi)核內(nèi)存中,和read() 一樣,復(fù)制工作也需要由特殊函數(shù)來完成:
          unsigned long copy_from_user(void *to, const void *from, unsigned long n);
          此外,get_user()函數(shù)用于內(nèi)核空間和用戶空間的單值交互(如char、int、long)。
          ioctl()函數(shù) 該函數(shù)是特殊的控制函數(shù),可以通過它向設(shè)備傳遞控制信息或從設(shè)備取得狀態(tài)信息,函數(shù)原型為:
          int (*ioctl) (struct inode * inode,struct file * filp,unsigned int cmd,unsigned long arg);
          參數(shù)cmd為設(shè)備驅(qū)動程序要執(zhí)行的命令的代碼,由用戶自定義,參數(shù)arg 為相應(yīng)的命令提供參數(shù),類型可以是整型、指針等。
          同樣,在驅(qū)動程序中,這些函數(shù)的定義也必須符合命名規(guī)則,按照本文約定,設(shè)備“exampledev”的驅(qū)動程序的這些函數(shù)應(yīng)分別命名為exampledev_open、exampledev_ release、exampledev_read、exampledev_write、exampledev_ioctl,因此設(shè)備“exampledev”的基本入口點(diǎn)結(jié)構(gòu)變量exampledev_fops 賦值如下(對較早版本的內(nèi)核):
          struct file_operations exampledev_fops {
           NULL ,
           exampledev_read ,
           exampledev_write ,
           NULL ,
           NULL ,
           exampledev_ioctl ,
           NULL ,
           exampledev_open ,
           exampledev_release ,
           NULL ,
           NULL ,
           NULL ,
           NULL
          } ;
          就目前而言,由于file_operations結(jié)構(gòu)體已經(jīng)很龐大,我們更適合用GNU擴(kuò)展的C語法來初始化exampledev_fops:
          struct file_operations exampledev_fops = {
          read: exampledev _read,
          write: exampledev _write,
          ioctl: exampledev_ioctl ,
          open: exampledev_open ,
          release : exampledev_release ,
          };
          看看第一章電路板硬件原理圖,板上包含四個(gè)用戶可編程的發(fā)光二極管(LED),這些LED連接在ARM處理器的可編程I/O口(GPIO)上,現(xiàn)在來編寫這些LED的驅(qū)動:
          #include
          #include
          #include
          #include
          #include
          #include
          #include
          #define DEVICE_NAME "leds" /*定義led 設(shè)備的名字*/
          #define LED_MAJOR 231 /*定義led 設(shè)備的主設(shè)備號*/
          static unsigned long led_table[] =
          {
          /*I/O 方式led 設(shè)備對應(yīng)的硬件資源*/
          GPIO_B10, GPIO_B8, GPIO_B5, GPIO_B6,
          };
          /*使用ioctl 控制led*/
          static int leds_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
          unsigned long arg)
          {
          switch (cmd)
          {
          case 0:
          case 1:
          if (arg > 4)
          {
          return -EINVAL;
          }
          write_gpio_bit(led_table[arg], !cmd);
          default:
          return -EINVAL;
          }
          }
          static struct file_operations leds_fops =
          {
          owner: THIS_MODULE, ioctl: leds_ioctl,
          };
          static devfs_handle_t devfs_handle;
          static int __init leds_init(void)
          {
          int ret;
          int i;
          /*在內(nèi)核中注冊設(shè)備*/
          ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &leds_fops);
          if (ret < 0)
          {
          printk(DEVICE_NAME " cant register major numbern");
          return ret;
          }
          devfs_handle = devfs_register(NULL, DEVICE_NAME, DEVFS_FL_DEFAULT, LED_MAJOR,
          0, S_IFCHR | S_IRUSR | S_IWUSR, &leds_fops, NULL);
          /*使用宏進(jìn)行端口初始化,set_gpio_ctrl 和write_gpio_bit 均為宏定義*/
          for (i = 0; i < 8; i++)
          {
          set_gpio_ctrl(led_table[i] | GPIO_PULLUP_EN | GPIO_MODE_OUT);
          write_gpio_bit(led_table[i], 1);
          }
          printk(DEVICE_NAME " initializedn");
          return 0;
          }
          static void __exit leds_exit(void)
          {
          devfs_unregister(devfs_handle);
          unregister_chrdev(LED_MAJOR, DEVICE_NAME);
          }
          module_init(leds_init);
          module_exit(leds_exit);
          使用命令方式編譯led 驅(qū)動模塊:
          #arm-linux-gcc -D__KERNEL__ -I/arm/kernel/include
          -DKBUILD_BASENAME=leds -DMODULE -c -o leds.o leds.c
          以上命令將生成leds.o 文件,把該文件復(fù)制到板子的/lib目錄下,使用以下命令就可以安裝leds驅(qū)動模塊:
          #insmod /lib/ leds.o
          刪除該模塊的命令是:
          #rmmod leds
          4.塊設(shè)備驅(qū)動
          塊設(shè)備驅(qū)動程序的編寫是一個(gè)浩繁的工程,其難度遠(yuǎn)超過字符設(shè)備,上千行的代碼往往只能搞定一個(gè)簡單的塊設(shè)備,而數(shù)十行代碼就可能搞定一個(gè)字符設(shè)備。因此,非得有相當(dāng)?shù)幕竟Σ拍芡瓿纱隧?xiàng)工作。下面先給出一個(gè)實(shí)例,即mtdblock塊設(shè)備的驅(qū)動。我們通過分析此實(shí)例中的代碼來說明塊設(shè)備驅(qū)動程序的寫法(由于篇幅的關(guān)系,大量的代碼被省略,只保留了必要的主干):
          #include
          #include
          static void mtd_notify_add(struct mtd_info* mtd);
          static void mtd_notify_remove(struct mtd_info* mtd);
          static struct mtd_notifier notifier = {
          mtd_notify_add,
          mtd_notify_remove,
          NULL
          };
          static devfs_handle_t devfs_dir_handle = NULL;
          static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES];
          static struct mtdblk_dev {
          struct mtd_info *mtd; /* Locked */
          int count;
          struct semaphore cache_sem;
          unsigned char *cache_data;
          unsigned long cache_offset;
          unsigned int cache_size;
          enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
          } *mtdblks[MAX_MTD_DEVICES];
          static spinlock_t mtdblks_lock;
          /* this lock is used just in kernels >= 2.5.x */
          static spinlock_t mtdblock_lock;
          static int mtd_sizes[MAX_MTD_DEVICES];
          static int mtd_blksizes[MAX_MTD_DEVICES];
          static void erase_callback(struct erase_info *done)
          {
          wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
          wake_up(wait_q);
          }
          static int erase_write (struct mtd_info *mtd, unsigned long pos,
          int len, const char *buf)
          {
          struct erase_info erase;
          DECLARE_WAITQUEUE(wait, current);
          wait_queue_head_t wait_q;
          size_t retlen;
          int ret;
          /*
          * First, lets erase the flash block.
          */
          init_waitqueue_head(&wait_q);
          erase.mtd = mtd;
          erase.callback = erase_callback;
          erase.addr = pos;
          erase.len = len;
          erase.priv = (u_long)&wait_q;
          set_current_state(TASK_INTERRUPTIBLE);
          add_wait_queue(&wait_q, &wait);
          ret = MTD_ERASE(mtd, &erase);
          if (ret) {
          set_current_state(TASK_RUNNING);
          remove_wait_queue(&wait_q, &wait);
          printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] "
          "on "%s" failedn",
          pos, len, mtd->name);
          return ret;
          }
          schedule(); /* Wait for erase to finish. */
          remove_wait_queue(&wait_q, &wait);
          /*
          * Next, writhe data to flash.
          */
          ret = MTD_WRITE (mtd, pos, len, &retlen, buf);
          if (ret)
          return ret;
          if (retlen != len)
          return -EIO;
          return 0;
          }
          static int write_cached_data (struct mtdblk_dev *mtdblk)
          {
          struct mtd_info *mtd = mtdblk->mtd;
          int ret;
          if (mtdblk->cache_state != STATE_DIRTY)
          return 0;
          DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: writing cached data for "%s" "
          "at 0x%lx, size 0x%xn", mtd->name,
          mtdblk->cache_offset, mtdblk->cache_size);
          ret = erase_write (mtd, mtdblk->cache_offset,
          mtdblk->cache_size, mtdblk->cache_data);
          if (ret)
          return ret;
          mtdblk->cache_state = STATE_EMPTY;
          return 0;
          }
          static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,
          int len, const char *buf)
          {
          }
          static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,
          int len, char *buf)
          {
          }
          static int mtdblock_open(struct inode *inode, struct file *file)
          {
          }
          static release_t mtdblock_release(struct inode *inode, struct file *file)
          {
          int dev;
          struct mtdblk_dev *mtdblk;
          DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_releasen");
          if (inode == NULL)
          release_return(-ENODEV);
          dev = minor(inode->i_rdev);
          mtdblk = mtdblks[dev];
          down(&mtdblk->cache_sem);
          write_cached_data(mtdblk);
          up(&mtdblk->cache_sem);
          spin_lock(&mtdblks_lock);
          if (!--mtdblk->count) {
          /* It was the last usage. Free the device */
          mtdblks[dev] = NULL;
          spin_unlock(&mtdblks_lock);
          if (mtdblk->mtd->sync)
          mtdblk->mtd->sync(mtdblk->mtd);
          put_mtd_device(mtdblk->mtd);
          vfree(mtdblk->cache_data);
          kfree(mtdblk);
          } else {
          spin_unlock(&mtdblks_lock);
          }
          DEBUG(MTD_DEBUG_LEVEL1, "okn");
          BLK_DEC_USE_COUNT;
          release_return(0);
          }
          /*
          * This is a special request_fn because it is executed in a process context
          * to be able to sleep independently of the caller. The
          * io_request_lock (for <2.5) or queue_lock (for >=2.5) is held upon entry
          * and exit. The head of our request queue is considered active so there is
          * no need to dequeue requests before we are done.
          */
          static void handle_mtdblock_request(void)
          {
          struct request *req;
          struct mtdblk_dev *mtdblk;
          unsigned int res;
          for (;;) {
          INIT_REQUEST;
          req = CURRENT;
          spin_unlock_irq(QUEUE_LOCK(QUEUE));
          mtdblk = mtdblks[minor(req->rq_dev)];
          res = 0;
          if (minor(req->rq_dev) >= MAX_MTD_DEVICES)
          panic("%s : minor out of bound", __FUNCTION__);
          if (!IS_REQ_CMD(req))
          goto end_req;
          if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9))
          goto end_req;
          // Handle the request
          switch (rq_data_dir(req))
          {
          int err;
          case READ:
          down(&mtdblk->cache_sem);
          err = do_cached_read (mtdblk, req->sector << 9,
          req->current_nr_sectors << 9,
          req->buffer);
          up(&mtdblk->cache_sem);
          if (!err)
          res = 1;
          break;
          case WRITE:
          // Read only device
          if ( !(mtdblk->mtd->flags & MTD_WRITEABLE) )
          break;
          // Do the write
          down(&mtdblk->cache_sem);
          err = do_cached_write (mtdblk, req->sector << 9,
          req->current_nr_sectors << 9,
          req->buffer);
          up(&mtdblk->cache_sem);
          if (!err)
          res = 1;
          break;
          }
          end_req:
          spin_lock_irq(QUEUE_LOCK(QUEUE));
          end_request(res);
          }
          }
          static volatile int leaving = 0;
          static DECLARE_MUTEX_LOCKED(thread_sem);
          static DECLARE_WAIT_QUEUE_HEAD(thr_wq);
          int mtdblock_thread(void *dummy)
          {
          }
          #define RQFUNC_ARG request_queue_t *q
          static void mtdblock_request(RQFUNC_ARG)
          {
          /* Dont do anything, except wake the thread if necessary */
          wake_up(&thr_wq);
          }
          static int mtdblock_ioctl(struct inode * inode, struct file * file,
          unsigned int cmd, unsigned long arg)
          {
          struct mtdblk_dev *mtdblk;
          mtdblk = mtdblks[minor(inode->i_rdev)];
          switch (cmd) {
          case BLKGETSIZE: /* Return device size */
          return put_user((mtdblk->mtd->size >> 9), (unsigned long *) arg);
          case BLKFLSBUF:
          if(!capable(CAP_SYS_ADMIN))
          return -EACCES;
          fsync_dev(inode->i_rdev);
          invalidate_buffers(inode->i_rdev);
          down(&mtdblk->cache_sem);
          write_cached_data(mtdblk);
          up(&mtdblk->cache_sem);
          if (mtdblk->mtd->sync)
          mtdblk->mtd->sync(mtdblk->mtd);
          return 0;
          default:
          return -EINVAL;
          }
          }
          static struct block_device_operations mtd_fops =
          {
          owner: THIS_MODULE,
          open: mtdblock_open,
          release: mtdblock_release,
          ioctl: mtdblock_ioctl
          };
          static void mtd_notify_add(struct mtd_info* mtd)
          {
          }
          static void mtd_notify_remove(struct mtd_info* mtd)
          {
          if (!mtd || mtd->type == MTD_ABSENT)
          return;
          devfs_unregister(devfs_rw_handle[mtd->index]);
          }
          int __init init_mtdblock(void)
          {
          int i;
          spin_lock_init(&mtdblks_lock);
          /* this lock is used just in kernels >= 2.5.x */
          spin_lock_init(&mtdblock_lock);
          #ifdef CONFIG_DEVFS_FS
          if (devfs_register_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME, &mtd_fops))
          {
          printk(KERN_NOTICE "Cant allocate major number %d for Memory Technology Devices.n",
          MTD_BLOCK_MAJOR);
          return -EAGAIN;
          }
          devfs_dir_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL);
          register_mtd_user(¬ifier);
          #else
          if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) {
          printk(KERN_NOTICE "Cant allocate major number %d for Memory Technology Devices.n",
          MTD_BLOCK_MAJOR);
          return -EAGAIN;
          }
          #endif
          /* We fill it in at open() time. */
          for (i=0; i< MAX_MTD_DEVICES; i++) {
          mtd_sizes[i] = 0;
          mtd_blksizes[i] = BLOCK_SIZE;
          }
          init_waitqueue_head(&thr_wq);
          /* Allow the block size to default to BLOCK_SIZE. */
          blksize_size[MAJOR_NR] = mtd_blksizes;
          blk_size[MAJOR_NR] = mtd_sizes;
          BLK_INIT_QUEUE(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request, &mtdblock_lock);
          kernel_thread (mtdblock_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
          return 0;
          }
          static void __exit cleanup_mtdblock(void)
          {
          leaving = 1;
          wake_up(&thr_wq);
          down(&thread_sem);
          #ifdef CONFIG_DEVFS_FS
          unregister_mtd_user(¬ifier);
          devfs_unregister(devfs_dir_handle);
          devfs_unregister_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME);
          #else
          unregister_blkdev(MAJOR_NR,DEVICE_NAME);
          #endif
          blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
          blksize_size[MAJOR_NR] = NULL;
          blk_size[MAJOR_NR] = NULL;
          }
          module_init(init_mtdblock);
          module_exit(cleanup_mtdblock);
          從上述源代碼中我們發(fā)現(xiàn),塊設(shè)備也以與字符設(shè)備register_chrdev、unregister_ chrdev 函數(shù)類似的方法進(jìn)行設(shè)備的注冊與釋放:
          int register_blkdev(unsigned int major, const char *name, struct block_device_operations *bdops);
          int unregister_blkdev(unsigned int major, const char *name);
          但是,register_chrdev使用一個(gè)向 file_operations 結(jié)構(gòu)的指針,而register_blkdev 則使用 block_device_operations 結(jié)構(gòu)的指針,其中定義的open、release 和 ioctl 方法和字符設(shè)備的對應(yīng)方法相同,但未定義 read 或者 write 操作。這是因?yàn)椋猩婕暗綁K設(shè)備的 I/O 通常由系統(tǒng)進(jìn)行緩沖處理。
          塊驅(qū)動程序最終必須提供完成實(shí)際塊 I/O 操作的機(jī)制,在 Linux 當(dāng)中,用于這些 I/O 操作的方法稱為“request(請求)”。在塊設(shè)備的注冊過程中,需要初始化request隊(duì)列,這一動作通過blk_init_queue來完成,blk_init_queue函數(shù)建立隊(duì)列,并將該驅(qū)動程序的 request 函數(shù)關(guān)聯(lián)到隊(duì)列。在模塊的清除階段,應(yīng)調(diào)用 blk_cleanup_queue 函數(shù)。
          本例中相關(guān)的代碼為:
          BLK_INIT_QUEUE(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request, &mtdblock_lock);
          blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
          每個(gè)設(shè)備有一個(gè)默認(rèn)使用的請求隊(duì)列,必要時(shí),可使用 BLK_DEFAULT_QUEUE(major) 宏得到該默認(rèn)隊(duì)列。這個(gè)宏在 blk_dev_struct 結(jié)構(gòu)形成的全局?jǐn)?shù)組(該數(shù)組名為 blk_dev)中搜索得到對應(yīng)的默認(rèn)隊(duì)列。blk_dev 數(shù)組由內(nèi)核維護(hù),并可通過主設(shè)備號索引。blk_dev_struct 接口定義如下:
          struct blk_dev_struct {
          /*
          * queue_proc has to be atomic
          */
          request_queue_t request_queue;
          queue_proc *queue;
          void *data;
          };
          request_queue 成員包含了初始化之后的 I/O 請求隊(duì)列,data 成員可由驅(qū)動程序使用,以便保存一些私有數(shù)據(jù)。
          request_queue定義為:
          struct request_queue
          {
          /*
          * the queue request freelist, one for reads and one for writes
          */
          struct request_list rq[2];
          /*
          * Together with queue_head for cacheline sharing
          */
          struct list_head queue_head;
          elevator_t elevator;
          request_fn_proc * request_fn;
          merge_request_fn * back_merge_fn;
          merge_request_fn * front_merge_fn;
          merge_requests_fn * merge_requests_fn;
          make_request_fn * make_request_fn;
          plug_device_fn * plug_device_fn;
          /*
          * The queue owner gets to use this for whatever they like.
          * ll_rw_blk doesnt touch it.
          */
          void * queuedata;
          /*
          * This is used to remove the plug when tq_disk runs.
          */
          struct tq_struct plug_tq;
          /*
          * Boolean that indicates whether this queue is plugged or not.
          */
          char plugged;
          /*
          * Boolean that indicates whether current_request is active or
          * not.
          */
          char head_active;
          /*
          * Is meant to protect the queue in the future instead of
          * io_request_lock
          */
          spinlock_t queue_lock;
          /*
          * Tasks wait here for free request
          */
          wait_queue_head_t wait_for_request;
          };
          下圖表征了blk_dev、blk_dev_struct和request_queue的關(guān)系:

          下圖則表征了塊設(shè)備的注冊和釋放過程:

          5.小結(jié)
          本章講述了Linux設(shè)備驅(qū)動程序的入口函數(shù)及驅(qū)動程序中的內(nèi)存申請、中斷等,并分別以實(shí)例講述了字符設(shè)備及塊設(shè)備的驅(qū)動開發(fā)方法。



          評論


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

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();