這個是我最近自己寫的一個driver,其實是我有寫兩種方式,或許你會好奇跟上面那篇文章,使用的方式為什麼差那麼多,因為你對應的handware不能說只有一種方式撰寫,但常用的寫法是哪些,為什麼大家面對那個device都這樣寫,自己要注意跟去比較,然後去挑你最適合的,雖然code可能長相會差很多部過流程都是差不多才對,你要先知道你撰寫方式的struct,對他填入正確呼叫的方式,像是我下列code的東西他的struct file_operations,在來你要了解他的方式為何,file_operations是定義驅動程式的system call與實作system call的struct,可參考Fs.h (include\linux)  :

struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
    int (*readdir) (struct file *, void *, filldir_t);
    unsigned int (*poll) (struct file *, struct poll_table_struct *);
    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, struct dentry *, int datasync);
    int (*fasync) (int, struct file *, int);
    int (*lock) (struct file *, int, struct file_lock *);
    ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
    ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
};

你會發現struct其實有很多可以加入function的地方,我也只是挑我要的部分來填入function,使我的ap可以call linux driver,i2c我是用gpio下去模擬出來,並非用reg 專用的介面(若是專用介面有特定的reg可以用另外一種寫法),換句話說就是control i/o up or down,那在這裡我的需求自然是 read/write data,ioctl 代表 input/output control 的意思,所以你可以看到我的code,在那部份只簡單填入一些value,主要來判斷是否還在忙碌,或是選擇i2c channel,若是ack error 在用reset之類的東西或再傳回ap通知,在撰寫過程盡量follow 原本的架構下去增加自己要的,重點是"open","release",why?because 大部分的程式碼在會call open, 你才有辦法在ap開始使用,主要的第一個動作就是MOD_INC_USE_COUNT 這個動作會讓模塊計數加一,防止模塊異常刪除,而在release 用 MOD_DEC_USE_COUNT就相對應的模組計數減一,只要有這兩個其實就是一個最最最簡單的driver module。

下列的code並沒有po完整因為我寫很了很多奇怪的東西...............所以po個大概可以參考.............

ssize_t gpio_i2c_read(struct file *filp,char *buffer,size_t count,loff_t *f_pos)
{

  ushort read_data;
  uchar *str_buf;
  int rcount=0;
 
  OP_LOCK(STATUS_WRITE);
  read_data = i2c_read_byte(uc_channel,us_read_addr);
  OP_UNLOCK(STATUS_WAIT_EVENT);
  sprintf(str_buf,"S%4x",read_data);
 
  while ((rcount + STRING_SIZE)<= count)
 {
     if (copy_to_user(buffer, STRING_SIZE, STRING_SIZE))
         return -EFAULT;
     rcount += STRING_SIZE;
 }

  return rcount;
}

ssize_t gpio_i2c_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
    switch (cmd)
    {
        case IOCTL_RESET:
        break;
        case IOCTL_WRITE:
        break;
        case IOCTL_READ:
        break;
        case IOCTL_SELECT_CH1:
            uc_channel=CHIP_CHANNEL_0;
        break;
        case IOCTL_SELECT_CH2:
            uc_channel=CHIP_CHANNEL_1;
        break;
           case IOCTL_STATUS:
            return i2c_status;
        break;

        default:
            return -1;
    }
return 0;
}


ssize_t gpio_i2c_write(struct file *filp, const char *buff,size_t count, loff_t *offp)
{
    char *str;
    unsigned char num[4],read_addr[2];
    int i;
    ushort write_address,write_data;


    if (count == 0) return 0;

    if (copy_from_user(str, buff, count))
        return -EFAULT;
    /* atoi() */
    for (i = 0; i < count; i++)
    {
        if(str[i]=='R')
        {
              .............................
            return 1;
            
        }
        if(str[i]=='Z')
        {
           ................................................
        }
        if(str[i]=='D')
        {
            ................................................
        }
        
    }
    write_address=................;
    write_data     =  ..............];

    OP_LOCK(STATUS_WRITE);
    i2c_write_byte(uc_channel,write_address,write_data);
    OP_UNLOCK(STATUS_WAIT_EVENT);
    return 1;
};


/**************************************************/


struct file_operations i2c_fops = {
open: gpio_i2c_open,
write: gpio_i2c_write,
read: gpio_i2c_read,
release: gpio_i2c_release,
ioctl: gpio_i2c_ioctl,
};


int gpio_i2c_release(struct inode *inode, struct file *filp)
{
    MOD_DEC_USE_COUNT;
    return 0;
};


int gpio_i2c_open(struct inode *inode, struct file *filp)
{
    MOD_INC_USE_COUNT;
    return 0;
};


static  int  init_gpio(void)
{

    if (register_chrdev(DEV_MAJOR, DEV_NAME, &i2c_fops) < 0)
    {
        MSG("Couldn't register a device.");
        return -1;
       }
    return 0;
}


static void cleanup_gpio(void)
{
     if (unregister_chrdev(DEV_MAJOR, DEV_NAME))
         MSG("failed to unregister driver");
      else
          MSG("driver un-installed\n");
}

module_init(init_gpio);
module_exit(cleanup_gpio);

-----------------------------------------------------------------------------------------------------------------------------------

再來當這樣寫好driver時候,你就可以過去ap來測試是否讀寫順利

int main(int argc, char *argv[])
{
    int devfd;
    unsigned char *buffer,*index;
    int len;
    unsigned int num = 0;

    
    index="......................";
    

    devfd = open("/dev/DEV_NAME", O_RDWR);
    if (devfd == -1) {
        printf("Can't open /dev/DEV_NAME\n");
    return -1;
    }


       printf("Resetting i2c connect ...\n");

/*

 ioctl(devfd, IOCTL_WRITE, Date);
 ioctl 可以get or set data 也是driver ioctl的 arg,but it declare "unsigned long" type,
像i2c基本的index address data 他塞不下去,所以請把它當作command control用,
不能把它當作傳送data橋樑比較好。

*/

       ioctl(devfd, IOCTL_RESET, NULL);
       write(devfd, index, strlen(index));
       memset(buffer,0,sizeof(buffer));
       len = read(devfd,buffer,STRING_SIZE);                        
       if(len < 0) {                                       
           printf("Error reading from device!\n"); }                                                      
       else
           {printf("read data[%s]!\n",buffer);}
                    
    close(devfd);
    
    //init_command();
}

在這也要補充一點,就是你一定要將你build好的driver,儎入到對應的root file位置,否則你在ap 怎麼call都是call不到的。

這裡其實又要討論兩個function copy_from_user和copy_to_user 在讀寫過程中,要小心撰寫這個部份一個是對AP(user space) get data一個是set data,這個可以等到下個driver 寫法PO完一併討論,還有大家應該也有注意到我用的reg function 是register_chrdev 字元模組註冊,而不是platform_device_register,因為我只針對單一i2c po driver,並無修改到原本其他東西,所以我才會一直說要找自己要的方式下去撰寫,像我寫的方法其實不是說很嚴謹,也沒有完全follow linux driver struct下去寫,其實也原因就是不夠熟析,若是再過一陣子我應該就可以達到比較好的寫法了,所以這是一種參考並非完美解答

arrow
arrow
    全站熱搜

    funlife1 發表在 痞客邦 留言(0) 人氣()