Linux L3 驱动

杨创开发板提供,仿照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 <[email protected]>
 *   - 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 */




 

你可能感兴趣的:(Linux L3 驱动)