什么是paltform总线:
一个现实的linux设备和驱动通常都需要挂接在一种总线上,比较常见的总线有USB、PCI总线等。但是,在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设却不依附与此类总线。基于这样的背景下,2.6内核加入了platform虚拟总线。platform机制将设备本身的资源注册进内核,有内核统一管理,在驱动程序使用这些资源时使用统一的接口,这样提高了程序的可移植性。
在platform总线上注册设备和驱动,只要定义指定的结构体后调用platform给出的注册函数就可以了
linux在系统启动时就注册了platform总线,看内核代码:
/*drivers/base/platform.c*/
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev;
pdev = container_of(dev, struct platform_device, dev);
return (strcmp(pdev->name, drv->name) == 0); //配对函数检验名字是否一致
}
。。。。。
struct bus_type platform_bus_type = {
.name = "platform", //定义了总线名字为platform,总线注册后新建目录sys/bus/platform
.dev_attrs = platform_dev_attrs,
.match = platform_match, //指定配对函数
.uevent = platform_uevent,
.pm = PLATFORM_PM_OPS_PTR,
};
总线中定义了成员名字和match函数,当有总线或者设备注册到platform总线时,内核自动调用match函数
platform设备对应的结构体paltform_device:
/*linux/platform_device.h*/
struct platform_device {
const char * name; //设备的名字,这将代替device->dev_id,用作sys/device下显示的目录名
int id; //设备id,用于给插入给该总线并且具有相同name的设备编号,如果只有一个设备的话填-1。
struct device dev; //结构体中内嵌的device结构体。
u32 num_resources; //资源数。
struct resource * resource; //用于存放资源的数组。
};
platform_device的注册和注销使用以下函数:
/*drivers/base/platform.c*/
int platform_device_register(struct platform_device *pdev) //同样的,需要判断返回值
。。。
void platform_device_unregister(struct platform_device *pdev)
会在/sys/device/目录下创建一个以name命名的目录,并且创建软连接到/sys/bus/platform/device下。
platform驱动对应的结构体paltform_driver:
/*linux/platform_device.h*/
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};
platform_driver结构体内嵌了device_driver,并且实现了prob、remove等操作。其实,当内核需要调用probe函数时,它会调用driver->probe,在dricer->probe中再调用platform_driver->probe。
platform_driver的注册和注销使用以下函数:
/*drivers/base/platform.c*/
int platform_driver_register(struct platform_driver *drv)
。。。。。
void platform_driver_unregister(struct platform_driver *drv)
注册成功后内核会在/sys/bus/platform/driver/目录下创建一个名字为driver->name的目录。
更多详细的关于platform或者platled的了解可以参考
http://blog.chinaunix.net/uid-25014876-id-111745.html
http://blog.youkuaiyun.com/zouleideboke/article/details/68969544
platform_LED驱动的代码:
#include "s3c_driver.h"
#define DRV_AUTHOR "fanmaolin <fanmaolinn@gmail.com>"
#define DRV_DESC "S3C24XX LED driver"
/* Driver version*/
#define DRV_MAJOR_VER 1
#define DRV_MINOR_VER 0
#define DRV_REVER_VER 0
#define DEV_NAME DEV_LED_NAME
#ifndef DEV_MAJOR
#define DEV_MAJOR 0 /* dynamic major by default */
#endif
#define TIMER_TIMEOUT 40
static int debug = DISABLE;
static int dev_major = DEV_MAJOR;
static int dev_minor = 0;
/* ============================ Platform Device part ===============================*/
/* LED hardware informtation structure*/
struct s3c_led_info
{
unsigned char num; /* The LED number */
unsigned int gpio; /* Which GPIO the LED used */
unsigned char active_level; /* The GPIO pin level(HIGHLEVEL or LOWLEVEL) to turn on or off */
unsigned char status; /* Current LED status: OFF/ON */
unsigned char blink; /* Blink or not */
};
/* The LED platform device private data structure */
struct s3c_led_platform_data
{
struct s3c_led_info *leds;
int nleds;
};
/* LED hardware informtation data*/
static struct s3c_led_info s3c_leds[] = {
[0] = {
.num = 1,
.gpio = S3C2410_GPB(5),
.active_level = LOWLEVEL,
.status = OFF,
.blink = ENABLE,
},
[1] = {
.num = 2,
.gpio = S3C2410_GPB(6),
.active_level = LOWLEVEL,
.status = OFF,
.blink = DISABLE,
},
[2] = {
.num = 3,
.gpio = S3C2410_GPB(8),
.active_level = LOWLEVEL,
.status = OFF,
.blink = DISABLE,
},
[3] = {
.num = 4,
.gpio = S3C2410_GPB(10),
.active_level = LOWLEVEL,
.status = OFF,
.blink = DISABLE,
},
};
/* The LED platform device private data */
static struct s3c_led_platform_data s3c_led_data = { //定义LED灯的结构体信息
.leds = s3c_leds, //LED灯的每条信息
.nleds = ARRAY_SIZE(s3c_leds), //灯数
};
struct led_device
{
struct s3c_led_platform_data *data;
struct cdev cdev;
struct class *dev_class;
struct timer_list blink_timer;
} led_device;
static void platform_led_release(struct device * dev) //撤销LED灯的函数
{
int i;
struct s3c_led_platform_data *pdata = dev->platform_data;
dbg_print("%s():%d\n", __FUNCTION__,__LINE__);
/* Turn all LED off */
for(i=0; i<pdata->nleds; i++)
{
s3c2410_gpio_setpin(pdata->leds[i].gpio, ~pdata->leds[i].active_level);
}
}
static struct platform_device s3c_led_device = { //定义设备结构体
.name = "s3c_led",
.id = 1,
.dev =
{
.platform_data = &s3c_led_data,
.release = platform_led_release,
},
};
/* ===================== led device driver part ===========================*/
void led_timer_handler(unsigned long data)
{
int i;
struct s3c_led_platform_data *pdata = (struct s3c_led_platform_data *)data;
for(i=0; i<pdata->nleds; i++)
{
if(ON == pdata->leds[i].status)
{
s3c2410_gpio_setpin(pdata->leds[i].gpio, pdata->leds[i].active_level);
}
else
{
s3c2410_gpio_setpin(pdata->leds[i].gpio, ~pdata->leds[i].active_level);
}
if(ENABLE == pdata->leds[i].blink ) /* LED should blink */
{
/* Switch status between 0 and 1 to turn LED ON or off */
pdata->leds[i].status = pdata->leds[i].status ^ 0x01;
}
mod_timer(&(led_device.blink_timer), jiffies + TIMER_TIMEOUT); //闪烁时间设置
}
}
static int led_open(struct inode *inode, struct file *file) //LED开启函数
{
struct led_device *pdev ;
struct s3c_led_platform_data *pdata;
pdev = container_of(inode->i_cdev,struct led_device, cdev);
/*container_of 在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,
就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址*/
pdata = pdev->data;
file->private_data = pdata;
return 0;
}
static int led_release(struct inode *inode, struct file *file) //关闭led
{
return 0;
}
static void print_led_help(void) //help信息
{
printk("Follow is the ioctl() command for LED driver:\n");
printk("Enable Driver debug command: %u\n", SET_DRV_DEBUG);
printk("Get Driver verion command : %u\n", GET_DRV_VER);
printk("Turn LED on command : %u\n", LED_ON);
printk("Turn LED off command : %u\n", LED_OFF);
printk("Turn LED blink command : %u\n", LED_BLINK);
}
/* compatible with kernel version >=2.6.38*/
static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct s3c_led_platform_data *pdata = file->private_data;
switch (cmd)
{
case SET_DRV_DEBUG:
dbg_print("%s driver debug now.\n", DISABLE == arg ? "Disable" : "Enable");
debug = (0==arg) ? DISABLE : ENABLE;
break;
case GET_DRV_VER:
print_version(DRV_VERSION);
return DRV_VERSION;
case LED_OFF:
if(pdata->nleds <= arg)
{
printk("LED%ld doesn't exist\n", arg);
return -ENOTTY;
}
pdata->leds[arg].status = OFF;
pdata->leds[arg].blink = DISABLE;
break;
case LED_ON:
if(pdata->nleds <= arg)
{
printk("LED%ld doesn't exist\n", arg);
return -ENOTTY;
}
pdata->leds[arg].status = ON;
pdata->leds[arg].blink = DISABLE;
break;
case LED_BLINK:
if(pdata->nleds <= arg)
{
printk("LED%ld doesn't exist\n", arg);
return -ENOTTY;
}
pdata->leds[arg].blink = ENABLE;
pdata->leds[arg].status = ON;
break;
default:
dbg_print("%s driver don't support ioctl command=%d\n", DEV_NAME, cmd);
print_led_help();
return -EINVAL;
}
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.unlocked_ioctl = led_ioctl, /* compatible with kernel version >=2.6.38*/
};
static int s3c_led_probe(struct platform_device *dev) //设备和驱动相互识别是调用由内核把platform_device传到probe函数
{
struct s3c_led_platform_data *pdata = dev->dev.platform_data;
int result = 0;
int i;
dev_t devno;
/* Initialize the LED status */
for(i=0; i<pdata->nleds; i++)
{
s3c2410_gpio_cfgpin(pdata->leds[i].gpio, S3C2410_GPIO_OUTPUT);
if(ON == pdata->leds[i].status)
{
s3c2410_gpio_setpin(pdata->leds[i].gpio, pdata->leds[i].active_level);
}
else
{
s3c2410_gpio_setpin(pdata->leds[i].gpio, ~pdata->leds[i].active_level);
}
}
/* Alloc the device for driver */
if (0 != dev_major)
{
devno = MKDEV(dev_major, dev_minor);
result = register_chrdev_region(devno, 1, DEV_NAME);
}
else //动态分配主次设备号
{
result = alloc_chrdev_region(&devno, dev_minor, 1, DEV_NAME);
dev_major = MAJOR(devno);
}
/* Alloc for device major failure */
if (result < 0)
{
printk("%s driver can't get major %d\n", DEV_NAME, dev_major);
return result;
}
/* Initialize button structure and register cdev*/
memset(&led_device, 0, sizeof(led_device));
led_device.data = dev->dev.platform_data;
cdev_init (&(led_device.cdev), &led_fops);
led_device.cdev.owner = THIS_MODULE;
result = cdev_add (&(led_device.cdev), devno , 1);
if (result)
{
printk (KERN_NOTICE "error %d add %s device", result, DEV_NAME);
goto ERROR;
}
led_device.dev_class = class_create(THIS_MODULE, DEV_NAME);
if(IS_ERR(led_device.dev_class))
{
printk("%s driver create class failture\n",DEV_NAME);
result = -ENOMEM;
goto ERROR;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
device_create(led_device.dev_class, NULL, MKDEV(dev_major,0), NULL, DEV_NAME);
#else
device_create (led_device.dev_class, NULL, MKDEV(dev_major,0), DEV_NAME);
#endif
/* Initial the LED blink timer 初始化闪烁定时器 */
init_timer(&(led_device.blink_timer));
led_device.blink_timer.function = led_timer_handler;
led_device.blink_timer.data = (unsigned long)pdata;
led_device.blink_timer.expires = jiffies + TIMER_TIMEOUT;//jiffies为当前时间
add_timer(&(led_device.blink_timer));
printk("S3C %s driver version %d.%d.%d initiliazed.\n", DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER, DRV_REVER_VER);
return 0;
ERROR:
printk("S3C %s driver version %d.%d.%d install failure.\n", DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER, DRV_REVER_VER);
cdev_del(&(led_device.cdev));
unregister_chrdev_region(devno, 1);
return result;
}
static int s3c_led_remove(struct platform_device *dev) //释放主次设备号给Linux内核
{
dev_t devno = MKDEV(dev_major, dev_minor);
del_timer(&(led_device.blink_timer));
cdev_del(&(led_device.cdev));
//for()
device_destroy(led_device.dev_class, devno);
class_destroy(led_device.dev_class);
unregister_chrdev_region(devno, 1);
printk("S3C %s driver removed\n", DEV_NAME);
return 0;
}
static struct platform_driver s3c_led_driver = { //定义总线驱动结构体
.probe = s3c_led_probe, //注册设备主次设备号
.remove = s3c_led_remove,
.driver = {
.name = "s3c_led",
.owner = THIS_MODULE,
},
};
static int __init s3c_led_init(void)
{
int ret = 0;
ret = platform_device_register(&s3c_led_device);
if(ret)
{
printk(KERN_ERR "%s:%d: Can't register platform device %d\n", __FUNCTION__,__LINE__, ret);
goto fail_reg_plat_dev;
}
dbg_print("Regist S3C LED Platform Device successfully.\n");
ret = platform_driver_register(&s3c_led_driver);
if(ret)
{
printk(KERN_ERR "%s:%d: Can't register platform driver %d\n", __FUNCTION__,__LINE__, ret);
goto fail_reg_plat_drv;
}
dbg_print("Regist S3C LED Platform Driver successfully.\n");
return 0;
fail_reg_plat_drv:
platform_driver_unregister(&s3c_led_driver);
fail_reg_plat_dev:
return ret;
}
static void s3c_led_exit(void)
{
dbg_print("%s():%d remove LED platform drvier\n", __FUNCTION__,__LINE__);
platform_driver_unregister(&s3c_led_driver);
dbg_print("%s():%d remove LED platform device\n", __FUNCTION__,__LINE__);
platform_device_unregister(&s3c_led_device);
}
module_init(s3c_led_init);
module_exit(s3c_led_exit);
module_param(debug, int, S_IRUGO);
module_param(dev_major, int, S_IRUGO);
module_param(dev_minor, int, S_IRUGO);
MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:S3C24XX_led");
Makefile:
obj-m:=plat_led.c
C=/opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-gcc
KDIR?=/home/fanmaolin/fl2440/kernel/linux-3.0
obj-m:=plat_led.o
default:
$(MAKE) -C $(KDIR) M=`pwd` modules
make clean
clean:
rm -f *.o *mod.c *.order *.symvers
make
测试程序vim plat_ioctl.c:
1 /*********************************************************************************
2 * Copyright: (C) 2017 fanmaolin<fanmaolinn@gmail.com>
3 * All rights reserved.
4 *
5 * Filename: plat_testled.c
6 * Description: This file
7 *
8 * Version: 1.0.0(03/25/2017)
9 * Author: fanmaolin <fanmaolinn@gmail.com>
10 * ChangeLog: 1, Release initial version on "03/25/2017 05:24:09 PM"
11 *
12 ********************************************************************************/
13
14 #include <stdio.h>
15 #include <stdarg.h>
16 #include <fcntl.h>
17 #include <sys/types.h>
18 #include <errno.h>
19 #include <sys/ioctl.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #define PLATDRV_MAGIC 0x60
24 #define LED_OFF _IO (PLATDRV_MAGIC, 0x18)
25 #define LED_ON _IO (PLATDRV_MAGIC, 0x19)
26 #define LED_BLINK _IO (PLATDRV_MAGIC, 0x1A)
27 #define DEVNAMELEN 10
28 int main (int argc,char **argv)
29 {
30 int fd;
31 fd=open("dev/led",O_RDWR);
32 ioctl(fd,LED_ON,0);
33 ioctl(fd,LED_BLINK,1);
34 ioctl(fd,LED_ON,2);
35 ioctl(fd,LED_BLINK,4);
36 close(fd);
37 return 0;
38 }
[fanmaolin@Centeros platform]$ /opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-gcc plat_testled.c
编译后在开发板上操作,与之前不同的是这一次程序中有了自动挂载的代码:
led_device.dev_class = class_create(THIS_MODULE, DEV_NAME);
if(IS_ERR(led_device.dev_class))
{
printk("%s driver create class failture\n",DEV_NAME);
result = -ENOMEM;
goto ERROR;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
device_create(led_device.dev_class, NULL, MKDEV(dev_major,0), NULL, DEV_NAME);
#else
device_create (led_device.dev_class, NULL, MKDEV(dev_major,0), DEV_NAME);
#endif
a.out 100% |*******************************| 5139 0:00:00 ETA
>: insmod plat_led.ko
S3C led driver version 1.0.0 initiliazed.
>: ./a.out
你会看到1、3灯常亮,2、4灯闪烁
问题总结分析:
s3c_drive.h:
#ifndef __S3C_DRIVER_H
#define __S3C_DRIVER_H
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kref.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/string.h>
#include <linux/bcd.h>
#include <linux/miscdevice.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/sysfs.h>
#include <linux/proc_fs.h>
#include <linux/rtc.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <asm/uaccess.h>
#include <asm/delay.h>
#include <linux/syscalls.h> /* For sys_access*/
#include <linux/platform_device.h>
#include <linux/unistd.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/irq.h>
#include <mach/regs-gpio.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
#include <mach/hardware.h>
#include <mach/gpio.h>
#include <asm/irq.h>
#else
#include <asm-arm/irq.h>
#include <asm/arch/gpio.h>
#include <asm/arch/hardware.h>
#endif
#include "plat_ioctl.h"
/* ===========================================================================
* * S3C24XX device driver common macro definition
* *===========================================================================*/
#define ENPULLUP 1
#define DISPULLUP 0
#define HIGHLEVEL 1
#define LOWLEVEL 0
#define INPUT 1
#define OUTPUT 0
#define OFF 0
#define ON 1
#define ENABLE 1
#define DISABLE 0
#define TRUE 1
#define FALSE 0
/* ===========================================================================
* * S3C24XX device driver name and Major number define
* *===========================================================================*/
#define DEV_LED_NAME "led"
#define DEV_LED_MAJOR 203
#define DEV_BUTTON_NAME "button"
#define DEV_BUTTON_MAJOR "211"
#define DEV_ADC_NAME "adc"
#define DEV_ADC_MAJOR "212"
/* ***** Bit Operate Define *****/
#define SET_BIT(data, i) ((data) |= (1 << (i))) /* Set the bit "i" in "data" to 1 */
#define CLR_BIT(data, i) ((data) &= ~(1 << (i))) /* Clear the bit "i" in "data" to 0 */
#define NOT_BIT(data, i) ((data) ^= (1 << (i))) /* Inverse the bit "i" in "data" */
#define GET_BIT(data, i) ((data) >> (i) & 1) /* Get the value of bit "i" in "data" */
#define L_SHIFT(data, i) ((data) << (i)) /* Shift "data" left for "i" bit */
#define R_SHIFT(data, i) ((data) >> (i)) /* Shift "data" Right for "i" bit */
/* ===========================================================================
* * S3C24XX device driver common function definition
* *===========================================================================*/
#define SLEEP(x) {DECLARE_WAIT_QUEUE_HEAD (stSleep); if (10 > x) mdelay ((x * 1000)); \
else wait_event_interruptible_timeout (stSleep, 0, (x / 10));}
#define VERSION_CODE(a,b,c) ( ((a)<<16) + ((b)<<8) + (c))
#define DRV_VERSION VERSION_CODE(DRV_MAJOR_VER, DRV_MINOR_VER, DRV_REVER_VER)
#define MAJOR_VER(a) ((a)>>16&0xFF)
#define MINOR_VER(a) ((a)>>8&0xFF)
#define REVER_VER(a) ((a)&0xFF)
#define dbg_print(format,args...) if(DISABLE!=debug) \
{printk("[kernel] ");printk(format, ##args);}
static inline void print_version(int version)
{
#ifdef __KERNEL__
printk("%d.%d.%d\n", MAJOR_VER(version), MINOR_VER(version), REVER_VER(version));
#else
printf("%d.%d.%d\n", MAJOR_VER(version), MINOR_VER(version), REVER_VER(version));
#endif
}
#endif /* __S3C_DRIVER_H */
plat_ioctl.h:
- #ifndef __PLAT_IOCTL_H
- #define __PLAT_IOCTL_H
- #include <asm/ioctl.h>
- /*===========================================================================
- * * Common ioctl command definition
- * *===========================================================================*/
- #define PLATDRV_MAGIC 0x60
- /*===========================================================================
- * * ioctl command for all the drivers 0x01~0x0F
- * *===========================================================================*/
- /*args is enable or disable*/
- #define SET_DRV_DEBUG _IO (PLATDRV_MAGIC, 0x01)
- #define GET_DRV_VER _IO (PLATDRV_MAGIC, 0x02)
- /*===========================================================================
- * * ioctl command for few ioctl() cmd driver 0x10~0x2F
- * *===========================================================================*/
- /* LED driver */
- #define LED_OFF _IO (PLATDRV_MAGIC, 0x18)
- #define LED_ON _IO (PLATDRV_MAGIC, 0x19)
- #define LED_BLINK _IO (PLATDRV_MAGIC, 0x1A)
- #define ADC_SET_CHANNEL _IO (PLATDRV_MAGIC, 0x1B)
- /*===========================================================================
- * * ioctl command for GPRS driver 0x30~0x4F
- * *===========================================================================*/
- #define GPRS_POWERDOWN _IO (PLATDRV_MAGIC, 0x30)
- #define GPRS_POWERON _IO (PLATDRV_MAGIC, 0x31)
- #define GPRS_RESET _IO (PLATDRV_MAGIC, 0x32)
- #define GPRS_POWERMON _IO (PLATDRV_MAGIC, 0x33)
- #define GPRS_CHK_SIMDOOR _IO (PLATDRV_MAGIC, 0x36)
- #define GPRS_SET_DTR _IO (PLATDRV_MAGIC, 0x37)
- #define GPRS_SET_RTS _IO (PLATDRV_MAGIC, 0x38)
- #define GPRS_GET_RING _IO (PLATDRV_MAGIC, 0x39)
- #define SET_PWUP_TIME _IO (PLATDRV_MAGIC, 0x3A)
- #define SET_PWDOWN_TIME _IO (PLATDRV_MAGIC, 0x3B)
- #define SET_RESET_TIME _IO (PLATDRV_MAGIC, 0x3C)
- #define GPRS_CHK_MODEL _IO (PLATDRV_MAGIC, 0x3E)
- /*===========================================================================
- * * ioctl command for EEPROM driver 0x50~0x5F
- * *===========================================================================*/
- #define LL_POWEROFF _IO (PLATDRV_MAGIC, 0x50)
- #define LL_POWERON _IO (PLATDRV_MAGIC, 0x51)
- #define LL_STOP _IO (PLATDRV_MAGIC, 0x52)
- #define LL_START _IO (PLATDRV_MAGIC, 0x53)
- #define LL_READ _IO (PLATDRV_MAGIC, 0x54)
- #define LL_WRITE _IO (PLATDRV_MAGIC, 0x55)
- #define LL_DATALOW _IO (PLATDRV_MAGIC, 0x56)
- #define LL_ACKNAK _IO (PLATDRV_MAGIC, 0x57)
- #endif /* __PLAT_IOCTL_H */