204 lines
4.7 KiB
C
204 lines
4.7 KiB
C
/*
|
|
* 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/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)
|
|
{
|
|
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;
|
|
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_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
|
|
* @adap: L3 bus adapter
|
|
* @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_adapter *adap, int addr, const char *buf, int len)
|
|
{
|
|
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
|
|
* @adap: L3 bus adapter
|
|
* @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_adapter *adap, int addr, char *buf, int len)
|
|
{
|
|
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_transfer);
|
|
EXPORT_SYMBOL(l3_write);
|
|
EXPORT_SYMBOL(l3_read);
|