小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

字符設(shè)備基礎(chǔ)

 wanwanstudy 2012-02-20
bash> ls -l /dev
total 0
crw-------   1 root root     5,   1 Jul 16 10:02 console
...
lrwxrwxrwx   1 root root          3 Oct 6 10:02  cdrom -> hdc
...
brw-rw----   1 root disk     3,   0 Oct 6 2007   hda
brw-rw----   1 root disk     3,   1 Oct 6 2007   hda1
...
crw-------   1 root tty      4,   1 Oct 6 10:20  tty1
crw-------   1 root tty      4,   2 Oct 6 10:02  tty2
第一個(gè)字符:c:字符設(shè)備、l:symlink、b:塊設(shè)備
第五個(gè)字符5,1:主設(shè)備號(hào),次設(shè)備號(hào);主設(shè)備號(hào)能夠?qū)ふ业津?qū)動(dòng),而次設(shè)備號(hào)則能夠定位設(shè)備。
字符設(shè)備和塊設(shè)備的空間不一樣,因此,可以使用同一主設(shè)備號(hào)
從編碼角度看需要:(1)、初始化并注冊(cè)設(shè)備。(2)、應(yīng)用通過(guò)/dev下節(jié)點(diǎn)的文件系統(tǒng)調(diào)用:open()、read()、ioctl()、llseek()、write()
(3)中斷處理程序、底半部、時(shí)鐘處理、內(nèi)核輔助線程和其他支撐架構(gòu)。
從數(shù)據(jù)流角度看,字符設(shè)備需要:(1)設(shè)備對(duì)應(yīng)結(jié)構(gòu)體;(2)struct cdev;(3)、struct file_operation;(4)struct file。
 
Device Example:System CMOS
BIOS使用CMOS來(lái)存儲(chǔ)諸如startup option,boot order,and the system date,您可以通過(guò)BIOS啟動(dòng)菜單修改。例子CMOS驅(qū)動(dòng)可以像普通文件一樣訪問(wèn)2個(gè)PC CMOS塊,應(yīng)用程序可以操作/dev/cmos/0和/dev/cmos/1,并使用I/O系統(tǒng)調(diào)用來(lái)訪問(wèn)兩個(gè)bank里面的數(shù)據(jù)。因?yàn)锽IOS給CMOS區(qū)域以位級(jí)粒度,驅(qū)動(dòng)也能夠以位級(jí)訪問(wèn)。所以read()可以讀取指定的位數(shù),并移動(dòng)相應(yīng)位數(shù)指針。

Table 5.1. Register Layout on the CMOS
Register NameDescription
CMOS_BANK0_INDEX_PORTSpecify the desired CMOS bank 0 offset in this register.
CMOS_BANK0_DATA_PORTRead/write data from/to the address specified in CMOS_BANK0_INDEX_PORT.
CMOS_BANK1_INDEX_PORTSpecify the desired CMOS bank 1 offset in this register.
CMOS_BANK1_DATA_PORTRead/write data from/to the address specified in CMOS_BANK1_INDEX_PORT.
Driver Initialization
 init()的責(zé)任:(1)請(qǐng)求分配設(shè)備主設(shè)備號(hào);(2)為每個(gè)設(shè)備結(jié)構(gòu)分配內(nèi)存;(3)鏈接字符設(shè)備驅(qū)動(dòng)cdev的入口點(diǎn)(open()、read()等);(4)聯(lián)系主設(shè)備號(hào)和驅(qū)動(dòng)的cdev;(5)在/dev和/sys下創(chuàng)建節(jié)點(diǎn);(6)初始化硬件。以下是示例程序:
Listing 5.1. CMOS Driver Initialization

#include <linux/fs.h>

/* Per-device (per-bank) structure */
struct cmos_dev {
  unsigned short current_pointer; /* Current pointer within the
                                     bank */
  unsigned int size;              /* Size of the bank */
  int bank_number;                /* CMOS bank number */
  struct cdev cdev;               /* The cdev structure */
  char name[10];                  /* Name of I/O region */
  /* ... */                       /* Mutexes, spinlocks, wait
                                     queues, .. */
} *cmos_devp;

