1 |
/* |
---|
2 |
* Copyright (C) 2009 by Jonathan Woithe |
---|
3 |
* |
---|
4 |
* This file is part of FFADO |
---|
5 |
* FFADO = Free FireWire (pro-)audio drivers for Linux |
---|
6 |
* |
---|
7 |
* FFADO is based upon FreeBoB. |
---|
8 |
* |
---|
9 |
* This program is free software: you can redistribute it and/or modify |
---|
10 |
* it under the terms of the GNU General Public License as published by |
---|
11 |
* the Free Software Foundation, either version 2 of the License, or |
---|
12 |
* (at your option) version 3 of the License. |
---|
13 |
* |
---|
14 |
* This program is distributed in the hope that it will be useful, |
---|
15 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
16 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
17 |
* GNU General Public License for more details. |
---|
18 |
* |
---|
19 |
* You should have received a copy of the GNU General Public License |
---|
20 |
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
---|
21 |
* |
---|
22 |
*/ |
---|
23 |
|
---|
24 |
/* |
---|
25 |
* This file implements a simple interface to a shared memory object used |
---|
26 |
* to share device configuration between FFADO components. This is required |
---|
27 |
* because a significant amount of device information is not available for |
---|
28 |
* reading from the device itself. |
---|
29 |
* |
---|
30 |
* The idea is that each RME FFADO process will call rme_shm_open() to |
---|
31 |
* obtain a pointer to shared memory containing the structure of interest. |
---|
32 |
* If no process has yet created the shared object it will be created at |
---|
33 |
* this point; otherwise the existing object will be used. Each new process |
---|
34 |
* to use the object increments a reference count. |
---|
35 |
* |
---|
36 |
* On exit, processes using this shared object will call rme_shm_close(). |
---|
37 |
* The reference counter is decremented and if it becomes zero the shared |
---|
38 |
* object will be unlinked. This way, so long as at least one RME process |
---|
39 |
* is active (which doesn't have to be the process which created the object |
---|
40 |
* initially) the device's configuration will be persistent. |
---|
41 |
*/ |
---|
42 |
|
---|
43 |
#define RME_SHM_NAME "/ffado:rme_shm-" |
---|
44 |
#define RME_SHM_SIZE sizeof(rme_shm_t) |
---|
45 |
|
---|
46 |
#define RME_SHM_LOCKNAME "/ffado:rme_shm_lock" |
---|
47 |
|
---|
48 |
#include <unistd.h> |
---|
49 |
#include <errno.h> |
---|
50 |
#include <string> |
---|
51 |
#include <stdio.h> |
---|
52 |
#include <sys/types.h> |
---|
53 |
#include <sys/mman.h> |
---|
54 |
#include <fcntl.h> |
---|
55 |
|
---|
56 |
#include "rme_shm.h" |
---|
57 |
|
---|
58 |
static signed int rme_shm_lock_for_setup(void) { |
---|
59 |
signed lockfd; |
---|
60 |
|
---|
61 |
do { |
---|
62 |
// The check for existance and shm creation are atomic so it's safe |
---|
63 |
// to use this as the basis for a global lock. |
---|
64 |
lockfd = shm_open(RME_SHM_LOCKNAME, O_RDWR | O_CREAT | O_EXCL, 0644); |
---|
65 |
if (lockfd < 0) |
---|
66 |
usleep(10000); |
---|
67 |
} while (lockfd < 0); |
---|
68 |
|
---|
69 |
return lockfd; |
---|
70 |
} |
---|
71 |
|
---|
72 |
static void rme_shm_unlock_for_setup(signed int lockfd) { |
---|
73 |
close(lockfd); |
---|
74 |
shm_unlink(RME_SHM_LOCKNAME); |
---|
75 |
} |
---|
76 |
|
---|
77 |
void rme_shm_lock(rme_shm_t *shm_data) { |
---|
78 |
pthread_mutex_lock(&shm_data->lock); |
---|
79 |
} |
---|
80 |
|
---|
81 |
void rme_shm_unlock(rme_shm_t *shm_data) { |
---|
82 |
pthread_mutex_unlock(&shm_data->lock); |
---|
83 |
} |
---|
84 |
|
---|
85 |
signed int rme_shm_open(std::string id, rme_shm_t **shm_data) { |
---|
86 |
|
---|
87 |
std::string shm_name; |
---|
88 |
signed int shmfd, lockfd; |
---|
89 |
rme_shm_t *data; |
---|
90 |
signed int created = 0; |
---|
91 |
|
---|
92 |
if (shm_data == NULL) { |
---|
93 |
return RSO_ERROR; |
---|
94 |
} |
---|
95 |
*shm_data = NULL; |
---|
96 |
|
---|
97 |
lockfd = rme_shm_lock_for_setup(); |
---|
98 |
|
---|
99 |
shm_name = std::string(RME_SHM_NAME); |
---|
100 |
shm_name.append(id); |
---|
101 |
|
---|
102 |
shmfd = shm_open(shm_name.c_str(), O_RDWR, 0644); |
---|
103 |
if (shmfd < 0) { |
---|
104 |
if (errno == ENOENT) { |
---|
105 |
shmfd = shm_open(shm_name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0644); |
---|
106 |
if (shmfd < 0) |
---|
107 |
return RSO_ERR_SHM; |
---|
108 |
else { |
---|
109 |
ftruncate(shmfd, RME_SHM_SIZE); |
---|
110 |
created = 1; |
---|
111 |
} |
---|
112 |
} else |
---|
113 |
return RSO_ERR_SHM; |
---|
114 |
} |
---|
115 |
|
---|
116 |
data = (rme_shm_t *)mmap(NULL, RME_SHM_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, shmfd, 0); |
---|
117 |
close(shmfd); |
---|
118 |
|
---|
119 |
if (data == MAP_FAILED) |
---|
120 |
return RSO_ERR_MMAP; |
---|
121 |
|
---|
122 |
if (created) { |
---|
123 |
pthread_mutex_init(&data->lock, NULL); |
---|
124 |
snprintf(data->shm_name, sizeof(data->shm_name), "%s", shm_name.c_str()); |
---|
125 |
} |
---|
126 |
|
---|
127 |
rme_shm_lock(data); |
---|
128 |
data->ref_count++; |
---|
129 |
rme_shm_unlock(data); |
---|
130 |
|
---|
131 |
rme_shm_unlock_for_setup(lockfd); |
---|
132 |
|
---|
133 |
*shm_data = data; |
---|
134 |
return created?RSO_OPEN_CREATED:RSO_OPEN_ATTACHED; |
---|
135 |
} |
---|
136 |
|
---|
137 |
signed int rme_shm_close(rme_shm_t *shm_data) { |
---|
138 |
|
---|
139 |
std::string shm_name = std::string(shm_data->shm_name); |
---|
140 |
signed int unlink = 0; |
---|
141 |
signed int lockfd; |
---|
142 |
|
---|
143 |
lockfd = rme_shm_lock_for_setup(); |
---|
144 |
|
---|
145 |
rme_shm_lock(shm_data); |
---|
146 |
shm_data->ref_count--; |
---|
147 |
unlink = (shm_data->ref_count == 0); |
---|
148 |
rme_shm_unlock(shm_data); |
---|
149 |
|
---|
150 |
if (unlink) { |
---|
151 |
// This is safe: if the reference count is zero there can't be any |
---|
152 |
// other process using the lock at this point. |
---|
153 |
pthread_mutex_destroy(&shm_data->lock); |
---|
154 |
} |
---|
155 |
|
---|
156 |
munmap(shm_data, RME_SHM_SIZE); |
---|
157 |
|
---|
158 |
if (unlink) |
---|
159 |
shm_unlink(shm_name.c_str()); |
---|
160 |
|
---|
161 |
rme_shm_unlock_for_setup(lockfd); |
---|
162 |
|
---|
163 |
return unlink?RSO_CLOSE_DELETE:RSO_CLOSE; |
---|
164 |
} |
---|