grainuum: add support libraries
Add supporting files for grainuum. Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
parent
397d153a44
commit
249c289aa7
@ -42,6 +42,8 @@
|
|||||||
#define GRAINUUM_STATE_EXTRA
|
#define GRAINUUM_STATE_EXTRA
|
||||||
#endif /* GRAINUUM_STATE_EXTRA */
|
#endif /* GRAINUUM_STATE_EXTRA */
|
||||||
|
|
||||||
|
#define GRAINUUM_PACKET_SIZE_MAX 64
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Extra fields for GrainuumUSB struct.
|
* @brief Extra fields for GrainuumUSB struct.
|
||||||
* @note Use this to store context and thread information.
|
* @note Use this to store context and thread information.
|
||||||
@ -152,9 +154,9 @@ struct usb_packet {
|
|||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
uint8_t pid;
|
uint8_t pid;
|
||||||
uint8_t data[10]; /* Including CRC */
|
uint8_t data[GRAINUUM_PACKET_SIZE_MAX + 2]; /* Including CRC */
|
||||||
} __attribute((packed, aligned(4)));
|
} __attribute((packed, aligned(4)));
|
||||||
uint8_t raw_data[11];
|
uint8_t raw_data[GRAINUUM_PACKET_SIZE_MAX + 3];
|
||||||
} __attribute((packed, aligned(4)));
|
} __attribute((packed, aligned(4)));
|
||||||
uint8_t size; /* Not including pid (so may be 0) */
|
uint8_t size; /* Not including pid (so may be 0) */
|
||||||
/* Checksum omitted */
|
/* Checksum omitted */
|
||||||
|
@ -31,7 +31,7 @@ SECTIONS
|
|||||||
*(.data .data.* .gnu.linkonce.d.*)
|
*(.data .data.* .gnu.linkonce.d.*)
|
||||||
*(.data1)
|
*(.data1)
|
||||||
_gp = ALIGN(16);
|
_gp = ALIGN(16);
|
||||||
*(.sdata .sdata.* .gnu.linkonce.s.*)
|
*(.sdata .sdata.* .gnu.linkonce.s.* .sdata2 .sdata2.*)
|
||||||
_edata = ALIGN(16); /* Make sure _edata is >= _gp. */
|
_edata = ALIGN(16); /* Make sure _edata is >= _gp. */
|
||||||
} > sram
|
} > sram
|
||||||
|
|
||||||
|
@ -139,8 +139,8 @@ static void grainuum_state_process_tx(struct GrainuumState *state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Keep the packet size to 8 bytes max */
|
/* Keep the packet size to 8 bytes max */
|
||||||
if (state->data_out_left > 8)
|
if (state->data_out_left > GRAINUUM_PACKET_SIZE_MAX)
|
||||||
state->packet.size = 8;
|
state->packet.size = GRAINUUM_PACKET_SIZE_MAX;
|
||||||
else
|
else
|
||||||
state->packet.size = state->data_out_left;
|
state->packet.size = state->data_out_left;
|
||||||
|
|
||||||
@ -172,9 +172,9 @@ static void usbStateTransferSuccess(struct GrainuumState *state)
|
|||||||
* to this function with state->data_out_left == 0. This will send
|
* to this function with state->data_out_left == 0. This will send
|
||||||
* a NULL packet, which indicates end-of-transfer.
|
* a NULL packet, which indicates end-of-transfer.
|
||||||
*/
|
*/
|
||||||
state->data_out_left -= 8;
|
state->data_out_left -= GRAINUUM_PACKET_SIZE_MAX;
|
||||||
state->data_out_max -= 8;
|
state->data_out_max -= GRAINUUM_PACKET_SIZE_MAX;
|
||||||
state->data_out += 8;
|
state->data_out += GRAINUUM_PACKET_SIZE_MAX;
|
||||||
|
|
||||||
if ((state->data_out_left < 0) || (state->data_out_max < 0)) {
|
if ((state->data_out_left < 0) || (state->data_out_max < 0)) {
|
||||||
grainuum_state_clear_tx(state, 0);
|
grainuum_state_clear_tx(state, 0);
|
||||||
@ -274,7 +274,7 @@ static int grainuum_state_process_setup(struct GrainuumState *state, const uint8
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void grainuum_state_parse_data(struct GrainuumState *state,
|
static void grainuum_state_parse_data(struct GrainuumState *state,
|
||||||
const uint8_t packet[10],
|
const uint8_t packet[GRAINUUM_PACKET_SIZE_MAX + 2],
|
||||||
uint32_t size)
|
uint32_t size)
|
||||||
{
|
{
|
||||||
(void)size;
|
(void)size;
|
||||||
@ -316,10 +316,10 @@ static inline void grainuum_state_parse_token(struct GrainuumState *state,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void grainuumProcess(struct GrainuumUSB *usb,
|
void grainuumProcess(struct GrainuumUSB *usb,
|
||||||
const uint8_t packet[12])
|
const uint8_t packet[GRAINUUM_PACKET_SIZE_MAX + 3])
|
||||||
{
|
{
|
||||||
|
|
||||||
uint32_t size = packet[11];
|
uint32_t size = packet[GRAINUUM_PACKET_SIZE_MAX + 3];
|
||||||
struct GrainuumState *state = &usb->state;
|
struct GrainuumState *state = &usb->state;
|
||||||
switch(packet[0]) {
|
switch(packet[0]) {
|
||||||
case USB_PID_SETUP:
|
case USB_PID_SETUP:
|
||||||
|
297
src/usb.c
297
src/usb.c
@ -1,11 +1,305 @@
|
|||||||
#include <grainuum.h>
|
#include <grainuum.h>
|
||||||
#include <usb.h>
|
#include <usb.h>
|
||||||
#include <generated/csr.h>
|
#include <generated/csr.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define NUM_BUFFERS 4
|
||||||
|
#define BUFFER_SIZE 64
|
||||||
|
#define EP_INTERVAL_MS 6
|
||||||
|
|
||||||
static struct GrainuumConfig cfg;
|
|
||||||
static struct GrainuumUSB usb;
|
static struct GrainuumUSB usb;
|
||||||
static uint8_t usb_buf[67];
|
static uint8_t usb_buf[67];
|
||||||
|
|
||||||
|
static uint32_t rx_buffer[NUM_BUFFERS][BUFFER_SIZE / sizeof(uint32_t)];
|
||||||
|
static uint8_t rx_buffer_head;
|
||||||
|
static uint8_t rx_buffer_tail;
|
||||||
|
|
||||||
|
static uint32_t rx_buffer_queries = 0;
|
||||||
|
|
||||||
|
|
||||||
|
static void set_usb_config_num(struct GrainuumUSB *usb, int configNum)
|
||||||
|
{
|
||||||
|
(void)usb;
|
||||||
|
(void)configNum;
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint8_t hid_report_descriptor[] = {
|
||||||
|
0x06, 0x00, 0xFF, // (GLOBAL) USAGE_PAGE 0xFF00 Vendor-defined
|
||||||
|
0x09, 0x00, // (LOCAL) USAGE 0xFF000000
|
||||||
|
0xA1, 0x01, // (MAIN) COLLECTION 0x01 Application (Usage=0xFF000000: Page=Vendor-defined, Usage=, Type=)
|
||||||
|
0x26, 0xFF, 0x00, // (GLOBAL) LOGICAL_MAXIMUM 0x00FF (255)
|
||||||
|
0x75, 0x08, // (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field
|
||||||
|
0x95, 0x08, // (GLOBAL) REPORT_COUNT 0x08 (8) Number of fields
|
||||||
|
0x06, 0xFF, 0xFF, // (GLOBAL) USAGE_PAGE 0xFFFF Vendor-defined
|
||||||
|
0x09, 0x01, // (LOCAL) USAGE 0xFFFF0001
|
||||||
|
0x81, 0x02, // (MAIN) INPUT 0x00000002 (8 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||||
|
0x09, 0x01, // (LOCAL) USAGE 0xFFFF0001
|
||||||
|
0x91, 0x02, // (MAIN) OUTPUT 0x00000002 (8 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||||
|
0xC0, // (MAIN) END_COLLECTION Application
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct usb_device_descriptor device_descriptor = {
|
||||||
|
.bLength = 18, //sizeof(struct usb_device_descriptor),
|
||||||
|
.bDescriptorType = DT_DEVICE, /* DEVICE */
|
||||||
|
.bcdUSB = 0x0200, /* USB 2.0 */
|
||||||
|
.bDeviceClass = 0x00,
|
||||||
|
.bDeviceSubClass = 0x00,
|
||||||
|
.bDeviceProtocol = 0x00,
|
||||||
|
.bMaxPacketSize0 = 0x08, /* 8-byte packets max */
|
||||||
|
.idVendor = 0x1209,
|
||||||
|
.idProduct = 0x9317,
|
||||||
|
.bcdDevice = 0x0114, /* Device release 1.14 */
|
||||||
|
.iManufacturer = 0x02, /* No manufacturer string */
|
||||||
|
.iProduct = 0x01, /* Product name in string #2 */
|
||||||
|
.iSerialNumber = 0x03, /* No serial number */
|
||||||
|
.bNumConfigurations = 0x01,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct usb_configuration_descriptor configuration_descriptor = {
|
||||||
|
.bLength = 9, //sizeof(struct usb_configuration_descriptor),
|
||||||
|
.bDescriptorType = DT_CONFIGURATION,
|
||||||
|
.wTotalLength = (9 + /*9 + 9 + 7 +*/ 9 + 9 + 7 + 7) /*
|
||||||
|
(sizeof(struct usb_configuration_descriptor)
|
||||||
|
+ sizeof(struct usb_interface_descriptor)
|
||||||
|
+ sizeof(struct usb_hid_descriptor)
|
||||||
|
+ sizeof(struct usb_endpoint_descriptor)*/,
|
||||||
|
.bNumInterfaces = 1,
|
||||||
|
.bConfigurationValue = 1,
|
||||||
|
.iConfiguration = 5,
|
||||||
|
.bmAttributes = 0x80, /* Remote wakeup not supported */
|
||||||
|
.bMaxPower = 100 / 2, /* 100 mA (in 2-mA units) */
|
||||||
|
.data = {
|
||||||
|
/* struct usb_interface_descriptor { */
|
||||||
|
/* uint8_t bLength; */ 9,
|
||||||
|
/* uint8_t bDescriptorType; */ DT_INTERFACE,
|
||||||
|
/* uint8_t bInterfaceNumber; */ 0,
|
||||||
|
/* uint8_t bAlternateSetting; */ 0,
|
||||||
|
/* uint8_t bNumEndpoints; */ 2, /* Two extra EPs */
|
||||||
|
/* uint8_t bInterfaceClass; */ 3, /* HID class */
|
||||||
|
/* uint8_t bInterfaceSubclass; */ 0, /* Boot Device subclass */
|
||||||
|
/* uint8_t bInterfaceProtocol; */ 0, /* 1 == keyboard, 2 == mouse */
|
||||||
|
/* uint8_t iInterface; */ 4, /* String index #4 */
|
||||||
|
/* }*/
|
||||||
|
|
||||||
|
/* struct usb_hid_descriptor { */
|
||||||
|
/* uint8_t bLength; */ 9,
|
||||||
|
/* uint8_t bDescriptorType; */ DT_HID,
|
||||||
|
/* uint16_t bcdHID; */ 0x11, 0x01,
|
||||||
|
/* uint8_t bCountryCode; */ 0,
|
||||||
|
/* uint8_t bNumDescriptors; */ 1, /* We have only one REPORT */
|
||||||
|
/* uint8_t bReportDescriptorType; */ DT_HID_REPORT,
|
||||||
|
/* uint16_t wReportDescriptorLength; */ sizeof(hid_report_descriptor),
|
||||||
|
sizeof(hid_report_descriptor) >> 8,
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
/* struct usb_endpoint_descriptor { */
|
||||||
|
/* uint8_t bLength; */ 7,
|
||||||
|
/* uint8_t bDescriptorType; */ DT_ENDPOINT,
|
||||||
|
/* uint8_t bEndpointAddress; */ 0x81, /* EP1 (IN) */
|
||||||
|
/* uint8_t bmAttributes; */ 3, /* Interrupt */
|
||||||
|
/* uint16_t wMaxPacketSize; */ 0x08, 0x00,
|
||||||
|
/* uint8_t bInterval; */ EP_INTERVAL_MS, /* Every 6 ms */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
/* struct usb_endpoint_descriptor { */
|
||||||
|
/* uint8_t bLength; */ 7,
|
||||||
|
/* uint8_t bDescriptorType; */ DT_ENDPOINT,
|
||||||
|
/* uint8_t bEndpointAddress; */ 0x01, /* EP1 (OUT) */
|
||||||
|
/* uint8_t bmAttributes; */ 3, /* Interrupt */
|
||||||
|
/* uint16_t wMaxPacketSize; */ 0x08, 0x00,
|
||||||
|
/* uint8_t bInterval; */ EP_INTERVAL_MS, /* Every 6 ms */
|
||||||
|
/* } */
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#define USB_STR_BUF_LEN 64
|
||||||
|
|
||||||
|
static uint32_t str_buf_storage[USB_STR_BUF_LEN / sizeof(uint32_t)];
|
||||||
|
static int send_string_descriptor(const char *str, const void **data)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
int max_len;
|
||||||
|
uint8_t *str_buf = (uint8_t *)str_buf_storage;
|
||||||
|
uint8_t *str_offset = str_buf;
|
||||||
|
|
||||||
|
len = strlen(str);
|
||||||
|
max_len = (USB_STR_BUF_LEN / 2) - 2;
|
||||||
|
|
||||||
|
if (len > max_len)
|
||||||
|
len = max_len;
|
||||||
|
|
||||||
|
*str_offset++ = (len * 2) + 2; // Two bytes for length count
|
||||||
|
*str_offset++ = DT_STRING; // Sending a string descriptor
|
||||||
|
|
||||||
|
while (len--)
|
||||||
|
{
|
||||||
|
*str_offset++ = *str++;
|
||||||
|
*str_offset++ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*data = str_buf;
|
||||||
|
|
||||||
|
// Return the size, which is stored in the first byte of the output data.
|
||||||
|
return str_buf[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_string_descriptor(struct GrainuumUSB *usb,
|
||||||
|
uint32_t num,
|
||||||
|
const void **data)
|
||||||
|
{
|
||||||
|
|
||||||
|
static const uint8_t en_us[] = {0x04, DT_STRING, 0x09, 0x04};
|
||||||
|
|
||||||
|
(void)usb;
|
||||||
|
|
||||||
|
if (num == 0)
|
||||||
|
{
|
||||||
|
*data = en_us;
|
||||||
|
return sizeof(en_us);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Product
|
||||||
|
if (num == 1)
|
||||||
|
return send_string_descriptor("Palawan Bootloader", data);
|
||||||
|
|
||||||
|
if (num == 2)
|
||||||
|
return send_string_descriptor("21", data);
|
||||||
|
|
||||||
|
if (num == 3)
|
||||||
|
return send_string_descriptor("1236", data);
|
||||||
|
|
||||||
|
if (num == 4)
|
||||||
|
return send_string_descriptor("12345", data);
|
||||||
|
|
||||||
|
if (num == 5)
|
||||||
|
return send_string_descriptor("54", data);
|
||||||
|
|
||||||
|
if (num == 6)
|
||||||
|
return send_string_descriptor("12345678901234", data);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_device_descriptor(struct GrainuumUSB *usb,
|
||||||
|
uint32_t num,
|
||||||
|
const void **data)
|
||||||
|
{
|
||||||
|
|
||||||
|
(void)usb;
|
||||||
|
|
||||||
|
if (num == 0)
|
||||||
|
{
|
||||||
|
*data = &device_descriptor;
|
||||||
|
return sizeof(device_descriptor);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_hid_report_descriptor(struct GrainuumUSB *usb,
|
||||||
|
uint32_t num,
|
||||||
|
const void **data)
|
||||||
|
{
|
||||||
|
|
||||||
|
(void)usb;
|
||||||
|
|
||||||
|
if (num == 0)
|
||||||
|
{
|
||||||
|
*data = &hid_report_descriptor;
|
||||||
|
return sizeof(hid_report_descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_configuration_descriptor(struct GrainuumUSB *usb,
|
||||||
|
uint32_t num,
|
||||||
|
const void **data)
|
||||||
|
{
|
||||||
|
|
||||||
|
(void)usb;
|
||||||
|
|
||||||
|
if (num == 0)
|
||||||
|
{
|
||||||
|
*data = &configuration_descriptor;
|
||||||
|
return configuration_descriptor.wTotalLength;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_descriptor(struct GrainuumUSB *usb,
|
||||||
|
const void *packet,
|
||||||
|
const void **response)
|
||||||
|
{
|
||||||
|
|
||||||
|
const struct usb_setup_packet *setup = packet;
|
||||||
|
|
||||||
|
switch (setup->wValueH)
|
||||||
|
{
|
||||||
|
case DT_DEVICE:
|
||||||
|
return get_device_descriptor(usb, setup->wValueL, response);
|
||||||
|
|
||||||
|
case DT_STRING:
|
||||||
|
return get_string_descriptor(usb, setup->wValueL, response);
|
||||||
|
|
||||||
|
case DT_CONFIGURATION:
|
||||||
|
return get_configuration_descriptor(usb, setup->wValueL, response);
|
||||||
|
|
||||||
|
case DT_HID_REPORT:
|
||||||
|
return get_hid_report_descriptor(usb, setup->wValueL, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *get_usb_rx_buffer(struct GrainuumUSB *usb,
|
||||||
|
uint8_t epNum,
|
||||||
|
int32_t *size)
|
||||||
|
{
|
||||||
|
(void)usb;
|
||||||
|
(void)epNum;
|
||||||
|
|
||||||
|
if (size)
|
||||||
|
*size = sizeof(rx_buffer[0]);
|
||||||
|
rx_buffer_queries++;
|
||||||
|
return rx_buffer[rx_buffer_head];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int received_data(struct GrainuumUSB *usb,
|
||||||
|
uint8_t epNum,
|
||||||
|
uint32_t bytes,
|
||||||
|
const void *data)
|
||||||
|
{
|
||||||
|
(void)usb;
|
||||||
|
(void)epNum;
|
||||||
|
(void)bytes;
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
if (epNum == 1)
|
||||||
|
{
|
||||||
|
rx_buffer_head = (rx_buffer_head + 1) & (NUM_BUFFERS - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return 0, indicating this packet is complete. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int send_data_finished(struct GrainuumUSB *usb, int result)
|
||||||
|
{
|
||||||
|
(void)usb;
|
||||||
|
(void)result;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct GrainuumConfig cfg = {
|
||||||
|
.getDescriptor = get_descriptor,
|
||||||
|
.getReceiveBuffer = get_usb_rx_buffer,
|
||||||
|
.receiveData = received_data,
|
||||||
|
.sendDataFinished = send_data_finished,
|
||||||
|
.setConfigNum = set_usb_config_num,
|
||||||
|
};
|
||||||
|
|
||||||
void usb_isr(void) {
|
void usb_isr(void) {
|
||||||
grainuumCaptureI(&usb, usb_buf);
|
grainuumCaptureI(&usb, usb_buf);
|
||||||
return;
|
return;
|
||||||
@ -13,6 +307,7 @@ void usb_isr(void) {
|
|||||||
|
|
||||||
void usb_init(void) {
|
void usb_init(void) {
|
||||||
grainuumInit(&usb, &cfg);
|
grainuumInit(&usb, &cfg);
|
||||||
|
grainuumConnect(&usb);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
42
third_party/libbase/libc.c
vendored
Normal file
42
third_party/libbase/libc.c
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/* $OpenBSD: strlen.c,v 1.8 2014/06/10 04:17:37 deraadt Exp $ */
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 1990, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
size_t
|
||||||
|
strlen(const char *str)
|
||||||
|
{
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
for (s = str; *s; ++s)
|
||||||
|
;
|
||||||
|
return (s - str);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user