#include<linux/module.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include<linux/device.h>
static unsigned int hello_major =0;
static struct cdev *hello_cdev =NULL;
/*6.实现操作硬件的方法*/
static int hello_open(struct inode *inode, struct file *file)
{
printk("hello open!\n");
return 0;
}
static ssize_t hello_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
printk("hello write!\n");
return 0;
}
static ssize_t hello_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
printk("hello read!\n");
return 0;
}
struct file_operations hello_fops ={
.owner =THIS_MODULE,
.open =hello_open,
.write =hello_write,
.read =hello_read,
};
static int hello_init(void)
{
dev_t devno;
/*1.申请设备号*/
//MKDEV(major,minor);
//register_chrdev(unsigned int major, const char * name, const struct file_operations * fops)
if(hello_major)
{
/*1.1静态申请*/
devno =MKDEV(hello_major, 0);
register_chrdev_region(devno, 1, "hello");
}
else
{
/*1.2动态申请*/
alloc_chrdev_region(&devno, 0,1, "hello");
hello_major =MAJOR(devno);
}
/*2. 构造一个struct cdev结构体*/
hello_cdev=cdev_alloc();
cdev_init(hello_cdev, &hello_fops);
hello_cdev->owner=THIS_MODULE,
cdev_add(hello_cdev, MKDEV(hello_major, 0), 1);
/*3. 创建设备节点*/
hello_class=class_create(THIS_MODULE,"hello_class");
class_device_create(hello_class, NULL, MKDEV(hello_major, 0),NULL, "hello");
return 0;
}
static void hello_exit(void)
{
unregister_chrdev_region(MKDEV(hello_major, 0), 1);
cdev_del(hello_cdev);
class_device_destroy(hello_class, MKDEV(hello_major, 0));
class_destroy(hello_class);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char **argv)
{
int fd;
char val =1;
fd =open("/dev/hello",O_RDWR);
if(fd<0)
{
printf("can not open /dev/ hello !\n");
return -1;
}
write(fd,&val,1);
return 0;
}
ifeq ($(KERNELRELEASE),)
KERNELDIR =/home/zgp/linux_kernel/s3c2410/linux-2.6.22.6
PWD =$(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *.ko .tmp_versions *.mod.c
else
obj-m :=hello.o
endif
平台设备驱动机制之源代码实现: //一般在写控制器驱动的时候才会用到
=======================================================================================
1.设备资源层:arch/arm/plat-s3c24xx/devs.c //所有的资源都放在这里面
arch/arm/plat-s3c24xx/devs.c :
====================================
struct resource:表示的是一个资源,irq资源 I/O资源
/*I2C控制器的资源*/
static struct resource s3c_i2c_resource[] = {
[0] = {
.start = S3C24XX_PA_IIC, //0x54000000
.end = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_IIC,
.end = IRQ_IIC,
.flags = IORESOURCE_IRQ,
}
};
struct platform_device s3c_device_i2c = {
.name = "s3c2410-i2c", //最关键的
.id = -1,
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource = s3c_i2c_resource,
};
如何注册资源:arch/arm/mach-s3c2410/mach-smdk2410.c
===============================================================
static struct platform_device *smdk2410_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
&s3c_device_sdi
};
入口函数
smdk2410_init
platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));
for (i = 0; i < num; i++) {
/*1.将s3c_device_i2c挂入平台设备资源链表
*2.搜索平台驱动链表,调用platform_bus.match函数实现按名字匹配,
*3.如果匹配成功,则调用驱动中的probe函数实际上就是s3c24xx_i2c_probe
*4.如果匹配不成功,它只会讲自己挂入设备链表,不会调用probe函数
*/
platform_device_register(devs[i]);
2.i2c控制器驱动层(控制器设备驱动层)drivers/i2c/i2c-s3c2410.c
static struct platform_driver s3c2410_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.resume = s3c24xx_i2c_resume,
.driver = {
.owner = THIS_MODULE,
.name = "s3c2410-i2c", //名字很重要
},
};
入口函数
module_init(i2c_adap_s3c_init);
i2c_adap_s3c_init
/*1.将s3c2410_i2c_driver结构体挂入平台驱动链表,
*2.搜索平台设备链表,调用platform_bus.match函数实现按名字匹配,
*3.如果匹配成功,则调用驱动中的probe函数实际上就是s3c24xx_i2c_probe
*4.如果匹配不成功,它只会讲自己挂入驱动链表,不会调用probe函数
*/
platform_driver_register(&s3c2410_i2c_driver);
s3c24xx_i2c_probe(struct platform_device *pdev)做了什么:
=======================================================
1.获取资源
/*获取IO资源*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/*获取中断资源*/
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
2.初始化控制器
i2c体系架构分析:
========================================================
1.driver/i2c/algos目录: 协议算法相关的东西
2.driver/i2c/chips目录: i2c设备驱动(e2prom,触摸屏,传感器) //策略性的问题
3.i2c-core.c文件: i2c核心层的东西: 提供注册/注销/调用等等的接口
4.driver/i2c/busses目录: i2c总线驱动(控制器驱动/适配器驱动) //操作硬件的方法
5.i2c-dev.c文件: 提供给上层的通用接口
app: open(),write, read
======================================================================================================================
kernel: sys_open sys_write sys_read
==============================================================================================================
自己写的接口 通用接口
file_operations
.open file_operations(i2cdev_fops)
.write .open =i2cdev_open
.read .read =i2cdev_read
.write =i2cdev_write
.ioctl =i2cdev_ioctl
1.设备驱动层(e2prom): 2.设备驱动(传感器) 3.通用接口:i2c-dev.c分析下面
=============================================== =================================================
i2c子系统核心层(i2c-core.c):
1.提供注册/注销的方法
2.提供通用的函数,结构体
3.提供上层与下层打交道的函数,
如:i2c_transfer,i2c_master_send,i2c_master_recv
===================================================
3.i2c总线驱动层(控制器驱动层)(i2c-s3c2410.c)分析见下面:
===========================================================================================================================
硬件: i2c控制器 i2c-adapter
e2prom 触摸屏 传感器
上层接口:
=========================================================================
i2d-dev.c文件分析:(内核做的)
完成的任务:
1.构建一个file_operations结构体
2.申请主设备号
3.创建一个类
4.构建一个i2c_driver结构体
5.将i2c_driver挂入链表
=========================================================================
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.ioctl = i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
static struct i2c_driver i2cdev_driver = {
.driver = {
.name = "dev_driver",
},
.id = I2C_DRIVERID_I2CDEV,
.attach_adapter = i2cdev_attach_adapter,
.detach_adapter = i2cdev_detach_adapter,
.detach_client = i2cdev_detach_client,
};
入口函数:
i2c_dev_init
res = register_chrdev(I2C_MAJOR(89), "i2c", &i2cdev_fops);
struct cdev
/*创建一个类*/
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
/*加载i2c设备驱动(万能接口),将结构体挂入到一个链表i2c_driver的链表*/
res = i2c_add_driver(&i2cdev_driver);
通用接口的用法:
================================================================
open
sys_open
i2cdev_open
unsigned int minor = iminor(inode);
struct i2c_client *client;
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
i2c_dev = i2c_dev_get_by_minor(minor);
adap = i2c_get_adapter(i2c_dev->adap->nr);
client = kzalloc(sizeof(*client), GFP_KERNEL);
client->driver = &i2cdev_driver;
client->adapter = adap; //适配器下面注册上来的,哪里注册的呢?
file->private_data = client;
ioctl
sys_ioctl
i2cdev_ioctl
struct i2c_client *client = (struct i2c_client *)file->private_data;
client->addr = arg;
write
sys_write
i2cdev_write
struct i2c_client *client = (struct i2c_client *)file->private_data;
copy_from_user(tmp,buf,count)
i2c_master_send(client,tmp,count);
struct i2c_adapter *adap=client->adapter;
struct i2c_msg msg;
msg.addr = client->addr; //从机设备地址(0xa0)
msg.flags = client->flags & I2C_M_TEN; //标号:表示的是读,还是写 0:表示写 1:表示读
msg.len = count; //要传送的字节的个数
msg.buf = (char *)buf; //要传送的数据
i2c_transfer(adap, &msg, 1); //次接口是在核心层实现的
/*开始调用底层的操作方法*/
if (adap->algo->master_xfer) {
ret = adap->algo->master_xfer(adap,msgs,num); ---------> //s3c24xx_i2c_xfer
read
sys_read
i2cdev_read
struct i2c_client *client = (struct i2c_client *)file->private_data;
ret = i2c_master_recv(client,tmp,count);
struct i2c_adapter *adap=client->adapter;
struct i2c_msg msg;
msg.addr = client->addr; //从机设备地址(0xa0)
msg.flags = client->flags & I2C_M_TEN; //标号:表示的是读,还是写 0:表示写 1:表示读
msg.flags |=I2C_M_RD;
msg.len = count; //要传送的字节的个数
msg.buf = (char *)buf; //要传送的数据
i2c_transfer(adap, &msg, 1); //次接口是在核心层实现的
if (adap->algo->master_xfer) {
ret = adap->algo->master_xfer(adap,msgs,num); ---------> //s3c24xx_i2c_xfer
copy_to_user(buf,tmp,count)
i2c-s3c2410.c分析:底层总线驱动分析:
=============================================================================================
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
static struct s3c24xx_i2c s3c24xx_i2c = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),
.wait = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),
.tx_setup = 50,
.adap = {
.name = "s3c2410-i2c",
.owner = THIS_MODULE,
.algo = &s3c24xx_i2c_algorithm,
.retries = 2,
.class = I2C_CLASS_HWMON,
},
};
1.利用平台设备驱动机制
2.调用probe
入口函数:
i2c_adap_s3c_init
ret = platform_driver_register(&s3c2410_i2c_driver);
s3c24xx_i2c_probe(struct platform_device *pdev)
/*1.构建一个本地结构体,帮我们构建了一个操作硬件的方法,而且次方法放在i2c_adapter结构体里面*/
struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
struct i2c_msg *msg;
struct i2c_adapter adap; /*一个i2c控制器*/
const struct i2c_algorithm *algo; /*访问总线的协议算法 */
int (*master_xfer) ---->s3c24xx_i2c_xfer /*底层的操作方法*/
struct device *dev;
/*2.初始化控制器*/
/*2.1 使能时钟*/
i2c->clk = clk_get(&pdev->dev, "i2c");
clk_enable(i2c->clk);
/*2.2 获取IO资源*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/*2.3 上面申请了IO内存后,在此要映射*/
i2c->regs = ioremap(res->start, (res->end-res->start)+1);
/*2.4 初始化I2C控制器*/
ret = s3c24xx_i2c_init(i2c);
/*2.4.1 设置管脚功能*/
/*2.4.2 配置时钟频率*/
/*2.4.3 使能中断,使能响应,使能i2c_控制器*/
/*2.5 获取中断资源*/
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
/*2.6 申请中断*/
ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,
pdev->name, i2c);
/*3.注册这套方法/实际上就是注册i2c_adapter*/
/*添加I2C适配器*/
ret = i2c_add_adapter(&i2c->adap);
res = idr_get_new_above(&i2c_adapter_idr, adapter,
__i2c_first_dynamic_bus_num, &id);
adapter->nr = id;
i2c_register_adapter(adapter);
跟i2c子系统相关的结构体:
============================================================
1.struct i2c_driver //描述的是一个i2c设备驱动
2.struct i2c_client *client; //描述的是一个i2c设备(如:e2prom)
3.struct i2c_adapter *adap; //描述的是一个i2c控制器(适配器)
4.struct i2c_dev *i2c_dev;
5.struct i2c_msg msg; //描述的是要发送的数据信息结构体
跟i2c子系统相关的函数接口:
===========================================================
1.i2c_master_send(struct i2c_client *client,const char *buf ,int count)
2.i2c_transfer(adap, &msg, 1);
3.i2c_master_recv(struct i2c_client *client, char *buf ,int count)
4.i2c_add_adapter(&i2c->adap); /*注册i2c适配器*/
5.i2c_add_driver(&i2cdev_driver); /*注册i2c通用接口驱动*/
如何用正缓冲设备
1.app:open
sys_open
进入最上层的fbmem.c
file_operations--->fb_fops.open =fb_open
int fbidx = iminor(inode);
struct fb_info *info;
/*从数组中获取fb_info*/
info = registered_fb[fbidx])
/*将fb_info放入file的私有数据*/
file->private_data = info;
/*问:fb_info->fbops->fb_open是否存在
*究竟是否存在,需要看底层
*/
if (info->fbops->fb_open)
res = info->fbops->fb_open(info,1);
2.app:ioctl 目的:获取某些参数,比如:分辨率,一个像素点用几个字节描述
sys_ioctl
进入最上层的fbmem.c
file_operations--->fb_fops.ioctl =fb_ioctl
int fbidx = iminor(inode);
/*再次获取fb_info*/
struct fb_info *info = registered_fb[fbidx];
/*获取底层的操作方法*/
struct fb_ops *fb = info->fbops;
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
/*获取可变参数*/
case FBIOGET_VSCREENINFO:
return copy_to_user(argp, &info->var,
sizeof(var)) ? -EFAULT : 0;
3.app:mmap
sys_mmap
进入最上层的fbmem.c
file_operations--->fb_fops.mmap =fb_mmap
int fbidx = iminor(file->f_path.dentry->d_inode);
struct fb_info *info = registered_fb[fbidx];
struct fb_ops *fb = info->fbops;
/*问:fb_info->fbops->fb_mmap是否存在
*究竟是否存在,需要看底层
*/
if (fb->fb_mmap)
res = fb->fb_mmap(info, vma);
/* frame buffer物理地址*/
start = info->fix.smem_start;
/*按页对齐*/
len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot)
4.app:在用户空间的虚拟地址中进行画图
驱动怎么做的:
1.上层:(fbmem.c帧缓冲设备的通用接口层)
==================================================================
static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read,
.write = fb_write,
.ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = fb_compat_ioctl,
#endif
.mmap = fb_mmap,
.open = fb_open,
.release = fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
.get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
.fsync = fb_deferred_io_fsync,
#endif
};
入口函数
fbmem_init
/*1.注册一个字符设备,申请设备号*/
register_chrdev(FB_MAJOR,"fb",&fb_fops(提供给上层的操作方法))
/*2.创建一个类*/
fb_class = class_create(THIS_MODULE, "graphics");
2.底层(lcd控制器驱动层s3c2410fb.c)
==================================================================
入口函数
s3c2410fb_init
platform_driver_register(&s3c2410fb_driver);
s3c2410fb_probe分析:
1.构建一个结构体 //struct fb_info
struct s3c2410fb_info *info
struct fb_info *fbinfo;
获取资源
mach_info = pdev->dev.platform_data;
irq = platform_get_irq(pdev, 0);
ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
使能时钟:
info->clk = clk_get(NULL, "lcd");
if (!info->clk || IS_ERR(info->clk)) {
printk(KERN_ERR "failed to get lcd clock source\n");
ret = -ENOENT;
goto release_irq;
}
clk_enable(info->clk);
2.分配空间
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
3.设置它
3.1 设置固定参数 //fb_fix_screeninfo
3.1.1 只需要这只物理显存,跟物理显存的大小
3.2 设置可变参数 //fb_var_screeninfo
3.2.1 图片的高度和宽度
3.2.2 可视分辨率,虚拟分辨率
3.2.3 时序--->需要配置到寄存器里面的
3.2.4 RGB分别占用那几根线
3.4 设置底层的操作方法
fbinfo->fbops = &s3c2410fb_ops;
3.3 分配显存地址
ret = s3c2410fb_map_video_memory(info);
初始化控制器
ret = s3c2410fb_init_registers(info);
1.1 配置功能管脚 数据管脚时序管脚背光灯管脚
modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask);
modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask);
modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);
1.2 配置lcdcon1-lcdcon5 //配置时钟频率 配置时序 屏幕类型选择
writel(fbi->regs.lcdcon1, S3C2410_LCDCON1);
writel(fbi->regs.lcdcon2, S3C2410_LCDCON2);
writel(fbi->regs.lcdcon3, S3C2410_LCDCON3);
writel(fbi->regs.lcdcon4, S3C2410_LCDCON4);
writel(fbi->regs.lcdcon5, S3C2410_LCDCON5);
1.3 配置显存
s3c2410fb_set_lcdaddr(fbi);
saddr1 = fbi->fb->fix.smem_start >> 1;
saddr2 = fbi->fb->fix.smem_start;
saddr2 += (var->xres * var->yres * var->bits_per_pixel)/8;
saddr2>>= 1;
saddr3 = S3C2410_OFFSIZE(0) | S3C2410_PAGEWIDTH((var->xres * var->bits_per_pixel / 16) & 0x3ff);
writel(saddr1, S3C2410_LCDSADDR1);
writel(saddr2, S3C2410_LCDSADDR2);
writel(saddr3, S3C2410_LCDSADDR3);
1.4 最后使能
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID;
writel(fbi->regs.lcdcon1, S3C2410_LCDCON1);
4.注册
register_frambuffer(struct fb_info)
帧缓冲设备涉及的重要结构体与函数:
=======================================================
struct fb_info
{
/*可变参数*/
struct fb_var_screeninfo var; /* Current var */
{
__u32 xres; /*可视分辨率,实际的屏幕大小*/
__u32 yres;
__u32 xres_virtual; /* virtual resolution */
__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible */
__u32 yoffset; /* resolution */
/*每个像素点占多少位*/
__u32 bits_per_pixel; /* guess what */
/*RGB:565 红绿蓝分别占用哪几根线 */
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */
__u32 height; /* height of picture in mm 240 */
__u32 width; /* width of picture in mm 320 */
/*时序相关的*/
__u32 pixclock; /* pixel clock in ps (pico seconds) */
__u32 left_margin; /* time from sync to picture */
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */
__u32 vsync_len; /* length of vertical sync */
__u32 sync; /* see FB_SYNC_* */
}
/*固定参数*/
struct fb_fix_screeninfo fix; /* Current fix */
{
unsigned long smem_start; /* Start of frame buffer mem 物理显存的起始地址*/
__u32 smem_len; /* Length of frame buffer mem 物理显存的大小*/
__u32 line_length; /* length of a line in bytes */
}
/*底层操作方法*/
struct fb_ops *fbops;
{
}
char __iomem *screen_base; /* Virtual address */
unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */
void *pseudo_palette; /* Fake palette of 16 colors */
}
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
static struct s3c2410fb_mach_info *mach_info;
struct s3c2410fb_mach_info {
unsigned char fixed_syncs; /* do not update sync/border */
/* LCD types */
int type;
/* Screen size */
int width;
int height;
/* Screen info */
struct s3c2410fb_val xres;
struct s3c2410fb_val yres;
struct s3c2410fb_val bpp;
/* lcd configuration registers */
struct s3c2410fb_hw regs;
/* GPIOs */
unsigned long gpcup;
unsigned long gpcup_mask;
unsigned long gpccon;
unsigned long gpccon_mask;
unsigned long gpdup;
unsigned long gpdup_mask;
unsigned long gpdcon;
unsigned long gpdcon_mask;
/* lpc3600 control register */
unsigned long lpcsel;
};
帧缓冲设备涉及的重要函数接口:
=======================================================
register_frambuffer
启动信息:
s3c2410-lcd s3c2410-lcd: no platform data for lcd, cannot attach
mach_info = pdev->dev.platform_data; //但这个设备的Platform_data里面被初始化了些什么值呢?
if (mach_info == NULL)
{
dev_err(&pdev->dev,"no platform data for lcd, cannot attach\n");
return -EINVAL;
}
s3c2410-lcd: probe of s3c2410-lcd failed with error -22
lcd测试程序:
static char *framebuffer_ptr
1.分配两个各缓冲区 buffer bmpfilename
2.打开图片文件
3.读取图片信息放入buffer缓冲区
4.初始化lcdinit_lcd
4.1 开lcd设备
framebuffer_fd = open("/dev/fb0", O_RDWR);
4.2 获取固定参数信息
ioctl(framebuffer_fd, FBIOGET_FSCREENINFO, &finfo)
4.3 获取可变参数
ioctl(framebuffer_fd, FBIOGET_VSCREENINFO, &vinfo)
4.3 mmap进行映射
framebuffer_ptr=(char *) mmap
4.4 清0
memset(framebuffer_ptr,0,screensize);
5.解码将jpg格式的图片解码为bmp格式
LoadJpegFile(buffer, bmpfilename) ;
6.由于lcd只能显示RGB的原始图片,再次解码,将bmp解码为RGB格式
draw_bmp(bmpfilename,(unsigned short *) framebuffer_ptr);
logo制作:
===========================================================
1.制作ppm各式图片
2.拷贝ppm图片到内核的driver/video/logo目录下
3.修改driver/video/logo目录下Makfile
在其中添加
obj-$(CONFIG_LOGO_FARSIGHT_CLUT224) += logo_farsight_clut224.o
4.修改driver/video/logo目录下Kconfig
在其中添加
config LOGO_FARSIGHT_CLUT224
bool "farsight Linux logo"
default y
5.修改logo.c文件
添加
extern const struct linux_logo logo_farsight_clut224;
#ifdef CONFIG_LOGO_FARSIGHT_CLUT224
/* Generic Linux logo */
logo = &logo_farsight_clut224;
#endif
6.执行make menuconfig
-> Device Drivers │
-> Graphics support
[*] farsight Linux logo (NEW)