杨创开发板提供,仿照I2C的系统模型写的。
adapter的代码
/*
* L3 bus algorithm module.
*
* Copyright (C) 2001 Russell King, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Note that L3 buses can share the same pins as I2C buses, so we must
* _not_ generate an I2C start condition. An I2C start condition is
* defined as a high-to-low transition of the data line while the clock
* is high. Therefore, we must only change the data line while the
* clock is low.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/l3/l3.h>
#include <linux/l3/algo-bit.h>
#define setdat(adap,val) adap->setdat(adap->data, val)
#define setclk(adap,val) adap->setclk(adap->data, val)
#define setmode(adap,val) adap->setmode(adap->data, val)
#define setdatin(adap) adap->setdir(adap->data, 1)
#define setdatout(adap) adap->setdir(adap->data, 0)
#define getdat(adap) adap->getdat(adap->data)
/*
* Send one byte of data to the chip. Data is latched into the chip on
* the rising edge of the clock.
*/
static void sendbyte(struct l3_algo_bit_data *adap, unsigned int byte)
{
int i;
for (i = 0; i < 8; i++) {
setclk(adap, 0);
udelay(adap->data_hold);
setdat(adap, byte & 1);
udelay(adap->data_setup);
setclk(adap, 1);
udelay(adap->clock_high);
byte >>= 1;
}
}
/*
* Send a set of bytes to the chip. We need to pulse the MODE line
* between each byte, but never at the start nor at the end of the
* transfer.
*/
static void sendbytes(struct l3_algo_bit_data *adap, const char *buf, int len)
{
int i;
for (i = 0; i < len; i++) {
if (i) {
udelay(adap->mode_hold);
setmode(adap, 0);
udelay(adap->mode);
}
setmode(adap, 1);
udelay(adap->mode_setup);
sendbyte(adap, buf[i]);
}
}
/*
* Read one byte of data from the chip. Data is latched into the chip on
* the rising edge of the clock.
*/
static unsigned int readbyte(struct l3_algo_bit_data *adap)
{
unsigned int byte = 0;
int i;
for (i = 0; i < 8; i++) {
setclk(adap, 0);
udelay(adap->data_hold + adap->data_setup);
setclk(adap, 1);
if (getdat(adap))
byte |= 1 << i;
udelay(adap->clock_high);
}
return byte;
}
/*
* Read a set of bytes from the chip. We need to pulse the MODE line
* between each byte, but never at the start nor at the end of the
* transfer.
*/
static void readbytes(struct l3_algo_bit_data *adap, char *buf, int len)
{
int i;
for (i = 0; i < len; i++) {
if (i) {
udelay(adap->mode_hold);
setmode(adap, 0);
}
setmode(adap, 1);
udelay(adap->mode_setup);
buf[i] = readbyte(adap);
}
}
static int l3_xfer(struct l3_adapter *l3_adap, struct l3_msg msgs[], int num)
{
struct l3_algo_bit_data *adap = l3_adap->algo_data;
int i;
/*
* If we share an I2C bus, ensure that it is in STOP mode
*/
setclk(adap, 1);
setdat(adap, 1);
setmode(adap, 1);
setdatout(adap);
udelay(adap->mode);
for (i = 0; i < num; i++) {
struct l3_msg *pmsg = &msgs[i];
if (!(pmsg->flags & L3_M_NOADDR)) {
setmode(adap, 0);
udelay(adap->mode_setup);
sendbyte(adap, pmsg->addr);
udelay(adap->mode_hold);
}
if (pmsg->flags & L3_M_RD) {
setdatin(adap);
readbytes(adap, pmsg->buf, pmsg->len);
} else {
setdatout(adap);
sendbytes(adap, pmsg->buf, pmsg->len);
}
}
/*
* Ensure that we leave the bus in I2C stop mode.
*/
setclk(adap, 1);
setdat(adap, 1);
setmode(adap, 0);
setdatin(adap);
return num;
}
static struct l3_algorithm l3_bit_algo = {
name: "L3 bit-shift algorithm",
xfer: l3_xfer,
};
int l3_bit_add_bus(struct l3_adapter *adap)
{
adap->algo = &l3_bit_algo;
return l3_add_adapter(adap);
}
int l3_bit_del_bus(struct l3_adapter *adap)
{
return l3_del_adapter(adap);
}
EXPORT_SYMBOL(l3_bit_add_bus);
EXPORT_SYMBOL(l3_bit_del_bus);
/*
* drivers/l3/l3-bit-elfin.c
*
* $Id: l3-bit-elfin.c,v 1.2 2004/05/12 06:28:52 laputa Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 2004-04-28 : Kwanghyun la <nala.la@samsung.com>
* - modified for sharing module device driver of samsung arch
*
*/
#include <linux/autoconf.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/l3/algo-bit.h>
#include <asm/semaphore.h>
#include <asm/system.h>
#include <asm/hardware.h>
#include <asm/mach-types.h>
#include <asm/arch/regs-gpio.h>
//#define GDEBUG
#ifdef GDEBUG
# define dprintk(x...) printk(x)
#else
# define dprintk(x...)
#endif
#define NAME "l3-bit-24x0-gpio"
struct bit_data {
unsigned int sda;
unsigned int scl;
unsigned int l3_mode;
};
static int getsda(void *data)
{
struct bit_data *bits = data;
return (int)s3c2410_gpio_getpin(bits->sda);
}
static DECLARE_MUTEX(l3_lock);
#define LOCK &l3_lock
/*
* iPAQs need the clock line driven hard high and low.
*/
static void l3_setscl(void *data, int state)
{
struct bit_data *bits = data;
unsigned long flags;
local_irq_save(flags);
if (state)
{
s3c2410_gpio_setpin(bits->scl, 1);
}
else
{
s3c2410_gpio_setpin(bits->scl, 0);
}
s3c2410_gpio_cfgpin( (bits->scl), S3C2410_GPB4_OUTP);
s3c2410_gpio_pullup( (bits->scl), 1);
local_irq_restore(flags);
}
static void l3_setsda(void *data, int state)
{
struct bit_data *bits = data;
if (state)
{
s3c2410_gpio_setpin(bits->sda, 1);
}
else
{
s3c2410_gpio_setpin(bits->sda, 0);
}
}
static void l3_setdir(void *data, int in)
{
struct bit_data *bits = data;
unsigned long flags;
local_irq_save(flags);
if (in)
{
s3c2410_gpio_cfgpin( (bits->sda), S3C2410_GPB3_INP);
s3c2410_gpio_pullup( (bits->sda), 1);
}
else
{
s3c2410_gpio_cfgpin( (bits->sda), S3C2410_GPB3_OUTP);
s3c2410_gpio_pullup( (bits->sda), 1);
}
local_irq_restore(flags);
}
static void l3_setmode(void *data, int state)
{
struct bit_data *bits = data;
if (state)
{
s3c2410_gpio_setpin(bits->l3_mode, 1);
}
else
{
s3c2410_gpio_setpin(bits->l3_mode, 0);
}
}
static struct l3_algo_bit_data l3_bit_data = {
data: NULL,
setdat: l3_setsda,
setclk: l3_setscl,
setmode: l3_setmode,
setdir: l3_setdir,
getdat: getsda,
data_hold: 1,
data_setup: 1,
clock_high: 1,
mode_hold: 1,
mode_setup: 1,
};
static struct l3_adapter l3_adapter = {
owner: THIS_MODULE,
name: NAME,
algo_data: &l3_bit_data,
lock: LOCK,
};
static int inline l3_start(struct bit_data *bits)
{
l3_bit_data.data = bits;
return l3_bit_add_bus(&l3_adapter);
}
static void inline l3_end(void)
{
l3_bit_del_bus(&l3_adapter);
}
static struct bit_data bit_data;
static int __init bus_init(void)
{
struct bit_data *bit = &bit_data;
unsigned long flags;
int ret;
#if defined( CONFIG_ARCH_UTU2440) || defined (CONFIG_BOARD_S3C2440_SMDK)
dprintk("l3_2440 0\n");
bit->sda = S3C2410_GPB3;
bit->scl = S3C2410_GPB4;
bit->l3_mode = S3C2410_GPB2;
#endif
if (!bit->sda)
return -ENODEV;
/*
* Default level for L3 mode is low.
*/
local_irq_save(flags);
/* L3 gpio interface set */
dprintk("l3_2440 1\n");
s3c2410_gpio_setpin(bit->l3_mode, 1);
s3c2410_gpio_setpin(bit->scl, 1);
s3c2410_gpio_cfgpin( (bit->scl), S3C2410_GPB4_OUTP);
s3c2410_gpio_pullup( (bit->scl), 1);
s3c2410_gpio_cfgpin( (bit->sda), S3C2410_GPB3_OUTP);
s3c2410_gpio_pullup( (bit->sda), 1);
s3c2410_gpio_cfgpin( (bit->l3_mode), S3C2410_GPB2_OUTP);
s3c2410_gpio_pullup( (bit->l3_mode), 1);
#if defined( CONFIG_ARCH_UTU2440) || defined (CONFIG_BOARD_S3C2440_SMDK)
/* IIS gpio interface set */
dprintk("l3_2440 2\n");
/* GPE 0: I2SLRCK */
s3c2410_gpio_cfgpin( S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
s3c2410_gpio_pullup( S3C2410_GPE0, 0);
/* GPE 1: I2SSCLK */
s3c2410_gpio_cfgpin( S3C2410_GPE1, S3C2410_GPE1_I2SSCLK);
s3c2410_gpio_pullup( S3C2410_GPE1, 0);
/* GPE 2: CDCLK */
s3c2410_gpio_cfgpin( S3C2410_GPE2, S3C2410_GPE2_CDCLK);
s3c2410_gpio_pullup( S3C2410_GPE2, 0);
/* GPE 3: I2SSDI */
s3c2410_gpio_cfgpin( S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
s3c2410_gpio_pullup( S3C2410_GPE3, 0);
/* GPE 4: I2SSDO */
s3c2410_gpio_cfgpin( S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
s3c2410_gpio_pullup( S3C2410_GPE4, 0);
#endif
local_irq_restore(flags);
ret = l3_start(bit);
if (ret)
l3_end();
printk("GPIO L3 bus interface for S3C2440, installed\n");
return ret;
}
static void __exit bus_exit(void)
{
l3_end();
printk("GPIO L3 bus interface for S3C2440, uninstalled\n");
}
module_init(bus_init);
module_exit(bus_exit);
/*
* linux/drivers/l3/l3-core.c
*
* Copyright (C) 2001 Russell King
*
* General structure taken from i2c-core.c by Simon G. Vogl
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* See linux/Documentation/l3 for further documentation.
*/
#include <linux/module.h>
#include <linux/autoconf.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/l3/l3.h>
static DECLARE_MUTEX(adapter_lock);
static LIST_HEAD(adapter_list);
static DECLARE_MUTEX(driver_lock);
static LIST_HEAD(driver_list);
/**
* l3_add_adapter - register a new L3 bus adapter
* @adap: l3_adapter structure for the registering adapter
*
* Make the adapter available for use by clients using name adap->name.
* The adap->adapters list is initialised by this function.
*
* Returns 0;
*/
int l3_add_adapter(struct l3_adapter *adap)
{
INIT_LIST_HEAD(&adap->clients);
down(&adapter_lock);
list_add(&adap->adapters, &adapter_list);
up(&adapter_lock);
return 0;
}
/**
* l3_del_adapter - unregister a L3 bus adapter
* @adap: l3_adapter structure to unregister
*
* Remove an adapter from the list of available L3 Bus adapters.
*
* Returns 0;
*/
int l3_del_adapter(struct l3_adapter *adap)
{
down(&adapter_lock);
list_del(&adap->adapters);
up(&adapter_lock);
return 0;
}
static struct l3_adapter *__l3_get_adapter(const char *name)
{
struct list_head *l;
list_for_each(l, &adapter_list) {
struct l3_adapter *adap = list_entry(l, struct l3_adapter, adapters);
if (strcmp(adap->name, name) == 0)
return adap;
}
return NULL;
}
/**
* l3_get_adapter - get a reference to an adapter
* @name: driver name
*
* Obtain a l3_adapter structure for the specified adapter. If the adapter
* is not currently load, then load it. The adapter will be locked in core
* until all references are released via l3_put_adapter.
*/
struct l3_adapter *l3_get_adapter(const char *name)
{
struct l3_adapter *adap = NULL;
int try;
for (try = 0; try < 2; try ++) {
down(&adapter_lock);
adap = __l3_get_adapter(name);
if (adap && !try_module_get(adap->owner))
adap = NULL;
up(&adapter_lock);
if (adap)
break;
if (try == 0)
request_module(name);
}
return adap;
}
/**
* l3_put_adapter - release a reference to an adapter
* @adap: driver to release reference
*
* Indicate to the L3 core that you no longer require the adapter reference.
* The adapter module may be unloaded when there are no references to its
* data structure.
*
* You must not use the reference after calling this function.
*/
void l3_put_adapter(struct l3_adapter *adap)
{
if (adap && adap->owner)
module_put(adap->owner);
}
/**
* l3_add_driver - register a new L3 device driver
* @driver - driver structure to make available
*
* Make the driver available for use by clients using name driver->name.
* The driver->drivers list is initialised by this function.
*
* Returns 0;
*/
int l3_add_driver(struct l3_driver *driver)
{
down(&driver_lock);
list_add(&driver->drivers, &driver_list);
up(&driver_lock);
return 0;
}
/**
* l3_del_driver - unregister a L3 device driver
* @driver: driver to remove
*
* Remove an driver from the list of available L3 Bus device drivers.
*
* Returns 0;
*/
int l3_del_driver(struct l3_driver *driver)
{
down(&driver_lock);
list_del(&driver->drivers);
up(&driver_lock);
return 0;
}
static struct l3_driver *__l3_get_driver(const char *name)
{
struct list_head *l;
list_for_each(l, &driver_list) {
struct l3_driver *drv = list_entry(l, struct l3_driver, drivers);
if (strcmp(drv->name, name) == 0)
return drv;
}
return NULL;
}
/**
* l3_get_driver - get a reference to a driver
* @name: driver name
*
* Obtain a l3_driver structure for the specified driver. If the driver is
* not currently load, then load it. The driver will be locked in core
* until all references are released via l3_put_driver.
*/
struct l3_driver *l3_get_driver(const char *name)
{
struct l3_driver *drv = NULL;
int try;
for (try = 0; try < 2; try ++) {
down(&adapter_lock);
drv = __l3_get_driver(name);
if (drv && !try_module_get(drv->owner))
drv = NULL;
up(&adapter_lock);
if (drv)
break;
if (try == 0)
request_module(name);
}
return drv;
}
/**
* l3_put_driver - release a reference to a driver
* @drv: driver to release reference
*
* Indicate to the L3 core that you no longer require the driver reference.
* The driver module may be unloaded when there are no references to its
* data structure.
*
* You must not use the reference after calling this function.
*/
void l3_put_driver(struct l3_driver *drv)
{
if (drv && drv->owner)
module_put(drv->owner);
}
/**
* l3_attach_client - attach a client to an adapter and driver
* @client: client structure to attach
* @adap: adapter (module) name
* @drv: driver (module) name
*
* Attempt to attach a client (a user of a device driver) to a particular
* driver and adapter. If the specified driver or adapter aren't registered,
* request_module is used to load the relevant modules.
*
* Returns 0 on success, or negative error code.
*/
int l3_attach_client(struct l3_client *client, const char *adap, const char *drv)
{
struct l3_adapter *adapter = l3_get_adapter(adap);
struct l3_driver *driver = l3_get_driver(drv);
int ret = -ENOENT;
if (!adapter)
printk(KERN_ERR "%s: unable to get adapter: %s\n",__FUNCTION__ , adap);
if (!driver)
printk(KERN_ERR "%s: unable to get driver: %s\n", __FUNCTION__ , drv);
if (adapter && driver) {
ret = 0;
client->adapter = adapter;
client->driver = driver;
list_add(&client->__adap, &adapter->clients);
if (driver->attach_client)
ret = driver->attach_client(client);
}
if (ret) {
l3_put_driver(driver);
l3_put_adapter(adapter);
}
return ret;
}
/**
* l3_detach_client - detach a client from an adapter and driver
* @client: client structure to detach
*
* Detach the client from the adapter and driver.
*/
int l3_detach_client(struct l3_client *client)
{
struct l3_adapter *adapter = client->adapter;
struct l3_driver *driver = client->driver;
driver->detach_client(client);
client->adapter = NULL;
client->driver = NULL;
l3_put_driver(driver);
l3_put_adapter(adapter);
list_del(&client->__adap);
return 0;
}
/**
* l3_transfer - transfer information on an L3 bus
* @adap: adapter structure to perform transfer on
* @msgs: array of l3_msg structures describing transfer
* @num: number of l3_msg structures
*
* Transfer the specified messages to/from a device on the L3 bus.
*
* Returns number of messages successfully transferred, otherwise negative
* error code.
*/
int l3_transfer(struct l3_adapter *adap, struct l3_msg msgs[], int num)
{
int ret = -ENOSYS;
if (adap->algo->xfer) {
down(adap->lock);
ret = adap->algo->xfer(adap, msgs, num);
up(adap->lock);
}
return ret;
}
/**
* l3_write - send data to a device on an L3 bus
* @client: registered client structure
* @addr: L3 bus address
* @buf: buffer for bytes to send
* @len: number of bytes to send
*
* Send len bytes pointed to by buf to device address addr on the L3 bus
* described by client.
*
* Returns the number of bytes transferred, or negative error code.
*/
int l3_write(struct l3_client *client, int addr, const char *buf, int len)
{
struct l3_adapter *adap = client->adapter;
struct l3_msg msg;
int ret;
msg.addr = addr;
msg.flags = 0;
msg.buf = (char *)buf;
msg.len = len;
ret = l3_transfer(adap, &msg, 1);
return ret == 1 ? len : ret;
}
/**
* l3_read - receive data from a device on an L3 bus
* @client: registered client structure
* @addr: L3 bus address
* @buf: buffer for bytes to receive
* @len: number of bytes to receive
*
* Receive len bytes from device address addr on the L3 bus described by
* client to a buffer pointed to by buf.
*
* Returns the number of bytes transferred, or negative error code.
*/
int l3_read(struct l3_client *client, int addr, char *buf, int len)
{
struct l3_adapter *adap = client->adapter;
struct l3_msg msg;
int ret;
msg.addr = addr;
msg.flags = L3_M_RD;
msg.buf = buf;
msg.len = len;
ret = l3_transfer(adap, &msg, 1);
return ret == 1 ? len : ret;
}
EXPORT_SYMBOL(l3_add_adapter);
EXPORT_SYMBOL(l3_del_adapter);
EXPORT_SYMBOL(l3_get_adapter);
EXPORT_SYMBOL(l3_put_adapter);
EXPORT_SYMBOL(l3_add_driver);
EXPORT_SYMBOL(l3_del_driver);
EXPORT_SYMBOL(l3_get_driver);
EXPORT_SYMBOL(l3_put_driver);
EXPORT_SYMBOL(l3_attach_client);
EXPORT_SYMBOL(l3_detach_client);
EXPORT_SYMBOL(l3_transfer);
EXPORT_SYMBOL(l3_write);
EXPORT_SYMBOL(l3_read);
/*
* linux/include/linux/l3/l3.h
*
* Copyright (C) 2001 Russell King, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* Derived from i2c.h by Simon G. Vogl
*/
#ifndef L3_H
#define L3_H
struct l3_msg {
unsigned char addr; /* slave address */
unsigned char flags;
#define L3_M_RD 0x01
#define L3_M_NOADDR 0x02
unsigned short len; /* msg length */
unsigned char *buf; /* pointer to msg data */
};
#ifdef __KERNEL__
#include <linux/types.h>
#include <linux/list.h>
struct l3_client;
struct l3_ops {
int (*open)(struct l3_client *);
int (*command)(struct l3_client *, int cmd, void *arg);
void (*close)(struct l3_client *);
};
/*
* A driver is capable of handling one or more physical devices present on
* L3 adapters. This information is used to inform the driver of adapter
* events.
*/
struct l3_driver {
/*
* This name is used to uniquely identify the driver.
* It should be the same as the module name.
*/
char name[32];
/*
* Notifies the driver that a new client wishes to use its
* services. Note that the module use count will be increased
* prior to this function being called. In addition, the
* clients driver and adapter fields will have been setup.
*/
int (*attach_client)(struct l3_client *);
/*
* Notifies the driver that the client has finished with its
* services, and any memory that it allocated for this client
* should be cleaned up. In addition the chip should be
* shut down.
*/
void (*detach_client)(struct l3_client *);
/*
* Possible operations on the driver.
*/
struct l3_ops *ops;
/*
* Module structure, if any.
*/
struct module *owner;
/*
* drivers list
*/
struct list_head drivers;
};
struct l3_adapter;
struct l3_algorithm {
/* textual description */
char name[32];
/* perform bus transactions */
int (*xfer)(struct l3_adapter *, struct l3_msg msgs[], int num);
};
struct semaphore;
/*
* l3_adapter is the structure used to identify a physical L3 bus along
* with the access algorithms necessary to access it.
*/
struct l3_adapter {
/*
* This name is used to uniquely identify the adapter.
* It should be the same as the module name.
*/
char name[32];
/*
* the algorithm to access the bus
*/
struct l3_algorithm *algo;
/*
* Algorithm specific data
*/
void *algo_data;
/*
* This may be NULL, or should point to the module struct
*/
struct module *owner;
/*
* private data for the adapter
*/
void *data;
/*
* Our lock. Unlike the i2c layer, we allow this to be used for
* other stuff, like the i2c layer lock. Some people implement
* i2c stuff using the same signals as the l3 bus.
*/
struct semaphore *lock;
/*
* List of attached clients.
*/
struct list_head clients;
/*
* List of all adapters.
*/
struct list_head adapters;
};
/*
* l3_client identifies a single device (i.e. chip) that is connected to an
* L3 bus. The behaviour is defined by the routines of the driver. This
* function is mainly used for lookup & other admin. functions.
*/
struct l3_client {
struct l3_adapter *adapter; /* the adapter we sit on */
struct l3_driver *driver; /* and our access routines */
void *driver_data; /* private driver data */
struct list_head __adap;
};
extern int l3_add_adapter(struct l3_adapter *);
extern int l3_del_adapter(struct l3_adapter *);
extern int l3_add_driver(struct l3_driver *);
extern int l3_del_driver(struct l3_driver *);
#if 0
extern void l3_put_adapter(struct l3_adapter *);
extern struct l3_adapter *l3_get_adapter(const char *name);
extern int l3_write(struct l3_adapter *, int, const char *, int);
extern int l3_read(struct l3_adapter *, int, char *, int);
#else // ghcstop add
extern int l3_attach_client(struct l3_client *, const char *, const char *);
extern int l3_detach_client(struct l3_client *);
extern int l3_transfer(struct l3_adapter *, struct l3_msg msgs[], int);
extern int l3_write(struct l3_client *, int, const char *, int);
extern int l3_read(struct l3_client *, int, char *, int);
/**
* l3_command - send a command to a L3 device driver
* @client: registered client structure
* @cmd: device driver command
* @arg: device driver arguments
*
* Ask the L3 device driver to perform some function. Further information
* should be sought from the device driver in question.
*
* Returns negative error code on failure.
*/
static inline int l3_command(struct l3_client *clnt, int cmd, void *arg)
{
struct l3_ops *ops = clnt->driver->ops;
int ret = -EINVAL;
if (ops && ops->command)
ret = ops->command(clnt, cmd, arg);
return ret;
}
static inline int l3_open(struct l3_client *clnt)
{
struct l3_ops *ops = clnt->driver->ops;
int ret = 0;
if (ops && ops->open)
ret = ops->open(clnt);
return ret;
}
static inline void l3_close(struct l3_client *clnt)
{
struct l3_ops *ops = clnt->driver->ops;
if (ops && ops->close)
ops->close(clnt);
}
#endif
#endif
#endif /* L3_H */