1 #define _GNU_SOURCE
2
3 #include <stdint.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <fcntl.h>
8 #include <unistd.h>
9 #include <argp.h>
10 #include <sys/stat.h>
11 #include <sys/mman.h>
12 #include <dirent.h>
13 #include "openbmc_intf.h"
14 #include "gpio.h"
15 #include "gpio_json.h"
16
17 #include <sys/ioctl.h>
18 #include <linux/gpio.h>
19
20 #define GPIO_PORT_OFFSET 8
21 #define GPIO_BASE_PATH "/sys/class/gpio"
22
23 cJSON* gpio_json = NULL;
24
gpio_write(GPIO * gpio,uint8_t value)25 int gpio_write(GPIO* gpio, uint8_t value)
26 {
27 g_assert (gpio != NULL);
28 struct gpiohandle_data data;
29 memset(&data, 0, sizeof(data));
30 data.values[0] = value;
31
32 if (gpio->fd <= 0)
33 {
34 return GPIO_ERROR;
35 }
36
37 if (ioctl(gpio->fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data) < 0)
38 {
39 return GPIO_WRITE_ERROR;
40 }
41
42 return GPIO_OK;
43 }
44
gpio_read(GPIO * gpio,uint8_t * value)45 int gpio_read(GPIO* gpio, uint8_t *value)
46 {
47 g_assert (gpio != NULL);
48 struct gpiohandle_data data;
49 memset(&data, 0, sizeof(data));
50
51 if (gpio->fd <= 0)
52 {
53 return GPIO_ERROR;
54 }
55
56 if (ioctl(gpio->fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data) < 0)
57 {
58 return GPIO_READ_ERROR;
59 }
60
61 *value = data.values[0];
62
63 return GPIO_OK;
64 }
65
66 /**
67 * Determine the GPIO base number for the system. It is found in
68 * the 'base' file in the /sys/class/gpio/gpiochipX/ directory where the
69 * /sys/class/gpio/gpiochipX/label file has a '1e780000.gpio' in it.
70 *
71 * Note: This method is ASPEED specific. Could add support for
72 * additional SOCs in the future.
73 *
74 * @return int - the GPIO base number, or < 0 if not found
75 */
get_gpio_base()76 int get_gpio_base()
77 {
78 int gpio_base = -1;
79
80 DIR* dir = opendir(GPIO_BASE_PATH);
81 if (dir == NULL)
82 {
83 fprintf(stderr, "Unable to open directory %s\n",
84 GPIO_BASE_PATH);
85 return -1;
86 }
87
88 struct dirent* entry;
89 while ((entry = readdir(dir)) != NULL)
90 {
91 /* Look in the gpiochip<X> directories for a file called 'label' */
92 /* that contains '1e780000.gpio', then in that directory read */
93 /* the GPIO base out of the 'base' file. */
94
95 if (strncmp(entry->d_name, "gpiochip", 8) != 0)
96 {
97 continue;
98 }
99
100 gboolean is_bmc = FALSE;
101 char* label_name;
102 asprintf(&label_name, "%s/%s/label",
103 GPIO_BASE_PATH, entry->d_name);
104
105 FILE* fd = fopen(label_name, "r");
106 free(label_name);
107
108 if (!fd)
109 {
110 continue;
111 }
112
113 char label[14];
114 if (fgets(label, 14, fd) != NULL)
115 {
116 if (strcmp(label, "1e780000.gpio") == 0)
117 {
118 is_bmc = TRUE;
119 }
120 }
121 fclose(fd);
122
123 if (!is_bmc)
124 {
125 continue;
126 }
127
128 char* base_name;
129 asprintf(&base_name, "%s/%s/base",
130 GPIO_BASE_PATH, entry->d_name);
131
132 fd = fopen(base_name, "r");
133 free(base_name);
134
135 if (!fd)
136 {
137 continue;
138 }
139
140 if (fscanf(fd, "%d", &gpio_base) != 1)
141 {
142 gpio_base = -1;
143 }
144 fclose(fd);
145
146 /* We found the right file. No need to continue. */
147 break;
148 }
149 closedir(dir);
150
151 if (gpio_base == -1)
152 {
153 fprintf(stderr, "Could not find GPIO base\n");
154 }
155
156 return gpio_base;
157 }
158
159 /**
160 * Converts the GPIO port/offset nomenclature value
161 * to a number. Supports the ASPEED method where
162 * num = base + (port * 8) + offset, and the port is an
163 * A-Z character that converts to 0 to 25. The base
164 * is obtained form the hardware.
165 *
166 * For example: "A5" -> 5, "Z7" -> 207
167 *
168 * @param[in] gpio - the GPIO name
169 *
170 * @return int - the GPIO number or < 0 if not found
171 */
convert_gpio_to_num(const char * gpio)172 int convert_gpio_to_num(const char* gpio)
173 {
174 size_t len = strlen(gpio);
175 if (len < 2)
176 {
177 fprintf(stderr, "Invalid GPIO name %s\n", gpio);
178 return -1;
179 }
180
181 /* Read the offset from the last character */
182 if (!isdigit(gpio[len-1]))
183 {
184 fprintf(stderr, "Invalid GPIO offset in GPIO %s\n", gpio);
185 return -1;
186 }
187
188 int offset = gpio[len-1] - '0';
189
190 /* Read the port from the second to last character */
191 if (!isalpha(gpio[len-2]))
192 {
193 fprintf(stderr, "Invalid GPIO port in GPIO %s\n", gpio);
194 return -1;
195 }
196 int port = toupper(gpio[len-2]) - 'A';
197
198 /* Check for a 2 character port, like AA */
199 if ((len == 3) && isalpha(gpio[len-3]))
200 {
201 port += 26 * (toupper(gpio[len-3]) - 'A' + 1);
202 }
203
204 return (port * GPIO_PORT_OFFSET) + offset;
205 }
206
207 /**
208 * Returns the cJSON pointer to the GPIO definition
209 * for the GPIO passed in.
210 *
211 * @param[in] gpio_name - the GPIO name, like BMC_POWER_UP
212 *
213 * @return cJSON* - pointer to the cJSON object or NULL
214 * if not found.
215 */
get_gpio_def(const char * gpio_name)216 cJSON* get_gpio_def(const char* gpio_name)
217 {
218 if (gpio_json == NULL)
219 {
220 gpio_json = load_json();
221 if (gpio_json == NULL)
222 {
223 return NULL;
224 }
225 }
226
227 cJSON* gpio_defs = cJSON_GetObjectItem(gpio_json, "gpio_definitions");
228 g_assert(gpio_defs != NULL);
229
230 cJSON* def;
231 cJSON_ArrayForEach(def, gpio_defs)
232 {
233 cJSON* name = cJSON_GetObjectItem(def, "name");
234 g_assert(name != NULL);
235
236 if (strcmp(name->valuestring, gpio_name) == 0)
237 {
238 return def;
239 }
240 }
241 return NULL;
242 }
243
244 /**
245 * Frees the gpio_json memory
246 *
247 * Can be called once when callers are done calling making calls
248 * to gpio_init() so that the JSON only needs to be loaded once.
249 */
gpio_inits_done()250 void gpio_inits_done()
251 {
252 if (gpio_json != NULL)
253 {
254 cJSON_Delete(gpio_json);
255 gpio_json = NULL;
256 }
257 }
258
259 /**
260 * Fills in the dev, direction, and num elements in
261 * the GPIO structure.
262 *
263 * @param gpio - the GPIO structure to fill in
264 *
265 * @return GPIO_OK if successful
266 */
gpio_get_params(GPIO * gpio)267 int gpio_get_params(GPIO* gpio)
268 {
269 gpio->dev = g_strdup(GPIO_BASE_PATH);
270
271 const cJSON* def = get_gpio_def(gpio->name);
272 if (def == NULL)
273 {
274 fprintf(stderr, "Unable to find GPIO %s in the JSON\n",
275 gpio->name);
276 return GPIO_LOOKUP_ERROR;
277 }
278
279 const cJSON* dir = cJSON_GetObjectItem(def, "direction");
280 g_assert(dir != NULL);
281 gpio->direction = g_strdup(dir->valuestring);
282
283 /* Must use either 'num', like 87, or 'pin', like "A5" */
284 const cJSON* num = cJSON_GetObjectItem(def, "num");
285 if ((num != NULL) && cJSON_IsNumber(num))
286 {
287 gpio->num = num->valueint;
288 }
289 else
290 {
291 const cJSON* pin = cJSON_GetObjectItem(def, "pin");
292 g_assert(pin != NULL);
293
294 gpio->num = convert_gpio_to_num(pin->valuestring);
295 if (gpio->num < 0)
296 {
297 return GPIO_LOOKUP_ERROR;
298 }
299 }
300 // TODO: For the purposes of skeleton and the projects that use it,
301 // it should be safe to assume this will always be 0. Eventually skeleton
302 // should be going away, but if the need arises before then this may need
303 // to be updated to handle non-zero cases.
304 gpio->chip_id = 0;
305 return GPIO_OK;
306 }
307
gpio_open(GPIO * gpio,uint8_t default_value)308 int gpio_open(GPIO* gpio, uint8_t default_value)
309 {
310 g_assert (gpio != NULL);
311
312 char buf[255];
313 sprintf(buf, "/dev/gpiochip%d", gpio->chip_id);
314 gpio->fd = open(buf, 0);
315 if (gpio->fd == -1)
316 {
317 return GPIO_OPEN_ERROR;
318 }
319
320 struct gpiohandle_request req;
321 memset(&req, 0, sizeof(req));
322 strncpy(req.consumer_label, "skeleton-gpio", sizeof(req.consumer_label));
323
324 // open gpio for writing or reading
325 if (gpio->direction == NULL)
326 {
327 gpio_close(gpio);
328 return GPIO_OPEN_ERROR;
329 }
330 req.flags = (strcmp(gpio->direction,"in") == 0) ? GPIOHANDLE_REQUEST_INPUT
331 : GPIOHANDLE_REQUEST_OUTPUT;
332
333 req.lineoffsets[0] = gpio->num;
334 req.lines = 1;
335
336 if (strcmp(gpio->direction,"out") == 0)
337 {
338 req.default_values[0] = default_value;
339 }
340
341 int rc = ioctl(gpio->fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
342 if (rc < 0)
343 {
344 gpio_close(gpio);
345 return GPIO_OPEN_ERROR;
346 }
347 gpio_close(gpio);
348 gpio->fd = req.fd;
349
350 return GPIO_OK;
351 }
352
gpio_close(GPIO * gpio)353 void gpio_close(GPIO* gpio)
354 {
355 if(gpio->fd < 0)
356 return;
357
358 close(gpio->fd);
359 gpio->fd = -1;
360 }
361