/* File operations structure. Defined in linux/fs.h */
static struct file_operations cmos_fops = {
  .owner    =   THIS_MODULE,      /* Owner */
  .open     =   cmos_open,        /* Open method */
  .release  =   cmos_release,     /* Release method */
  .read     =   cmos_read,        /* Read method */
  .write    =   cmos_write,       /* Write method */
  .llseek   =   cmos_llseek,      /* Seek method */
  .ioctl    =   cmos_ioctl,       /* Ioctl method */
};

static dev_t cmos_dev_number;   /* Allotted device number */
struct class *cmos_class;       /* Tie with the device model */

#define NUM_CMOS_BANKS          2
#define CMOS_BANK_SIZE          (0xFF*8)
#define DEVICE_NAME             "cmos"
#define CMOS_BANK0_INDEX_PORT   0x70
#define CMOS_BANK0_DATA_PORT    0x71
#define CMOS_BANK1_INDEX_PORT   0x72
#define CMOS_BANK1_DATA_PORT    0x73

unsigned char addrports[NUM_CMOS_BANKS] = {CMOS_BANK0_INDEX_PORT,
                                           CMOS_BANK1_INDEX_PORT,};

unsigned char dataports[NUM_CMOS_BANKS] = {CMOS_BANK0_DATA_PORT,
                                           CMOS_BANK1_DATA_PORT,};

/*
 * Driver Initialization
 */
