/* * 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 * 2002 Christian Toegel * * 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 * configuration ROM manipulation * address range mapping * Christian Toegel * address range mapping * reset notification control (switch on/off) * reset with selection of type (short/long) */ #include #include #include #include #include #include #include #include #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; }