/* * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * */ #include #include #include #include #include #include #include #include #include #include #include "RCX_Pipe.h" #include "PDebug.h" #define kVendorID 1684 #define kProductID 1 #define kConfiguration 0 #define kReadPipe 1 #define kWritePipe 2 // these should come from a LEGO include #define LTW_REQ_GET_PARM 1 #define LTW_REQ_SET_PARM 2 #define LTW_PARM_RANGE 2 #define LTW_RANGE_SHORT 1 #define LTW_RANGE_MEDIUM 2 #define LTW_REQ_SET_TX_SPEED 0xEF #define LTW_REQ_SET_RX_SPEED 0xF1 #define SPEED_COMM_BAUD_2400 8 #define SPEED_COMM_BAUD_4800 0x10 #define LTW_REQ_SET_TX_CARRIER_FREQUENCY 0xF4 #define UInt32 uint32_t #define UInt16 uint16_t #define UInt8 uint8_t #define IOReturn int typedef struct LTW_REQ_REPLY_HEADER { UInt16 wNoOfBytes; // Number of bytes in the reply UInt8 bErrCode; // Request return code UInt8 bValue; // Request return value } LTW_REQ_REPLY_HEADER; typedef LTW_REQ_REPLY_HEADER LTW_REQ_GET_SET_PARM_REPLY; class RCX_USBTowerPipe_fbsd : public RCX_Pipe { public: RCX_USBTowerPipe_fbsd(); ~RCX_USBTowerPipe_fbsd() { Close(); } virtual RCX_Result Open(const char *name, int mode); virtual void Close(); virtual int GetCapabilities() const; virtual RCX_Result SetMode(int mode); virtual long Read(void *ptr, long count, long timeout_ms); virtual long Write(const void *ptr, long count); virtual bool IsUSB() const { return true; }; private: enum { kReadPacketSize = 8 }; IOReturn OpenDevice(short vendorID, short productID); IOReturn OpenInterface(); IOReturn ControlRequest(UInt8 request, UInt16 value); IOReturn ControlRequest(UInt8 request, UInt8 loByte, UInt8 hiByte) { return ControlRequest(request, loByte + (hiByte << 8)); } void SetTimeout(long timeout_ms); int fdCntrl; int fdRd; int fdWr; char *devname; }; RCX_Pipe* RCX_NewUSBTowerPipe() { return new RCX_USBTowerPipe_fbsd(); } RCX_USBTowerPipe_fbsd::RCX_USBTowerPipe_fbsd() : fdCntrl(0), devname(0) { } RCX_Result RCX_USBTowerPipe_fbsd::Open(const char *name, int mode) { IOReturn err; err = OpenDevice(kVendorID, kProductID); PREQUIRENOT(err, Fail_OpenDevice); err = OpenInterface(); PREQUIRENOT(err, Fail_OpenInterface); UInt8 range; range = (strcmp(name, "short")==0) ? LTW_RANGE_SHORT : LTW_RANGE_MEDIUM; ControlRequest(LTW_REQ_SET_PARM, LTW_PARM_RANGE,range); RCX_Result err2; err2 = SetMode(mode); if (err2 != kRCX_OK) { Close(); return err2; } return kRCX_OK; Fail_OpenInterface: Fail_OpenDevice: Close(); return kRCX_OpenSerialError; } void RCX_USBTowerPipe_fbsd::Close() { if (devname != NULL) { close(fdWr); close(fdRd); close(fdCntrl); devname = NULL; } } int RCX_USBTowerPipe_fbsd::GetCapabilities() const { return kNormalIrMode | kFastIrMode | kSpyboticsMode | kFastOddParityFlag | kAbsorb55Flag; } RCX_Result RCX_USBTowerPipe_fbsd::SetMode(int mode) { switch(mode) { case kNormalIrMode: ControlRequest(LTW_REQ_SET_TX_SPEED, SPEED_COMM_BAUD_2400); ControlRequest(LTW_REQ_SET_RX_SPEED, SPEED_COMM_BAUD_2400); return kRCX_OK; case kFastIrMode: ControlRequest(LTW_REQ_SET_PARM, LTW_PARM_RANGE, LTW_RANGE_SHORT); ControlRequest(LTW_REQ_SET_TX_SPEED, SPEED_COMM_BAUD_4800); ControlRequest(LTW_REQ_SET_RX_SPEED, SPEED_COMM_BAUD_4800); ControlRequest(LTW_REQ_SET_TX_CARRIER_FREQUENCY, 38); return kRCX_OK; case kSpyboticsMode: ControlRequest(LTW_REQ_SET_PARM, LTW_PARM_RANGE, LTW_RANGE_SHORT); ControlRequest(LTW_REQ_SET_TX_SPEED, SPEED_COMM_BAUD_4800); ControlRequest(LTW_REQ_SET_RX_SPEED, SPEED_COMM_BAUD_4800); return kRCX_OK; default: return kRCX_PipeModeError; } } #define MAX_PACKET 200 long RCX_USBTowerPipe_fbsd::Write(const void *ptr, long length) { const unsigned char *data = (const unsigned char *)ptr; int total = 0; // printf("Write %ld\n", length); while(length > 0) { IOReturn err; int count = length; if (count > MAX_PACKET) count = MAX_PACKET; err = write(fdWr, (void *)data, count); if (err == count) err = 0; PREQUIRENOT(err, Fail_WritePipe); length -= count; data += count; total += count; } Fail_WritePipe: // printf("Done %d\n", total); return total; } void RCX_USBTowerPipe_fbsd::SetTimeout(long timeout_ms) { usleep(timeout_ms * 1000 / 2); } long RCX_USBTowerPipe_fbsd::Read(void *data, long length, long timeout_ms) { const unsigned char *rdptr = (const unsigned char *)data; IOReturn err; int count; int total = 0; struct pollfd pfd; // SetTimeout(timeout_ms); // printf("Read %ld, timeout %ld\n", length, timeout_ms); while(length > 0) { pfd.fd = fdRd; pfd.events = POLLIN; usleep(timeout_ms * 1000 / 8); err = poll(&pfd, 1, timeout_ms); if (err == 0) break; if (err == -1) { if(errno == EINTR) continue; else break; } count = length; if (count > MAX_PACKET) count = MAX_PACKET; err = read(fdRd, (void *)rdptr, count); if (err == -1) break; // printf("err %d\n", err); if (err == 0 || err == -1) goto Fail_ReadPipe; count = err; length -= count; rdptr += count; total += count; } Fail_ReadPipe: // printf("Got %d\n", total); return total; } IOReturn RCX_USBTowerPipe_fbsd::OpenDevice(short vendorID, short productID) { char *dn = "/dev/ugen0"; int err, fd; usb_device_descriptor_t usbdd; fd = open(dn, O_RDWR); if (fd == -1) return errno; err = ioctl(fd, USB_GET_DEVICE_DESC, &usbdd); if (err == -1) { perror("ioctl error"); return errno; } // printf("idVendor %d\n", UGETW(usbdd.idVendor)); // printf("idProduct %d\n", UGETW(usbdd.idProduct)); if (UGETW(usbdd.idVendor) != kVendorID || UGETW(usbdd.idProduct) != kProductID) { fprintf(stderr, "This is not an USB IR Tower!\n"); return -1; } devname = dn; fdCntrl = fd; return 0; } IOReturn RCX_USBTowerPipe_fbsd::OpenInterface() { char *dn; int err, iov; dn = (char *)malloc(strlen(devname) + 2 + 1); strcpy(dn, devname); strcat(dn, ".1"); fdRd = open(dn, O_RDONLY|O_NONBLOCK); if (fdRd == -1) { perror(dn); return -1; } iov = 1; err = ioctl(fdRd, USB_SET_SHORT_XFER, &iov); if (fdRd == -1) { perror("ioctl USB_SET_SHORT_XFER"); return -1; } iov = 5000; err = ioctl(fdRd, USB_SET_TIMEOUT, &iov); if (fdRd == -1) { perror("ioctl USB_SET_TIMEOUT"); return -1; } strcpy(dn, devname); strcat(dn, ".2"); fdWr = open(dn, O_WRONLY); iov = 5000; // ioctl(fdWr, USB_SET_TIMEOUT, &iov); return 0; } IOReturn RCX_USBTowerPipe_fbsd::ControlRequest(UInt8 request, UInt16 value) { IOReturn err; struct usb_ctl_request req; LTW_REQ_GET_SET_PARM_REPLY reply; req.ucr_request.bmRequestType = UT_READ_VENDOR_DEVICE; req.ucr_request.bRequest = request; USETW(req.ucr_request.wValue, value); USETW(req.ucr_request.wIndex, 0); USETW(req.ucr_request.wLength, sizeof(reply)); req.ucr_data = &reply; err = ioctl(fdCntrl, USB_DO_REQUEST, &req); // size of actual reply in req.wLenDone //printf("%d %d %d\n", reply.wNoOfBytes, reply.bErrCode, reply.bValue); return err; }