這個是我最近自己寫的一個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下去寫,其實也原因就是不夠熟析,若是再過一陣子我應該就可以達到比較好的寫法了,所以這是一種參考並非完美解答。
留言列表