int __init
cmos_init(void)
{
  int i;

  /* Request dynamic allocation of a device major number */
  if (alloc_chrdev_region(&cmos_dev_number, 0,
                          NUM_CMOS_BANKS, DEVICE_NAME) < 0) {
    printk(KERN_DEBUG "Can't register device\n"); return -1;
  }

  /* Populate sysfs entries */
  cmos_class = class_create(THIS_MODULE, DEVICE_NAME);

  for (i=0; i<NUM_CMOS_BANKS; i++) {
    /* Allocate memory for the per-device structure */
    cmos_devp = kmalloc(sizeof(struct cmos_dev), GFP_KERNEL);
    if (!cmos_devp) {
      printk("Bad Kmalloc\n"); return 1;
    }

    /* Request I/O region */
    sprintf(cmos_devp->name, "cmos%d", i);
    if (!(request_region(addrports[i], 2, cmos_devp->name)) {
      printk("cmos: I/O port 0x%x is not free.\n", addrports[i]);
      return –EIO;
    }
    /* Fill in the bank number to correlate this device
       with the corresponding CMOS bank */
    cmos_devp->bank_number = i;

    /* Connect the file operations with the cdev */
    cdev_init(&cmos_devp->cdev, &cmos_fops);
    cmos_devp->cdev.owner = THIS_MODULE;

    /* Connect the major/minor number to the cdev */
    if (cdev_add(&cmos_devp->cdev, (dev_number + i), 1)) {
      printk("Bad cdev\n");
      return 1;
    }

    /* Send uevents to udev, so it'll create /dev nodes */
    class_device_create(cmos_class, NULL, (dev_number + i),
                        NULL, "cmos%d", i);
  }

  printk("CMOS Driver Initialized.\n");
  return 0;
}

/* Driver Exit */
void __exit
cmos_cleanup(void)
{
  int i;

  /* Remove the cdev */
  cdev_del(&cmos_devp->cdev);

  /* Release the major number */
  unregister_chrdev_region(MAJOR(dev_number), NUM_CMOS_BANKS);

  /* Release I/O region */
  for (i=0; i<NUM_CMOS_BANKS; i++) {
    class_device_destroy(cmos_class, MKDEV(MAJOR(dev_number), i));
    release_region(addrports[i], 2);
  }
  /* Destroy cmos_class */
  class_destroy(cmos_class);
  return();
}

module_init(cmos_init);
module_exit(cmos_cleanup);

cmos_init()使用alloc_chrdev_region()來(lái)動(dòng)態(tài)請(qǐng)求未使用的主設(shè)備號(hào),dev_number包含了得到的主設(shè)備號(hào),第二個(gè)和第三個(gè)參數(shù)用于指定起始次設(shè)備號(hào)和支持的次設(shè)備數(shù)目,最后一個(gè)參數(shù)是設(shè)備名,可用于在/proc/devices中指定CMOS:
bash> cat /proc/devices | grep cmos
253 cmos
在2.6以前,無(wú)法動(dòng)態(tài)分配主設(shè)備號(hào),只有通過(guò)register_chrdev()來(lái)注冊(cè)指定的主設(shè)備號(hào)。
cdev_init()把文件操作cmos_fopscdev聯(lián)系起來(lái),cdev_add()alloc_chrdev_region()分配的主/次設(shè)備號(hào)給cdev。
class_create()為該設(shè)備產(chǎn)生一個(gè)sysfs入口,class_device_create()則導(dǎo)致產(chǎn)生兩個(gè)uevent:cmos0和cmos1,udevd會(huì)偵聽uevent并在查詢/etc/udev/rules.d里的規(guī)則庫(kù)后產(chǎn)生/dev/cmos/0和/dev/cmos/1對(duì)應(yīng)的設(shè)備節(jié)點(diǎn)。要在/etc/udev/rules.d/里面加入:
KERNEL="cmos[0-1]*", NAME="cmos/%n"
設(shè)備驅(qū)動(dòng)需要操作一個(gè)I/O地址范圍,因此使用request_region()來(lái)請(qǐng)求該地址,該技術(shù)會(huì)使其他請(qǐng)求該I/O地址空間的請(qǐng)求者失敗,直到擁有者釋放空間release_region().request_region()通常由總線申請(qǐng),如PCI、ISA。request_region()的最后一個(gè)參數(shù)是/proc/ioports使用的標(biāo)記,因此:
bash>  grep cmos /proc/ioports
0070-0071  :  cmos0
0072-0073  :  cmos1
Open and Release
當(dāng)應(yīng)用程序打開節(jié)點(diǎn)時(shí),就會(huì)激發(fā)驅(qū)動(dòng)程序的open()。您可以通過(guò)bash> cat /dev/cmos/0來(lái)激發(fā)cmos_open()的執(zhí)行,當(dāng)cat關(guān)閉/dev/cmos/0的文件描述符時(shí),會(huì)導(dǎo)致cmos_release()的執(zhí)行。
Listing 5.2. Open and Release

/*
 * Open CMOS bank
 */
int
cmos_open(struct inode *inode, struct file *file)
{
  struct cmos_dev *cmos_devp;

  /* Get the per-device structure that contains this cdev */
  cmos_devp = container_of(inode->i_cdev, struct cmos_dev, cdev);

  /* Easy access to cmos_devp from rest of the entry points */
  file->private_data = cmos_devp;

  /* Initialize some fields */
  cmos_devp->size = CMOS_BANK_SIZE;
  cmos_devp->current_pointer = 0;

  return 0;
}

/*
 * Release CMOS bank
 */
int
cmos_release(struct inode *inode, struct file *file)
{
  struct cmos_dev *cmos_devp = file->private_data;

  /* Reset file pointer */
  cmos_devp->current_pointer = 0;

  return 0;
}

Exchanging Data

read()和write()是用戶空間和設(shè)備交換的基本函數(shù),其他的還有fsync(),aio_read(),aio_write()和mmap()。

COMS是一個(gè)簡(jiǎn)單的memory設(shè)備,因此CMOS數(shù)據(jù)訪問(wèn)程序不必是sleep-wait 設(shè)備IO完成,但其他的一些字符設(shè)備都要支持blocking和nonblocking操作。除非設(shè)備文件以nonblocking(O_NONBLOCK)方式打開,否則read()和write()允許calling進(jìn)程睡眠直到相應(yīng)操作完成;(2)CMOS驅(qū)動(dòng)以完全同步方式而不是依賴于中斷。
內(nèi)核空間和用戶空間的交互使用函數(shù)copy_to_user()和copy_from_user(),由于這兩個(gè)函數(shù)會(huì)陷入睡眠,所以調(diào)用時(shí)不能持有spinlock鎖。讀取或?qū)懭氩煌笮〉臄?shù)據(jù)使用一些架構(gòu)無(wú)關(guān)的函數(shù)in[b|w|l|sb|sl]()和out[b|w|l|sb|sl]().
Listing 5.3. Read and Write

