驅動程序:
- #include <linux/module.h> //內核模塊頭文件
- #include <linux/moduleparam.h> //內核模塊參數頭文件
- #include <linux/kernel.h> //printk頭文件
-
- #include<asm/io.h>//ioremap需要
- //包含有可裝載模塊需要的大量符合和函數的定義;
- #include <linux/init.h>
- //指定初始化和清除函數;
-
- //struct file_operactions 相關頭文件
- #include <linux/fs.h>
- #include <asm/uaccess.h>
-
- //struct cdev 相關頭文件
- #include <linux/types.h>
- #include <linux/cdev.h>
-
- //定義設備名稱
- #define DEVICE_NAME "led2"
-
- //定義主次設備號
- static unsigned int LedMajor=0;
- static unsigned int LedMinor=0;
-
- /* 注冊字符設備 */
- static struct cdev *led_cdev;
- static dev_t dev; //設備號
-
- volatile unsigned int long *gpb_con = NULL;
- volatile unsigned int long *gpb_data = NULL;
-
-
- static int led_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
- {
-
- if((cmd>1) | (cmd<0) | (arg>3) | (arg<0))
- return -EINVAL;
-
- switch(cmd)
- {
- case 0:
- *gpb_data&= ~(1<<(arg+5)); //0
- break;
- case 1:
- *gpb_data|= (1<<(arg+5)); //1
- break;
-
- default:
- return -EINVAL;
-
- }
-
- return 0;
- }
-
-
- static int led_open(struct inode *inode, struct file *file)
- {
- printk("led_open\n");
-
- //映射I/O內存
- gpb_con = (volatile unsigned long *)ioremap(0x56000010,16); //0x56000010為GPIOB控制寄存器的物理地址
- gpb_data = gpb_con+1; //這裡+1是加了4個字節,因為指針+1是以指針的長度為單位的(unsigned long *)
-
- /* 配置GPB5,6,7,8為輸出 */
- *gpb_con |= (1<<(5*2)) | (1<<(6*2)) | (1<<(7*2)) | (1<<(8*2));
- /* 初始化為滅 */
- *gpb_data |=(1<<5) | (1<<6) | (1<<7) | (1<<8);
-
- printk("leaving led_open\n");
- return 0;
-
- }
-
- static int led_release(struct inode *inode,struct file *file)
- {
-
- printk("close major=%d, minor=%d\n", imajor(inode), iminor(inode));
- return 0;
- }
-
- static struct file_operations chardev_fops={
- .owner = THIS_MODULE,
- .open = led_open,
- .release = led_release,
- .ioctl = led_ioctl,
- };
-
-
- static int __init dev_init(void)
- {
- int result;
- /*分配設備編號*/
- if(LedMajor)
- {
- dev=MKDEV(LedMajor,LedMinor);//創建設備編號
- result=register_chrdev_region(dev,1,DEVICE_NAME);
- }
- else
- {
- result=alloc_chrdev_region(&dev,LedMinor,1,DEVICE_NAME);
- LedMajor=MAJOR(dev);
- }
- if(result<0)
- {
- printk(KERN_WARNING"LED: cannot get major %d \n",LedMajor);
- return result;
- }
- /* 注冊字符設備 */
- led_cdev=cdev_alloc();//為struct cdev 分配空間
-
- cdev_init(led_cdev,&chardev_fops);//初始化struct cdev
-
- led_cdev->owner=THIS_MODULE;
-
- result=cdev_add(led_cdev,dev,1);
-
- if(result)
- printk("<1>Error %d while register led device!\n",result);
-
- printk("initialzed.\n");
-
- return 0;
- }
-
- static void __exit dev_exit(void)
- {
- unregister_chrdev_region(MKDEV(LedMajor,LedMinor),1);
- cdev_del(led_cdev);
- }
-
- module_init(dev_init);
- module_exit(dev_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Bai");
這段代碼把GPB寄存器的物理地址映射到內存上,再進行操作。
注冊一個獨立的cdev設備的基本過程如下:
1、為struct cdev 分配空間
struct cdev *my_cdev = cdev_alloc();
2、初始化struct cdev ,主要是對 file_operations成員賦值,
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
3、初始化cdev.owner 指針,實現模塊管理時的指針引用
cdev.owner = THIS_MODULE;
4、cdev設置完成後,向內核字符設備數組添加新的struct cdev的信息(在執行這步之前必須確定你對struct cdev的以上設置已經完成)
int cdev_add(struct cdev *dev, dev_t devno, unsigned count)
dev 是 cdev 結構, devno是這個設備響應的第一個設備號, count 是應當關聯到設備的設備號的數目.
5、從系統中移除一個字符設備:
void cdev_del(struct cdev *dev)