#define RIOCM_TX_RING_SIZE 128 #define RIOCM_RX_RING_SIZE 128 #define RIOCM_CONNECT_TO 3 /* connect response TO (in sec) */
#define RIOCM_MAX_CHNUM 0xffff /* Use full range of u16 field */ #define RIOCM_CHNUM_AUTO 0 #define RIOCM_MAX_EP_COUNT 0x10000 /* Max number of endpoints */
staticvoid *riocm_rx_get_msg(struct cm_dev *cm)
{ void *msg; int i;
msg = rio_get_inb_message(cm->mport, cmbox); if (msg) { for (i = 0; i < RIOCM_RX_RING_SIZE; i++) { if (cm->rx_buf[i] == msg) {
cm->rx_buf[i] = NULL;
cm->rx_slots++; break;
}
}
if (i == RIOCM_RX_RING_SIZE)
riocm_warn("no record for buffer 0x%p", msg);
}
return msg;
}
/* * riocm_rx_fill - fills a ring of receive buffers for given cm device * @cm: cm_dev object * @nent: max number of entries to fill * * Returns: none
*/ staticvoid riocm_rx_fill(struct cm_dev *cm, int nent)
{ int i;
if (cm->rx_slots == 0) return;
for (i = 0; i < RIOCM_RX_RING_SIZE && cm->rx_slots && nent; i++) { if (cm->rx_buf[i] == NULL) {
cm->rx_buf[i] = kmalloc(RIO_MAX_MSG_SIZE, GFP_KERNEL); if (cm->rx_buf[i] == NULL) break;
rio_add_inb_buffer(cm->mport, cmbox, cm->rx_buf[i]);
cm->rx_slots--;
nent--;
}
}
}
/* * riocm_rx_free - frees all receive buffers associated with given cm device * @cm: cm_dev object * * Returns: none
*/ staticvoid riocm_rx_free(struct cm_dev *cm)
{ int i;
for (i = 0; i < RIOCM_RX_RING_SIZE; i++) { if (cm->rx_buf[i] != NULL) {
kfree(cm->rx_buf[i]);
cm->rx_buf[i] = NULL;
}
}
}
/* * riocm_req_handler - connection request handler * @cm: cm_dev object * @req_data: pointer to the request packet * * Returns: 0 if success, or * -EINVAL if channel is not in correct state, * -ENODEV if cannot find a channel with specified ID, * -ENOMEM if unable to allocate memory to store the request
*/ staticint riocm_req_handler(struct cm_dev *cm, void *req_data)
{ struct rio_channel *ch; struct conn_req *req; struct rio_ch_chan_hdr *hh = req_data;
u16 chnum;
chnum = ntohs(hh->dst_ch);
ch = riocm_get_channel(chnum);
if (!ch) return -ENODEV;
if (ch->state != RIO_CM_LISTEN) {
riocm_debug(RX_CMD, "channel %d is not in listen state", chnum);
riocm_put_channel(ch); return -EINVAL;
}
/* * riocm_resp_handler - response to connection request handler * @resp_data: pointer to the response packet * * Returns: 0 if success, or * -EINVAL if channel is not in correct state, * -ENODEV if cannot find a channel with specified ID,
*/ staticint riocm_resp_handler(void *resp_data)
{ struct rio_channel *ch; struct rio_ch_chan_hdr *hh = resp_data;
u16 chnum;
chnum = ntohs(hh->dst_ch);
ch = riocm_get_channel(chnum); if (!ch) return -ENODEV;
if (ch->state != RIO_CM_CONNECT) {
riocm_put_channel(ch); return -EINVAL;
}
/* * riocm_close_handler - channel close request handler * @req_data: pointer to the request packet * * Returns: 0 if success, or * -ENODEV if cannot find a channel with specified ID, * + error codes returned by riocm_ch_close.
*/ staticint riocm_close_handler(void *data)
{ struct rio_channel *ch; struct rio_ch_chan_hdr *hh = data; int ret;
ret = riocm_ch_close(ch); if (ret)
riocm_debug(RX_CMD, "riocm_ch_close() returned %d", ret);
return 0;
}
/* * rio_cm_handler - function that services request (non-data) packets * @cm: cm_dev object * @data: pointer to the packet
*/ staticvoid rio_cm_handler(struct cm_dev *cm, void *data)
{ struct rio_ch_chan_hdr *hdr;
if (!rio_mport_is_running(cm->mport)) goto out;
hdr = data;
riocm_debug(RX_CMD, "OP=%x for ch=%d from %d",
hdr->ch_op, ntohs(hdr->dst_ch), ntohs(hdr->src_ch));
switch (hdr->ch_op) { case CM_CONN_REQ:
riocm_req_handler(cm, data); break; case CM_CONN_ACK:
riocm_resp_handler(data); break; case CM_CONN_CLOSE:
riocm_close_handler(data); break; default:
riocm_error("Invalid packet header"); break;
}
out:
kfree(data);
}
/* * rio_rx_data_handler - received data packet handler * @cm: cm_dev object * @buf: data packet * * Returns: 0 if success, or * -ENODEV if cannot find a channel with specified ID, * -EIO if channel is not in CONNECTED state, * -ENOMEM if channel RX queue is full (packet discarded)
*/ staticint rio_rx_data_handler(struct cm_dev *cm, void *buf)
{ struct rio_ch_chan_hdr *hdr; struct rio_channel *ch;
ch = riocm_get_channel(ntohs(hdr->dst_ch)); if (!ch) { /* Discard data message for non-existing channel */
kfree(buf); return -ENODEV;
}
/* Place pointer to the buffer into channel's RX queue */
spin_lock(&ch->lock);
if (ch->state != RIO_CM_CONNECTED) { /* Channel is not ready to receive data, discard a packet */
riocm_debug(RX_DATA, "ch=%d is in wrong state=%d",
ch->id, ch->state);
spin_unlock(&ch->lock);
kfree(buf);
riocm_put_channel(ch); return -EIO;
}
if (ch->rx_ring.count == RIOCM_RX_RING_SIZE) { /* If RX ring is full, discard a packet */
riocm_debug(RX_DATA, "ch=%d is full", ch->id);
spin_unlock(&ch->lock);
kfree(buf);
riocm_put_channel(ch); return -ENOMEM;
}
while (1) {
mutex_lock(&cm->rx_lock);
data = riocm_rx_get_msg(cm); if (data)
riocm_rx_fill(cm, 1);
mutex_unlock(&cm->rx_lock);
if (data == NULL) break;
hdr = data;
if (hdr->bhdr.type != RIO_CM_CHAN) { /* For now simply discard packets other than channel */
riocm_error("Unsupported TYPE code (0x%x). Msg dropped",
hdr->bhdr.type);
kfree(data); continue;
}
/* Process a channel message */ if (hdr->ch_op == CM_DATA_MSG)
rio_rx_data_handler(cm, data); else
rio_cm_handler(cm, data);
}
}
staticvoid riocm_inb_msg_event(struct rio_mport *mport, void *dev_id, int mbox, int slot)
{ struct cm_dev *cm = dev_id;
if (rio_mport_is_running(cm->mport) && !work_pending(&cm->rx_work))
queue_work(cm->rx_wq, &cm->rx_work);
}
/* * rio_txcq_handler - TX completion handler * @cm: cm_dev object * @slot: TX queue slot * * TX completion handler also ensures that pending request packets are placed * into transmit queue as soon as a free slot becomes available. This is done * to give higher priority to request packets during high intensity data flow.
*/ staticvoid rio_txcq_handler(struct cm_dev *cm, int slot)
{ int ack_slot;
/* ATTN: Add TX completion notification if/when direct buffer * transfer is implemented. At this moment only correct tracking * of tx_count is important.
*/
riocm_debug(TX_EVENT, "for mport_%d slot %d tx_cnt %d",
cm->mport->id, slot, cm->tx_cnt);
/* * If there are pending requests, insert them into transmit queue
*/ if (!list_empty(&cm->tx_reqs) && (cm->tx_cnt < RIOCM_TX_RING_SIZE)) { struct tx_req *req, *_req; int rc;
/* * riocm_ch_send - sends a data packet to a remote device * @ch_id: local channel ID * @buf: pointer to a data buffer to send (including CM header) * @len: length of data to transfer (including CM header) * * ATTN: ASSUMES THAT THE HEADER SPACE IS RESERVED PART OF THE DATA PACKET * * Returns: 0 if success, or * -EINVAL if one or more input parameters is/are not valid, * -ENODEV if cannot find a channel with specified ID, * -EAGAIN if a channel is not in CONNECTED state, * + error codes returned by HW send routine.
*/ staticint riocm_ch_send(u16 ch_id, void *buf, int len)
{ struct rio_channel *ch; struct rio_ch_chan_hdr *hdr; int ret;
if (buf == NULL || ch_id == 0 || len == 0 || len > RIO_MAX_MSG_SIZE) return -EINVAL;
if (len < sizeof(struct rio_ch_chan_hdr)) return -EINVAL; /* insufficient data from user */
ch = riocm_get_channel(ch_id); if (!ch) {
riocm_error("%s(%d) ch_%d not found", current->comm,
task_pid_nr(current), ch_id); return -ENODEV;
}
if (!riocm_cmp(ch, RIO_CM_CONNECTED)) {
ret = -EAGAIN; goto err_out;
}
/* * Fill buffer header section with corresponding channel data
*/
hdr = buf;
/* ATTN: the function call below relies on the fact that underlying * HW-specific add_outb_message() routine copies TX data into its own * internal transfer buffer (true for all RIONET compatible mport * drivers). Must be reviewed if mport driver uses the buffer directly.
*/
ret = riocm_post_send(ch->cmdev, ch->rdev, buf, len); if (ret)
riocm_debug(TX, "ch %d send_err=%d", ch->id, ret);
err_out:
riocm_put_channel(ch); return ret;
}
staticint riocm_ch_free_rxbuf(struct rio_channel *ch, void *buf)
{ int i, ret = -EINVAL;
spin_lock_bh(&ch->lock);
for (i = 0; i < RIOCM_RX_RING_SIZE; i++) { if (ch->rx_ring.inuse[i] == buf) {
ch->rx_ring.inuse[i] = NULL;
ch->rx_ring.inuse_cnt--;
ret = 0; break;
}
}
spin_unlock_bh(&ch->lock);
if (!ret)
kfree(buf);
return ret;
}
/* * riocm_ch_receive - fetch a data packet received for the specified channel * @ch: local channel ID * @buf: pointer to a packet buffer * @timeout: timeout to wait for incoming packet (in jiffies) * * Returns: 0 and valid buffer pointer if success, or NULL pointer and one of: * -EAGAIN if a channel is not in CONNECTED state, * -ENOMEM if in-use tracking queue is full, * -ETIME if wait timeout expired, * -EINTR if wait was interrupted.
*/ staticint riocm_ch_receive(struct rio_channel *ch, void **buf, long timeout)
{ void *rxmsg = NULL; int i, ret = 0; long wret;
if (!riocm_cmp(ch, RIO_CM_CONNECTED)) {
ret = -EAGAIN; goto out;
}
if (ch->rx_ring.inuse_cnt == RIOCM_RX_RING_SIZE) { /* If we do not have entries to track buffers given to upper * layer, reject request.
*/
ret = -ENOMEM; goto out;
}
for (i = 0; i < RIOCM_RX_RING_SIZE; i++) { if (ch->rx_ring.inuse[i] == NULL) {
ch->rx_ring.inuse[i] = rxmsg;
ch->rx_ring.inuse_cnt++;
ret = 0; break;
}
}
if (ret) { /* We have no entry to store pending message: drop it */
kfree(rxmsg);
rxmsg = NULL;
}
/* * riocm_ch_connect - sends a connect request to a remote device * @loc_ch: local channel ID * @cm: CM device to send connect request * @peer: target RapidIO device * @rem_ch: remote channel ID * * Returns: 0 if success, or * -EINVAL if the channel is not in IDLE state, * -EAGAIN if no connection request available immediately, * -ETIME if ACK response timeout expired, * -EINTR if wait for response was interrupted.
*/ staticint riocm_ch_connect(u16 loc_ch, struct cm_dev *cm, struct cm_peer *peer, u16 rem_ch)
{ struct rio_channel *ch = NULL; struct rio_ch_chan_hdr *hdr; int ret; long wret;
ch = riocm_get_channel(loc_ch); if (!ch) return -ENODEV;
if (!riocm_cmp_exch(ch, RIO_CM_IDLE, RIO_CM_CONNECT)) {
ret = -EINVAL; goto conn_done;
}
/* ATTN: the function call below relies on the fact that underlying * HW-specific add_outb_message() routine copies TX data into its * internal transfer buffer. Must be reviewed if mport driver uses * this buffer directly.
*/
ret = riocm_post_send(cm, peer->rdev, hdr, sizeof(*hdr));
if (ret != -EBUSY) {
kfree(hdr);
} else {
ret = riocm_queue_req(cm, peer->rdev, hdr, sizeof(*hdr)); if (ret)
kfree(hdr);
}
if (ret) {
riocm_cmp_exch(ch, RIO_CM_CONNECT, RIO_CM_IDLE); goto conn_done;
}
/* Wait for connect response from the remote device */
wret = wait_for_completion_interruptible_timeout(&ch->comp,
RIOCM_CONNECT_TO * HZ);
riocm_debug(WAIT, "wait on %d returns %ld", ch->id, wret);
if (!wret)
ret = -ETIME; elseif (wret == -ERESTARTSYS)
ret = -EINTR; else
ret = riocm_cmp(ch, RIO_CM_CONNECTED) ? 0 : -1;
conn_done:
riocm_put_channel(ch); return ret;
}
staticint riocm_send_ack(struct rio_channel *ch)
{ struct rio_ch_chan_hdr *hdr; int ret;
hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); if (hdr == NULL) return -ENOMEM;
/* ATTN: the function call below relies on the fact that underlying * add_outb_message() routine copies TX data into its internal transfer * buffer. Review if switching to direct buffer version.
*/
ret = riocm_post_send(ch->cmdev, ch->rdev, hdr, sizeof(*hdr));
if (ret)
riocm_error("send ACK to ch_%d on %s failed (ret=%d)",
ch->id, rio_name(ch->rdev), ret); return ret;
}
/* * riocm_ch_accept - accept incoming connection request * @ch_id: channel ID * @new_ch_id: local mport device * @timeout: wait timeout (if 0 non-blocking call, do not wait if connection * request is not available). * * Returns: pointer to new channel struct if success, or error-valued pointer: * -ENODEV - cannot find specified channel or mport, * -EINVAL - the channel is not in IDLE state, * -EAGAIN - no connection request available immediately (timeout=0), * -ENOMEM - unable to allocate new channel, * -ETIME - wait timeout expired, * -EINTR - wait was interrupted.
*/ staticstruct rio_channel *riocm_ch_accept(u16 ch_id, u16 *new_ch_id, long timeout)
{ struct rio_channel *ch; struct rio_channel *new_ch; struct conn_req *req; struct cm_peer *peer; int found = 0; int err = 0; long wret;
ch = riocm_get_channel(ch_id); if (!ch) return ERR_PTR(-EINVAL);
if (!riocm_cmp(ch, RIO_CM_LISTEN)) {
err = -EINVAL; goto err_put;
}
/* Don't sleep if this is a non blocking call */ if (!timeout) { if (!try_wait_for_completion(&ch->comp)) {
err = -EAGAIN; goto err_put;
}
} else {
riocm_debug(WAIT, "on %d", ch->id);
err_put: if (ch)
riocm_put_channel(ch);
*new_ch_id = 0; return ERR_PTR(err);
}
/* * riocm_ch_listen - puts a channel into LISTEN state * @ch_id: channel ID * * Returns: 0 if success, or * -EINVAL if the specified channel does not exists or * is not in CHAN_BOUND state.
*/ staticint riocm_ch_listen(u16 ch_id)
{ struct rio_channel *ch = NULL; int ret = 0;
riocm_debug(CHOP, "(ch_%d)", ch_id);
ch = riocm_get_channel(ch_id); if (!ch) return -EINVAL; if (!riocm_cmp_exch(ch, RIO_CM_CHAN_BOUND, RIO_CM_LISTEN))
ret = -EINVAL;
riocm_put_channel(ch); return ret;
}
/* * riocm_ch_bind - associate a channel object and an mport device * @ch_id: channel ID * @mport_id: local mport device ID * @context: pointer to the additional caller's context * * Returns: 0 if success, or * -ENODEV if cannot find specified mport, * -EINVAL if the specified channel does not exist or * is not in IDLE state.
*/ staticint riocm_ch_bind(u16 ch_id, u8 mport_id, void *context)
{ struct rio_channel *ch = NULL; struct cm_dev *cm; int rc = -ENODEV;
riocm_debug(CHOP, "ch_%d to mport_%d", ch_id, mport_id);
/* * riocm_ch_alloc - channel object allocation helper routine * @ch_num: channel ID (1 ... RIOCM_MAX_CHNUM, 0 = automatic) * * Return value: pointer to newly created channel object, * or error-valued pointer
*/ staticstruct rio_channel *riocm_ch_alloc(u16 ch_num)
{ int id; int start, end; struct rio_channel *ch;
ch = kzalloc(sizeof(*ch), GFP_KERNEL); if (!ch) return ERR_PTR(-ENOMEM);
if (ch_num) { /* If requested, try to obtain the specified channel ID */
start = ch_num;
end = ch_num + 1;
} else { /* Obtain channel ID from the dynamic allocation range */
start = chstart;
end = RIOCM_MAX_CHNUM + 1;
}
/* * riocm_ch_create - creates a new channel object and allocates ID for it * @ch_num: channel ID (1 ... RIOCM_MAX_CHNUM, 0 = automatic) * * Allocates and initializes a new channel object. If the parameter ch_num > 0 * and is within the valid range, riocm_ch_create tries to allocate the * specified ID for the new channel. If ch_num = 0, channel ID will be assigned * automatically from the range (chstart ... RIOCM_MAX_CHNUM). * Module parameter 'chstart' defines start of an ID range available for dynamic * allocation. Range below 'chstart' is reserved for pre-defined ID numbers. * Available channel numbers are limited by 16-bit size of channel numbers used * in the packet header. * * Return value: PTR to rio_channel structure if successful (with channel number * updated via pointer) or error-valued pointer if error.
*/ staticstruct rio_channel *riocm_ch_create(u16 *ch_num)
{ struct rio_channel *ch = NULL;
ch = riocm_ch_alloc(*ch_num);
if (IS_ERR(ch))
riocm_debug(CHOP, "Failed to allocate channel %d (err=%ld)",
*ch_num, PTR_ERR(ch)); else
*ch_num = ch->id;
if (ch->rx_ring.inuse_cnt) { for (i = 0;
i < RIOCM_RX_RING_SIZE && ch->rx_ring.inuse_cnt; i++) { if (ch->rx_ring.inuse[i] != NULL) {
kfree(ch->rx_ring.inuse[i]);
ch->rx_ring.inuse_cnt--;
}
}
}
if (ch->rx_ring.count) for (i = 0; i < RIOCM_RX_RING_SIZE && ch->rx_ring.count; i++) { if (ch->rx_ring.buf[i] != NULL) {
kfree(ch->rx_ring.buf[i]);
ch->rx_ring.count--;
}
}
complete(&ch->comp_close);
}
staticint riocm_send_close(struct rio_channel *ch)
{ struct rio_ch_chan_hdr *hdr; int ret;
/* * Send CH_CLOSE notification to the remote RapidIO device
*/
hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); if (hdr == NULL) return -ENOMEM;
/* ATTN: the function call below relies on the fact that underlying * add_outb_message() routine copies TX data into its internal transfer * buffer. Needs to be reviewed if switched to direct buffer mode.
*/
ret = riocm_post_send(ch->cmdev, ch->rdev, hdr, sizeof(*hdr));
if (ret)
riocm_error("ch(%d) send CLOSE failed (ret=%d)", ch->id, ret);
return ret;
}
/* * riocm_ch_close - closes a channel object with specified ID (by local request) * @ch: channel to be closed
*/ staticint riocm_ch_close(struct rio_channel *ch)
{ unsignedlong tmo = msecs_to_jiffies(3000); enum rio_cm_state state; long wret; int ret = 0;
riocm_debug(CHOP, "ch_%d by %s(%d)",
ch->id, current->comm, task_pid_nr(current));
state = riocm_exch(ch, RIO_CM_DESTROYING); if (state == RIO_CM_CONNECTED)
riocm_send_close(ch);
riocm_debug(WAIT, "wait on %d returns %ld", ch->id, wret);
if (wret == 0) { /* Timeout on wait occurred */
riocm_debug(CHOP, "%s(%d) timed out waiting for ch %d",
current->comm, task_pid_nr(current), ch->id);
ret = -ETIMEDOUT;
} elseif (wret == -ERESTARTSYS) { /* Wait_for_completion was interrupted by a signal */
riocm_debug(CHOP, "%s(%d) wait for ch %d was interrupted",
current->comm, task_pid_nr(current), ch->id);
ret = -EINTR;
}
if (!ret) {
riocm_debug(CHOP, "ch_%d resources released", ch->id);
kfree(ch);
} else {
riocm_debug(CHOP, "failed to release ch_%d resources", ch->id);
}
/* Check if there are channels associated with this file descriptor */
spin_lock_bh(&idr_lock);
idr_for_each_entry(&ch_idr, ch, i) { if (ch && ch->filp == filp) {
riocm_debug(EXIT, "ch_%d not released by %s(%d)",
ch->id, current->comm,
task_pid_nr(current));
idr_remove(&ch_idr, ch->id);
list_add(&ch->ch_node, &list);
}
}
spin_unlock_bh(&idr_lock);
((u32 *)buf)[0] = i; /* report an updated number of entries */
((u32 *)buf)[1] = info[1]; /* put back an mport ID */ if (copy_to_user(arg, buf, sizeof(u32) * (info[0] + 2)))
ret = -EFAULT;
kfree(buf); return ret;
}
/* * cm_mport_get_list() - Returns list of available local mport devices
*/ staticint cm_mport_get_list(void __user *arg)
{ int ret = 0;
u32 entries; void *buf; struct cm_dev *cm;
u32 *entry_ptr; int count = 0;
if (copy_from_user(&entries, arg, sizeof(entries))) return -EFAULT; if (entries == 0 || entries > RIO_MAX_MPORTS) return -EINVAL;
buf = kcalloc(entries + 1, sizeof(u32), GFP_KERNEL); if (!buf) return -ENOMEM;
/* * riocm_add_mport - add new local mport device into channel management core * @dev: device object associated with mport * * When a new mport device is added, CM immediately reserves inbound and * outbound RapidIO mailboxes that will be used.
*/ staticint riocm_add_mport(struct device *dev)
{ int rc; int i; struct cm_dev *cm; struct rio_mport *mport = to_rio_mport(dev);
riocm_debug(MPORT, "add mport %s", mport->name);
cm = kzalloc(sizeof(*cm), GFP_KERNEL); if (!cm) return -ENOMEM;
cm->mport = mport;
rc = rio_request_outb_mbox(mport, cm, cmbox,
RIOCM_TX_RING_SIZE, riocm_outb_msg_event); if (rc) {
riocm_error("failed to allocate OBMBOX_%d on %s",
cmbox, mport->name);
kfree(cm); return -ENODEV;
}
rc = rio_request_inb_mbox(mport, cm, cmbox,
RIOCM_RX_RING_SIZE, riocm_inb_msg_event); if (rc) {
riocm_error("failed to allocate IBMBOX_%d on %s",
cmbox, mport->name);
rio_release_outb_mbox(mport, cmbox);
kfree(cm); return -ENODEV;
}
/* * Allocate and register inbound messaging buffers to be ready * to receive channel and system management requests
*/ for (i = 0; i < RIOCM_RX_RING_SIZE; i++)
cm->rx_buf[i] = NULL;
/* * riocm_remove_mport - remove local mport device from channel management core * @dev: device object associated with mport * * Removes a local mport device from the list of registered devices that provide * channel management services. Returns an error if the specified mport is not * registered with the CM core.
*/ staticvoid riocm_remove_mport(struct device *dev)
{ struct rio_mport *mport = to_rio_mport(dev); struct cm_dev *cm; struct cm_peer *peer, *temp; struct rio_channel *ch, *_c; unsignedint i; bool found = false;
LIST_HEAD(list);
riocm_debug(MPORT, "%s", mport->name);
/* Find a matching cm_dev object */
down_write(&rdev_sem);
list_for_each_entry(cm, &cm_dev_list, list) { if (cm->mport == mport) {
list_del(&cm->list);
found = true; break;
}
}
up_write(&rdev_sem); if (!found) return;
/* * If there are any channels left in connected state send * close notification to the connection partner. * First build a list of channels that require a closing * notification because function riocm_send_close() should * be called outside of spinlock protected code.
*/
spin_lock_bh(&idr_lock);
idr_for_each_entry(&ch_idr, ch, i) { if (ch->state == RIO_CM_CONNECTED) {
riocm_debug(EXIT, "close ch %d", ch->id);
idr_remove(&ch_idr, ch->id);
list_add(&ch->ch_node, &list);
}
}
spin_unlock_bh(&idr_lock);
/* Create device class needed by udev */
ret = class_register(&dev_class); if (ret) {
riocm_error("Cannot create " DRV_NAME " class"); return ret;
}
ret = alloc_chrdev_region(&dev_number, 0, 1, DRV_NAME); if (ret) {
class_unregister(&dev_class); return ret;
}
dev_major = MAJOR(dev_number);
dev_minor_base = MINOR(dev_number);
riocm_debug(INIT, "Registered class with %d major", dev_major);
/* * Register as rapidio_port class interface to get notifications about * mport additions and removals.
*/
ret = class_interface_register(&rio_mport_interface); if (ret) {
riocm_error("class_interface_register error: %d", ret); goto err_reg;
}
/* * Register as RapidIO bus interface to get notifications about * addition/removal of remote RapidIO devices.
*/
ret = subsys_interface_register(&riocm_interface); if (ret) {
riocm_error("subsys_interface_register error: %d", ret); goto err_cl;
}
ret = register_reboot_notifier(&rio_cm_notifier); if (ret) {
riocm_error("failed to register reboot notifier (err=%d)", ret); goto err_sif;
}
ret = riocm_cdev_add(dev_number); if (ret) {
unregister_reboot_notifier(&rio_cm_notifier);
ret = -ENODEV; goto err_sif;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.