3. LED數碼管驅動編寫
在第二節驅動的基礎上我們加入LED數碼管驅動,這里我們簡單點直接構建一個簡單的字符設備驅動即可。
3.1. 字符設備注冊
static int register_led(void)
{
int ret;
dev_t devno = MKDEV(led_major, led_minor);
ret = register_chrdev_region(devno, 1, "led");
if (ret < 0) {
printk("Failed: register_chrdev_region\n");
return -1;
}
cdev_init(&zlg7290->cdev, &zlg7290_led_fops);
zlg7290->cdev.owner = THIS_MODULE;
ret = cdev_add(&zlg7290->cdev, devno, 1);
if (ret < 0) {
unregister_chrdev_region(devno, 1);
printk("Failed: cdev_add\n");
return -1;
}
return 0;
}
static int unregister_led(void)
{
dev_t devno = MKDEV(led_major, led_minor);
cdev_del(&zlg7290->cdev);
unregister_chrdev_region(devno, 1);
return 0;
}
3.2. 用戶接口實現
static int zlg7290_open(struct inode *inode, struct file *file)
{
return 0;
}
static int zlg7290_release(struct inode *inode, struct file *file)
{
return 0;
}
static long zlg7290_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
unsigned char buf[8] = {0};
ssize_t len = 0;
unsigned char val[2] = {0};
unsigned char reg[8] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};
int i = 0;
switch (cmd) {
case SET_VAL:
if (copy_from_user(buf, (void *)arg, 8))
return -EFAULT;
for(i = 0; i < 8; i++) {
val[0] = reg[i];
switch(buf[i]) {
case '0': val[1] = 0xfc; break;
case '1': val[1] = 0x0c; break;
case '2': val[1] = 0xda; break;
case '3': val[1] = 0xf2; break;
case '4': val[1] = 0x66; break;
case '5': val[1] = 0xb6; break;
case '6': val[1] = 0xbe; break;
case '7': val[1] = 0xe0; break;
case '8': val[1] = 0xfe; break;
case '9': val[1] = 0xf6; break;
case 'a':
case 'A': val[1] = 0xee; break;
case 'b':
case 'B': val[1] = 0x3e; break;
case 'c':
case 'C': val[1] = 0x9c; break;
case 'd':
case 'D': val[1] = 0x7a; break;
case 'e':
case 'E': val[1] = 0x9e; break;
case 'f':
case 'F': val[1] = 0x8e; break;
case ' ': val[1] = 0x00; break;
default:
val[1] = 0x00; break;
}
msleep(10);
zlg7290_hw_write(zlg7290, 2, &len, val);
}
break;
}
return 0;
}
static struct file_operations zlg7290_led_fops = {
.owner = THIS_MODULE,
.open = zlg7290_open,
.release = zlg7290_release,
.unlocked_ioctl = zlg7290_ioctl,
};
3.3. 測試程序
#include
#include
#include
#include
#include
#include
#include
#define SET_VAL _IO('Z', 0)
int main(int argc, const char *argv[])
{
time_t t;
struct tm *tm;
char buf[8] = "8765 321";
int fd = open("/dev/zlg7290", O_RDWR);
if (fd < 0) {
perror("open");
exit(1);
}
while(1) {
time(&t);
tm = localtime(&t);
sprintf(buf, "%02d %02d %02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
printf("%s\n", buf);
ioctl(fd, SET_VAL, buf);
sleep(1);
}
return 0;
}
當執行應用程序后8個數碼管會見識當前系統時間如下:
10 01 01
4. 完整驅動
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ZLG7290_NAME "zlg7290"
#define SET_VAL _IO('Z', 0)
unsigned int led_major = 800;
unsigned int led_minor = 0;
/*zlg7290 device struct*/
struct zlg7290
{
struct i2c_client *client;
struct work_struct work;
struct input_dev *key;
struct cdev cdev;
};
struct zlg7290 *zlg7290;
unsigned int key_value[65] = {
0,
1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56,
57, 58, 59, 60, 61, 62, 63, 64,
};
/*****************Character device File operations application interface part
* begin********/
static int zlg7290_hw_write(struct zlg7290 *ctr_zlg7290,
int len, size_t *retlen, char *buf)
{
struct i2c_client *client = ctr_zlg7290->client;
int ret;
struct i2c_msg msg[] = {
{ client->addr, 0, len, buf}, /*the buf contains register address*/
};
ret =i2c_transfer(client->adapter, msg, 1);
if (ret < 0)
{
printk("ret=%d,addr=%x\n",ret,client->addr);
dev_err(&client->dev, "i2c read error/n");
return -EIO;
}
*retlen = len;
return 0;
}
static int zlg7290_hw_read(struct zlg7290 *ctr_zlg7290 ,
int len, size_t *retlen, char *buf)
{
struct i2c_client *client = ctr_zlg7290->client;
int ret;
struct i2c_msg msg[] = {
{ client->addr, 0, len, buf}, /*the buf contains register address*/
{ client->addr, I2C_M_RD, len, buf },/*the buf contains register value*/
};
ret =i2c_transfer(client->adapter, msg, 2);
if (ret < 0)
{
printk("ret=%d,addr=%x\n",ret,client->addr);
dev_err(&client->dev, "i2c read error/n");
return -EIO;
}
*retlen = len;
return 0;
}
static int zlg7290_open(struct inode *inode, struct file *file)
{
return 0;
}
static int zlg7290_release(struct inode *inode, struct file *file)
{
return 0;
}
static void zlg7290_work(struct work_struct *work)
{
struct zlg7290 *ctr_zlg7290 = container_of(work, struct zlg7290, work);
unsigned char val = 0;
size_t len;
unsigned char status = 0;
zlg7290_hw_read(ctr_zlg7290, 1, &len, &status);
if(status & 0x1) {
val = 1;
zlg7290_hw_read(ctr_zlg7290, 1, &len, &val);
if (val == 0) {
val = 3;
zlg7290_hw_read(ctr_zlg7290, 1, &len, &val);
if (val == 0 || val == 0xFF)
goto out;
}
//printk("key_val = %02x\n", val);
if (val > 56) {
switch (val) {
case 0xFE: val = 57; break;
case 0xFD: val = 58; break;
case 0xFB: val = 59; break;
case 0xF7: val = 60; break;
case 0xEF: val = 61; break;
case 0xDF: val = 62; break;
case 0xBF: val = 63; break;
case 0x7F: val = 64; break;
}
}
//printk("key_val = %02x\n", val);
input_report_key(ctr_zlg7290->key, key_value[val], 1);
input_report_key(ctr_zlg7290->key, key_value[val], 0);
input_sync(ctr_zlg7290->key);
}
out:
return;
//printk("jiffies = %ld\n", jiffies);
//schedule_delayed_work(&zlg7290->work, HZ / 5);
}
static long zlg7290_ioctl(struct file *file,
unsigned int cmd, unsigned long arg) {
unsigned char buf[8] = {0};
ssize_t len = 0;
unsigned char val[2] = {0};
unsigned char reg[8] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};
int i = 0;
switch (cmd) {
case SET_VAL:
if (copy_from_user(buf, (void *)arg, 8))
return -EFAULT;
for(i = 0; i < 8; i++) {
val[0] = reg[i];
switch(buf[i]) {
case '0': val[1] = 0xfc; break;
case '1': val[1] = 0x0c; break;
case '2': val[1] = 0xda; break;
case '3': val[1] = 0xf2; break;
case '4': val[1] = 0x66; break;
case '5': val[1] = 0xb6; break;
case '6': val[1] = 0xbe; break;
case '7': val[1] = 0xe0; break;
case '8': val[1] = 0xfe; break;
case '9': val[1] = 0xf6; break;
case 'a':
case 'A': val[1] = 0xee; break;
case 'b':
case 'B': val[1] = 0x3e; break;
case 'c':
case 'C': val[1] = 0x9c; break;
case 'd':
case 'D': val[1] = 0x7a; break;
case 'e':
case 'E': val[1] = 0x9e; break;
case 'f':
case 'F': val[1] = 0x8e; break;
case ' ': val[1] = 0x00; break;
default:
val[1] = 0x00; break;
}
msleep(10);
zlg7290_hw_write(zlg7290, 2, &len, val);
}
break;
}
return 0;
}
static struct file_operations zlg7290_led_fops = {
.owner = THIS_MODULE,
.open = zlg7290_open,
.release = zlg7290_release,
.unlocked_ioctl = zlg7290_ioctl,
};
static int register_led(void)
{
int ret;
dev_t devno = MKDEV(led_major, led_minor);
ret = register_chrdev_region(devno, 1, "led");
if (ret < 0) {
printk("Failed: register_chrdev_region\n");
return -1;
}
cdev_init(&zlg7290->cdev, &zlg7290_led_fops);
zlg7290->cdev.owner = THIS_MODULE;
ret = cdev_add(&zlg7290->cdev, devno, 1);
if (ret < 0) {
unregister_chrdev_region(devno, 1);
printk("Failed: cdev_add\n");
return -1;
}
return 0;
}
static int unregister_led(void)
{
dev_t devno = MKDEV(led_major, led_minor);
cdev_del(&zlg7290->cdev);
unregister_chrdev_region(devno, 1);
return 0;
}
irqreturn_t zlg7290_interrupt(int irq, void *devid)
{
printk("irq = %d\n", irq);
schedule_work(&zlg7290->work);
return IRQ_HANDLED;
}
static int zlg7290_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct input_dev *key_dev;
int ret = 0;
int i = 0;
int err = -EINVAL;
if (!(zlg7290 = kzalloc(sizeof(struct zlg7290), GFP_KERNEL)))
return -ENOMEM;
printk("irq = %d\n", client->irq);
zlg7290->key = input_allocate_device();
if (zlg7290->key == NULL) {
kfree(zlg7290);
return -ENOMEM;
}
key_dev = zlg7290->key;
key_dev->name = "zlg7290";
key_dev->id.bustype = BUS_HOST;
key_dev->id.vendor = 0x0001;
key_dev->id.product = 0x0001;
key_dev->id.version = 0x0001;
key_dev->evbit[0] = BIT_MASK(EV_KEY);
for(i = 1; i <= 64; i++) {
key_dev->keybit[BIT_WORD(key_value[i])] |= BIT_MASK(key_value[i]);
}
ret = input_register_device(key_dev);
if (ret < 0) {
printk("Failed to register input device\n");
goto err1;
}
printk("zlg7290 probe\n");
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
return err;
}
zlg7290->client = client;
i2c_set_clientdata(client, zlg7290);
INIT_WORK(&zlg7290->work, zlg7290_work);
// schedule_delayed_work(&zlg7290->work, HZ / 5);
ret = request_irq(client->irq, zlg7290_interrupt,
IRQF_DISABLED|IRQF_TRIGGER_FALLING, "zlg7290", NULL);
if (ret < 0) {
goto err1;
}
ret = register_led();
if (ret < 0)
goto err1;
return 0;
err1:
input_free_device(key_dev);
kfree(zlg7290);
return ret;
}
static int zlg7290_remove(struct i2c_client *client)
{
unregister_led();
free_irq(client->irq, NULL);
i2c_set_clientdata(client, NULL);
input_unregister_device(zlg7290->key);
input_free_device(zlg7290->key);
kfree(zlg7290);
return 0;
}
static const struct i2c_device_id zlg7290_id[] = {
{ZLG7290_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, zlg7290_id);
static struct i2c_driver zlg7290_driver= {
.probe = zlg7290_probe,
.remove = zlg7290_remove,
.id_table = zlg7290_id,
.driver = {
.name = ZLG7290_NAME,
.owner = THIS_MODULE,
},
};
static int __init zlg7290_init(void)
{
return i2c_add_driver(&zlg7290_driver);
}
static void __exit zlg7290_exit(void)
{
i2c_del_driver(&zlg7290_driver);
}
MODULE_AUTHOR("farsight");
MODULE_DESCRIPTION("zlg7290 driver");
MODULE_LICENSE("GPL");
module_init(zlg7290_init);
module_exit(zlg7290_exit);