468 lines
10 KiB
C
468 lines
10 KiB
C
|
//#define DEBUG
|
||
|
#include <stdio.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <errno.h>
|
||
|
#include <string.h>
|
||
|
#include <unistd.h>
|
||
|
#include <sys/types.h>
|
||
|
|
||
|
#include <sys/socket.h>
|
||
|
#include <sys/epoll.h>
|
||
|
#include <linux/netlink.h>
|
||
|
#include <sys/stat.h>
|
||
|
|
||
|
#include "MtpServer.h"
|
||
|
#include "Disk/Disk.h"
|
||
|
|
||
|
|
||
|
#define KB(size) (size*1024)
|
||
|
#define MB(size) (KB(size)*1024)
|
||
|
|
||
|
#define AID_MEDIA_RW 1023
|
||
|
|
||
|
#ifdef MTP_SERVER_DAEMON
|
||
|
|
||
|
static struct MtpStorage *storage;
|
||
|
|
||
|
typedef struct {
|
||
|
struct MtpStorage *storage;
|
||
|
uint32_t id;
|
||
|
char path[256];
|
||
|
char description[32];
|
||
|
uint64_t reserve_space;
|
||
|
uint64_t max_file_size;
|
||
|
}mtp_storage_t;
|
||
|
|
||
|
|
||
|
typedef struct {
|
||
|
struct MtpServer *server;
|
||
|
mtp_storage_t *storage_array;
|
||
|
uint32_t storage_array_size;
|
||
|
}mtp_handle_t;
|
||
|
|
||
|
static mtp_storage_t gStorageArray[] = {
|
||
|
{NULL, 65537, "/mnt/UDISK", "Tina存储设备", 0, 0},
|
||
|
//{NULL, 65539, "/boot", "boot", 0, 0},
|
||
|
//{NULL, 65540, "/boot-res", "boot-res", 0, 0},
|
||
|
};
|
||
|
|
||
|
static mtp_handle_t gMtpHandle = {
|
||
|
.server = NULL,
|
||
|
.storage_array = gStorageArray,
|
||
|
.storage_array_size = sizeof(gStorageArray)/sizeof(mtp_storage_t),
|
||
|
};
|
||
|
|
||
|
static int mtp_server_init()
|
||
|
{
|
||
|
uint32_t i;
|
||
|
|
||
|
printf("MtpServer init!\n");
|
||
|
/* MtpServer init */
|
||
|
gMtpHandle.server = MtpServerInit(AID_MEDIA_RW, 0664, 0775);
|
||
|
if (!gMtpHandle.server)
|
||
|
return -1;
|
||
|
/* MtpStorage init */
|
||
|
for (i = 0; i < gMtpHandle.storage_array_size; i++) {
|
||
|
mtp_storage_t *storage = &gMtpHandle.storage_array[i];
|
||
|
storage->storage =
|
||
|
MtpStorageInit(storage->id, storage->path,
|
||
|
storage->description,
|
||
|
storage->reserve_space,
|
||
|
storage->max_file_size);
|
||
|
if (!storage->storage)
|
||
|
return -1;
|
||
|
/* Add storage into MtpServer */
|
||
|
MtpServerAddStorage(storage->storage, gMtpHandle.server);
|
||
|
/* Add storage into MtpDatabase */
|
||
|
MtpDataBaseAddStorage(storage->storage, gMtpHandle.server->mDataBase);
|
||
|
}
|
||
|
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void mtp_server_exit()
|
||
|
{
|
||
|
uint32_t i;
|
||
|
printf("MtpServer exit!\n");
|
||
|
if (!gMtpHandle.server)
|
||
|
return;
|
||
|
for (i = 0; i < gMtpHandle.storage_array_size; i++) {
|
||
|
mtp_storage_t *storage = &gMtpHandle.storage_array[i];
|
||
|
if (storage->storage != NULL) {
|
||
|
MtpStorageRelease(storage->storage);
|
||
|
storage->storage = NULL;
|
||
|
}
|
||
|
}
|
||
|
MtpServerRelease(gMtpHandle.server);
|
||
|
gMtpHandle.server = NULL;
|
||
|
}
|
||
|
static int tina_usb_uevent_socket(void)
|
||
|
{
|
||
|
struct sockaddr_nl nls;
|
||
|
int nlbufsize = 64*1024;
|
||
|
int s;
|
||
|
|
||
|
memset(&nls, 0, sizeof(struct sockaddr_nl));
|
||
|
nls.nl_family = AF_NETLINK;
|
||
|
nls.nl_pid = getpid();
|
||
|
nls.nl_groups = -1;
|
||
|
|
||
|
if ((s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) == -1) {
|
||
|
printf("Failed to open socket: %s\n", strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
if (setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &nlbufsize, sizeof(nlbufsize))) {
|
||
|
printf("Failed to resize receive buffer: %s\n", strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
if (bind(s, (void *)&nls, sizeof(struct sockaddr_nl))) {
|
||
|
printf("Failed to bind socket: %s\n", strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
fcntl(s, F_SETFL, O_NONBLOCK);
|
||
|
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
enum {
|
||
|
USB_STATE_CONNECTED = 1,
|
||
|
USB_STATE_DISCONNECTED,
|
||
|
};
|
||
|
|
||
|
static int tina_usb_uevent_detect(int socket)
|
||
|
{
|
||
|
#define UEVENT_MSG_LEN 1024
|
||
|
char buf[UEVENT_MSG_LEN];
|
||
|
int len, i = 0;
|
||
|
|
||
|
memset(buf, 0, sizeof(buf));
|
||
|
len = recv(socket, buf, sizeof(buf), MSG_DONTWAIT);
|
||
|
if (len < 1)
|
||
|
return -1;
|
||
|
if (len >= UEVENT_MSG_LEN)
|
||
|
return -1;
|
||
|
buf[len] = '\0';
|
||
|
#if 0
|
||
|
printf("=========================\n");
|
||
|
{
|
||
|
int l=0;
|
||
|
printf("recv uevent:\n");
|
||
|
while(l < len) {
|
||
|
printf("%s\n", &buf[l]);
|
||
|
l += strlen(&buf[l]) + 1;
|
||
|
}
|
||
|
printf("=========================\n");
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
while (i < len) {
|
||
|
int l = strlen(buf + i)+1;
|
||
|
if (strstr(&buf[i], "USB_STATE=CONNECTED"))
|
||
|
return USB_STATE_CONNECTED;
|
||
|
if (strstr(&buf[i], "USB_STATE=DISCONNECTED"))
|
||
|
return USB_STATE_DISCONNECTED;
|
||
|
i += l;
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static void usb_uevent_handle(int arg)
|
||
|
{
|
||
|
int socket = arg;
|
||
|
int ret = -1;
|
||
|
ret = tina_usb_uevent_detect(socket);
|
||
|
switch(ret){
|
||
|
case USB_STATE_CONNECTED:
|
||
|
#if 1
|
||
|
if (gMtpHandle.server != NULL) {
|
||
|
printf("mtp didn't release\n");
|
||
|
return;
|
||
|
}
|
||
|
usleep(500000);
|
||
|
if (mtp_server_init() != 0) {
|
||
|
printf("mtp server init failed\n");
|
||
|
return ;
|
||
|
}
|
||
|
gMtpHandle.server->run(gMtpHandle.server);
|
||
|
#else
|
||
|
printf("recv connected state\n");
|
||
|
#endif
|
||
|
break;
|
||
|
case USB_STATE_DISCONNECTED:
|
||
|
#if 1
|
||
|
mtp_server_exit();
|
||
|
#else
|
||
|
printf("recv disconnected state\n");
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
typedef struct {
|
||
|
uint32_t action; /* 0:add, 1:remove, 2:update */
|
||
|
uint32_t type; /* 0:file, 2:dir */
|
||
|
uint32_t srcPathLen; /* src path buffer length */
|
||
|
uint32_t destPathLen; /* dest path buffer length */
|
||
|
char *path; /* object path */
|
||
|
}mtp_command_t;
|
||
|
|
||
|
#define MTP_FIFO_NAME "/tmp/.mtp_fifo"
|
||
|
|
||
|
static void mtp_local_fs_update_handle(int fd)
|
||
|
{
|
||
|
int ret;
|
||
|
uint32_t id, i;
|
||
|
char buf[512];
|
||
|
char *srcPath = NULL, *destPath = NULL;
|
||
|
mtp_command_t *command;
|
||
|
struct MtpServer *server = NULL;
|
||
|
struct MtpStorage *storage = NULL;
|
||
|
|
||
|
mtp_tools_function_t func;
|
||
|
enum {
|
||
|
MTP_TOOLS_TYPE_FILE = 0,
|
||
|
MTP_TOOLS_TYPE_DIR,
|
||
|
} type;
|
||
|
|
||
|
|
||
|
memset(buf, 0, sizeof(buf));
|
||
|
ret = read(fd, buf, sizeof(buf));
|
||
|
command = (mtp_command_t *)buf;
|
||
|
printf("ret = %d, command: action:%u, type:%u, srcPathLen:%u, destPathLen:%u\n",
|
||
|
ret, command->action, command->type, command->srcPathLen, command->destPathLen);
|
||
|
|
||
|
if (ret < sizeof(mtp_command_t)) {
|
||
|
printf("invalid mtp command size!\n");
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
command->path = (char *)&command[1];
|
||
|
printf("sLen:%u, dLen:%u, path: %s\n", command->srcPathLen, command->destPathLen, command->path);
|
||
|
|
||
|
if (command->srcPathLen > 1) {
|
||
|
srcPath = command->path;
|
||
|
} else {
|
||
|
printf("invalid src path length!\n");
|
||
|
return ;
|
||
|
}
|
||
|
if (command->destPathLen > 1)
|
||
|
destPath = command->path + command->srcPathLen;
|
||
|
|
||
|
func = command->action;
|
||
|
|
||
|
if (command->type == MTP_TOOLS_TYPE_FILE)
|
||
|
type = MTP_FORMAT_UNDEFINED;
|
||
|
else if (command->type == MTP_TOOLS_TYPE_DIR)
|
||
|
type = MTP_FORMAT_ASSOCIATION;
|
||
|
|
||
|
for (i = 0; i < gMtpHandle.storage_array_size; i++) {
|
||
|
char *p = gMtpHandle.storage_array[i].path;
|
||
|
if (!strncmp(p, srcPath, strlen(p))) {
|
||
|
id = gMtpHandle.storage_array[i].id;
|
||
|
storage = gMtpHandle.storage_array[i].storage;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
server = gMtpHandle.server;
|
||
|
if (!server) {
|
||
|
DLOG("MtpServer not connect.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch (func) {
|
||
|
case MTP_TOOLS_FUNCTION_ADD:
|
||
|
case MTP_TOOLS_FUNCTION_REMOVE:
|
||
|
case MTP_TOOLS_FUNCTION_UPDATE:
|
||
|
ret = MtpToolsCommandControl(func, srcPath, type,
|
||
|
server->mFileGroup, server->mFilePermission,
|
||
|
id, storage,
|
||
|
gMtpHandle.server->mDataBase);
|
||
|
break;
|
||
|
case MTP_TOOLS_FUNCTION_CUT:
|
||
|
ret = MtpToolsCommandControl(MTP_TOOLS_FUNCTION_ADD, destPath, type,
|
||
|
server->mFileGroup, server->mFilePermission,
|
||
|
id, storage,
|
||
|
gMtpHandle.server->mDataBase);
|
||
|
ret += MtpToolsCommandControl(MTP_TOOLS_FUNCTION_REMOVE, srcPath, type,
|
||
|
server->mFileGroup, server->mFilePermission,
|
||
|
id, storage,
|
||
|
gMtpHandle.server->mDataBase);
|
||
|
break;
|
||
|
case MTP_TOOLS_FUNCTION_COPY:
|
||
|
ret = MtpToolsCommandControl(MTP_TOOLS_FUNCTION_ADD, destPath, type,
|
||
|
server->mFileGroup, server->mFilePermission,
|
||
|
id, storage,
|
||
|
gMtpHandle.server->mDataBase);
|
||
|
break;
|
||
|
default:
|
||
|
ret = -1;
|
||
|
printf("unknow mtp tools function:%d\n", func);
|
||
|
break;
|
||
|
}
|
||
|
if (ret < 0)
|
||
|
printf("handle mtp command failed\n");
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
#define s2ms(n) (n*1000)
|
||
|
#define MAX_EPOLL_EVENTS 40
|
||
|
|
||
|
int epollfd;
|
||
|
int event_cnt;
|
||
|
|
||
|
void register_usb_state_event(int socket)
|
||
|
{
|
||
|
struct epoll_event ev;
|
||
|
|
||
|
ev.events = EPOLLIN;
|
||
|
ev.data.fd = socket;
|
||
|
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, socket, &ev) == -1) {
|
||
|
printf("epoll_ctl socket failed\n");
|
||
|
close(epollfd);
|
||
|
exit(1);
|
||
|
}
|
||
|
event_cnt++;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void register_local_fs_update_event(int fd)
|
||
|
{
|
||
|
struct epoll_event ev;
|
||
|
|
||
|
ev.events = EPOLLET | EPOLLIN;
|
||
|
ev.data.fd = fd;
|
||
|
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
|
||
|
printf("epoll_ctl pipe_r failed\n");
|
||
|
close(epollfd);
|
||
|
exit(1);
|
||
|
}
|
||
|
event_cnt++;
|
||
|
}
|
||
|
|
||
|
int main()
|
||
|
{
|
||
|
int socket;
|
||
|
int timeout = s2ms(20), event_num = 0;
|
||
|
struct epoll_event events[MAX_EPOLL_EVENTS];
|
||
|
int fd;
|
||
|
|
||
|
epollfd = epoll_create(MAX_EPOLL_EVENTS);
|
||
|
if (epollfd == -1) {
|
||
|
printf("epoll_create failed\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* open netlink socket */
|
||
|
socket = tina_usb_uevent_socket();
|
||
|
if (socket < 0)
|
||
|
return -1;
|
||
|
|
||
|
/* create fifo */
|
||
|
if (access(MTP_FIFO_NAME, F_OK) == -1) {
|
||
|
if (mkfifo(MTP_FIFO_NAME, 0600) != 0) {
|
||
|
printf("mkfifo %s failed\n", MTP_FIFO_NAME);
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
/* open fifo */
|
||
|
fd = open(MTP_FIFO_NAME, O_RDONLY | O_NONBLOCK, 0);
|
||
|
if (fd < 0) {
|
||
|
printf("open %s failed\n", MTP_FIFO_NAME);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* listen USB uevent */
|
||
|
register_usb_state_event(socket);
|
||
|
/* listen local fs update command from pipe */
|
||
|
register_local_fs_update_event(fd);
|
||
|
|
||
|
|
||
|
while(1) {
|
||
|
int i;
|
||
|
|
||
|
event_num = epoll_wait(epollfd, events, event_cnt, timeout);
|
||
|
|
||
|
if (event_num == -1)
|
||
|
break;
|
||
|
for (i = 0; i < event_num; i++) {
|
||
|
#if 0
|
||
|
if (events[i].data.ptr)
|
||
|
(*(void (*)(int))events[i].data.ptr) (socket);
|
||
|
#endif
|
||
|
if (events[i].data.fd == socket)
|
||
|
usb_uevent_handle(socket);
|
||
|
else if (events[i].data.fd == fd)
|
||
|
mtp_local_fs_update_handle(fd);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unlink(MTP_FIFO_NAME);
|
||
|
close(socket);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
#else
|
||
|
|
||
|
int main()
|
||
|
{
|
||
|
#if 0
|
||
|
struct MtpServer *mserver;
|
||
|
struct MtpStorage *mStorage;
|
||
|
|
||
|
mserver = MtpServerInit(AID_MEDIA_RW, 0664, 0775);
|
||
|
|
||
|
if (!mserver)
|
||
|
goto err;
|
||
|
|
||
|
//mStorage = MtpStorageInit(65537, "/mnt/UDISK", "Tina存储设备", 0, MB(1));
|
||
|
mStorage = MtpStorageInit(65537, "/mnt/UDISK", "Tina存储设备", 0, 0);
|
||
|
MtpServerAddStorage(mStorage, mserver);
|
||
|
MtpDataBaseAddStorage(mStorage, mserver->mDataBase);
|
||
|
|
||
|
mserver->run(mserver);
|
||
|
|
||
|
err:
|
||
|
printf("MtpServer exit!\n");
|
||
|
MtpServerRelease(mserver);
|
||
|
mserver = NULL;
|
||
|
#else
|
||
|
while (1) {
|
||
|
struct MtpServer *mserver = NULL;
|
||
|
struct MtpStorage *mStorage = NULL;
|
||
|
int timeout = 20;
|
||
|
|
||
|
printf("MtpServer init!\n");
|
||
|
mserver = MtpServerInit(AID_MEDIA_RW, 0664, 0775);
|
||
|
|
||
|
if (!mserver)
|
||
|
goto err;
|
||
|
|
||
|
//mStorage = MtpStorageInit(65537, "/mnt/UDISK", "Tina存储设备", 0, MB(1));
|
||
|
DLOG("");
|
||
|
mStorage = MtpStorageInit(65537, "/mnt/UDISK", "Tina存储设备", 0, 0);
|
||
|
DLOG("");
|
||
|
MtpServerAddStorage(mStorage, mserver);
|
||
|
DLOG("");
|
||
|
MtpDataBaseAddStorage(mStorage, mserver->mDataBase);
|
||
|
DLOG("");
|
||
|
|
||
|
mserver->run(mserver);
|
||
|
|
||
|
printf("sleep %d second\n", timeout);
|
||
|
sleep(timeout);
|
||
|
//getchar();
|
||
|
err:
|
||
|
printf("MtpServer exit!\n");
|
||
|
MtpStorageRelease(mStorage);
|
||
|
MtpServerRelease(mserver);
|
||
|
break;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|