2016年12月8日木曜日

FON2405E カスタムファーム用のドライバ開発

前回、準備としてなんちゃってドライバを作成して、insmodするところまでいけた。
次は自力シリアル通信ドライバを作成(ラズベリー・パイ用に作った奴の移植)をしてみる。



1.概要
ドライバ名はMy2nd(まだ名称が決まってないOrz)コードは末尾に記載。
Makefileは前回からドライバ名のみ変更した。


2.導入
nfsマウント
mount -o nolock 10.10.10.3:/var/lib/tftpboot /mnt
insmodしてみる

メッセージのメジャー番号が253 マイナー番号が0が重要で、コレがないと/dev/●●が作成できない。
カーネルのバージョンがアレなので、手動でスペシャルファイル?を作成する必要がある。
mknod /dev/My2nd0 c 253 0
とするとMy2nd0というデバイスファイルが作成できる。

3.テスト
 作成したMy2nd0に対してechoで文字列を送ってみる。
 echo -n HelloWorld!! > /dev/My2nd0

するとPIC側のコンソールに表示される。

やったーうまくいった!

4.コード
こんな感じ


#ifndef __MY2ND_KM_H__
#define __MY2ND_KM_H__
#include <asm/rt2880/rt_mmap.h>
/*
* Address of RALINK_ Registers
*/
#define RALINK_PRGIO_ADDR RALINK_PIO_BASE // Programmable I/O
#define RALINK_REG_PIODATA (RALINK_PRGIO_ADDR + 0x20)
#define RALINK_REG_PIODIR (RALINK_PRGIO_ADDR + 0x24)
#define RALINK_REG_PIOSET (RALINK_PRGIO_ADDR + 0x2C)
#define RALINK_REG_PIORESET (RALINK_PRGIO_ADDR + 0x30)
#define RALINK_GPIO(x) (1 << x)
#endif
view raw my2nd.h hosted with ❤ by GitHub
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/timer.h>
#include "my2nd.h"
MODULE_AUTHOR("Adeno");
MODULE_LICENSE("Dual BSD/GPL");
#define MYSFR9_VERSION "0.4"
#define MYSFR9_NUM_DEVS 1 /* このドライバが制御するデバイスの数 */
#define MYSFR9_DEVNAME "My2nd" /* このデバイスドライバの名称 */
#define MYSFR9_MAJOR 0 /* メジャー番号だが自動設定なので0 */
#define MYSFR9_MINOR 0 /* マイナー番号のベース番号 */
#define GPIONO_CLKOUT 11 //LAN-LED output
#define GPIONO_DATAOUT 12 //NET-LED output rev
#define GPIONO_DATAIN 14 //WPS-LED input
#define UDELAY 50 //us
#define CHARDELAY 592 //us
#define RCVPOLL 10000 //us
#define DEFAULT_RCV_QUEUE_SIZE 8 //SFR ->
#define DEFAULT_SND_QUEUE_SIZE 8 // -> SFR
static int _mysfr9_major = MYSFR9_MAJOR;
static int _mysfr9_minor = MYSFR9_MINOR;
static struct cdev *mysfr9_cdev_array = NULL;
static struct class *mysfr9_class = NULL;
static struct timer_list poll_timer;
//RCV関係 SFR ->
static int rcv_queue_size = DEFAULT_RCV_QUEUE_SIZE;
static unsigned char ircv,rcv;
static unsigned char *rcv_queue = NULL;
static int rq_head, rq_tail;
//SND関係 -> SFR
static int snd_queue_size = DEFAULT_SND_QUEUE_SIZE;
static unsigned char isnd,snd;
static unsigned char *snd_queue = NULL;
static int sq_head, sq_tail;
/*プロトタイプ宣言*/
//GPIO関係
static void mysfr9_gpio_setup(void);
static int mysfr9_gpio_getpin(int pin);
static void mysfr9_gpio_setpin(int pin,uint32_t val);
static void gpio_put_msb(unsigned char isend ,unsigned char send, unsigned char *ircv,unsigned char *rcv);
//カーネルへの登録関係
static int mysfr9_register_dev(void);
//デバイスに対する処理
static int mysfr9_open(struct inode *inode, struct file *filep);
static int mysfr9_release(struct inode *inode, struct file *filep);
static ssize_t mysfr9_write(struct file *filep,const char __user *buf, size_t count, loff_t *f_pos);
static ssize_t mysfr9_read(struct file *filep,char __user *buf,size_t count, loff_t *f_pos);
//File Operations構造体
struct file_operations mysfr9_fops = {
.open = mysfr9_open,
.release = mysfr9_release,
.write = mysfr9_write,
.read = mysfr9_read,
};
//タイマー関係
static void register_poll_timer(void);
static void poll_timer_handler(unsigned long data);
//QUEUE関係
static int ArrayEnqueue(unsigned char *queue, int data, int *head, int *tail, size_t n);
static unsigned char ArrayDequeue(unsigned char *queue, int *head, int *tail, size_t n);
//Sleep
static DECLARE_WAIT_QUEUE_HEAD(wq);
static int wq_flg = 0;
///////////////////////////////////////////////////////////////////////////////////////////////////
//デバイスドライバがロードされた時の処理
static int mysfr9_init(void)
{
int retval,i;
/* 開始のメッセージ */
printk(KERN_INFO "%s Ver.%s loading...\n", MYSFR9_DEVNAME,MYSFR9_VERSION );
/* queue */
sq_head=0, sq_tail=0;
rq_head=0, rq_tail=0;
if(!(rcv_queue_size > 0 && rcv_queue_size < 65536)){
rcv_queue_size = DEFAULT_RCV_QUEUE_SIZE;
}
rcv_queue = kmalloc(rcv_queue_size, GFP_KERNEL);
if (!rcv_queue) return -ENOMEM;
if(!(snd_queue_size > 0 && snd_queue_size < 65536)){
snd_queue_size = DEFAULT_SND_QUEUE_SIZE;
}
snd_queue = kmalloc(rcv_queue_size, GFP_KERNEL);
if (!snd_queue) return -ENOMEM;
/* デバイスドライバをカーネルに登録 */
retval = mysfr9_register_dev();
if( retval != 0 ) {
printk( KERN_ALERT "[%s] driver register failed.\n",MYSFR9_DEVNAME);
return retval;
}
printk( KERN_INFO "[%s] driver register sccessed.\n",MYSFR9_DEVNAME);
/* GPIO初期化 */
mysfr9_gpio_setup();
//for(i=0;i<10;i++){
isnd=1; snd='I';
ircv=0; rcv=0;
gpio_put_msb(isnd,snd,&ircv,&rcv);
//}
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//デバイスドライバがアンロードされた時の処理
static void mysfr9_exit(void)
{
int i;
dev_t devno;
del_timer_sync(&poll_timer);
/* キャラクタデバイスの登録解除 */
for( i = 0; i < MYSFR9_NUM_DEVS; i++ ) {
cdev_del(&(mysfr9_cdev_array[i]));
devno = MKDEV(_mysfr9_major, _mysfr9_minor+i);
device_destroy(mysfr9_class, devno);
}
/* メジャー番号/マイナー番号を取り除く */
devno = MKDEV(_mysfr9_major,_mysfr9_minor);
unregister_chrdev_region(devno, MYSFR9_NUM_DEVS);
/* デバイスノードを取り除く */
class_destroy(mysfr9_class);
kfree(mysfr9_cdev_array);
/* queue */
kfree(rcv_queue);
kfree(snd_queue);
printk( KERN_INFO "[%s] driver unregister sccessed.\n",MYSFR9_DEVNAME);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//デバイスに対する処理
static int mysfr9_open(struct inode *inode, struct file *filep)
{
printk(KERN_INFO "[%s] Device open sccessed.\n",MYSFR9_DEVNAME);
printk( KERN_INFO "[%s] FON HZ = %d\n",MYSFR9_DEVNAME,HZ);
register_poll_timer();
return 0;
}
static int mysfr9_release(struct inode *inode, struct file *filep)
{
//まだSFRに送信できていないないものがあるので、待ってみる
while ( sq_tail - sq_head ) {
//udelay(UDELAY);
msleep(1);
}
del_timer_sync(&poll_timer);
printk(KERN_INFO "[%s] Device release sccessed.\n",MYSFR9_DEVNAME);
return 0;
}
static ssize_t mysfr9_write(struct file *filep,const char __user *buf, size_t count, loff_t *f_pos)
{
unsigned char c;
if(count > 0) {
if(copy_from_user( &c, buf, sizeof(char) )) {
return -EFAULT;
}
while(ArrayEnqueue(snd_queue, c, &sq_head, &sq_tail, snd_queue_size) == 0){
//udelay(UDELAY);
msleep(1);
}
return sizeof(char);
}
return 0;
}
static ssize_t mysfr9_read(struct file *filep,char __user *buf,size_t count, loff_t *f_pos){
unsigned char c;
//printk( KERN_INFO "RCV READ cont=%d, RCVQ=%d\n",count,(rq_tail - rq_head));
if(count > 0) {
if((rq_tail - rq_head) == 0){
printk( KERN_INFO "[%s] <read> Process %i (%s) going to sleep.\n",
MYSFR9_DEVNAME,current->pid,current->comm);
wait_event_interruptible(wq,wq_flg != 0);
wq_flg = 0;
printk( KERN_INFO "[%s] <read> Awoken Process %i (%s).\n",
MYSFR9_DEVNAME,current->pid,current->comm);
}
c = ArrayDequeue(rcv_queue, &rq_head, &rq_tail, rcv_queue_size);
if(copy_to_user( buf,&c, sizeof(char) )) {
return -EFAULT;
}
return sizeof(char);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//カーネルへの登録
static int mysfr9_register_dev(void)
{
int retval;
dev_t dev;
size_t size;
int i;
/* 空いているメジャー番号を使ってメジャー&
マイナー番号をカーネルに登録する */
retval = alloc_chrdev_region(
&dev, /* 結果を格納するdev_t構造体 */
MYSFR9_MAJOR, /* ベースマイナー番号 */
MYSFR9_NUM_DEVS, /* デバイスの数 */
MYSFR9_DEVNAME /* デバイスドライバの名前 */
);
if( retval < 0 ) {
printk(KERN_ERR "[%s] alloc_chrdev_region failed.\n",MYSFR9_DEVNAME);
return retval;
}
_mysfr9_major = MAJOR(dev);
//printk(KERN_INFO "[%s] alloc_chrdev_region sccessed.\n",MYSFR9_DEVNAME);
/* デバイスクラスを作成する */
mysfr9_class = class_create(THIS_MODULE,MYSFR9_DEVNAME);
if(IS_ERR(mysfr9_class))
return PTR_ERR(mysfr9_class);
/* cdev構造体の用意 */
size = sizeof(struct cdev) * MYSFR9_NUM_DEVS;
mysfr9_cdev_array = (struct cdev*)kmalloc(size, GFP_KERNEL);
/* デバイスの数だけキャラクタデバイスを登録する */
/* ただしデバイスは1個しかない */
for( i = 0; i < MYSFR9_NUM_DEVS; i++ ) {
dev_t devno = MKDEV(_mysfr9_major, _mysfr9_minor+i);
/* キャラクタデバイスとしてこのモジュールをカーネルに登録する */
cdev_init(&(mysfr9_cdev_array[i]), &mysfr9_fops);
mysfr9_cdev_array[i].owner = THIS_MODULE;
if( cdev_add( &(mysfr9_cdev_array[i]), devno, 1) < 0 ) {
/* 登録に失敗した */
printk(KERN_ERR "[%s] cdev_add failed minor = %d\n",MYSFR9_DEVNAME, _mysfr9_minor+i );
}
else {
printk(KERN_INFO "[%s] cdev_add major = %d minor = %d.\n",MYSFR9_DEVNAME,_mysfr9_major,_mysfr9_minor+i);
/* デバイスノードの作成 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
device_create(
mysfr9_class,
NULL,
devno,
NULL,
MYSFR9_DEVNAME"%u",_mysfr9_minor+i
);
#else
device_create(
mysfr9_class,
NULL,
devno,
MYSFR9_DEVNAME"%u",_mysfr9_minor+i
);
#endif
}
}
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
//GPIO関係
static void mysfr9_gpio_setup(void)
{
//入出力の設定
//RALINK_GPIO_SET_DIR: Bit 1 means output
//GPIONO_CLKOUT
//GPIONO_DATAOUT
//GPIONO_DATAIN
unsigned long tmp;
tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODIR));
tmp |= cpu_to_le32(RALINK_GPIO(GPIONO_CLKOUT));
tmp |= cpu_to_le32(RALINK_GPIO(GPIONO_DATAOUT));
tmp &= ~cpu_to_le32(RALINK_GPIO(GPIONO_DATAIN));
*(volatile u32 *)(RALINK_REG_PIODIR) = tmp;
printk(KERN_INFO "[%s] Setup GPIO ...%ld\n", MYSFR9_DEVNAME,tmp);
//CLKとDATAOUT
mysfr9_gpio_setpin((volatile u32)GPIONO_CLKOUT,1);
udelay(1); //debug用 200ns程度しか無いから
mysfr9_gpio_setpin((volatile u32)GPIONO_DATAOUT,0);
}
static int mysfr9_gpio_getpin(int pin){
volatile unsigned long arg,tmp;
arg = RALINK_GPIO(pin);
tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODATA));
tmp &= RALINK_GPIO(pin);
return (tmp > 0 ? 1 : 0);
}
static void mysfr9_gpio_setpin(int pin,uint32_t val){
unsigned long arg;
arg = RALINK_GPIO(pin);
if(val > 0)
*(volatile u32 *)(RALINK_REG_PIOSET) = cpu_to_le32(arg);
else
*(volatile u32 *)(RALINK_REG_PIORESET) = cpu_to_le32(arg);
}
static void gpio_put_msb(unsigned char isend ,unsigned char send, unsigned char *ircv,unsigned char *rcv)
{
volatile int i=0;
volatile unsigned char _rcv = 0;
volatile unsigned char _send = send;
unsigned int _ircv = 0;
unsigned int rcvbit=0;
//D0 データ有無ビット
//--DATA
mysfr9_gpio_setpin((volatile u32)GPIONO_DATAOUT,isend);
udelay(1); //debug用 200ns程度しか無いから
//--CLK=H
mysfr9_gpio_setpin((volatile u32)GPIONO_CLKOUT,0);
udelay(UDELAY);
//--データ有無ビット入力
_ircv = mysfr9_gpio_getpin((volatile u32)GPIONO_DATAIN);
//--CLK=L
mysfr9_gpio_setpin((volatile u32)GPIONO_CLKOUT,1);
udelay(UDELAY);
//printf("データ有無\t送信:%d(%c)\t受信:%d(%c)\n",isend,((isend>0) ? 'X':'-'),_ircv,((_ircv>0) ? 'X':'-'));
for(i=0;i<8;i++){
//D1-9 データビット
//--データ出力
//printf("i=%d,v=%#x,v2=%d\n",i,v,((v & 0x80) == 0x80 ? 1 : 0));
//printf("%d",((v & 0x80) == 0x80 ? 1 : 0));
if((_send & 0x80) == 0x80){
mysfr9_gpio_setpin((volatile u32)GPIONO_DATAOUT,1);
}else{
mysfr9_gpio_setpin((volatile u32)GPIONO_DATAOUT,0);
}
udelay(1); //debug用 200ns程度しか無いから
//--CLK=H
mysfr9_gpio_setpin((volatile u32)GPIONO_CLKOUT,0);
udelay(UDELAY);
//--データ入力
_rcv = _rcv << 1 ;
rcvbit = mysfr9_gpio_getpin((volatile u32)GPIONO_DATAIN);
if(rcvbit > 0){
_rcv = _rcv | 0x01;
}
_send=_send<<1;
//--CLK=L
mysfr9_gpio_setpin((volatile u32)GPIONO_CLKOUT,1);
udelay(UDELAY);
}
*ircv = _ircv;
*rcv = _rcv;
//printf("----------------------------------------------------------------------------------------------------\n");
/*printk(KERN_INFO "### Send:%c = %c(%#x) Rcv:%c = %c(%#x) \n",
((isend>0) ? 'O':'X'),((isend>0) ? send:' '),((isend>0) ? send:0x00),
((_ircv>0) ? 'O':'X'),((_ircv>0) ? _rcv:' '),((_ircv>0) ? _rcv:0x00));*/
udelay(CHARDELAY);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//タイマー処理関係
static void register_poll_timer(void)
{
init_timer(&poll_timer);
//poll_timer.expires = jiffies + HZ;
poll_timer.expires = jiffies + (HZ * RCVPOLL) / 1000000;
/* 最短のTickより短い場合は最短のTickに */
if( poll_timer.expires == 0 ) poll_timer.expires = jiffies + 1;
poll_timer.data = jiffies;
poll_timer.function = poll_timer_handler;
add_timer(&poll_timer);
}
static void poll_timer_handler(unsigned long data)
{
do{
if(sq_tail - sq_head){
isnd = 1;
snd = ArrayDequeue(snd_queue, &sq_head, &sq_tail, snd_queue_size);
}else{
isnd = 0;
}
gpio_put_msb(isnd,snd,&ircv,&rcv);
if(ircv > 0){
//printk( KERN_INFO "RCV %c \n",rcv);
if(ArrayEnqueue(rcv_queue, rcv, &rq_head, &rq_tail, rcv_queue_size) > 0){
wq_flg = 1;
wake_up_interruptible(&wq);
}else{
printk( KERN_ALERT "[%s] RCV Buffer Full.\n",MYSFR9_DEVNAME);
}
}
}while ((sq_tail - sq_head) || (ircv > 0));
register_poll_timer();
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//Queue
static int ArrayEnqueue(unsigned char *queue, int data, int *head, int *tail, size_t n) {
if (*head % n != (*tail + 1) % n) {
queue[(*tail)++ % n] = data;
return *tail - *head;
} else {
return 0;
}
}
static unsigned char ArrayDequeue(unsigned char *queue, int *head, int *tail, size_t n) {
if (*head != *tail) {
return queue[(*head)++ % n];
} else {
return (unsigned char)0;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//initの登録
module_init(mysfr9_init);
//exitの登録
module_exit(mysfr9_exit);
view raw my2nd.c hosted with ❤ by GitHub

0 件のコメント:

コメントを投稿