|
/* |
|
* pic_cled.c |
|
* |
|
* Created on: 2018/07/08 |
|
* Author: adeno |
|
*/ |
|
|
|
|
|
#include <linux/module.h> |
|
#include <linux/moduleparam.h> |
|
#include <linux/kernel.h> |
|
#include <linux/cdev.h> |
|
#include <linux/delay.h> |
|
#include <linux/uaccess.h> //copy_from_user |
|
#include <linux/gpio.h> |
|
#include <linux/spi/spi.h> |
|
|
|
MODULE_AUTHOR("Adeno"); |
|
MODULE_LICENSE("GPL v2"); |
|
|
|
//======================================================================================= |
|
/* ドライバ */ |
|
#define DRV_NUM_DEVS 1 /* このドライバが制御するデバイスの数 */ |
|
#define DRV_DEVNAME "PicClcdDriver" /* このデバイスドライバの名称 */ |
|
#define DRV_MAJOR 0 /* メジャー番号だが自動設定なので0 */ |
|
#define DRV_MINOR 0 /* マイナー番号のベース番号 */ |
|
#define DRV_GPIO_MAPNAME "pcd_gpio_map" |
|
|
|
|
|
/* SPI PIC設定 */ |
|
#define SPCD_PACKET_SIZE 1 |
|
|
|
|
|
const uint8_t SPCD_LCD_PWM = 0x01; |
|
const uint8_t SPCD_LCD_CMD = 0x02; |
|
const uint8_t SPCD_LCD_DATA = 0x03; |
|
|
|
const uint8_t SPCD_LCD_CMD_CLEARDISPLAY = 0b00000001; |
|
const uint8_t SPCD_LCD_CMD_RETURNHOME = 0b00000010; |
|
const uint8_t SPCD_LCD_CMD_SETCGRAM = 0b01000000; |
|
const uint8_t SPCD_LCD_CMD_SETDDRAM = 0b10000000; |
|
|
|
|
|
//======================================================================================= |
|
/* ドライバ */ |
|
static int _spcd_major = DRV_MAJOR; |
|
static int _spcd_minor = DRV_MINOR; |
|
|
|
/* SPI */ |
|
struct drvdata { |
|
struct spi_device *spi; |
|
struct mutex lock; |
|
unsigned char tx[SPCD_PACKET_SIZE]; |
|
unsigned char rx[SPCD_PACKET_SIZE]; |
|
struct spi_transfer xfer; |
|
struct spi_message msg; |
|
dev_t md_dev; |
|
struct class *drv_class; |
|
struct cdev *drv_cdev_array; |
|
} ____cacheline_aligned; |
|
static struct drvdata spcd; |
|
|
|
static struct spi_board_info spcd_info = { |
|
.modalias = "spcd", |
|
.max_speed_hz = 1000000, |
|
.bus_num = 0, |
|
.chip_select = 0, |
|
.mode = SPI_MODE_3, |
|
}; |
|
|
|
static struct spi_device_id spcd_id[] = { |
|
{ "spcd", 0 }, |
|
{ }, |
|
}; |
|
MODULE_DEVICE_TABLE(spi, spcd_id); |
|
|
|
/* Parameters Setting */ |
|
static int gpiono = 9; |
|
static int show_debug = 0; |
|
//static int hexmode = 0; |
|
|
|
//======================================================================================= |
|
/* プロトタイプ宣言 */ |
|
static unsigned int spcd_set_value( struct drvdata *data,unsigned int v); |
|
static void spi_remove_device(struct spi_master *master, unsigned int cs); |
|
static int spcd_probe(struct spi_device *spi); |
|
static int spcd_remove(struct spi_device *spi); |
|
static int spcd_open(struct inode *inode, struct file *filep); |
|
static int spcd_release(struct inode *inode, struct file *filep); |
|
static ssize_t spcd_write(struct file *filep, const char __user *buf, size_t count, loff_t *f_pos); |
|
static int spcd_register_dev(void); |
|
static int spcd_init(void); |
|
static void spcd_exit(void); |
|
//======================================================================================= |
|
|
|
/* SPIデバイス */ |
|
static struct spi_driver spcd_driver = { |
|
.driver = { |
|
.name = DRV_DEVNAME, |
|
.owner = THIS_MODULE, |
|
}, |
|
.id_table = spcd_id, |
|
.probe = spcd_probe, |
|
.remove = spcd_remove, |
|
}; |
|
|
|
/* キャラクタデバイス */ |
|
struct file_operations spcd_fops = { |
|
.open = spcd_open, |
|
.release = spcd_release, |
|
.write = spcd_write, |
|
}; |
|
|
|
//======================================================================================= |
|
|
|
static int lcd_clear_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
|
{ |
|
int size = strlen( buf ); |
|
struct drvdata *drd = spi_get_drvdata((struct spi_device *)dev); |
|
|
|
spcd_set_value(drd,SPCD_LCD_CMD); |
|
spcd_set_value(drd,SPCD_LCD_CMD_CLEARDISPLAY); |
|
// spcd_set_value(drd,SPCD_LCD_FIN); |
|
|
|
return size; |
|
} |
|
static DEVICE_ATTR(lcd_clear, S_IWUSR|S_IWGRP, NULL, lcd_clear_store ); |
|
|
|
static int lcd_set_cgram_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
|
{ |
|
//CharNo Line(0-7) |
|
//[1] [2]x8 |
|
int b[9],i; |
|
int size = strlen( buf ); |
|
struct drvdata *drd = spi_get_drvdata((struct spi_device *)dev); |
|
if(size >= 17){ |
|
if(buf[0] >='0' && buf[0] <= '9') b[0] = buf[0]-'0'; |
|
b[0] = SPCD_LCD_CMD_SETCGRAM | (((b[0] & 0b111) << 3) & 0b111000); |
|
if(show_debug) printk(KERN_INFO "LCD SET CG Adr size=%d,C=%d\n",size,b[0]); |
|
|
|
// /*if(show_debug)*/ printk(KERN_INFO "LCD SET CG Adr output=%d\n",b[0]); |
|
spcd_set_value(drd,SPCD_LCD_CMD); spcd_set_value(drd,b[0]); |
|
|
|
for(i=0; i<8; i++){ |
|
if(buf[i*2+1] >='0' && buf[i*2+1] <= '9') b[i] = (buf[i*2+1]-'0') * 0x10; |
|
else if(buf[i*2+1] >='A' && buf[i*2+1] <= 'F') b[i] += (buf[i*2+1]-'A'+0xA) * 0x10; |
|
else if(buf[i*2+1] >='a' && buf[i*2+1] <= 'f') b[i] += (buf[i*2+1]-'A'+0xA) * 0x10; |
|
|
|
if(buf[i*2+2] >='0' && buf[i*2+2] <= '9') b[i] += (buf[i*2+2]-'0'); |
|
else if(buf[i*2+2] >='A' && buf[i*2+2] <= 'F') b[i] += (buf[i*2+2]-'A'+0xA); |
|
else if(buf[i*2+2] >='a' && buf[i*2+2] <= 'f') b[i] += (buf[i*2+2]-'A'+0xA); |
|
|
|
spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,b[i]); |
|
|
|
} |
|
|
|
// spcd_set_value(drd,SPCD_LCD_CMD); spcd_set_value(drd,0b10); //ホームに |
|
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,buf[0]-'0'); |
|
|
|
// |
|
// |
|
// |
|
// if((buf[0] >='0' && buf[0] <= '9') && (buf[1] >='0' && buf[1] <= '9')){ |
|
// int cno = buf[0]-'0'; |
|
// int lno = buf[1]-'0'; |
|
// int b = 0x40 | (((cno & 0b111) << 3) | (lno & 0b111)); |
|
// /*if(show_debug)*/ printk(KERN_INFO "LCD SET CG Adr size=%d,C=%d,L=%d,output=%d\n",size,cno,lno,b); |
|
// |
|
// spcd_set_value(drd,SPCD_LCD_CMD); spcd_set_value(drd,b); |
|
//// spcd_set_value(drd,SPCD_LCD_FIN); |
|
// |
|
// //debug |
|
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,0b10101); |
|
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,0b01010); |
|
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,0b10101); |
|
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,0b01010); |
|
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,0b10101); |
|
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,0b01010); |
|
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,0b10101); |
|
// spcd_set_value(drd,SPCD_LCD_DATA); spcd_set_value(drd,0b00000); |
|
//// spcd_set_value(drd,SPCD_LCD_FIN); |
|
|
|
// } |
|
} |
|
|
|
return size; |
|
} |
|
static DEVICE_ATTR(lcd_set_cgram, S_IWUSR|S_IWGRP, NULL, lcd_set_cgram_store ); |
|
|
|
static int lcd_set_ddram_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
|
{ |
|
//Adr |
|
//[2] |
|
int b; |
|
int size = strlen( buf ); |
|
struct drvdata *drd = spi_get_drvdata((struct spi_device *)dev); |
|
if(size >= 1){ |
|
if(buf[0] >='0' && buf[0] <= '9') b = (buf[0]-'0') * 0x10; |
|
else if(buf[0] >='A' && buf[0] <= 'F') b += (buf[0]-'A'+0xA) * 0x10; |
|
else if(buf[0] >='a' && buf[0] <= 'f') b += (buf[0]-'A'+0xA) * 0x10; |
|
|
|
if(buf[1] >='0' && buf[1] <= '9') b += (buf[1]-'0'); |
|
else if(buf[1] >='A' && buf[1] <= 'F') b += (buf[1]-'A'+0xA); |
|
else if(buf[1] >='a' && buf[1] <= 'f') b += (buf[1]-'A'+0xA); |
|
|
|
/*if(show_debug)*/ printk(KERN_INFO "LCD SET DD Adr size=%d,C=%d\n",size,(b | SPCD_LCD_CMD_SETDDRAM)); |
|
spcd_set_value(drd,SPCD_LCD_CMD); spcd_set_value(drd,(b | SPCD_LCD_CMD_SETDDRAM)); |
|
|
|
} |
|
|
|
return size; |
|
} |
|
static DEVICE_ATTR(lcd_set_ddram, S_IWUSR|S_IWGRP, NULL, lcd_set_ddram_store ); |
|
|
|
|
|
static int lcd_contrast_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
|
{ |
|
int size = strlen( buf ); |
|
struct drvdata *drd = spi_get_drvdata((struct spi_device *)dev); |
|
|
|
if(size > 0){ |
|
if(show_debug) printk(KERN_INFO "Change LCD Contrast %d,%c\n", size,buf[0]); |
|
|
|
spcd_set_value(drd,SPCD_LCD_PWM); |
|
|
|
switch(buf[0]){ |
|
case '0': |
|
spcd_set_value(drd,0x00); |
|
break; |
|
case '1': |
|
spcd_set_value(drd,0x01); |
|
break; |
|
case '2': |
|
spcd_set_value(drd,0x02); |
|
break; |
|
case '3': |
|
spcd_set_value(drd,0x03); |
|
break; |
|
case '4': |
|
spcd_set_value(drd,0x04); |
|
break; |
|
case '5': |
|
spcd_set_value(drd,0x05); |
|
break; |
|
case '6': |
|
spcd_set_value(drd,0x06); |
|
break; |
|
default: |
|
spcd_set_value(drd,0x03); |
|
break; |
|
} |
|
|
|
|
|
} |
|
return size; |
|
} |
|
static DEVICE_ATTR(lcd_contrast, S_IWUSR|S_IWGRP, NULL, lcd_contrast_store ); |
|
|
|
static unsigned int spcd_set_value( struct drvdata *data,unsigned int v) |
|
{ |
|
unsigned int r = 0; |
|
|
|
|
|
mutex_lock( &data->lock ); |
|
|
|
data->tx[0] = v; |
|
|
|
if( spi_sync( data->spi, &data->msg) ) { |
|
if(show_debug) printk(KERN_INFO "spi_sync_transfer returned non zero\n" ); |
|
} |
|
usleep_range(1000,2000); |
|
|
|
mutex_unlock(&data->lock); |
|
|
|
return r; |
|
} |
|
|
|
|
|
/** |
|
* SPIデバイスの削除 |
|
*/ |
|
static void spi_remove_device(struct spi_master *master, unsigned int cs) |
|
{ |
|
struct device *dev; |
|
char str[128]; |
|
|
|
snprintf(str, sizeof(str), "%s.%u", dev_name(&master->dev), cs); |
|
if(show_debug) printk(KERN_INFO "[spi_remove_device] %s\n",str); |
|
// SPIデバイスを探す |
|
dev = bus_find_device_by_name(&spi_bus_type, NULL, str); |
|
// あったら削除 |
|
if( dev ){ |
|
if(show_debug) printk(KERN_INFO "Delete %s\n", str); |
|
device_del(dev); |
|
} |
|
} |
|
|
|
/** |
|
* SPI通信初期化 |
|
*/ |
|
static int spcd_probe(struct spi_device *spi) |
|
{ |
|
// struct mcp_drvdata *data; |
|
int retval; |
|
|
|
if(show_debug) printk(KERN_INFO "spcd spi probe\n"); |
|
|
|
/* SPIを設定する */ |
|
spi->max_speed_hz = spcd_info.max_speed_hz; |
|
spi->mode = spcd_info.mode; |
|
spi->bits_per_word = 8; |
|
if( spi_setup( spi ) ) { |
|
printk(KERN_ERR "spi_setup returned error\n"); |
|
return -ENODEV; |
|
} |
|
|
|
spcd.spi = spi; |
|
mutex_init( &spcd.lock ); |
|
|
|
spcd.xfer.tx_buf = spcd.tx; |
|
spcd.xfer.rx_buf = 0;//data->rx; |
|
spcd.xfer.bits_per_word = 8; |
|
spcd.xfer.len = SPCD_PACKET_SIZE; |
|
spcd.xfer.cs_change = 0; |
|
spcd.xfer.delay_usecs = 0; |
|
spcd.xfer.speed_hz = 1000000; |
|
spi_message_init_with_transfers( &spcd.msg, &spcd.xfer, 1 ); |
|
|
|
spi_set_drvdata( spi, &spcd ); |
|
|
|
mutex_lock( &spcd.lock ); |
|
spcd.tx[0] = 0x00; |
|
|
|
|
|
if( spi_sync( spcd.spi, &spcd.msg) ) { |
|
printk(KERN_INFO "spi_sync_transfer returned non zero\n" ); |
|
} |
|
|
|
|
|
mutex_unlock(&spcd.lock); |
|
|
|
retval = device_create_file( &spi->dev, &dev_attr_lcd_clear ); |
|
if(retval) { |
|
printk(KERN_ERR "failed to add lcd_clear attribute\n" ); |
|
} |
|
retval = device_create_file( &spi->dev, &dev_attr_lcd_contrast ); |
|
if(retval) { |
|
printk(KERN_ERR "failed to add lcd_contrast attribute\n" ); |
|
} |
|
retval = device_create_file( &spi->dev, &dev_attr_lcd_set_cgram ); |
|
if(retval) { |
|
printk(KERN_ERR "failed to add lcd_set_cgram attribute\n" ); |
|
} |
|
retval = device_create_file( &spi->dev, &dev_attr_lcd_set_ddram ); |
|
if(retval) { |
|
printk(KERN_ERR "failed to add lcd_set_ddram attribute\n" ); |
|
} |
|
|
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* SPI通信終了 |
|
*/ |
|
static int spcd_remove(struct spi_device *spi) |
|
{ |
|
if(show_debug) printk(KERN_INFO "[spcd spi_remove]\n"); |
|
struct drvdata *drd = spi_get_drvdata(spi); |
|
|
|
|
|
mutex_lock( &drd->lock ); |
|
drd->tx[0] = 0x00; |
|
|
|
|
|
if( spi_sync( drd->spi, &drd->msg) ) { |
|
printk(KERN_INFO "spi_sync_transfer returned non zero\n" ); |
|
} |
|
|
|
|
|
mutex_unlock(&drd->lock); |
|
|
|
device_remove_file( &spi->dev, &dev_attr_lcd_clear); |
|
device_remove_file( &spi->dev, &dev_attr_lcd_contrast); |
|
device_remove_file( &spi->dev, &dev_attr_lcd_set_cgram); |
|
device_remove_file( &spi->dev, &dev_attr_lcd_set_ddram); |
|
|
|
|
|
spi_set_drvdata(drd->spi, NULL); |
|
drd->spi = NULL; |
|
mutex_destroy(&spcd.lock); |
|
if(gpiono != 0) gpio_free(gpiono); |
|
|
|
return 0; |
|
} |
|
|
|
|
|
|
|
/** |
|
* デバイスオープン時の処理 |
|
*/ |
|
static int spcd_open(struct inode *inode, struct file *filep) |
|
{ |
|
if(show_debug) printk(KERN_INFO "[spcd_open]\n"); |
|
filep->private_data = &spcd; |
|
return 0; |
|
} |
|
|
|
/** |
|
* デバイスクローズ時の処理 |
|
*/ |
|
static int spcd_release(struct inode *inode, struct file *filep) |
|
{ |
|
if(show_debug) printk(KERN_INFO "[spcd_release]\n"); |
|
struct drvdata *drd = filep->private_data; |
|
if ((drd == NULL) || (drd->spi == NULL)) |
|
return -ENODEV; |
|
|
|
filep->private_data = NULL; |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* デバイス書き込み処理 |
|
*/ |
|
static ssize_t spcd_write(struct file *filep, const char __user *buf, size_t count, loff_t *f_pos) |
|
{ |
|
char cvalue; |
|
// int i; |
|
|
|
struct drvdata *drd = filep->private_data; |
|
if ((drd == NULL) || (drd->spi == NULL)) |
|
return -ENODEV; |
|
|
|
// struct mcp_drvdata *data = (struct mcp_drvdata *)dev_get_drvdata(filep->private_data); |
|
|
|
if(count > 0) { |
|
if(gpiono != 0) gpio_set_value(gpiono,1); |
|
if(copy_from_user( &cvalue, buf, sizeof(char) )) { |
|
return -EFAULT; |
|
} |
|
|
|
spcd_set_value(drd,SPCD_LCD_DATA); |
|
spcd_set_value(drd,cvalue); |
|
// spcd_set_value(drd,SPCD_LCD_FIN); |
|
// if((cvalue >=0) && (cvalue <= 9)) spcd_set_value(drd,cvalue); |
|
// else if(('0' <= cvalue) && (cvalue <= '9')) { |
|
// spcd_set_value(drd,0x03); |
|
// spcd_set_value(drd,cvalue); |
|
// spcd_set_value(drd,0x00); |
|
// } |
|
// else{ |
|
// if(hexmode){ |
|
// if(('A' <= cvalue) && (cvalue <= 'F')) spcd_set_value(drd,cvalue - 'A' + 10); |
|
// else if(('a' <= cvalue) && (cvalue <= 'f')) spcd_set_value(drd,cvalue - 'a' + 10); |
|
// } |
|
// else if(show_debug) printk(KERN_ALERT "Can not display %d\n", cvalue ); |
|
// } |
|
|
|
if(gpiono != 0) gpio_set_value(gpiono,0); |
|
|
|
return sizeof(char); |
|
} |
|
return 0; |
|
} |
|
|
|
|
|
|
|
/** |
|
* デバイスドライバをカーネルに登録 |
|
*/ |
|
static int spcd_register_dev(void) |
|
{ |
|
int retval; |
|
// dev_t dev; |
|
size_t size; |
|
int i; |
|
|
|
/* 空いているメジャー番号を使ってメジャー& |
|
マイナー番号をカーネルに登録する */ |
|
retval = alloc_chrdev_region( |
|
&spcd.md_dev, /* 結果を格納するdev_t構造体 */ |
|
DRV_MINOR, /* ベースマイナー番号 */ |
|
DRV_NUM_DEVS, /* デバイスの数 */ |
|
DRV_DEVNAME /* デバイスドライバの名前 */ |
|
); |
|
|
|
if( retval < 0 ) { |
|
printk(KERN_ERR "alloc_chrdev_region failed.\n" ); |
|
return retval; |
|
} |
|
_spcd_major = MAJOR(spcd.md_dev); |
|
|
|
/* デバイスクラスを作成する */ |
|
spcd.drv_class = class_create(THIS_MODULE,DRV_DEVNAME); |
|
if(IS_ERR(spcd.drv_class)) |
|
return PTR_ERR(spcd.drv_class); |
|
|
|
/* cdev構造体の用意 */ |
|
size = sizeof(struct cdev) * DRV_NUM_DEVS; |
|
spcd.drv_cdev_array = (struct cdev*)kmalloc(size, GFP_KERNEL); |
|
|
|
/* デバイスの数だけキャラクタデバイスを登録する */ |
|
/* ただし7セグLEDは1個しかない */ |
|
for( i = 0; i < DRV_NUM_DEVS; i++ ) { |
|
dev_t devno = MKDEV(_spcd_major, _spcd_minor+i); |
|
/* キャラクタデバイスとしてこのモジュールをカーネルに登録する */ |
|
cdev_init(&(spcd.drv_cdev_array[i]), &spcd_fops); |
|
spcd.drv_cdev_array[i].owner = THIS_MODULE; |
|
if( cdev_add( &(spcd.drv_cdev_array[i]), devno, 1) < 0 ) { |
|
/* 登録に失敗した */ |
|
printk(KERN_ERR "cdev_add failed minor = %d\n", _spcd_minor+i ); |
|
} |
|
else { |
|
/* デバイスノードの作成 */ |
|
device_create( |
|
spcd.drv_class, |
|
NULL, |
|
devno, |
|
NULL, |
|
DRV_DEVNAME"%u",_spcd_minor+i |
|
); |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
/** |
|
* モジュールの初期化処理 |
|
*/ |
|
static int spcd_init(void) |
|
{ |
|
int retval; |
|
int i; |
|
|
|
/* 開始のメッセージ */ |
|
printk(KERN_INFO "%s loading...\n", DRV_DEVNAME ); |
|
if(gpiono != 0){ |
|
/* GPIOレジスタがマップ可能か調べる */ |
|
retval = gpio_request_one(gpiono, GPIOF_OUT_INIT_LOW, DRV_GPIO_MAPNAME); |
|
if (retval) { |
|
printk(KERN_ERR "Unable to request GPIOs: %d\n", retval); |
|
return retval; |
|
} |
|
for( i=0; i<10; i++ ) { |
|
gpio_set_value(gpiono,1); |
|
msleep(10); |
|
gpio_set_value(gpiono,0); |
|
msleep(10); |
|
} |
|
} |
|
/* デバイスドライバをカーネルに登録 */ |
|
retval = spcd_register_dev(); |
|
if( retval != 0 ) { |
|
printk( KERN_ALERT "mcp7d driver register failed.\n"); |
|
return retval; |
|
} |
|
if(show_debug) |
|
printk( KERN_INFO "mcp7d driver register sccessed.\n"); |
|
|
|
struct spi_master *master; |
|
struct spi_device *spi_device; |
|
|
|
spi_register_driver(&spcd_driver); |
|
|
|
spcd_info.bus_num = 0; |
|
spcd_info.chip_select = 1; |
|
master = spi_busnum_to_master(spcd_info.bus_num); |
|
if( ! master ) { |
|
printk( KERN_ERR "spi_busnum_to_master returned NULL\n"); |
|
spi_unregister_driver(&spcd_driver); |
|
return -ENODEV; |
|
} |
|
|
|
spi_remove_device(master, spcd_info.chip_select); |
|
|
|
spi_device = spi_new_device( master, &spcd_info ); |
|
if( !spi_device ) { |
|
printk(KERN_ERR "spi_new_device returned NULL\n" ); |
|
spi_unregister_driver(&spcd_driver); |
|
return -ENODEV; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* モジュールの終了処理 |
|
*/ |
|
static void spcd_exit(void) |
|
{ |
|
int i; |
|
dev_t devno; |
|
if(show_debug) printk(KERN_INFO "[spcd_exit]\n"); |
|
|
|
if(show_debug) printk(KERN_INFO "[cdev_del]\n"); |
|
|
|
/* キャラクタデバイスの登録解除 */ |
|
for( i = 0; i < DRV_NUM_DEVS; i++ ) { |
|
cdev_del(&(spcd.drv_cdev_array[i])); |
|
devno = MKDEV(_spcd_major, _spcd_minor+i); |
|
device_destroy(spcd.drv_class, devno); |
|
} |
|
/* メジャー番号/マイナー番号を取り除く */ |
|
devno = MKDEV(_spcd_major,_spcd_minor); |
|
unregister_chrdev_region(devno, DRV_NUM_DEVS); |
|
/* デバイスノードを取り除く */ |
|
class_destroy( spcd.drv_class ); |
|
kfree(spcd.drv_cdev_array); |
|
|
|
if(show_debug) printk(KERN_INFO "[spcd_del]\n"); |
|
|
|
spi_unregister_driver(&spcd_driver); |
|
} |
|
|
|
module_init(spcd_init); |
|
module_exit(spcd_exit); |
|
|
|
module_param( show_debug, int, S_IRUSR | S_IRGRP | S_IROTH ); |
|
module_param( gpiono, int, S_IRUSR | S_IRGRP | S_IROTH ); |
|
//module_param( hexmode, int, S_IRUSR | S_IRGRP | S_IROTH ); |
|
|
|
|