CÁC THAO TÁC TRÊN FILE DEVICE :
Hàm leds_open() được gọi để mở file . Nhiệm vụ của bạn ở đây là lưu cấu trúc leds_dev liến kết đến device (led), bởi vì ta sẽ sử dụng cấu trúc này cho function read và write. Việc này được thực hiện thông qua hàm container_of() ở dòng số 7. Con trỏ đến leds_dev được lưu trữ trong file->private_data ở dòng số 10.
Trong hàm leds_read(), ta khôi phục lại cấu trúc của led là leds_dev trong file->private_data ở dòng số 26, ta kiểm tra trạng thái của led và trả về giá trị “1” nếu led on và ngược lại “0” nếu led off. Ta thấy rằng, để trả dữ liệu lên lớp ứng dụng thì mình cần sử dụng hàm copy_to_user(). Đây là một hàm chuẩn của kernel làm nhiệm vụ gửi data đến application .
Trong hàm leds_write() ta cũng cần khôi phục cấu trúc led là leds_dev trong file->private_date ở dòng 42 và cũng sử dụng hàm chuyển tiếp copy_from_user() ở dòng 45 để đọc dữ liệu từ application. sau đó tùy thuộc vào giá trị đọc về là “0” hay “1” mà ta quyết định thay đổi trạng thái của led.
Trên đây là những thao tác cơ bản và hầu như bắt buộc phải có trong device driver. Phần tiếp theo ta đề cập đến cách truy cập và điều khiển port/IO để quản lý trạng thái các led trên mini2440. Đây là 4 led tương ứng với 4 chân GPIO trên portB .
Ta cần phải config pin là output qua thanh ghi GPBCON và thay đổi trạng thái của pin qua thanh ghi GPBDAT.
MCU S3C2440 map GPB qua địa chỉ bộ nhớ vật lý là 0×56000010. Tuy nhiên, đây là một real address và bạn phải luôn làm việc với kernel thông qua các virtual address (địa chỉ ảo) .Xem ở kernel source,ta sẽ tìm thấy địa chỉ virtal address được gán với actual adress là 0xFB000010 . xem trong file “arch/arm/mach-s3c2410/include/mach/regs-gpio.h”.
Chúng ta cùng xem đoạn code bên dưới đây :
BIÊN DỊCH VÀ TEST DRIVER
Phần phương pháp tạo makefile mình chưa đề cập ở đây mà sẽ giành nó cho những bài viết sau. Ở đây mình sẽ đưa ra 1 makefile có sẵn như sau :
Sau khi biên dịch driver trên ta sẽ có được file leds.ko. Tiến hành chép file driver này xuống thư mục trên kit mini2440 theo đường dẫn sau : /lib/modules/2.6.32.2-FriendlyARM/kernel/drivers/char/
* Từng bước thao tác như sau :
– Đầu tiên ta insert driver này vào kernel ,nếu thành công sẽ có 1 thông báo xuất hiện :
$ Insmod / lib / modules / 2.6.32.2-FriendlyARM / kernel / drivers / char / leds.ko
LED driver initialized.
- Tiếp theo ta kiểm tra số định danh “major number” được kernel cung cấp cho driver sử dụng lệnh sau (ở đây kernel cung cấp cho ta 1 số định danh là 253) :
$ grep leds / proc / devices
253 leds
– Khi đã có “major number” trong tay ta tiến hành tạo tất cả các device file cần thiết với lệnh sau:
$ mknod / dev / led1 c 253 0
$ mknod / dev / LED2 c 253 1
$ mknod / dev / LED3 c 253 2
$ mknod / dev / LED4 c 253 3
$ chmod a + w / dev / led *
– Rồi ,bây giờ đến lúc test led. Để bật led sáng chỉ cần send 1 ký tự “1” . Sử dụng lệnh sau để bật led trên led1 và led3 :
$ echo “1″ > / dev / led1
$ echo “1″ > / dev / LED3
và khi mún tắt led thì ngược lại chỉ cần send ký tự “0” :
$ echo “0″ > / dev / LED3
– Để kiểm tra trạng thái hiện tại của led ta sử dụng lệnh “cat” ,ví dụ : cat /dev/led1
- Cuối cùng để gỡ bỏ driver ra khỏi kernel , ta sử dụng lệnh rmmod như sau :
$ Rmmod leds
leds Exiting driver.
Toàn bộ code của driver :
#include "linux/fs.h"
#include "linux/cdev.h"
#include "linux/module.h"
#include "linux/kernel.h"
#include "linux/device.h"
#include "asm/uaccess.h"
#include "asm/io.h"
#include "linux/slab.h"
#define DEVICE_NAME "leds" #define NUM_LEDS 4
#define LED_ON 1
#define LED_OFF 0
#define GPB_BASE 0xFB000010
#define GPBCON GPB_BASE #define GPBDAT GPB_BASE + 4
#define CLEAR_PORT_MASK 0xFFFC03FF
#define SET_WRITE_PORT_MASK 0x00015400
/* prototypes */
int leds_open(struct inode *inode, struct file *file);
ssize_t leds_read(struct file *file, char *buf, size_t count, loff_t *ppos);
ssize_t leds_write(struct file *file, const char *buf, size_t count, loff_t *ppos);
int leds_release(struct inode *inode, struct file *file);
/* per led structure */
struct leds_device {
int number;
int status;
struct cdev cdev;
} leds_dev[NUM_LEDS];
/* file operations structure */
static struct file_operations leds_fops = {
.owner = THIS_MODULE,
.open = leds_open,
.release = leds_release,
.read = leds_read,
.write = leds_write, };
/* leds driver major number */
static dev_t leds_dev_number;
/* initialize led port - GPB *
/ void initLedPort(void)
{
void __iomem *base = (void __iomem *)GPBCON;
u32 port = __raw_readl(base);
port &= CLEAR_PORT_MASK;
port |= SET_WRITE_PORT_MASK;
__raw_writel(port, base);
}
/* change led status */
void changeLedStatus(int led_num, int status)
{
void __iomem *base = (void __iomem *)GPBDAT;
u32 mask, data;
data = __raw_readl(base);
mask = 0x01 << (4 + led_num);
switch (status) {
case LED_ON:
mask = ~mask;
data &= mask;
break;
case LED_OFF:
data |= mask;
break;
}
__raw_writel(data, base);
}
/* driver initialization */
int __init leds_init(void)
{
int ret, i;
/* request device major number */
if ((ret = alloc_chrdev_region(&leds_dev_number, 0, NUM_LEDS, DEVICE_NAME) < 0)) {
printk(KERN_DEBUG "Error registering device!\n");
return ret;
}
/* init leds GPIO port */
initLedPort();
/* init each led device */
for (i = 0; i < NUM_LEDS; i++) {
/* init led status */
leds_dev[i].number = i + 1;
leds_dev[i].status = LED_OFF;
/* connect file operations to this device */
cdev_init(&leds_dev[i].cdev, &leds_fops);
leds_dev[i].cdev.owner = THIS_MODULE;
/* connect major/minor numbers */
if ((ret = cdev_add(&leds_dev[i].cdev, (leds_dev_number + i), 1))) {
printk(KERN_DEBUG "Error adding device!\n");
return ret;
}
/* init led status */
changeLedStatus(leds_dev[i].number, LED_OFF);
}
printk("Leds driver initialized.\n");
return 0;
}
/* driver exit */
void __exit leds_exit(void) {
/* release major number */
unregister_chrdev_region(leds_dev_number, NUM_LEDS);
printk("Exiting leds driver.\n");
}
/* open led file */
int leds_open(struct inode *inode, struct file *file)
{
struct leds_device *leds_devp;
/* get cdev struct */
leds_devp = container_of(inode->i_cdev, struct leds_device, cdev);
/* save cdev pointer */
file->private_data = leds_devp;
/* return OK */
return 0;
}
/* close led file */
int leds_release(struct inode *inode, struct file *file)
{
/* return OK */
return 0;
}
/* read led status */
ssize_t leds_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
struct leds_device *leds_devp = file->private_data;
if (leds_devp->status == LED_ON) {
if (copy_to_user(buf, "1", 1))
return -EIO;
}
else {
if (copy_to_user(buf, "0", 1))
return -EIO;
}
return 1;
}
ssize_t leds_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
struct leds_device *leds_devp = file->private_data;
char kbuf = 0;
if (copy_from_user(&kbuf, buf, 1)) {
return -EFAULT;
}
if (kbuf == '1') {
changeLedStatus(leds_devp->number, LED_ON);
leds_devp->status = LED_ON;
}
else if (kbuf == '0') {
changeLedStatus(leds_devp->number, LED_OFF);
leds_devp->status = LED_OFF;
}
return count;
}
module_init(leds_init);
module_exit(leds_exit);
MODULE_AUTHOR("hethongnhung.com");
MODULE_LICENSE("GPL");
Tác giả :Pham Van Dong
Bạn có đam mê ngành thiết kế vi mạch và bạn muốn có mức lương 1000 usd cùng lúc bạn
đang muốn tìm một Trung tâm để học vậy hãy đến với ngành vi mạch tại SEMICON
HotLine: 0972 800 931 or 0938 838 404 Ms Duyên