/*
 * Read from a CMOS Bank at bit-level granularity
 */
ssize_t
cmos_read(struct file *file, char *buf,
          size_t count, loff_t *ppos)
{
  struct cmos_dev *cmos_devp = file->private_data;
  char data[CMOS_BANK_SIZE];
  unsigned char mask;
  int xferred = 0, i = 0, l, zero_out;
  int start_byte = cmos_devp->current_pointer/8;
  int start_bit  = cmos_devp->current_pointer%8;

  if (cmos_devp->current_pointer >= cmos_devp->size) {
    return 0; /*EOF*/
  }

  /* Adjust count if it edges past the end of the CMOS bank */
  if (cmos_devp->current_pointer + count > cmos_devp->size) {
    count = cmos_devp->size - cmos_devp->current_pointer;
  }

  /* Get the specified number of bits from the CMOS */
  while (xferred < count) {
    data[i] = port_data_in(start_byte, cmos_devp->bank_number)
              >> start_bit;
    xferred += (8 - start_bit);
    if ((start_bit) && (count + start_bit > 8)) {
      data[i] |= (port_data_in (start_byte + 1,
                  cmos_devp->bank_number) << (8 - start_bit));
      xferred += start_bit;
    }
    start_byte++;
    i++;
  }
  if (xferred > count) {
    /* Zero out (xferred-count) bits from the MSB
       of the last data byte */
    zero_out = xferred - count;
    mask = 1 << (8 - zero_out);
    for (l=0; l < zero_out; l++) {
      data[i-1] &= ~mask; mask <<= 1;
    }
    xferred = count;
  }

  if (!xferred) return -EIO;

  /* Copy the read bits to the user buffer */
  if (copy_to_user(buf, (void *)data, ((xferred/8)+1)) != 0) {
    return -EIO;
  }

  /* Increment the file pointer by the number of xferred bits */
  cmos_devp->current_pointer += xferred;
  return xferred; /* Number of bits read */
}

/*
 * Write to a CMOS bank at bit-level granularity. 'count' holds the
 * number of bits to be written.
 */
