概要
カーネルモジュール(ドライバー?)を作成して、GPIOをパタパタさせる。
8月からやっているヤツのデバドラ版。
デバイスとして扱われるので、echoやcatで入出力ができたり、既存の通信プログラムでも使えると思う。
出力されるクロックはdelayを使っているので、安定していると思う。
データのポーリングはカーネルタイマーを使っているので、
ラズベリー・パイの場合は100Hzが(10ms)が限界。
性能評価はこれから。
cat /dev/zero > /dev/MySFR9km0
とやってみると、大体350 Char/Secくらい。意外と遅い。
PIC側の設計上は960Char/Secまで行けるのはずなのだが??
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 "rpi_gpio.h" | |
MODULE_AUTHOR("Adeno"); | |
MODULE_LICENSE("Dual BSD/GPL"); | |
#define MYSFR9_NUM_DEVS 1 /* このドライバが制御するデバイスの数 */ | |
#define MYSFR9_DEVNAME "MySFR9km" /* このデバイスドライバの名称 */ | |
#define MYSFR9_MAJOR 0 /* メジャー番号だが自動設定なので0 */ | |
#define MYSFR9_MINOR 0 /* マイナー番号のベース番号 */ | |
#define MySFR9_GPIO_MAPNAME "mysfr9_gpio_map" | |
#define MYSFR_GPIO_BASE 7 | |
#define UDELAY 50 //us | |
#define CHARDELAY 592 //us | |
#define RCVPOLL 10000 //us | |
#define RCV_QUEUE_SIZE 8 //SFR -> | |
#define 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 void __iomem *gpio_map; | |
volatile uint32_t *gpio_base; | |
static struct timer_list poll_timer; | |
//RCV関係 SFR -> | |
static unsigned char ircv,rcv; | |
static unsigned char rcv_queue[RCV_QUEUE_SIZE]; | |
static int rq_head, rq_tail; | |
//SND関係 -> SFR | |
static unsigned char isnd,snd; | |
static unsigned char snd_queue[SND_QUEUE_SIZE]; | |
static int sq_head, sq_tail; | |
/*プロトタイプ宣言*/ | |
//GPIOレジスタのマップ関係 | |
static int mysfr9_gpio_map(void); | |
static int mysfr9_gpio_unmap(void); | |
static void mysfr9_gpio_setup(void); | |
static int rpi_gpio_function_set(int pin, uint32_t func); | |
static int rpi_gpio_pull_control(int pin, uint32_t pullmode); | |
static void rpi_gpio_set32( uint32_t mask, uint32_t val ); | |
//static uint32_t rpi_gpio_get32( uint32_t mask ); | |
static void rpi_gpio_clear32( uint32_t mask, uint32_t val ); | |
static uint32_t rpi_gpio_getpin( int pin ); | |
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); | |
/////////////////////////////////////////////////////////////////////////////////////////////////// | |
//デバイスドライバがロードされた時の処理 | |
static int mysfr9_init(void) | |
{ | |
int retval; | |
/* 開始のメッセージ */ | |
printk(KERN_INFO "%s loading...\n", MYSFR9_DEVNAME ); | |
sq_head=0, sq_tail=0; | |
rq_head=0, rq_tail=0; | |
/* GPIOレジスタがマップ可能か調べる */ | |
retval = mysfr9_gpio_map(); | |
if( retval != 0 ) { | |
printk( KERN_ALERT "Can not use GPIO registers.\n"); | |
return -EBUSY; | |
} | |
/* GPIO初期化 */ | |
mysfr9_gpio_setup(); | |
isnd=1; snd='I'; | |
ircv=0; rcv=0; | |
gpio_put_msb(isnd,snd,&ircv,&rcv); | |
/* デバイスドライバをカーネルに登録 */ | |
retval = mysfr9_register_dev(); | |
if( retval != 0 ) { | |
printk( KERN_ALERT "MySFR9 driver register failed.\n"); | |
return retval; | |
} | |
printk( KERN_INFO "MySFR9 driver register sccessed.\n"); | |
/* GPIOレジスタのアンマップ */ | |
mysfr9_gpio_unmap(); | |
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 ); | |
/* GPIOレジスタのアンマップ */ | |
mysfr9_gpio_unmap(); | |
kfree(mysfr9_cdev_array); | |
} | |
/////////////////////////////////////////////////////////////////////////////////////////////////// | |
//デバイスに対する処理 | |
static int mysfr9_open(struct inode *inode, struct file *filep) | |
{ | |
int retval; | |
if( gpio_base != NULL ) { | |
/* すでにオープンされている */ | |
printk(KERN_ERR "MySFR9 is already open.\n" ); | |
return -EIO; | |
} | |
retval = mysfr9_gpio_map(); | |
if( retval != 0 ) { | |
printk(KERN_ERR "Can not open MySFR9.\n" ); | |
return retval; | |
} | |
printk( KERN_INFO "RPI HZ = %d\n", HZ ); | |
register_poll_timer(); | |
return 0; | |
} | |
static int mysfr9_release(struct inode *inode, struct file *filep) | |
{ | |
//まだSFRに送信できていないないものがあるので、待ってみる | |
while ( sq_tail - sq_head ) { | |
udelay(UDELAY); | |
} | |
del_timer_sync(&poll_timer); | |
mysfr9_gpio_unmap(); | |
return 0; | |
} | |
static ssize_t mysfr9_write(struct file *filep,const char __user *buf, size_t count, loff_t *f_pos) | |
{ | |
//char cvalue; | |
//int retval; | |
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); | |
} | |
//isnd=1; | |
//ircv=0; rcv=0; | |
//gpio_put_msb(isnd,snd,&ircv,&rcv); | |
/*retval = ssegled_put(cvalue); | |
if( retval != 0 ) { | |
printk(KERN_ALERT "Can not display %d\n", cvalue ); | |
} | |
else { | |
ssegled_display_value = cvalue; | |
}*/ | |
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) { | |
while((rq_tail - rq_head) == 0){ | |
//udelay(UDELAY); | |
msleep(1); | |
} | |
c = ArrayDequeue(rcv_queue, &rq_head, &rq_tail, RCV_QUEUE_SIZE); | |
//printk( KERN_INFO "RCV READ char=%c\n",c); | |
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 "alloc_chrdev_region failed.\n" ); | |
return retval; | |
} | |
_mysfr9_major = MAJOR(dev); | |
/* デバイスクラスを作成する */ | |
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); | |
/* デバイスの数だけキャラクタデバイスを登録する */ | |
/* ただし7セグLEDは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 "cdev_add failed minor = %d\n", _mysfr9_minor+i ); | |
} | |
else { | |
/* デバイスノードの作成 */ | |
device_create( | |
mysfr9_class, | |
NULL, | |
devno, | |
NULL, | |
MYSFR9_DEVNAME"%u",_mysfr9_minor+i | |
); | |
} | |
} | |
return 0; | |
} | |
/////////////////////////////////////////////////////////////////////////////////////////////////// | |
//GPIOレジスタのマップ関係 | |
static int mysfr9_gpio_map(void) | |
{ | |
if( !request_mem_region(RPI_GPIO_BASE, | |
RPI_BLOCK_SIZE, | |
MySFR9_GPIO_MAPNAME) ) { | |
printk( KERN_ALERT "request_mem_region failed.\n"); | |
//return -EBUSY; | |
} | |
gpio_map = ioremap_nocache(RPI_GPIO_BASE, BLOCK_SIZE); | |
gpio_base = (volatile uint32_t *)gpio_map; | |
return 0; | |
} | |
static int mysfr9_gpio_unmap(void) | |
{ | |
iounmap(gpio_map); | |
release_mem_region(RPI_GPIO_BASE, RPI_BLOCK_SIZE); | |
gpio_map = NULL; | |
gpio_base = NULL; | |
return 0; | |
} | |
static void mysfr9_gpio_setup(void) | |
{ | |
rpi_gpio_function_set(MYSFR_GPIO_BASE+0, RPI_GPF_OUTPUT);//CLK-OUT | |
rpi_gpio_function_set(MYSFR_GPIO_BASE+1, RPI_GPF_OUTPUT);//DATA-OUT | |
rpi_gpio_function_set(MYSFR_GPIO_BASE+2, RPI_GPF_INPUT);//DATA-INPUT | |
rpi_gpio_pull_control(MYSFR_GPIO_BASE+2,RPI_GPIO_PULLUP); | |
} | |
static int rpi_gpio_function_set(int pin, uint32_t func) | |
{ | |
int index = RPI_GPFSEL0_INDEX + pin / 10; | |
uint32_t mask = ~(0x7 << ((pin % 10) * 3)); | |
gpio_base[index] = (gpio_base[index] & mask) | ((func & 0x7) << ((pin % 10) * 3)); | |
return 1; | |
} | |
static int rpi_gpio_pull_control(int pin, uint32_t pullmode) | |
{ | |
gpio_base[RPI_GPPUD_INDEX] = pullmode & 0x03; | |
gpio_base[RPI_GPPUDCLK0_INDEX] = 0x01 << pin; | |
// wait > 150 cycles | |
udelay(100); | |
gpio_base[RPI_GPPUDCLK0_INDEX] = 0; | |
gpio_base[RPI_GPPUD_INDEX] = 0; | |
return 1; | |
} | |
static void rpi_gpio_set32(uint32_t mask, uint32_t val ) | |
{ | |
gpio_base[RPI_GPSET0_INDEX] = val & mask; | |
} | |
/*static uint32_t rpi_gpio_get32( uint32_t mask ) | |
{ | |
return gpio_base[RPI_GPLEV0_INDEX] & mask; | |
}*/ | |
static void rpi_gpio_clear32(uint32_t mask, uint32_t val ) | |
{ | |
gpio_base[RPI_GPCLR0_INDEX] = val & mask; | |
} | |
static uint32_t rpi_gpio_getpin(int pin ) | |
{ | |
return (gpio_base[RPI_GPLEV0_INDEX] & (1 << pin)) != 0; | |
} | |
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 | |
if(isend){ | |
rpi_gpio_set32( RPI_GPIO_P1MASK, (2 & 0x0F) << MYSFR_GPIO_BASE); | |
}else{ | |
rpi_gpio_clear32( RPI_GPIO_P1MASK, (2 & 0x0F) << MYSFR_GPIO_BASE); | |
} | |
//--CLK=H | |
rpi_gpio_set32( RPI_GPIO_P1MASK, (1 & 0x0F) << MYSFR_GPIO_BASE); | |
udelay(UDELAY); | |
//--データ有無ビット入力 | |
_ircv = rpi_gpio_getpin(MYSFR_GPIO_BASE+2); | |
//--CLK=L | |
rpi_gpio_clear32( RPI_GPIO_P1MASK, (1 & 0x0F) << MYSFR_GPIO_BASE); | |
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){ | |
rpi_gpio_set32( RPI_GPIO_P1MASK, (2 & 0x0F) << MYSFR_GPIO_BASE); | |
}else{ | |
rpi_gpio_clear32( RPI_GPIO_P1MASK, (2 & 0x0F) << MYSFR_GPIO_BASE); | |
} | |
//--CLK=H | |
rpi_gpio_set32( RPI_GPIO_P1MASK, (1 & 0x0F) << MYSFR_GPIO_BASE); | |
udelay(UDELAY); | |
//--データ入力 | |
_rcv = _rcv << 1 ; | |
rcvbit = rpi_gpio_getpin(MYSFR_GPIO_BASE+2); | |
//printf("c=%d,gpin=%d\n",c,gpin); | |
if(rcvbit > 0){ | |
_rcv = _rcv | 0x01; | |
} | |
_send=_send<<1; | |
//--CLK=L | |
rpi_gpio_clear32( RPI_GPIO_P1MASK, (1 & 0x0F) << MYSFR_GPIO_BASE); | |
udelay(UDELAY); | |
} | |
*ircv = _ircv; | |
*rcv = _rcv; | |
//printf(" 内容\t送信:%c(%#x)\t受信:%c(%#x)\n",send,send,*rcv,*rcv); | |
//printf("----------------------------------------------------------------------------------------------------\n"); | |
//printf("### 送信:%c - %c(%#x) 受信:%c - %c(%#x)\n",((isend>0) ? 'O':'X'),send,send,((_ircv>0) ? 'O':'X'),_rcv,_rcv); | |
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) | |
{ | |
//unsigned char ircv,rcv; | |
//unsigned char isnd,snd; | |
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){ | |
}else{ | |
printk( KERN_ALERT "RCV Buffer Full.\n"); | |
} | |
} | |
}while ((sq_tail - sq_head) || (ircv > 0)); | |
/*while ((q_tail - q_head) || (ircv > 0)) { | |
if(q_tail - q_head){ | |
isnd = 1; | |
snd = ArrayDequeue(queue, &q_head, &q_tail, QUEUE_SIZE); | |
}else{ | |
isnd = 0; | |
} | |
gpio_put_msb(isnd,snd,&ircv,&rcv); | |
isnd = 0; | |
}*/ | |
//isnd=1; snd='P'; | |
//ircv=0; rcv=0; | |
//gpio_put_msb(isnd,snd,&ircv,&rcv); | |
//isnd=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); |
メモ
request_mem_regionが毎回失敗するのはなぜだろう
早速Amisでも移植してみるか?