blob: d83256edc2b3395e48dfb0561b8ab943d66d2512 [file] [log] [blame]
/*
* libraw1394 - library for raw access to the 1394 bus with the Linux subsystem.
*
* Copyright (C) 1999,2000,2001,2002 Andreas Bombe
* 2001, 2002 Manfred Weihs <weihs@ict.tuwien.ac.at>
* 2002 Christian Toegel <christian.toegel@gmx.at>
*
* This library is licensed under the GNU Lesser General Public License (LGPL),
* version 2.1 or later. See the file COPYING.LIB in the distribution for
* details.
*
*
* Contributions:
*
* Manfred Weihs <weihs@ict.tuwien.ac.at>
* configuration ROM manipulation
* address range mapping
* Christian Toegel <christian.toegel@gmx.at>
* address range mapping
* reset notification control (switch on/off)
* reset with selection of type (short/long)
*/
#include <config.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <limits.h>
#include "raw1394.h"
#include "csr.h"
#include "kernel-raw1394.h"
#include "raw1394_private.h"
static int bus_reset_default(struct raw1394_handle *handle, unsigned int gen)
{
raw1394_update_generation(handle, gen);
return 0;
}
static int tag_handler_default(struct raw1394_handle *handle, unsigned long tag,
int error)
{
struct raw1394_reqhandle *rh;
if (tag) {
rh = (struct raw1394_reqhandle *)tag;
return rh->callback(handle, rh->data, error);
} else {
return -1;
}
}
static int arm_tag_handler_default(struct raw1394_handle *handle, unsigned long tag,
byte_t request_type, unsigned int requested_length,
void *data)
{
struct raw1394_arm_reqhandle *rh;
struct raw1394_arm_request_response *arm_req_resp;
if (tag) {
rh = (struct raw1394_arm_reqhandle *)tag;
arm_req_resp = (struct raw1394_arm_request_response *) data;
return rh->arm_callback(handle, arm_req_resp,
requested_length, rh->pcontext,
request_type);
} else {
/* error ... */
return -1;
}
}
int _raw1394_sync_cb(struct raw1394_handle *unused, struct sync_cb_data *data,
int error)
{
data->errcode = error;
data->done = 1;
return 0;
}
static unsigned int init_rawdevice(struct ieee1394_handle *h)
{
struct raw1394_request req;
CLEAR_REQ(&req);
req.type = RAW1394_REQ_INITIALIZE;
req.misc = RAW1394_KERNELAPI_VERSION;
h->protocol_version = RAW1394_KERNELAPI_VERSION;
if (write(h->fd, &req, sizeof(req)) < 0) return -1;
if (read(h->fd, &req, sizeof(req)) < 0) return -1;
if (req.error == RAW1394_ERROR_COMPAT && req.misc == 3) {
h->protocol_version = 3;
if (write(h->fd, &req, sizeof(req)) < 0) return -1;
if (read(h->fd, &req, sizeof(req)) < 0) return -1;
}
if (req.error) {
errno = EPROTO;
return -1;
}
memset(h->buffer, 0, HBUF_SIZE);
return req.generation;
}
struct ieee1394_handle *ieee1394_new_handle(void)
{
struct ieee1394_handle *handle;
const char *defaultDevice = "/dev/raw1394";
handle = malloc(sizeof(struct ieee1394_handle));
if (!handle) {
errno = ENOMEM;
return NULL;
}
handle->fd = open(getenv("RAW1394DEV") ? getenv("RAW1394DEV"): defaultDevice, O_RDWR);
if (handle->fd < 0) {
/* failover to default in attempt to idiot proof */
handle->fd = open(defaultDevice, O_RDWR);
if (handle->fd < 0) {
free(handle);
return NULL;
}
}
handle->generation = init_rawdevice(handle);
if (handle->generation == -1) {
/* failover to default in attempt to idiot proof */
close(handle->fd);
handle->fd = open(defaultDevice, O_RDWR);
if (handle->fd < 0) {
free(handle);
return NULL;
}
handle->generation = init_rawdevice(handle);
if (handle->generation == -1) {
close(handle->fd);
free(handle);
return NULL;
}
}
handle->err = 0;
handle->bus_reset_handler = bus_reset_default;
handle->tag_handler = tag_handler_default;
handle->arm_tag_handler = arm_tag_handler_default;
handle->iso_buffer = NULL;
handle->iso_mode = ISO_INACTIVE;
handle->iso_packet_infos = NULL;
return handle;
}
void ieee1394_destroy_handle(struct ieee1394_handle *handle)
{
if (handle) {
if(handle->iso_mode != ISO_INACTIVE) {
ieee1394_iso_shutdown(handle);
}
close(handle->fd);
free(handle);
}
}
int ieee1394_get_fd(struct ieee1394_handle *handle)
{
return handle->fd;
}
unsigned int raw1394_get_generation(struct raw1394_handle *handle)
{
if (!handle) {
errno = EINVAL;
return UINT_MAX;
}
if (handle->is_fw)
return handle->mode.fw->generation;
else
return handle->mode.ieee1394->generation;
}
void raw1394_update_generation(struct raw1394_handle *handle, unsigned int gen)
{
if (!handle) {
return;
}
if (handle->is_fw)
handle->mode.fw->generation = gen;
else
handle->mode.ieee1394->generation = gen;
}
int ieee1394_get_nodecount(struct ieee1394_handle *handle)
{
if (!handle) {
errno = EINVAL;
return UINT_MAX;
}
return handle->num_of_nodes;
}
nodeid_t ieee1394_get_local_id(struct ieee1394_handle *handle)
{
if (!handle) {
errno = EINVAL;
return 0xFFFF;
}
return handle->local_id;
}
nodeid_t ieee1394_get_irm_id(struct ieee1394_handle *handle)
{
if (!handle) {
errno = EINVAL;
return 0xFFFF;
}
return handle->irm_id;
}
void raw1394_set_userdata(struct raw1394_handle *handle, void *data)
{
if (!handle) {
return;
}
if (handle->is_fw)
handle->mode.fw->userdata = data;
else
handle->mode.ieee1394->userdata = data;
}
void *raw1394_get_userdata(struct raw1394_handle *handle)
{
if (!handle) {
errno = EINVAL;
return NULL;
}
if (handle->is_fw)
return handle->mode.fw->userdata;
else
return handle->mode.ieee1394->userdata;
}
int ieee1394_get_port_info(struct ieee1394_handle *handle,
struct raw1394_portinfo *pinf, int maxports)
{
struct raw1394_request req;
CLEAR_REQ(&req);
req.type = RAW1394_REQ_LIST_CARDS;
req.generation = handle->generation;
/* IMPORTANT: raw1394 will be writing directly into the memory you
provide in pinf. The viability of this approach assumes that the
structure of libraw1394's raw1394_portinfo and the kernel's
raw1394_khost_list structs are the same!!
*/
req.recvb = ptr2int(pinf);
req.length = sizeof(struct raw1394_portinfo) * maxports;
while (1) {
if (write(handle->fd, &req, sizeof(req)) < 0) return -1;
if (read(handle->fd, &req, sizeof(req)) < 0) return -1;
if (!req.error) break;
if (req.error == RAW1394_ERROR_GENERATION) {
handle->generation = req.generation;
continue;
}
return -1;
}
return req.misc;
}
int ieee1394_set_port(struct ieee1394_handle *handle, int port)
{
struct raw1394_request req;
CLEAR_REQ(&req);
req.type = RAW1394_REQ_SET_CARD;
req.generation = handle->generation;
req.misc = port;
if (write(handle->fd, &req, sizeof(req)) < 0) return -1;
if (read(handle->fd, &req, sizeof(req)) < 0) return -1;
switch (req.error) {
case RAW1394_ERROR_GENERATION:
handle->generation = req.generation;
errno = ESTALE;
return -1;
case RAW1394_ERROR_INVALID_ARG:
errno = EINVAL;
return -1;
case RAW1394_ERROR_NONE:
if (handle->protocol_version == 3) {
handle->num_of_nodes = req.misc & 0xffff;
handle->local_id = req.misc >> 16;
} else {
handle->num_of_nodes = req.misc & 0xff;
handle->irm_id = ((req.misc >> 8) & 0xff) | 0xffc0;
handle->local_id = req.misc >> 16;
}
handle->generation = req.generation;
return 0;
default:
errno = 0;
return -1;
}
}
ieee1394handle_t ieee1394_new_handle_on_port(int port)
{
ieee1394handle_t handle = ieee1394_new_handle();
if (!handle)
return NULL;
tryagain:
if (ieee1394_get_port_info(handle, NULL, 0) < 0) {
ieee1394_destroy_handle(handle);
return NULL;
}
if (ieee1394_set_port(handle, port)) {
if (errno == ESTALE || errno == EINTR) {
goto tryagain;
} else {
ieee1394_destroy_handle(handle);
return NULL;
}
}
return handle;
}
int ieee1394_reset_bus_new(struct ieee1394_handle *handle, int type)
{
struct raw1394_request req;
CLEAR_REQ(&req);
req.type = RAW1394_REQ_RESET_BUS;
req.generation = handle->generation;
req.misc = type;
if (write(handle->fd, &req, sizeof(req)) < 0) return -1;
return 0; /* success */
}
int raw1394_reset_bus(struct raw1394_handle *handle)
{
return raw1394_reset_bus_new (handle, RAW1394_LONG_RESET);
}
int ieee1394_busreset_notify (struct ieee1394_handle *handle,
int off_on_switch)
{
struct raw1394_request req;
CLEAR_REQ(&req);
req.type = RAW1394_REQ_RESET_NOTIFY;
req.generation = handle->generation;
req.misc = off_on_switch;
if (write(handle->fd, &req, sizeof(req)) < 0) return -1;
return 0; /* success */
}
int ieee1394_update_config_rom(ieee1394handle_t handle, const quadlet_t
*new_rom, size_t size, unsigned char rom_version)
{
struct raw1394_request req;
int status;
CLEAR_REQ(&req);
req.type = RAW1394_REQ_UPDATE_ROM;
req.sendb = (unsigned long) new_rom;
req.length = size;
req.misc = rom_version;
req.recvb = (unsigned long) &status;
if (write(handle->fd, &req, sizeof(req)) < 0) return -8;
return status;
}
int ieee1394_get_config_rom(ieee1394handle_t handle, quadlet_t *buffer,
size_t buffersize, size_t *rom_size, unsigned char *rom_version)
{
struct raw1394_request req;
int status;
CLEAR_REQ(&req);
req.type = RAW1394_REQ_GET_ROM;
req.recvb = (unsigned long) buffer;
req.length = buffersize;
req.tag = (unsigned long) rom_size;
req.address = (unsigned long) rom_version;
req.sendb = (unsigned long) &status;
if (write(handle->fd, &req, sizeof(req)) < 0) return -8;
return status;
}
int ieee1394_bandwidth_modify (raw1394handle_t handle, unsigned int bandwidth,
enum raw1394_modify_mode mode)
{
quadlet_t buffer, compare, swap, new;
int retry = 3;
int result;
if (bandwidth == 0)
return 0;
/* Reading current bandwidth usage from IRM. */
result = raw1394_read (handle, raw1394_get_irm_id (handle),
CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE,
sizeof (quadlet_t), &buffer);
if (result < 0)
return -1;
buffer = ntohl (buffer);
compare = buffer;
while (retry > 0) {
if (mode == RAW1394_MODIFY_ALLOC ) {
if (compare < bandwidth) {
return -1;
}
swap = compare - bandwidth;
}
else {
swap = compare + bandwidth;
if( swap > MAXIMUM_BANDWIDTH ) {
swap = MAXIMUM_BANDWIDTH;
}
}
result = raw1394_lock (handle, raw1394_get_irm_id (handle),
CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE,
RAW1394_EXTCODE_COMPARE_SWAP, ntohl(swap), ntohl(compare),
&new);
if (result < 0)
return -1;
new = ntohl (new);
if (new != compare) {
compare = new;
retry--;
if ( retry == 0 )
return -1;
}
else {
/* Success. */
retry = 0;
return 0;
}
}
return 0;
}
int ieee1394_channel_modify (raw1394handle_t handle, unsigned int channel,
enum raw1394_modify_mode mode)
{
quadlet_t buffer;
int result;
nodeaddr_t addr = CSR_REGISTER_BASE;
unsigned int c = channel;
quadlet_t compare, swap = 0, new;
if (c > 31 && c < 64) {
addr += CSR_CHANNELS_AVAILABLE_LO;
c -= 32;
} else if (c < 64)
addr += CSR_CHANNELS_AVAILABLE_HI;
else
return -1;
c = 31 - c;
result = raw1394_read (handle, raw1394_get_irm_id (handle), addr,
sizeof (quadlet_t), &buffer);
if (result < 0)
return -1;
buffer = ntohl (buffer);
if ( mode == RAW1394_MODIFY_ALLOC ) {
if( (buffer & (1 << c)) == 0 )
return -1;
swap = htonl (buffer & ~(1 << c));
}
else if ( mode == RAW1394_MODIFY_FREE ) {
if ( (buffer & (1 << c)) != 0 )
return -1;
swap = htonl (buffer | (1 << c));
}
compare = htonl (buffer);
result = raw1394_lock (handle, raw1394_get_irm_id (handle), addr,
RAW1394_EXTCODE_COMPARE_SWAP, swap, compare, &new);
if ( (result < 0) || (new != compare) )
return -1;
return 0;
}