ssize_t
cmos_write(struct file *file, const char *buf,
           size_t count, loff_t *ppos)
{
  struct cmos_dev *cmos_devp = file->private_data;
  int xferred = 0, i = 0, l, end_l, start_l;
  char *kbuf, tmp_kbuf;
  unsigned char tmp_data = 0, mask;
  int start_byte = cmos_devp->current_pointer/8;
  int start_bit  = cmos_devp->current_pointer%8;

  if (cmos_devp->current_pointer >= cmos_devp->size) {
    return 0; /* EOF */
  }
  /* Adjust count if it edges past the end of the CMOS bank */
  if (cmos_devp->current_pointer + count > cmos_devp->size) {
    count = cmos_devp->size - cmos_devp->current_pointer;
  }

  kbuf = kmalloc((count/8)+1,GFP_KERNEL);
  if (kbuf==NULL)
    return -ENOMEM;

  /* Get the bits from the user buffer */
  if (copy_from_user(kbuf,buf,(count/8)+1)) {
    kfree(kbuf);
    return -EFAULT;
  }

  /* Write the specified number of bits to the CMOS bank */
  while (xferred < count) {
    tmp_data = port_data_in(start_byte, cmos_devp->bank_number);
    mask = 1 << start_bit;
    end_l = 8;
    if ((count-xferred) < (8 - start_bit)) {
      end_l = (count - xferred) + start_bit;
    }

    for (l = start_bit; l < end_l; l++) {
      tmp_data &= ~mask; mask <<= 1;
    }
    tmp_kbuf = kbuf[i];
    mask = 1 << end_l;
    for (l = end_l; l < 8; l++) {
      tmp_kbuf &= ~mask;
      mask <<= 1;
    }

    port_data_out(start_byte,
                  tmp_data |(tmp_kbuf << start_bit),
                  cmos_devp->bank_number);
    xferred += (end_l - start_bit);

    if ((xferred < count) && (start_bit) &&
        (count + start_bit > 8)) {
      tmp_data = port_data_in(start_byte+1,
                              cmos_devp->bank_number);
      start_l = ((start_bit + count) % 8);
      mask = 1 << start_l;
      for (l=0; l < start_l; l++) {
        mask >>= 1;
        tmp_data &= ~mask;
      }
      port_data_out((start_byte+1),
                    tmp_data |(kbuf[i] >> (8 - start_bit)),
                    cmos_devp->bank_number);
      xferred += start_l;
    }

    start_byte++;
    i++;
  }

  if (!xferred) return -EIO;

  /* Push the offset pointer forward */
  cmos_devp->current_pointer += xferred;
  return xferred; /* Return the number of written bits */
}

/*
 * Read data from specified CMOS bank
 */
unsigned char
port_data_in(unsigned char offset, int bank)
{
  unsigned char data;

  if (unlikely(bank >= NUM_CMOS_BANKS)) {
    printk("Unknown CMOS Bank\n");
    return 0;
  } else {
    outb(offset, addrports[bank]); /* Read a byte */
    data = inb(dataports[bank]);
  }
  return data;

}
/*
 * Write data to specified CMOS bank
 */
void
port_data_out(unsigned char offset, unsigned char data,
                int bank)
{
  if (unlikely(bank >= NUM_CMOS_BANKS)) {
    printk("Unknown CMOS Bank\n");
    return;
  } else {
    outb(offset, addrports[bank]); /* Output a byte */
    outb(data, dataports[bank]);
  }
  return;
}

當(dāng)write()成功返回時(shí),說(shuō)明應(yīng)用程序已經(jīng)將數(shù)據(jù)成功傳給了驅(qū)動(dòng),但并不保證數(shù)據(jù)寫到了設(shè)備里面,如果需要這個(gè)保證,那么需要調(diào)用fsync()。當(dāng)應(yīng)用程序的數(shù)據(jù)來(lái)源自不同的分散的buffers時(shí),需要使用readv和writev(),從2.6.19以后這兩個(gè)函數(shù)被folded into Linux AIO層里面了,the vector driver相關(guān)的原型包括:

ssize_t aio_read(struct kiocb *iocb, const struct iovec *vector,
                 unsigned long count, loff_t offset);
ssize_t aio_write(struct kiocb *iocb, const struct iovec *vector,
                  unsigned long count, loff_t offset);
第一個(gè)參數(shù)描述了AIO操作,第二個(gè)參數(shù)則是iovecs的數(shù)組,記錄了buffers的地址和長(zhǎng)度,可查看iovecs的定義和/drivers/net/tun.c下對(duì)vectors drivers的實(shí)現(xiàn)。另外一個(gè)數(shù)據(jù)訪問(wèn)方法是mmap(),它將設(shè)備內(nèi)存和用戶虛擬內(nèi)存聯(lián)系起來(lái)。應(yīng)用可以調(diào)用同名系統(tǒng)調(diào)用mmap(),并在返回的內(nèi)存區(qū)域上直接操作設(shè)備駐留內(nèi)存。可查看/drivers/char/mem.c查看實(shí)例。
Seek
內(nèi)核使用內(nèi)部指針來(lái)跟蹤文件內(nèi)訪問(wèn)位置,應(yīng)用程序調(diào)用lseek(),進(jìn)而調(diào)用相應(yīng)的驅(qū)動(dòng)llseek()。這里的CMOS驅(qū)動(dòng)是按bit移動(dòng)的。先看一下lseek()使用的幾個(gè)參數(shù):(1)SEEK_SET,從文件頭計(jì)算;(2)SEEK_CUR,從當(dāng)前位置計(jì)算;(3)SEEK_END,從文件尾部計(jì)算。
Listing 5.4. Seek

