| /* |
| * 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; |
| } |