/*
 * Seek to a bit offset within a CMOS bank
 */
static loff_t
cmos_llseek(struct file *file, loff_t offset,
            int orig)
{
  struct cmos_dev *cmos_devp = file->private_data;

  switch (orig) {
    case 0: /* SEEK_SET */
      if (offset >= cmos_devp->size) {
        return -EINVAL;
      }
      cmos_devp->current_pointer = offset; /* Bit Offset */
      break;

    case 1: /* SEEK_CURR */
      if ((cmos_devp->current_pointer + offset) >=
           cmos_devp->size) {
        return -EINVAL;
      }
      cmos_devp->current_pointer = offset; /* Bit Offset */
      break;

    case 2: /* SEEK_END - Not supported */
      return -EINVAL;

    default:
      return -EINVAL;
  }

  return(cmos_devp->current_pointer);
}

 Control
ioctl用于接收用戶空間命令并請(qǐng)求特殊操作,CMOS memory由cyclic redundancy check(CRC)算法保護(hù)。為檢測(cè)數(shù)據(jù)是否崩潰,CMOS驅(qū)動(dòng)支持兩個(gè)ioctl命令:(1)Adjust checksum,用于當(dāng)CMOS內(nèi)容改變后重新計(jì)算CRC。計(jì)算出的checksum放在實(shí)現(xiàn)定義好的CMOS bank1的某處。(2)Verify checksum,用于檢查CMOS的內(nèi)容是否healthy,通過(guò)比較當(dāng)前內(nèi)容的CRC和預(yù)存的CRC來(lái)判斷。應(yīng)用程序想進(jìn)行校驗(yàn)操作時(shí),就將這些命令通過(guò)ioctl()系統(tǒng)調(diào)用發(fā)給驅(qū)動(dòng)。
Listing 5.5. I/O Control

#define CMOS_ADJUST_CHECKSUM 1
#define CMOS_VERIFY_CHECKSUM 2

#define CMOS_BANK1_CRC_OFFSET 0x1E

/*
 * Ioctls to adjust and verify CRC16s.
 */
static int
cmos_ioctl(struct inode *inode, struct file *file,
           unsigned int cmd, unsigned long arg)
{
  unsigned short crc = 0;
  unsigned char buf;

  switch (cmd) {
    case CMOS_ADJUST_CHECKSUM:
      /* Calculate the CRC of bank0 using a seed of 0 */
      crc = adjust_cmos_crc(0, 0);

      /* Seed bank1 with CRC of bank0 */
      crc = adjust_cmos_crc(1, crc);

      /* Store calculated CRC */
      port_data_out(CMOS_BANK1_CRC_OFFSET,
                    (unsigned char)(crc & 0xFF), 1);
      port_data_out((CMOS_BANK1_CRC_OFFSET + 1),
                    (unsigned char) (crc >> 8), 1);
      break;

    case CMOS_VERIFY_CHECKSUM:
     /* Calculate the CRC of bank0 using a seed of 0 */
      crc = adjust_cmos_crc(0, 0);

     /* Seed bank1 with CRC of bank0 */
     crc = adjust_cmos_crc(1, crc);

     /* Compare the calculated CRC with the stored CRC */
     buf = port_data_in(CMOS_BANK1_CRC_OFFSET, 1);
     if (buf != (unsigned char) (crc & 0xFF)) return -EINVAL;

     buf = port_data_in((CMOS_BANK1_CRC_OFFSET+1), 1);
     if (buf != (unsigned char)(crc >> 8)) return -EINVAL;
     break;
     default:
       return -EIO;
  }

  return 0;
}

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多